diff --git a/.babelrc b/.babelrc deleted file mode 100644 index bb75829c..00000000 --- a/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ -"compact":false, -"presets": -["babel-preset-env"] -} diff --git a/.bootstraprc b/.bootstraprc index 1327a743..278db7d9 100644 --- a/.bootstraprc +++ b/.bootstraprc @@ -1,7 +1,6 @@ { "bootstrapVersion": 3, "styleLoaders": ["style", "css", "sass"], - "extractStyles": true, "styles": { "mixins": true, "grid": true, diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..e94f8140 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1 @@ +defaults diff --git a/.env.template b/.env.template index b6128da6..98a5b349 100644 --- a/.env.template +++ b/.env.template @@ -24,7 +24,7 @@ export MAILCHIMP_REDIRECT_URL='REPLACE' export FACEBOOK_APP_ID="REPLACE" -export CYPHER_KEY="REPLACE" # used for mailchimp integration +export CYPHER_KEY="-- secret string --" # used for mailchimp integration diff --git a/.github/workflows/full_build.yml b/.github/workflows/full_build.yml new file mode 100644 index 00000000..10bcec63 --- /dev/null +++ b/.github/workflows/full_build.yml @@ -0,0 +1,111 @@ +name: Houdini build +on: [push, pull_request] +jobs: + notice_ruby: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + - uses: actions/cache@v1 + name: Use Gem cache + with: + path: vendor/bundle + key: bundle-use-ruby-ubuntu-latest-2.6.6-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + bundle-use-ruby-ubuntu-latest-2.6.6- + - name: bundle install + run: | + bundle config deployment true + bundle config path vendor/bundle + bundle install --jobs 4 + - name: run notice:ruby:verify + run: | + bin/rails notice:ruby:verify + notice_js: + runs-on: ubuntu-latest + needs: notice_ruby + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + - name: Setup Node.js environment + uses: actions/setup-node@v1.4.2 + - uses: actions/cache@v1 + name: Use Node package cache + with: + path: node_modules + key: bundle-use-node-js-ubuntu-latest-12-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + bundle-use-node-js-ubuntu-latest-12- + - uses: actions/cache@v1 + name: Use Gem cache + with: + path: vendor/bundle + key: bundle-use-ruby-ubuntu-latest-2.6.6-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + bundle-use-ruby-ubuntu-latest-2.6.6- + - name: bundle install + run: | + bundle config deployment true + bundle config path vendor/bundle + bundle install --jobs 4 + - name: install node packages + run: yarn install --frozen-lockfile + - name: run notice:js:verify + run: | + bin/rails notice:js:verify + main_build: + needs: [notice_js] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + node: [12] + ruby: [2.6.6] + steps: + - uses: actions/checkout@v2 + - name: Setup PostgreSQL with PostgreSQL extensions and unprivileged user + uses: Daniel-Marynicz/postgresql-action@0.1.0 + with: + postgres_image_tag: 11-alpine + postgres_user: houdini_user + postgres_password: password + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0 + - name: Setup Node.js environment + uses: actions/setup-node@v1.4.2 + - uses: actions/cache@v1 + name: Use Gem cache + with: + path: vendor/bundle + key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby }}- + - name: bundle install + run: | + bundle config deployment true + bundle config path vendor/bundle + bundle install --jobs 4 + - uses: actions/cache@v1 + name: Use Node package cache + with: + path: node_modules + key: bundle-use-node-js-${{ matrix.os }}-${{ matrix.node }}-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + bundle-use-node-js-${{ matrix.os }}-${{ matrix.node }}- + - name: run setup + run: bin/setup ci + - name: run spec + run: bin/rails spec + - name: run jest + run: yarn jest \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2a4f41ce..3ee9fc10 100755 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,11 @@ custom_plan.rb # Ignore VCR cassettes spec/fixtures/vcr_cassettes +#dev files .env .idea/ .vscode +.byebug_history # Jasmine specs build file spec/javascripts/bundle.js @@ -46,9 +48,6 @@ config/environments/*.local.yml #local bins we use for builds .bin -#generated typescript APIs from Grape -javascripts/api - # thing we want to keep in public !public/*.html !public/maintenance.html @@ -64,3 +63,13 @@ javascripts/api !public/css/donate-button.v2.css !public/svgs !public/svgs/* + +/public/packs +/public/packs-test +/node_modules +/yarn-error.log +yarn-debug.log* +.yarn-integrity + +# we don't want the file storage directory to be committed +storage \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..63c13a93 --- /dev/null +++ b/.nvmrc @@ -0,0 +1,2 @@ +12.0 + diff --git a/.rspec b/.rspec index 83e16f80..0f30adeb 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,2 @@ --color ---require spec_helper +--require rails_helper diff --git a/.rspec_parallel b/.rspec_parallel new file mode 100644 index 00000000..54b33804 --- /dev/null +++ b/.rspec_parallel @@ -0,0 +1,2 @@ +--format progress +--format ParallelTests::RSpec::SummaryLogger --out tmp/spec_summary.log diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 00000000..460f55aa --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +fundraising diff --git a/.ruby-version b/.ruby-version index 00355e29..338a5b5d 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.3.7 +2.6.6 diff --git a/.solargraph.yml b/.solargraph.yml deleted file mode 100644 index 1f5c24cd..00000000 --- a/.solargraph.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -include: -- "**/*.rb" -exclude: -- spec/**/* -- test/**/* -- vendor/**/* -- ".bundle/**/*" -require: [] -domains: [] -reporters: -- rubocop -- require_not_found -plugins: [] -require_paths: [] -max_files: 5000 diff --git a/Gemfile b/Gemfile index c3149bf2..97f8765d 100755 --- a/Gemfile +++ b/Gemfile @@ -1,146 +1,108 @@ +# frozen_string_literal: true + source 'https://rubygems.org' -ruby '2.3.7' -gem 'rake', '~> 12.3.3' -gem 'rails', '3.2.22.5' -gem 'rails_12factor' -# https://stripe.com/docs/api -gem 'stripe' +ruby '2.6.6' +gem 'rails', '~> 6.0.3' +gem 'jbuilder', '~> 2.10' +gem 'bootsnap', '~> 1.4', require: false # Large rails application booting enhancer +gem 'font_assets', '~> 0.1.14' # for serving fonts on cdn https://github.com/ericallam/font_assets +gem 'hamster', '~> 3.0' # Thread-safe collection classes for Ruby +gem 'parallel', '~> 1.17' # run processes in parallel +gem 'puma_worker_killer', '~> 0.1.1' # TODO: Investigate why puma workers need to be killed. +gem 'puma', '~> 4.0', '>= 4.0.1' +gem 'rabl', '~> 0.14.1' # JSON serialization https://github.com/nesquena/rabl +gem 'rake', '~> 12.3.2' +gem 'sassc-rails', '~> 2.1', '>= 2.1.2' +gem 'sassc', '~> 2.0', '>= 2.0.1' +gem 'stripe', '~> 1.58' # January 19, 2017 version of the Stripe API https://stripe.com/docs/api +gem 'uglifier', '~> 4.1', '>= 4.1.20' +gem 'ffi', '~> 1.11', '>= 1.11.1' +gem 'webpacker', '~> 5.1.1' -# Compression of assets on heroku -# https://github.com/romanbsd/heroku-deflater -gem 'heroku-deflater', :group => :production +gem 'httparty', '~> 0.17.0' # https://github.com/jnunemaker/httparty +gem 'rack-attack', '~> 5.2' # for blocking ip addressses +gem 'rack-ssl', '~> 1.4' +gem 'sprockets', '~> 3.7' -# json serialization -# https://github.com/nesquena/rabl -gem 'rabl' +# AWS services +gem 'aws-sdk', '~> 1.67' +gem 'aws-ses', '~> 0.6.0' # REST email integration API -gem 'parallel' +# External Services +gem 'fullcontact', '~> 0.18.0' # Full Contact API; includes #Hashie::Mash -gem 'puma' -gem 'bootsnap', require: false -gem 'rack-timeout' -gem 'puma_worker_killer' - -gem 'test-unit', '~> 3.0' -gem 'hamster' - -gem 'aws-ses' -gem 'aws-sdk' - -# for blocking ip addressses -gem 'rack-attack' - -# For modularizing javascript -# https://github.com/browserify-rails/browserify-rails -gem 'browserify-rails' -gem 'sprockets' - -# for serving fonts on cdn -# https://github.com/ericallam/font_assets -gem 'font_assets', '~> 0.1.14' - -# Database (postgres) -gem 'pg' # Postgresql -gem 'qx', path: 'gems/ruby-qx' -gem 'dalli' -gem 'memcachier' +# Helpers +gem 'chronic', '~> 0.10.2' # For nat lang parsing of dates +gem 'colorize', '~> 0.8.1' # Print colorized text in debugger/console +gem 'countries', '~> 3.0' +gem 'geocoder', '~> 1.6.3' # for adding latitude and longitude to location-based tables http://www.rubygeocoder.com/ +gem 'i18n-js', '~> 3.3' +gem 'lograge', '~> 0.11.2' # make logging less terrible in rails +gem 'nearest_time_zone', '~> 0.0.4' # for detecting timezone from lat/lng https://github.com/buytruckload/nearest_time_zone +gem 'rails-i18n', '~> 6.0.0', '~> 6' +gem 'roadie-rails', '~> 2.1' # email generation helpers +gem 'table_print', '~> 1.5', '>= 1.5.6' # Nice table printing of data for the console +# Database and Events +gem 'bunny', '~> 2.14', '>= 2.14.2' # RabittMQ +gem 'pg', '~> 0.11' gem 'param_validation', path: 'gems/ruby-param-validation' - -# Print colorized text lol -gem 'colorize' - -# https://github.com/collectiveidea/delayed_job_active_record -gem 'delayed_job_active_record' - -# for styling emails -# https://github.com/Mange/roadie-rails -gem 'roadie-rails' - -# For nat lang parsing of dates -gem 'chronic' +gem 'qx', path: 'gems/ruby-qx' # Images -# https://github.com/carrierwaveuploader/carrierwave -gem 'carrierwave' -gem 'carrierwave-aws' # for uploading images to amazon s3 -gem 'mini_magick' - -# https://github.com/jnunemaker/httparty -gem 'httparty' +gem 'image_processing', '~> 1.10.3' # User authentication # https://github.com/plataformatec/devise -gem 'devise' -gem 'devise-async' - -# http://www.rubygeocoder.com/ -gem 'geocoder' # for adding latitude and longitude to location-based tables - -# https://github.com/buytruckload/nearest_time_zone -gem 'nearest_time_zone' # for detecting timezone from lat/lng - -gem 'mail_view' - -gem 'fullcontact' # Full Contact API; includes #Hashie::Mash - -# Nice table printing of data for the console -gem 'table_print' - -gem 'bunny', '>= 2.6.3' - -gem 'rails-i18n', '~> 3.0.0' # For 3.x -gem 'i18n-js' -gem 'countries' +gem 'devise-async', '~> 1.0' +gem 'devise', '~> 4.7' +# API Tools +gem 'config', '> 1.5' +gem 'dry-validation', '~> 0.13.3' # used only for config validation +gem 'foreman', '~> 0.87.1' +gem 'wisper', '~> 2.0' +gem 'wisper-activejob', '~> 1.0.0' group :development, :ci do - gem 'traceroute' - gem 'debase' - gem 'ruby-debug-ide' + gem 'debase', '~> 0.2.3' + gem 'ruby-debug-ide', '~> 0.7.0' + gem 'traceroute', '~> 0.8.0' end group :development, :ci, :test do - gem 'timecop' - gem 'pry' - #gem 'pry-byebug' - gem 'binding_of_caller' - gem 'rspec' - gem 'rspec-rails' - gem 'database_cleaner' - gem 'dotenv-rails' + gem 'binding_of_caller', '~> 0.8.0' + gem 'byebug', '~> 11.0', '>= 11.0.1' + gem 'dotenv-rails', '~> 2.7', '>= 2.7.5' + gem 'mail_view', '~> 2.0' + gem 'pry', '~> 0.12.2' + gem 'pry-byebug', '~> 3.7.0' gem 'ruby-prof', '0.15.9' - gem 'stripe-ruby-mock', '~> 2.4.1', :require => 'stripe_mock', git: 'https://github.com/commitchange/stripe-ruby-mock.git', :branch => '2.4.1' - gem 'factory_bot' - gem 'factory_bot_rails' - gem 'action_mailer_matchers' + gem 'standard', '~> 0.1.2' + gem 'rspec-rails', '~> 4.0.0' + gem 'rspec', '~> 3.9.0' + gem 'parallel_tests', '~> 2.32' + gem 'factory_bot_rails', '~> 5.0', '>= 5.0.2' + gem 'factory_bot', '~> 5.0', '>= 5.0.2' + gem 'listen' +end + +group :ci, :test do + gem 'action_mailer_matchers', '~> 1.2' + gem 'database_cleaner-active_record' gem 'simplecov', '~> 0.16.1', require: false - gem 'bundler', require: false + gem 'stripe-ruby-mock', '~> 2.4.1', require: 'stripe_mock', git: 'https://github.com/commitchange/stripe-ruby-mock.git', branch: '2.4.1' + gem 'test-unit', '~> 3.3' + gem 'timecop', '~> 0.9.1' + gem 'webmock', '~> 3.6', '>= 3.6.2' + gem 'wisper-rspec', '~> 1.1.0' end -group :test do - gem 'webmock' +group :production do + # Compression of assets on heroku + # https://github.com/romanbsd/heroku-deflater + gem 'heroku-deflater', '~> 0.6.3' + gem 'rack-timeout', '~> 0.5.1' end - -# Gems used for asset compilation -gem 'sass', '3.2.19' -gem 'sass-rails', '3.2.6' -gem 'uglifier' - -# make logging less terrible in rails -gem 'lograge' - -gem 'config', '> 1.5' -gem 'dry-validation' # used only for config validation - -gem 'foreman' - -gem 'grape', '~> 1.1.0' -gem 'grape-entity', git: 'https://github.com/ruby-grape/grape-entity.git', ref: '0e04aa561373b510c2486282979085eaef2ae663' -gem 'grape-swagger' -gem 'grape-swagger-entity' -gem 'grape_url_validator' -gem 'grape_logging' -gem 'grape_devise', path: 'gems/grape_devise' diff --git a/Gemfile.lock b/Gemfile.lock index 3f113970..a51e8511 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,23 +8,6 @@ GIT multi_json (~> 1.0) stripe (>= 1.31.0, <= 1.58.0) -GIT - remote: https://github.com/ruby-grape/grape-entity.git - revision: 0e04aa561373b510c2486282979085eaef2ae663 - ref: 0e04aa561373b510c2486282979085eaef2ae663 - specs: - grape-entity (0.7.1) - activesupport (>= 3.0.0) - multi_json (>= 1.3.2) - -PATH - remote: gems/grape_devise - specs: - grape_devise (0.1.1) - devise (>= 2.2.8, < 5) - grape (> 0.7) - rails (> 3.2, < 6) - PATH remote: gems/ruby-param-validation specs: @@ -41,466 +24,510 @@ PATH GEM remote: https://rubygems.org/ specs: - action_mailer_matchers (1.0.0) - actionmailer (3.2.22.5) - actionpack (= 3.2.22.5) - mail (~> 2.5.4) - actionpack (3.2.22.5) - activemodel (= 3.2.22.5) - activesupport (= 3.2.22.5) - builder (~> 3.0.0) - erubis (~> 2.7.0) - journey (~> 1.0.4) - rack (~> 1.4.5) - rack-cache (~> 1.2) - rack-test (~> 0.6.1) - sprockets (~> 2.2.1) - activemodel (3.2.22.5) - activesupport (= 3.2.22.5) - builder (~> 3.0.0) - activerecord (3.2.22.5) - activemodel (= 3.2.22.5) - activesupport (= 3.2.22.5) - arel (~> 3.0.2) - tzinfo (~> 0.3.29) - activeresource (3.2.22.5) - activemodel (= 3.2.22.5) - activesupport (= 3.2.22.5) - activesupport (3.2.22.5) - i18n (~> 0.6, >= 0.6.4) - multi_json (~> 1.0) - addressable (2.3.8) - amq-protocol (2.2.0) + action_mailer_matchers (1.2.0) + actioncable (6.0.3.1) + actionpack (= 6.0.3.1) + nio4r (~> 2.0) + websocket-driver (>= 0.6.1) + actionmailbox (6.0.3.1) + actionpack (= 6.0.3.1) + activejob (= 6.0.3.1) + activerecord (= 6.0.3.1) + activestorage (= 6.0.3.1) + activesupport (= 6.0.3.1) + mail (>= 2.7.1) + actionmailer (6.0.3.1) + actionpack (= 6.0.3.1) + actionview (= 6.0.3.1) + activejob (= 6.0.3.1) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (6.0.3.1) + actionview (= 6.0.3.1) + activesupport (= 6.0.3.1) + rack (~> 2.0, >= 2.0.8) + rack-test (>= 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.0.3.1) + actionpack (= 6.0.3.1) + activerecord (= 6.0.3.1) + activestorage (= 6.0.3.1) + activesupport (= 6.0.3.1) + nokogiri (>= 1.8.5) + actionview (6.0.3.1) + activesupport (= 6.0.3.1) + builder (~> 3.1) + erubi (~> 1.4) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (6.0.3.1) + activesupport (= 6.0.3.1) + globalid (>= 0.3.6) + activemodel (6.0.3.1) + activesupport (= 6.0.3.1) + activerecord (6.0.3.1) + activemodel (= 6.0.3.1) + activesupport (= 6.0.3.1) + activestorage (6.0.3.1) + actionpack (= 6.0.3.1) + activejob (= 6.0.3.1) + activerecord (= 6.0.3.1) + marcel (~> 0.3.1) + activesupport (6.0.3.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2, >= 2.2.2) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + amq-protocol (2.3.0) andand (1.3.3) - arel (3.0.3) - aws-sdk (1.66.0) - aws-sdk-v1 (= 1.66.0) - aws-sdk-v1 (1.66.0) + ast (2.4.0) + aws-sdk (1.67.0) + aws-sdk-v1 (= 1.67.0) + aws-sdk-v1 (1.67.0) json (~> 1.4) - nokogiri (>= 1.4.4) + nokogiri (~> 1) aws-ses (0.6.0) builder mail (> 2.2.5) mime-types xml-simple - axiom-types (0.1.1) - descendants_tracker (~> 0.0.4) - ice_nine (~> 0.11.0) - thread_safe (~> 0.3, >= 0.3.1) - bcrypt (3.1.11) - binding_of_caller (0.7.2) + bcrypt (3.1.13) + binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) - bootsnap (1.1.7) + bootsnap (1.4.4) msgpack (~> 1.0) - browserify-rails (0.9.3) - sprockets (~> 2.2) - builder (3.0.4) - bunny (2.7.1) - amq-protocol (>= 2.2.0) - carrierwave (0.10.0) - activemodel (>= 3.2.0) - activesupport (>= 3.2.0) - json (>= 1.7) - mime-types (>= 1.16) - carrierwave-aws (0.5.0) - aws-sdk (~> 1.58) - carrierwave (~> 0.7) + builder (3.2.4) + bunny (2.14.2) + amq-protocol (~> 2.3, >= 2.3.0) + byebug (11.0.1) chronic (0.10.2) coderay (1.1.2) - coercible (1.0.0) - descendants_tracker (~> 0.0.1) colorize (0.8.1) - concurrent-ruby (1.0.5) - config (1.7.0) + concurrent-ruby (1.1.6) + config (1.7.2) activesupport (>= 3.0) - deep_merge (~> 1.2.1) - dry-validation (>= 0.10.4) - countries (2.1.2) + deep_merge (~> 1.2, >= 1.2.1) + dry-validation (~> 0.12, >= 0.12.2, < 1.0.0) + countries (3.0.0) i18n_data (~> 0.8.0) - money (~> 6.9) sixarm_ruby_unaccent (~> 1.1) unicode_utils (~> 1.4) - crack (0.4.2) + crack (0.4.3) safe_yaml (~> 1.0.0) - css_parser (1.3.6) + crass (1.0.6) + css_parser (1.7.0) addressable - dalli (2.7.6) dante (0.2.0) - database_cleaner (1.6.1) - debase (0.2.2) + database_cleaner (1.8.5) + database_cleaner-active_record (1.8.0) + activerecord + database_cleaner (~> 1.8.0) + debase (0.2.4) debase-ruby_core_source (>= 0.10.2) - debase-ruby_core_source (0.10.3) - debug_inspector (0.0.2) + debase-ruby_core_source (0.10.5) + debug_inspector (0.0.3) deep_merge (1.2.1) - delayed_job (4.1.2) - activesupport (>= 3.0, < 5.1) - delayed_job_active_record (4.1.1) - activerecord (>= 3.0, < 5.1) - delayed_job (>= 3.0, < 5) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) - devise (3.5.10) + devise (4.7.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 3.2.6, < 5) + railties (>= 4.1.0) responders - thread_safe (~> 0.1) warden (~> 1.2.3) - devise-async (0.9.0) - devise (~> 3.2) - diff-lcs (1.2.5) - docile (1.3.1) - domain_name (0.5.20160615) + devise-async (1.0.0) + activejob (>= 5.0) + devise (>= 4.0) + diff-lcs (1.3) + docile (1.3.2) + domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.0.1) - dotenv-rails (2.0.1) - dotenv (= 2.0.1) - dry-configurable (0.7.0) + dotenv (2.7.5) + dotenv-rails (2.7.5) + dotenv (= 2.7.5) + railties (>= 3.2, < 6.1) + dry-configurable (0.8.3) concurrent-ruby (~> 1.0) - dry-container (0.6.0) + dry-core (~> 0.4, >= 0.4.7) + dry-container (0.7.2) concurrent-ruby (~> 1.0) dry-configurable (~> 0.1, >= 0.1.3) - dry-core (0.4.5) + dry-core (0.4.9) + concurrent-ruby (~> 1.0) + dry-equalizer (0.2.2) + dry-inflector (0.1.2) + dry-logic (0.6.1) concurrent-ruby (~> 1.0) - dry-equalizer (0.2.0) - dry-logic (0.4.2) - dry-container (~> 0.2, >= 0.2.6) dry-core (~> 0.2) dry-equalizer (~> 0.2) - dry-types (0.12.2) + dry-types (0.14.1) concurrent-ruby (~> 1.0) - dry-configurable (~> 0.1) dry-container (~> 0.3) - dry-core (~> 0.2, >= 0.2.1) + dry-core (~> 0.4, >= 0.4.4) dry-equalizer (~> 0.2) - dry-logic (~> 0.4, >= 0.4.2) - inflecto (~> 0.0.0, >= 0.0.2) - dry-validation (0.11.1) + dry-inflector (~> 0.1, >= 0.1.2) + dry-logic (~> 0.5, >= 0.5) + dry-validation (0.13.3) concurrent-ruby (~> 1.0) dry-configurable (~> 0.1, >= 0.1.3) dry-core (~> 0.2, >= 0.2.1) dry-equalizer (~> 0.2) - dry-logic (~> 0.4, >= 0.4.0) - dry-types (~> 0.12.0) - equalizer (0.0.11) - erubis (2.7.0) - execjs (2.5.2) - factory_bot (4.8.2) - activesupport (>= 3.0.0) - factory_bot_rails (4.8.2) - factory_bot (~> 4.8.2) - railties (>= 3.0.0) - faraday (0.9.1) + dry-logic (~> 0.5, >= 0.5.0) + dry-types (~> 0.14.0) + erubi (1.9.0) + execjs (2.7.0) + factory_bot (5.0.2) + activesupport (>= 4.2.0) + factory_bot_rails (5.0.2) + factory_bot (~> 5.0.2) + railties (>= 4.2.0) + faraday (0.11.0) multipart-post (>= 1.2, < 3) - faraday_middleware (0.9.1) - faraday (>= 0.7.4, < 0.10) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + ffi (1.11.1) font_assets (0.1.14) rack - foreman (0.84.0) - thor (~> 0.19.1) - fullcontact (0.9.0) - faraday (~> 0.9.0) - faraday_middleware (>= 0.9) + foreman (0.87.1) + fullcontact (0.18.0) + faraday (~> 0.11.0) + faraday_middleware (>= 0.10) hashie (>= 2.0, < 4.0) - plissken - geocoder (1.2.11) - get_process_mem (0.2.1) - grape (1.1.0) - activesupport - builder - mustermann-grape (~> 1.0.0) - rack (>= 1.3.0) - rack-accept - virtus (>= 1.0.0) - grape-swagger (0.28.0) - grape (>= 0.16.2) - grape-swagger-entity (0.2.3) - grape-entity (>= 0.5.0) - grape-swagger (>= 0.20.4) - grape_logging (1.8.0) - grape - rack - grape_url_validator (1.0.0) - grape (>= 0.12.0) + geocoder (1.6.3) + get_process_mem (0.2.4) + ffi (~> 1.0) + globalid (0.4.2) + activesupport (>= 4.2.0) hamster (3.0.0) concurrent-ruby (~> 1.0) - hashie (3.4.1) - heroku-deflater (0.5.3) + hashdiff (1.0.0) + hashie (3.6.0) + heroku-deflater (0.6.3) rack (>= 1.4.5) - hike (1.2.3) - http-cookie (1.0.2) + http-cookie (1.0.3) domain_name (~> 0.5) - httparty (0.13.3) - json (~> 1.8) + httparty (0.17.0) + mime-types (~> 3.0) multi_xml (>= 0.5.2) - i18n (0.9.5) + i18n (1.8.2) concurrent-ruby (~> 1.0) - i18n-js (3.0.2) - i18n (~> 0.6, >= 0.6.6) + i18n-js (3.3.0) + i18n (>= 0.6.6) i18n_data (0.8.0) - ice_nine (0.11.2) - inflecto (0.0.2) - journey (1.0.4) + image_processing (1.10.3) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) + jaro_winkler (1.5.3) + jbuilder (2.10.0) + activesupport (>= 5.0.0) json (1.8.6) - kdtree (0.3) - lograge (0.3.6) - actionpack (>= 3) - activesupport (>= 3) - railties (>= 3) - mail (2.5.5) - mime-types (~> 1.16) - treetop (~> 1.4.8) + kdtree (0.4) + listen (3.2.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + lograge (0.11.2) + actionpack (>= 4) + activesupport (>= 4) + railties (>= 4) + request_store (~> 1.0) + loofah (2.5.0) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + mail (2.7.1) + mini_mime (>= 0.1.1) mail_view (2.0.4) tilt - memcachier (0.0.2) - method_source (0.9.0) - mime-types (1.25.1) - mini_magick (4.9.5) - mini_portile2 (2.1.0) - money (6.10.0) - i18n (>= 0.6.4, < 1.0) - msgpack (1.2.0) + marcel (0.3.3) + mimemagic (~> 0.3.2) + method_source (0.9.2) + mime-types (3.2.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2019.0331) + mimemagic (0.3.5) + mini_magick (4.10.1) + mini_mime (1.0.2) + mini_portile2 (2.4.0) + minitest (5.14.1) + msgpack (1.3.1) multi_json (1.13.1) - multi_xml (0.5.5) - multipart-post (2.0.0) - mustermann (1.0.3) - mustermann-grape (1.0.0) - mustermann (~> 1.0.0) + multi_xml (0.6.0) + multipart-post (2.1.1) nearest_time_zone (0.0.4) andand kdtree require_all netrc (0.11.0) - nokogiri (1.6.8.1) - mini_portile2 (~> 2.1.0) + nio4r (2.5.2) + nokogiri (1.10.9) + mini_portile2 (~> 2.4.0) orm_adapter (0.5.0) - parallel (1.6.1) - pg (0.18.3) - plissken (0.2.0) - symbolize (~> 4.2) - polyglot (0.3.5) - power_assert (1.1.1) - pry (0.11.3) + parallel (1.17.0) + parallel_tests (2.32.0) + parallel + parser (2.6.3.0) + ast (~> 2.4.0) + pg (0.21.0) + power_assert (1.1.4) + pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) - puma (3.12.4) - puma_worker_killer (0.1.0) + pry-byebug (3.7.0) + byebug (~> 11.0) + pry (~> 0.10) + public_suffix (3.1.1) + puma (4.1.0) + nio4r (~> 2.0) + puma_worker_killer (0.1.1) get_process_mem (~> 0.2) - puma (>= 2.7, < 4) - rabl (0.11.6) + puma (>= 2.7, < 5) + rabl (0.14.1) activesupport (>= 2.3.14) - rack (1.4.7) - rack-accept (0.4.5) - rack (>= 0.4) - rack-attack (4.2.0) + rack (2.2.2) + rack-attack (5.4.2) + rack (>= 1.0, < 3) + rack-proxy (0.6.5) rack - rack-cache (1.7.2) - rack (>= 0.4) - rack-ssl (1.3.4) + rack-ssl (1.4.1) rack - rack-test (0.6.3) - rack (>= 1.0) - rack-timeout (0.4.2) - rails (3.2.22.5) - actionmailer (= 3.2.22.5) - actionpack (= 3.2.22.5) - activerecord (= 3.2.22.5) - activeresource (= 3.2.22.5) - activesupport (= 3.2.22.5) - bundler (~> 1.0) - railties (= 3.2.22.5) - rails-i18n (3.0.1) - i18n (~> 0.5) - rails (>= 3.0.0, < 4.0.0) - rails_12factor (0.0.3) - rails_serve_static_assets - rails_stdout_logging - rails_serve_static_assets (0.0.4) - rails_stdout_logging (0.0.3) - railties (3.2.22.5) - actionpack (= 3.2.22.5) - activesupport (= 3.2.22.5) - rack-ssl (~> 1.3.2) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rack-timeout (0.5.1) + rails (6.0.3.1) + actioncable (= 6.0.3.1) + actionmailbox (= 6.0.3.1) + actionmailer (= 6.0.3.1) + actionpack (= 6.0.3.1) + actiontext (= 6.0.3.1) + actionview (= 6.0.3.1) + activejob (= 6.0.3.1) + activemodel (= 6.0.3.1) + activerecord (= 6.0.3.1) + activestorage (= 6.0.3.1) + activesupport (= 6.0.3.1) + bundler (>= 1.3.0) + railties (= 6.0.3.1) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.3) + activesupport (>= 4.2.0) + nokogiri (>= 1.6) + rails-html-sanitizer (1.3.0) + loofah (~> 2.3) + rails-i18n (6.0.0) + i18n (>= 0.7, < 2) + railties (>= 6.0.0, < 7) + railties (6.0.3.1) + actionpack (= 6.0.3.1) + activesupport (= 6.0.3.1) + method_source rake (>= 0.8.7) - rdoc (~> 3.4) - thor (>= 0.14.6, < 2.0) + thor (>= 0.20.3, < 2.0) + rainbow (3.0.0) rake (12.3.3) - rdoc (3.12.2) - json (~> 1.4) - require_all (1.3.2) - responders (1.1.2) - railties (>= 3.2, < 4.2) - rest-client (1.8.0) + rb-fsevent (0.10.4) + rb-inotify (0.10.1) + ffi (~> 1.0) + request_store (1.4.1) + rack (>= 1.4) + require_all (2.0.0) + responders (3.0.0) + actionpack (>= 5.0) + railties (>= 5.0) + rest-client (2.0.2) http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 3.0) - netrc (~> 0.7) - roadie (3.0.4) - css_parser (~> 1.3.4) - nokogiri (~> 1.6.0) - roadie-rails (1.0.5) - railties (>= 3.0, < 4.3) - roadie (~> 3.0) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.1) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + roadie (3.5.0) + css_parser (~> 1.4) + nokogiri (~> 1.8) + roadie-rails (2.1.0) + railties (>= 5.1, < 6.1) + roadie (~> 3.1) + rspec (3.9.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-core (3.9.2) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-mocks (3.5.0) + rspec-support (~> 3.9.0) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-rails (3.5.0) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - ruby-debug-ide (0.6.1) + rspec-support (~> 3.9.0) + rspec-rails (4.0.1) + actionpack (>= 4.2) + activesupport (>= 4.2) + railties (>= 4.2) + rspec-core (~> 3.9) + rspec-expectations (~> 3.9) + rspec-mocks (~> 3.9) + rspec-support (~> 3.9) + rspec-support (3.9.3) + rubocop (0.72.0) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.6) + rainbow (>= 2.2.2, < 4.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 1.7) + rubocop-performance (1.4.1) + rubocop (>= 0.71.0) + ruby-debug-ide (0.7.0) rake (>= 0.8.1) ruby-prof (0.15.9) - safe_yaml (1.0.4) - sass (3.2.19) - sass-rails (3.2.6) - railties (~> 3.2.0) - sass (>= 3.1.10) - tilt (~> 1.3) + ruby-progressbar (1.10.1) + ruby-vips (2.0.17) + ffi (~> 1.9) + safe_yaml (1.0.5) + sassc (2.0.1) + ffi (~> 1.9) + rake + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt + semantic_range (2.3.0) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) sixarm_ruby_unaccent (1.2.0) - sprockets (2.2.3) - hike (~> 1.2) - multi_json (~> 1.0) - rack (~> 1.0) - tilt (~> 1.1, != 1.3.0) - stripe (1.49.0) - rest-client (>= 1.4, < 3.0) - symbolize (4.5.2) - activemodel (>= 3.2, < 5) - activesupport (>= 3.2, < 5) - i18n - table_print (1.5.4) - test-unit (3.2.7) + sprockets (3.7.2) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.2.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + standard (0.1.2) + rubocop (~> 0.72.0) + rubocop-performance (~> 1.4.0) + stripe (1.58.0) + rest-client (>= 1.4, < 4.0) + table_print (1.5.6) + test-unit (3.3.3) power_assert - thor (0.19.4) + thor (1.0.1) thread_safe (0.3.6) - tilt (1.4.1) - timecop (0.7.3) - traceroute (0.5.0) + tilt (2.0.9) + timecop (0.9.1) + traceroute (0.8.0) rails (>= 3.0.0) - treetop (1.4.15) - polyglot - polyglot (>= 0.3.1) - tzinfo (0.3.54) - uglifier (2.7.1) - execjs (>= 0.3.0) - json (>= 1.8.0) + tzinfo (1.2.7) + thread_safe (~> 0.1) + uglifier (4.1.20) + execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext - unf_ext (0.0.7.2) + unf_ext (0.0.7.6) + unicode-display_width (1.6.0) unicode_utils (1.4.0) - virtus (1.0.5) - axiom-types (~> 0.1) - coercible (~> 1.0) - descendants_tracker (~> 0.0, >= 0.0.3) - equalizer (~> 0.0, >= 0.0.9) - warden (1.2.7) - rack (>= 1.0) - webmock (1.21.0) + warden (1.2.8) + rack (>= 2.0.6) + webmock (3.6.2) addressable (>= 2.3.6) crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + webpacker (5.1.1) + activesupport (>= 5.2) + rack-proxy (>= 0.6.1) + railties (>= 5.2) + semantic_range (>= 2.3.0) + websocket-driver (0.7.1) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.4) + wisper (2.0.1) + wisper-activejob (1.0.0) + activejob (>= 4.0.0) + wisper + wisper-rspec (1.1.0) xml-simple (1.1.5) + zeitwerk (2.3.0) PLATFORMS ruby DEPENDENCIES - action_mailer_matchers - aws-sdk - aws-ses - binding_of_caller - bootsnap - browserify-rails - bundler - bunny (>= 2.6.3) - carrierwave - carrierwave-aws - chronic - colorize + action_mailer_matchers (~> 1.2) + aws-sdk (~> 1.67) + aws-ses (~> 0.6.0) + binding_of_caller (~> 0.8.0) + bootsnap (~> 1.4) + bunny (~> 2.14, >= 2.14.2) + byebug (~> 11.0, >= 11.0.1) + chronic (~> 0.10.2) + colorize (~> 0.8.1) config (> 1.5) - countries - dalli - database_cleaner - debase - delayed_job_active_record - devise - devise-async - dotenv-rails - dry-validation - factory_bot - factory_bot_rails + countries (~> 3.0) + database_cleaner-active_record + debase (~> 0.2.3) + devise (~> 4.7) + devise-async (~> 1.0) + dotenv-rails (~> 2.7, >= 2.7.5) + dry-validation (~> 0.13.3) + factory_bot (~> 5.0, >= 5.0.2) + factory_bot_rails (~> 5.0, >= 5.0.2) + ffi (~> 1.11, >= 1.11.1) font_assets (~> 0.1.14) - foreman - fullcontact - geocoder - grape (~> 1.1.0) - grape-entity! - grape-swagger - grape-swagger-entity - grape_devise! - grape_logging - grape_url_validator - hamster - heroku-deflater - httparty - i18n-js - lograge - mail_view - memcachier - mini_magick - nearest_time_zone - parallel + foreman (~> 0.87.1) + fullcontact (~> 0.18.0) + geocoder (~> 1.6.3) + hamster (~> 3.0) + heroku-deflater (~> 0.6.3) + httparty (~> 0.17.0) + i18n-js (~> 3.3) + image_processing (~> 1.10.3) + jbuilder (~> 2.10) + listen + lograge (~> 0.11.2) + mail_view (~> 2.0) + nearest_time_zone (~> 0.0.4) + parallel (~> 1.17) + parallel_tests (~> 2.32) param_validation! - pg - pry - puma - puma_worker_killer + pg (~> 0.11) + pry (~> 0.12.2) + pry-byebug (~> 3.7.0) + puma (~> 4.0, >= 4.0.1) + puma_worker_killer (~> 0.1.1) qx! - rabl - rack-attack - rack-timeout - rails (= 3.2.22.5) - rails-i18n (~> 3.0.0) - rails_12factor - rake (~> 12.3.3) - roadie-rails - rspec - rspec-rails - ruby-debug-ide + rabl (~> 0.14.1) + rack-attack (~> 5.2) + rack-ssl (~> 1.4) + rack-timeout (~> 0.5.1) + rails (~> 6.0.3) + rails-i18n (~> 6.0.0, ~> 6) + rake (~> 12.3.2) + roadie-rails (~> 2.1) + rspec (~> 3.9.0) + rspec-rails (~> 4.0.0) + ruby-debug-ide (~> 0.7.0) ruby-prof (= 0.15.9) - sass (= 3.2.19) - sass-rails (= 3.2.6) + sassc (~> 2.0, >= 2.0.1) + sassc-rails (~> 2.1, >= 2.1.2) simplecov (~> 0.16.1) - sprockets - stripe + sprockets (~> 3.7) + standard (~> 0.1.2) + stripe (~> 1.58) stripe-ruby-mock (~> 2.4.1)! - table_print - test-unit (~> 3.0) - timecop - traceroute - uglifier - webmock + table_print (~> 1.5, >= 1.5.6) + test-unit (~> 3.3) + timecop (~> 0.9.1) + traceroute (~> 0.8.0) + uglifier (~> 4.1, >= 4.1.20) + webmock (~> 3.6, >= 3.6.2) + webpacker (~> 5.1.1) + wisper (~> 2.0) + wisper-activejob (~> 1.0.0) + wisper-rspec (~> 1.1.0) RUBY VERSION - ruby 2.3.7p456 + ruby 2.6.6p146 BUNDLED WITH 1.17.3 diff --git a/Procfile b/Procfile index d6f09003..19810b76 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,2 @@ web: bundle exec puma -C ./config/puma.rb -worker: bundle exec rake jobs:work diff --git a/README.md b/README.md index 72937dcd..5cd868aa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ [![](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://houdini.zulipchat.com) [![Build Status](https://travis-ci.com/houdiniproject/houdini.svg?branch=master)](https://travis-ci.com/houdiniproject/houdini) +* NOTE: This is the latest version (pre-2.0) of Houdini and +is currently in HEAVY development. You may want +to use +[v1](https://github.com/houdiniproject/houdini/tree/1-0-stable) +instead. + + The Houdini Project is free and open source fundraising infrastructure. It includes... - Crowdfunding campaigns - Donate widget page and generator @@ -12,15 +19,19 @@ The Houdini Project is free and open source fundraising infrastructure. It inclu - Nonprofit org user account management - Simple donation management for donors -This is a Rails 3.2 app; [we want to upgrade](https://github.com/houdiniproject/houdini/issues/47). - -Much of the business logic is in `/lib`. - The frontend is written in a few custom frameworks, the largest of which is called Flimflam. We endeavor to migrate to React as quickly as possible to increase development comfort and speed. -All backend code and React components should be TDD. +All new backend code and React components well tested. + +## Prerequisites +Houdini is designed and tested to run with the following: +* Ruby 2.6 +* Node 12 +* Yarn +* PostgreSQL 11 +* Ubuntu 20.04 or equivalent ## Get involved Houdini's success depends on you! @@ -30,82 +41,119 @@ https://houdini.zulipchat.com ### Help with translations Visit the Internationalization channel on Houdini Zulip and discuss -## Dev Setup -#### Get the code +## Dev Setup +#### Tips for specific circumstances +* Docker: Docker was previously used for development of Houdini. +See [docker.md](docs/docker.md) for more info. +* Mac: Mac dev setup may require some unique configuration. +See [mac_getting_started.md](docs/mac_getting_started.md) for more info. + +### Installation prep +Houdini requires a few pieces of software be installed, as well as some optional pieces +which make development much easier. + +These include: + +* PostgreSQL 11 +* NodeJS 12 LTS +* Ruby 2.6.6 (NOTE: the default of Ruby 2.7.1 in Debian should +function but you will receive a ton of deprecation +warnings from Ruby) + +There a few optional tools which make working on Houdini +easiter +* Ruby Version Manager (RVM) - RVM makes it simple to switch +between versions of Ruby for different projects. Additionally, you can +use different "gemsets" per version so you can separate the +state of a set of different projects. It will also switch +versions at the console when you change to a directory for +an project prepared for RVM, like Houdini. +* Automatic Version Switching for Node (AVN) - similar to RVM, AVN makes it simple to switch between versions of Node. When +properly configured, it automatically switches version at +the console whe you change to a directory for a project +prepared for AVN, like Houdini. + +#### One-time setup + +You'll want to run the next commands as root or via sudo. You could do this by typing `sudo /bin/sh` running the commands from there. + +TIP: this is the root shell. There's no restrictions on what you do here so be careful! +```bash +apt update +apt install curl -yy +curl -sL https://deb.nodesource.com/setup_12.x | bash - +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list +apt update +apt install git postgresql-11 libpq-dev libjemalloc-dev libvips42 yarn -yy +``` + +You'll run the next commands as your normal user. + +NOTE: in the case of a production instance, this might be +your web server's user. + +NOTE 2: We use [RVM](https://rvm.io) to have more control over the exact version of Ruby. For development, it's also way easier because you can +use a consistent version of Ruby (and different sets of installed gems) for different projects. You could also use rbenv +or simply build ruby from source. + +NOTE 3: We don't recommend using Ruby 2.7, the current Ubuntu default at this time. Ruby 2.7 will function but spits out tons +of deprecation warnings when using Rails applications. + +NOTE 4: We recommend building Ruby with jemalloc support as we +do in these instructions. In practice, it manages memory far +more efficiently in Rails-based projects. + +TIP: To get out of the root shell, run `exit` + +```bash +# add rvm keys +curl -sSL https://rvm.io/mpapis.asc | gpg --import - +curl -sSL https://rvm.io/pkuczynski.asc | gpg --import - +curl -sSL https://get.rvm.io | bash -s stable +source $HOME/.rvm/scripts/rvm +echo 'source "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc +rvm install 2.6.6 --disable-binary --with-jemalloc +``` + + Run the following command as the `postgres` user and then enter your houdini_user + password at the prompt. + +NOTE: For development, Houdini expects the password to be 'password'. This would be terrible +for production but for development, it's likely not a huge issue. + +TIP: To run this, add `sudo -u postgres ` to the beginning of the following command. + +`createuser houdini_user -s -d -P` + +Now that we have all of our prerequisites prepared, we need to get the Houdini code. + `git clone https://github.com/HoudiniProject/houdini` -#### Docker install (if you don't have docker and docker-compose installed) -##### install Docker and Docker compose -You need to install Docker and Docker Compose. -* *Note:* Docker and Docker Compose binaries from Docker itself are proprietary software based entirely upon -free software. If you feel more comfortable, you may build them from source. +This will download the latest Houdini code. Change to the +`houdini` directory and we can set the rest of Houdini up. -* *Note 2:* For Debian, the Docker package is simply too out of date to be usable. -Even the version for latest Ubuntu LTS is too old. For reliability, we strongly -recommend using the Docker debian feed from docker itself OR making sure you keep your -own build up to date. +Let's run the Houdini project setup and we'll be ready to go! -##### Add yourself to the docker group -Adding yourself as a Docker group user as follows: - -`sudo usermod -aG docker $USER` - -You will likely need to logout and log back in again. - -#### Build your docker-container and start it up for initial set up. -We'll keep this running in the console we'll call **console 1** -``` -./dc build -./dc up -``` -#### System configuration -There are a number of steps for configuring your Houdini instance for startup -##### Start a new console we'll call **console 2**. - -##### In console 2, copy the env template to your .env file - ``` - cp .env.template .env - ``` -##### In console 2, run the following and copy the output to you .env file to set you `DEVISE_SECRET_KEY` environment variable. -`./run rake secret # copy this result into your DEVISE_SECRET_KEY` - -##### In console 2, , run the following and copy the output to you .env file to set you `SECRET_TOKEN` environment variable. -``` -./run rake secret # copy this result into your SECRET_TOKEN +```bash +bin/setup ``` -##### Set the following secrets in your .env file with your Stripe account information -- `STRIPE_API_KEY` with your Stripe PRIVATE key -- `STRIPE_API_PUBLIC` with your Stripe PUBLIC key +NOTE: The .env file holds your environment variables for development; on production you might +have these set somewhere else other than this file. -##### You SHOULD set your AMAZON s3 information (optional but STRONGLY recommended) -If you don't, file uploads WILL NOT WORK but it's not required. +TIP: On Heroku, the environment variables are set in your Dashboard. -##### In console 2, install npm packages -`./run npm install` - -##### In console 2, fill the db -`./run rake db:create db:structure:load db:seed test:prepare` - -##### Set up mailer info -You can set this in `config/default_organization.yml` or better yet, make a copy with your own org name and add that to your .env file as `ORG_NAME` -If you need help setting up your mailer, visit `config/environment.rb` where the settings schema is verified and documented. +Also, you should set the STRIPE_API_KEY and STRIPE_API_PUBLIC +environment variables which you'd get from the Stripe +dashboard. On your development environment, +make sure to use test keys. If you don't, you're +going to be charged real money! #### Startup -##### Switch back to console 1 and run `Ctrl-c` to end the session. - -##### In console 1, restart the containers -`./dc up` - -##### In console 2, run: -`./run npm run watch` - -##### You can go to http://localhost:5000 - -To get started, register your nonprofit using the "Get Started" link. - -## Additional info +`bin/rails server` +You can connect to your server at http://localhost:5000 ##### Super admin There is a way to set your user as a super_admin. This role lets you access any of the nonprofits @@ -114,7 +162,7 @@ nonprofits, which is located at `/admin` url. To create the super user, go to the rails console by calling: -`./dc run web rails console` +`bin/rails console` In the console, run the following: @@ -123,37 +171,23 @@ admin=User.find(1) #or the id of the user you want to add the role role=Role.create(user:admin,name: "super_admin") ``` - -## To run in production - -##### Docker -While Docker should be very possible to use for production, the current Docker solution -is optimized heavily for dev purposes. If you know more about creating a solid production Docker setup, please do -contribute! - -(To be continued) -- rake assets:precompile -- if production: make sure memcached is running. +## Known Issues +For a list of [how to solve known issues](docs/KNOWN_ISSUES.MD) -## Frontend +## Run in production +You will likely want to make a few changes in your configuration of Houdini before running in production as you +would for any Rails project. These include: -Assets get compiled from `/client` to `/public/client` - -## React Generators -If creating new React or Typescript code, please use the Rails generators with the 'react:' prefix. This include: - -### react:packroot -This generator creates a new entry for Webpack. This is a place where Webpack will start -when packing a new javascript output file. It also creates a corresponding component for the entry. -Usually, you will have one of these per page. - -### react:component -This generator creates a React component along with a test file for testing with Jest. -Each component should have its own file. - -### react:lib -This generator creates a basic Typescript module along with a test file. +* Using a [different ActiveJob backend](https://guides.rubyonrails.org/active_job_basics.html). NOTE: The Sneakers for RabbitMQ doesn't +work properly. There are +[forks of Sneakers](https://github.com/veeqo/advanced-sneakers-activejob) +which might work but they haven't been tested. **If you do test +them please let us know!** +* Use a [proper cache store](https://guides.rubyonrails.org/caching_with_rails.html#cache-stores). The development uses + `memory_store` which isn't shared between processes or server + and clears every time your server software restarts. Memcached + or Redis are good choices here. ### Providing the complete corresponding source code @@ -170,24 +204,4 @@ For this to work though, the following characteristics must be true: * Your have to have committed any changes you made to the project in `HEAD` in your git repository * The `.git` folder for your repository must be a direct subfolder of your `$RAILS_ROOT` -* Your web server must be able to run `git archive`. - - -### Style - -#### Ruby -- 2 spaces for tabs - -#### New frontend code -- All new front end code should be written in Typescript -and React (using TSX files). Please use the React Generators for creation. -- 2 spaces for tabs - -#### Legacy Javascript -- 2 spaces for tabs -- Comma-led lines -- ES6 imports - -#### Git - -- No need to rebase, just merge +* Your web server must be able to run `git archive`. \ No newline at end of file diff --git a/Rakefile b/Rakefile index c099dee5..9a148353 100755 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,9 @@ #!/usr/bin/env rake +# frozen_string_literal: true + # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. -require File.expand_path('../config/application', __FILE__) +require File.expand_path('config/application', __dir__) Commitchange::Application.load_tasks diff --git a/app/api/houdini/v1/api.rb b/app/api/houdini/v1/api.rb deleted file mode 100644 index d0e3a921..00000000 --- a/app/api/houdini/v1/api.rb +++ /dev/null @@ -1,22 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'houdini/v1/validations' -class Houdini::V1::API < Grape::API - logger.formatter = GrapeLogging::Formatters::Rails.new - use GrapeLogging::Middleware::RequestLogger, { logger: logger } - content_type :json, 'application/json' - default_format :json - rescue_from Grape::Exceptions::ValidationErrors do |e| - output = {errors: e} - error! output, 400 - end - - #include Houdini::V1::Helpers::ApplicationHelper - mount Houdini::V1::Nonprofit => '/nonprofit' - # Additional mounts are added via generators above this line - # DON'T REMOVE THIS OR THE PREVIOUS LINES!!! - uri_for_host = URI.parse(Settings.api_domain&.url || Settings.cdn.url) - add_swagger_documentation \ - host: "#{uri_for_host.host}#{uri_for_host.port ? ":#{uri_for_host.port}" : ""}", - schemes: [uri_for_host.scheme], - base_path: '/api/v1' -end \ No newline at end of file diff --git a/app/api/houdini/v1/base_api.rb b/app/api/houdini/v1/base_api.rb deleted file mode 100644 index 17a5dd34..00000000 --- a/app/api/houdini/v1/base_api.rb +++ /dev/null @@ -1,30 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::BaseAPI < Grape::API - #helpers ApplicationHelper - # helpers do - # def session - # env['rack.session'] - # end - # - # def protect_against_forgery - # unless verified_request? - # error!('Unauthorized', 401) - # end - # end - # - # def verified_request? - # !protect_against_forgery? || request.get? || request.head? || - # form_authenticity_token == request.headers['X-CSRF-Token'] || - # form_authenticity_token == request.headers['X-Csrf-Token'] - # end - # - # def form_authenticity_token - # session[:_csrf_token] ||= SecureRandom.base64(32) - # end - # - # def protect_against_forgery? - # allow_forgery_protection = Rails.configuration.action_controller.allow_forgery_protection - # allow_forgery_protection.nil? || allow_forgery_protection - # end - # end -end \ No newline at end of file diff --git a/app/api/houdini/v1/entities/validation_error.rb b/app/api/houdini/v1/entities/validation_error.rb deleted file mode 100644 index af9d7342..00000000 --- a/app/api/houdini/v1/entities/validation_error.rb +++ /dev/null @@ -1,5 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Entities::ValidationError < Grape::Entity - expose :params, documentation: {type: 'String', desc: 'Params where the following had an error.', is_array: true} - expose :messages, documentation: {type:'String', desc: 'The validation messages for the params', is_array: true} -end \ No newline at end of file diff --git a/app/api/houdini/v1/entities/validation_errors.rb b/app/api/houdini/v1/entities/validation_errors.rb deleted file mode 100644 index 07f03c73..00000000 --- a/app/api/houdini/v1/entities/validation_errors.rb +++ /dev/null @@ -1,4 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Entities::ValidationErrors < Grape::Entity - expose :errors, documentation: {type: ValidationError, desc: 'errors', is_array:true} -end \ No newline at end of file diff --git a/app/api/houdini/v1/helpers/application_helper.rb b/app/api/houdini/v1/helpers/application_helper.rb deleted file mode 100644 index 0c1345d6..00000000 --- a/app/api/houdini/v1/helpers/application_helper.rb +++ /dev/null @@ -1,45 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Houdini::V1::Helpers::ApplicationHelper - extend Grape::API::Helpers - - - def session - env['rack.session'] - end - - def protect_against_forgery - unless verified_request? - error!('Unauthorized', 401) - end - end - - def verified_request? - !protect_against_forgery? || request.get? || request.head? || - form_authenticity_token == request.headers['X-CSRF-Token'] || - form_authenticity_token == request.headers['X-Csrf-Token'] - end - - def form_authenticity_token - session[:_csrf_token] ||= SecureRandom.base64(32) - end - - def protect_against_forgery? - allow_forgery_protection = Rails.configuration.action_controller.allow_forgery_protection - allow_forgery_protection.nil? || allow_forgery_protection - end - - - # def rescue_ar_invalid( *class_to_hash) - # rescue_with ActiveRecord::RecordInvalid do |error| - # output = [] - # error.record.errors do |attr,message| - # output.push({params: "#{class_to_hash[error.record.class]}['#{attr}']", - # message: message}) - # end - # raise Grape::Exceptions::ValidationErrors.new(output) - # - # end - # end - -end - diff --git a/app/api/houdini/v1/helpers/rescue_helper.rb b/app/api/houdini/v1/helpers/rescue_helper.rb deleted file mode 100644 index 458343e3..00000000 --- a/app/api/houdini/v1/helpers/rescue_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Houdini::V1::Helpers::RescueHelper - require 'active_support/concern' - - extend ActiveSupport::Concern - include Grape::DSL::Configuration - module ClassMethods - def rescue_ar_invalid( *class_to_hash) - rescue_with ActiveRecord::RecordInvalid do |error| - output = [] - error.record.errors do |attr,message| - output.push({params: "#{class_to_hash[error.record.class]}['#{attr}']", - message: message}) - end - raise Grape::Exceptions::ValidationErrors.new(output) - - end - end - end -end \ No newline at end of file diff --git a/app/api/houdini/v1/nonprofit.rb b/app/api/houdini/v1/nonprofit.rb deleted file mode 100644 index e01bc7cc..00000000 --- a/app/api/houdini/v1/nonprofit.rb +++ /dev/null @@ -1,120 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Nonprofit < Houdini::V1::BaseAPI - helpers Houdini::V1::Helpers::ApplicationHelper, Houdini::V1::Helpers::RescueHelper - - before do - protect_against_forgery - end - - desc 'Return a nonprofit.' do - success Houdini::V1::Entities::Nonprofit - end - params do - requires :id, type: Integer, desc: 'Status id.' - end - route_param :id do - get do - np = Nonprofit.find(params[:id]) - present np, as: Houdini::V1::Entities::Nonprofit - end - end - - desc 'Register a nonprofit' do - success Houdini::V1::Entities::Nonprofit - - #this needs to be a validation an array - failure [{code:400, message:'Validation Errors', model: Houdini::V1::Entities::ValidationErrors}] - end - - params do - - requires :nonprofit, type: Hash do - requires :name, type:String, desc: 'Organization Name', allow_blank: false, documentation: { param_type: 'body' } - optional :website, type:String, desc: 'Organization website URL', allow_blank:true, regexp: URI::regexp, documentation: { param_type: 'body' }, coerce_with: ->(url) { - coerced_url = url - unless (url =~ /\Ahttp:\/\/.*/i || url =~ /\Ahttps:\/\/.*/i) - coerced_url = 'http://'+ coerced_url - end - coerced_url - } - requires :zip_code, type:String, allow_blank: false, desc: "Organization Address ZIP Code", documentation: { param_type: 'body' } - requires :state_code, type:String, allow_blank: false, desc: "Organization Address State Code", documentation: { param_type: 'body' } - requires :city, type:String, allow_blank: false, desc: "Organization Address City", documentation: { param_type: 'body' } - optional :email, type:String, desc: 'Organization email (public)', regexp: Email::Regex, documentation: { param_type: 'body' } - optional :phone, type:String, desc: 'Organization phone (public)', documentation: { param_type: 'body' } - end - - requires :user, type: Hash do - requires :name, type:String, desc: 'Full name', allow_blank:false, documentation: { param_type: 'body' } - requires :email, type:String, desc: 'Username', allow_blank: false, documentation: { param_type: 'body' } - requires :password, type:String, desc: 'Password', allow_blank: false, is_equal_to: :password_confirmation, documentation: { param_type: 'body' } - requires :password_confirmation, type:String, desc: 'Password confirmation', allow_blank: false, documentation: { param_type: 'body' } - end - - - end - post do - declared_params = declared(params) - np = nil - u = nil - Qx.transaction do - begin - np = Nonprofit.new(OnboardAccounts.set_nonprofit_defaults(declared_params[:nonprofit])) - - begin - np.save! - rescue ActiveRecord::RecordInvalid => e - if (e.record.errors[:slug]) - begin - slug = SlugNonprofitNamingAlgorithm.new(np.state_code_slug, np.city_slug).create_copy_name(np.slug) - np.slug = slug - np.save! - rescue UnableToCreateNameCopyError - raise Grape::Exceptions::ValidationErrors.new(errors:[Grape::Exceptions::Validation.new( - - params: ["nonprofit[name]"], - message: "has an invalid slug. Contact support for help." - )]) - end - else - raise e - end - end - - u = User.new(declared_params[:user]) - u.save! - - role = u.roles.build(host: np, name: 'nonprofit_admin') - role.save! - - billing_plan = BillingPlan.find(Settings.default_bp.id) - b_sub = np.build_billing_subscription(billing_plan: billing_plan, status: 'active') - b_sub.save! - rescue ActiveRecord::RecordInvalid => e - class_to_name = {Nonprofit => 'nonprofit', User => 'user'} - if class_to_name[e.record.class] - errors = e.record.errors.keys.map {|k| - - errors = e.record.errors[k].uniq - errors.map{|error| Grape::Exceptions::Validation.new( - - params: ["#{class_to_name[e.record.class]}[#{k.to_s}]"], - message: error - - )} - } - - raise Grape::Exceptions::ValidationErrors.new(errors:errors.flatten) - else - raise e - end - - end - end - #onboard callback - present np, with: Houdini::V1::Entities::Nonprofit - end - - - -end \ No newline at end of file diff --git a/app/api/houdini/v1/validators/is_equal_to.rb b/app/api/houdini/v1/validators/is_equal_to.rb deleted file mode 100644 index c84b8075..00000000 --- a/app/api/houdini/v1/validators/is_equal_to.rb +++ /dev/null @@ -1,8 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Validators::IsEqualTo < Grape::Validations::Base - def validate_param!(attr_name, params) - if params[attr_name] != params[@option] - fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name), @scope.full_name(@option)], message: message(:is_equal_to) - end - end -end \ No newline at end of file diff --git a/app/assets/fonts/Bitter/Bitter-Bold.eot b/app/assets/fonts/Bitter/Bitter-Bold.eot deleted file mode 100644 index 533502da..00000000 Binary files a/app/assets/fonts/Bitter/Bitter-Bold.eot and /dev/null differ diff --git a/app/assets/fonts/Bitter/Bitter-Bold.svg b/app/assets/fonts/Bitter/Bitter-Bold.svg deleted file mode 100644 index ec5b943f..00000000 --- a/app/assets/fonts/Bitter/Bitter-Bold.svg +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Bitter/Bitter-Bold.ttf b/app/assets/fonts/Bitter/Bitter-Bold.ttf deleted file mode 100644 index 1390173b..00000000 Binary files a/app/assets/fonts/Bitter/Bitter-Bold.ttf and /dev/null differ diff --git a/app/assets/fonts/Bitter/Bitter-Regular.eot b/app/assets/fonts/Bitter/Bitter-Regular.eot deleted file mode 100644 index 46a2fc12..00000000 Binary files a/app/assets/fonts/Bitter/Bitter-Regular.eot and /dev/null differ diff --git a/app/assets/fonts/Bitter/Bitter-Regular.svg b/app/assets/fonts/Bitter/Bitter-Regular.svg deleted file mode 100644 index 8293c82f..00000000 --- a/app/assets/fonts/Bitter/Bitter-Regular.svg +++ /dev/null @@ -1,274 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Bitter/Bitter-Regular.ttf b/app/assets/fonts/Bitter/Bitter-Regular.ttf deleted file mode 100644 index 3b66905a..00000000 Binary files a/app/assets/fonts/Bitter/Bitter-Regular.ttf and /dev/null differ diff --git a/app/assets/fonts/FontAwesome/FontAwesome.otf b/app/assets/fonts/FontAwesome/FontAwesome.otf deleted file mode 100644 index 3461e3fc..00000000 Binary files a/app/assets/fonts/FontAwesome/FontAwesome.otf and /dev/null differ diff --git a/app/assets/fonts/FontAwesome/fontawesome-webfont.eot b/app/assets/fonts/FontAwesome/fontawesome-webfont.eot deleted file mode 100755 index 6cfd5660..00000000 Binary files a/app/assets/fonts/FontAwesome/fontawesome-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/FontAwesome/fontawesome-webfont.svg b/app/assets/fonts/FontAwesome/fontawesome-webfont.svg deleted file mode 100755 index a9f84695..00000000 --- a/app/assets/fonts/FontAwesome/fontawesome-webfont.svg +++ /dev/null @@ -1,504 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/FontAwesome/fontawesome-webfont.ttf b/app/assets/fonts/FontAwesome/fontawesome-webfont.ttf deleted file mode 100755 index 5cd6cff6..00000000 Binary files a/app/assets/fonts/FontAwesome/fontawesome-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-bold-webfont.eot b/app/assets/fonts/Open_Sans/opensans-bold-webfont.eot deleted file mode 100644 index e23a5d3a..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-bold-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-bold-webfont.svg b/app/assets/fonts/Open_Sans/opensans-bold-webfont.svg deleted file mode 100644 index 063afd04..00000000 --- a/app/assets/fonts/Open_Sans/opensans-bold-webfont.svg +++ /dev/null @@ -1,1825 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Open_Sans/opensans-bold-webfont.ttf b/app/assets/fonts/Open_Sans/opensans-bold-webfont.ttf deleted file mode 100644 index 71117445..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-bold-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-light-webfont.eot b/app/assets/fonts/Open_Sans/opensans-light-webfont.eot deleted file mode 100644 index 77dc71d6..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-light-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-light-webfont.svg b/app/assets/fonts/Open_Sans/opensans-light-webfont.svg deleted file mode 100644 index 2419d92f..00000000 --- a/app/assets/fonts/Open_Sans/opensans-light-webfont.svg +++ /dev/null @@ -1,1825 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Open_Sans/opensans-light-webfont.ttf b/app/assets/fonts/Open_Sans/opensans-light-webfont.ttf deleted file mode 100644 index 209e0524..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-light-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-regular-webfont.eot b/app/assets/fonts/Open_Sans/opensans-regular-webfont.eot deleted file mode 100644 index 17e30945..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-regular-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/Open_Sans/opensans-regular-webfont.svg b/app/assets/fonts/Open_Sans/opensans-regular-webfont.svg deleted file mode 100644 index 800a03a3..00000000 --- a/app/assets/fonts/Open_Sans/opensans-regular-webfont.svg +++ /dev/null @@ -1,1825 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Open_Sans/opensans-regular-webfont.ttf b/app/assets/fonts/Open_Sans/opensans-regular-webfont.ttf deleted file mode 100644 index c6413c2a..00000000 Binary files a/app/assets/fonts/Open_Sans/opensans-regular-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.eot b/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.eot deleted file mode 100644 index 78127513..00000000 Binary files a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.eot and /dev/null differ diff --git a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.svg b/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.svg deleted file mode 100644 index a65a2fd3..00000000 --- a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.svg +++ /dev/null @@ -1,1398 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.ttf b/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.ttf deleted file mode 100644 index 384a91c9..00000000 Binary files a/app/assets/fonts/Open_Sans_Condensed/opensans-condbold-webfont.ttf and /dev/null differ diff --git a/app/assets/fonts/Streamline/streamline-30px.eot b/app/assets/fonts/Streamline/streamline-30px.eot deleted file mode 100644 index a16a50ce..00000000 Binary files a/app/assets/fonts/Streamline/streamline-30px.eot and /dev/null differ diff --git a/app/assets/fonts/Streamline/streamline-30px.svg b/app/assets/fonts/Streamline/streamline-30px.svg deleted file mode 100644 index d457189c..00000000 --- a/app/assets/fonts/Streamline/streamline-30px.svg +++ /dev/null @@ -1,1652 +0,0 @@ - - - -Generated by Fontastic.me - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/assets/fonts/Streamline/streamline-30px.ttf b/app/assets/fonts/Streamline/streamline-30px.ttf deleted file mode 100644 index c5375ded..00000000 Binary files a/app/assets/fonts/Streamline/streamline-30px.ttf and /dev/null differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 00000000..31390449 Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/stylesheets/boot/editor.css.scss b/app/assets/stylesheets/boot/editor.css.scss new file mode 100644 index 00000000..5e38a9f6 --- /dev/null +++ b/app/assets/stylesheets/boot/editor.css.scss @@ -0,0 +1,4 @@ +// License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> + +@import "common/vendor/froala_editor"; +@import "common/vendor/quill.bubble"; diff --git a/app/assets/stylesheets/boot/editor.css.scss.erb b/app/assets/stylesheets/boot/editor.css.scss.erb deleted file mode 100644 index bcf1e7c1..00000000 --- a/app/assets/stylesheets/boot/editor.css.scss.erb +++ /dev/null @@ -1,3 +0,0 @@ -<% # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> -@import 'common/vendor/froala_editor'; -@import 'common/vendor/quill.bubble'; diff --git a/app/assets/stylesheets/boot/font-awesome.css.scss.erb b/app/assets/stylesheets/boot/font-awesome.css.scss similarity index 97% rename from app/assets/stylesheets/boot/font-awesome.css.scss.erb rename to app/assets/stylesheets/boot/font-awesome.css.scss index 85fbe497..b88ea8e8 100644 --- a/app/assets/stylesheets/boot/font-awesome.css.scss.erb +++ b/app/assets/stylesheets/boot/font-awesome.css.scss @@ -1,15 +1,12 @@ -<% # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> +// License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later /*! * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ -$path: "<%= asset_path('FontAwesome') %>"; - @font-face { - font-family: 'FontAwesome'; - src: url($path + '/fontawesome-webfont.eot?v=4.1.0'); - src: url($path + '/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), url($path + '/fontawesome-webfont.woff?v=4.1.0') format('woff'), url($path + '/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), url($path + '/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); + font-family: "FontAwesome"; + src: font-url("FontAwesome/fontawesome-webfont.woff?v=4.1.0") format("woff"); font-weight: normal; font-style: normal; } @@ -63,9 +60,9 @@ $path: "<%= asset_path('FontAwesome') %>"; left: -1.85714286em; } .fa-border { - padding: .2em .25em .15em; + padding: 0.2em 0.25em 0.15em; border: solid 0.08em #eeeeee; - border-radius: .1em; + border-radius: 0.1em; } .pull-right { float: right; @@ -74,10 +71,10 @@ $path: "<%= asset_path('FontAwesome') %>"; float: left; } .fa.pull-left { - margin-right: .3em; + margin-right: 0.3em; } .fa.pull-right { - margin-left: .3em; + margin-left: 0.3em; } .fa-spin { -webkit-animation: spin 2s infinite linear; diff --git a/app/assets/stylesheets/boot/google-webfonts.css.scss b/app/assets/stylesheets/boot/google-webfonts.css.scss new file mode 100644 index 00000000..7e543438 --- /dev/null +++ b/app/assets/stylesheets/boot/google-webfonts.css.scss @@ -0,0 +1,48 @@ +// License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> + +/* Open Sans */ + +@font-face { + font-family: "Open Sans"; + src: font-url("Open_Sans/opensans-regular-webfont.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Open Sans"; + src: font-url("Open_Sans/opensans-light-webfont.woff") format("woff"); + font-weight: 200; + font-style: normal; +} + +@font-face { + font-family: "Open Sans"; + src: font-url("Open_Sans/opensans-bold-webfont.woff") format("woff"); + font-weight: bold; + font-style: normal; +} + +/* Bitter */ + +@font-face { + font-family: "OpenSansCondensed"; + src: font-url("Open_Sans_Condensed/opensans-condbold-webfont.woff") + format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Bitter"; + src: font-url("Bitter/Bitter-Regular.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Bitter"; + src: font-url("Bitter/Bitter-Bold.woff") format("woff"); + font-weight: bold; + font-style: normal; +} diff --git a/app/assets/stylesheets/boot/google-webfonts.css.scss.erb b/app/assets/stylesheets/boot/google-webfonts.css.scss.erb deleted file mode 100644 index 31ef30d0..00000000 --- a/app/assets/stylesheets/boot/google-webfonts.css.scss.erb +++ /dev/null @@ -1,77 +0,0 @@ -<% # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> - -/* Open Sans */ - -@font-face { - font-family: 'Open Sans'; - src: url('<%= asset_path('Open_Sans/opensans-regular-webfont.eot') %>'); - src: url('<%= asset_path('Open_Sans/opensans-regular-webfont.eot?#iefix') %>') format('embedded-opentype'), - url('<%= asset_path('Open_Sans/opensans-regular-webfont.woff') %>') format('woff'), - url('<%= asset_path('Open_Sans/opensans-regular-webfont.ttf') %>') format('truetype'), - url('<%= asset_path('Open_Sans/opensans-regular-webfont.svg#open_sansregular') %>') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('<%= asset_path('Open_Sans/opensans-light-webfont.eot') %>'); - src: url('<%= asset_path('Open_Sans/opensans-light-webfont.eot?#iefix') %>') format('embedded-opentype'), - url('<%= asset_path('Open_Sans/opensans-light-webfont.woff') %>') format('woff'), - url('<%= asset_path('Open_Sans/opensans-light-webfont.ttf') %>') format('truetype'), - url('<%= asset_path('Open_Sans/opensans-light-webfont.svg#open_sanslight') %>') format('svg'); - font-weight: 200; - font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('<%= asset_path('Open_Sans/opensans-bold-webfont.eot') %>'); - src: url('<%= asset_path('Open_Sans/opensans-bold-webfont.eot?#iefix') %>') format('embedded-opentype'), - url('<%= asset_path('Open_Sans/opensans-bold-webfont.woff') %>') format('woff'), - url('<%= asset_path('Open_Sans/opensans-bold-webfont.ttf') %>') format('truetype'), - url('<%= asset_path('Open_Sans/opensans-bold-webfont.svg#open_sansbold') %>') format('svg'); - font-weight: bold; - font-style: normal; -} - - -/* Bitter */ - -$condensed: '<%= asset_path('Open_Sans_Condensed') %>'; - -@font-face { - font-family: 'OpenSansCondensed'; - src: url($condensed + '/opensans-condbold-webfont.eot'); - src: url($condensed + '/opensans-condbold-webfont.eot?#iefix') format('embedded-opentype'), - url($condensed + '/opensans-condbold-webfont.woff') format('woff'), - url($condensed + '/opensans-condbold-webfont.ttf') format('truetype'), - url($condensed + '/opensans-condbold-webfont.svg') format('svg'); - font-weight: normal; - font-style: normal; -} - - -@font-face { - font-family: 'Bitter'; - src: url('<%= asset_path('Bitter/Bitter-Regular.eot') %>'); - src: url('<%= asset_path('Bitter/Bitter-Regular.eot?#iefix') %>') format('embedded-opentype'), - url('<%= asset_path('Bitter/Bitter-Regular.woff') %>') format('woff'), - url('<%= asset_path('Bitter/Bitter-Regular.ttf') %>') format('truetype'), - url('<%= asset_path('Bitter/Bitter-Regular.svg#bitterregular') %>') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'Bitter'; - src: url('<%= asset_path('Bitter/Bitter-Bold.eot') %>'); - src: url('<%= asset_path('Bitter/Bitter-Bold.eot?#iefix') %>') format('embedded-opentype'), - url('<%= asset_path('Bitter/Bitter-Bold.woff') %>') format('woff'), - url('<%= asset_path('Bitter/Bitter-Bold.ttf') %>') format('truetype'), - url('<%= asset_path('Bitter/Bitter-Bold.svg#bitterbold') %>') format('svg'); - font-weight: bold; - font-style: normal; -} - - diff --git a/app/assets/stylesheets/boot/streamline-icons.css.scss.erb b/app/assets/stylesheets/boot/streamline-icons.css.scss similarity index 99% rename from app/assets/stylesheets/boot/streamline-icons.css.scss.erb rename to app/assets/stylesheets/boot/streamline-icons.css.scss index 36a5ea78..5bb3150e 100644 --- a/app/assets/stylesheets/boot/streamline-icons.css.scss.erb +++ b/app/assets/stylesheets/boot/streamline-icons.css.scss @@ -1,17 +1,11 @@ -<% # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later %> +// License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later @charset "UTF-8"; -$path: "<%= asset_path('Streamline') %>"; - @font-face { - font-family: "streamline-30px"; - src:url($path + "/streamline-30px.eot"); - src:url($path + "/streamline-30px.eot?#iefix") format("embedded-opentype"), - url($path + "/streamline-30px.woff") format("woff"), - url($path + "/streamline-30px.ttf") format("truetype"), - url($path + "/streamline-30px.svg#streamline-30px") format("svg"); - font-weight: normal; - font-style: normal; + font-family: "streamline-30px"; + src: font-url("Streamline/streamline-30px.woff") format("woff"); + font-weight: normal; + font-style: normal; } [data-icon]:before { @@ -31,8 +25,8 @@ $path: "<%= asset_path('Streamline') %>"; [class*=" icon-"]:before { font-family: "streamline-30px" !important; font-style: normal !important; - font-weight: normal !important; - font-variant: normal !important; + font-weight: normal !important; + font-variant: normal !important; text-transform: none !important; speak: none; line-height: 1; diff --git a/app/assets/stylesheets/common/editable.css.scss.erb b/app/assets/stylesheets/common/editable.css.scss similarity index 100% rename from app/assets/stylesheets/common/editable.css.scss.erb rename to app/assets/stylesheets/common/editable.css.scss diff --git a/app/assets/stylesheets/donate-button/donate-button.v2.css b/app/assets/stylesheets/donate-button/donate-button.v2.css new file mode 100644 index 00000000..7c1edd47 --- /dev/null +++ b/app/assets/stylesheets/donate-button/donate-button.v2.css @@ -0,0 +1,160 @@ +/* License: LGPL-3.0-or-later */ +/* css for public/js/donate-button.v2.js */ +iframe.commitchange-btn-iframe { + border: none; + height: 58px; + width: 165px; + overflow: hidden; + position: relative; +} +a.commitchange-donate { + overflow: hidden; + cursor: pointer; + position: relative; + display: inline-block; +} +a.commitchange-donate[data-embedded]:before { +} +a.commitchange-donate:before { + position: absolute; + background: rgba(0,0,0,0); + top: 0; + left: 0; + width: 100%; + height: 100%; + content: ''; + z-index: 9; +} + +a.commitchange-donate[data-embedded]:before { + height: 0; +} +a.commitchange-donate[data-embedded], +a.commitchange-donate[data-custom] { + opacity: 1; +} +a.commitchange-donate { + -webkit-transition: opacity .2s ease-in-out; + -moz-transition: opacity .2s ease-in-out; + -ms-transition: opacity .2s ease-in-out; + transition: opacity .2s ease-in-out; + -webkit-opacity: 0.8; + -moz-opacity: 0.8; + -ms-opacity: 0.8; + opacity: 0.8; +} +a.commitchange-donate:hover { + -webkit-opacity: 1; + -moz-opacity: 1; + -ms-opacity: 1; + opacity: 1; +} +li.commitchange-donate.hide-iframe iframe { + display: none !important; + /* used for WordPress menus, where data-custom is not accessible */ +} +.commitchange-iframe { + width: 406px; + height: 0; + max-width: 100%; + position: fixed; + top: 50%; left: 50%; + margin: 0; + margin-top: -243px; + margin-left: -195px; + background-color: rgba(0, 0, 0, 0); + border: none; + display: block; + padding: 0px; + z-index: 99999; + visibility: visible; + overflow: hidden; + -webkit-opacity: 0; + -moz-opacity: 0; + -ms-opacity: 0; + opacity: 0; + -webkit-transition: opacity .5s ease-in-out; + -moz-transition: opacity .5s ease-in-out; + -ms-transition: opacity .5s ease-in-out; + transition: opacity .5s ease-in-out; +} +.commitchange-iframe-embedded { + border: 0; + position: static; + margin: 0; + height: 450px; + width: 390px; + max-width: 100%; + opacity: 1; +} +.commitchange-iframe.commitchange-open { + height: 510px; + border: 0; + opacity: 1; +} +.commitchange-overlay { + z-index: 9999; + position: fixed; + top: 0; + left: 0%; + width: 100%; + height: 0; + background-color: rgba(0, 0, 0, 0.4); + -webkit-opacity: 0; + -moz-opacity: 0; + -ms-opacity: 0; + opacity: 0; + -webkit-transition: opacity .5s ease-in-out; + -moz-transition: opacity .5s ease-in-out; + -ms-transition: opacity .5s ease-in-out; + transition: opacity .5s ease-in-out; +} +.commitchange-overlay.commitchange-open { + height: 100%; +} +.commitchange-overlay.commitchange-closed, +.commitchange-iframe.commitchange-closed { + height: 0; + -webkit-opacity: 0; + -moz-opacity: 0; + -ms-opacity: 0; + opacity: 0; +} +.commitchange-overlay.commitchange-open, +.commitchange-iframe.commitchange-open { + -webkit-opacity: 1; + -moz-opacity: 1; + -ms-opacity: 1; + opacity: 1; +} +.commitchange-donate[data-fixed] { + position: fixed; + bottom: 0; + right: 22px; + height: 50px; + z-index: 99999 !important; + -webkit-opacity: 1; + -moz-opacity: 1; + -ms-opacity: 1; + opacity: 1; +} +.commitchange-donate[data-fixed] iframe.commitchange-btn-iframe { + border: none; + height: 50px; + width: 125px; +} +@media screen and (max-width: 380px) { + .commitchange-iframe { + left: 0; + margin: 0; + top: 0; + } +} +@media screen and (max-height: 640px) { + .commitchange-iframe.commitchange-open { + margin-top: 0; + top: 0; + height: 100%; + overflow-y: scroll; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/experiment/global/buttons.css b/app/assets/stylesheets/experiment/global/buttons.css new file mode 100644 index 00000000..b9e0bbb8 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/buttons.css @@ -0,0 +1,40 @@ +/* License: LGPL-3.0-or-later */ +.btn, +button { + border-width: 2px; + border-style: solid; + border-color: var(--blue); + color: var(--blue); + background: white; + transition: color 100ms ease, background 100ms ease, border-color 100ms ease; + padding: .25em .5em; +} + +.btn-main, +.btn:hover, +button:hover { + color: white; + background: var(--blue); +} + +[data-ff-confirmation-button="yes"], +.btn-danger { + border-color: var(--red); + color: var(--red); +} + +[data-ff-confirmation-button="yes"]:hover, +.btn-danger:hover { + background: var(--red); +} + +.btn-main:hover { + border-color: color(var(--blue) l(40%)); + background-color: color(var(--blue) l(40%)); +} + +.buttons [class*=btn]:last-of-type, +.buttons button:last-of-type { + border-radius: 0 3px 3px 0; + border-right-width: 2px; +} diff --git a/app/assets/stylesheets/experiment/global/colors.css b/app/assets/stylesheets/experiment/global/colors.css new file mode 100644 index 00000000..c8ddb442 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/colors.css @@ -0,0 +1,45 @@ +/* License: LGPL-3.0-or-later */ +:root { + --grey-05: color(black l(99.4%)); + --grey-1: color(black l(98%)); + --grey-2: color(black l(96%)); + --grey-3: color(black l(92%)); + --grey-4: color(black l(86%)); + --grey-5: color(black l(65%)); + --grey-6: color(black l(50%)); + --black: #2f2f2f; + + --scrim: rgba(0,0,0,.5); + + --blue: #0176f5; + --blue-light: #78b6ff; + --blue-lighter: #d2e6ff; + --green: #82c13e; + --red: #ff0f0f; +} + +.bg-grey-1 { background: var(--grey-1)} +.bg-grey-2 { background: var(--grey-2)} +.bg-grey-3 { background: var(--grey-3)} +.bg-grey-4 { background: var(--grey-4)} +.bg-grey-5 { background: var(--grey-5)} +.bg-scrim { background: var(--scrim)} +.bg-black { background: var(--black)} +.bg-blue { background: var(--blue)} +.bg-blue-light { background: var(--blue-light)} +.bg-blue-lighter { background: var(--blue-lighter)} +.bg-red { background: var(--red)} +.bg-green { background: var(--green)} + +.color-white { color: white} +.color-grey { color: var(--grey-6)} +.color-black { color: var(--black)} +.color-red { color: var(--red)} +.color-green { color: var(--green)} +.color-blue { color: var(--blue)} + +.border-color-grey { border-color: var(--grey-4)} +.border-color-red { border-color: var(--red)} +.border-color-green { border-color: var(--green)} +.border-color-blue { border-color: var(--blue-light)} + diff --git a/app/assets/stylesheets/experiment/global/confirmation.css b/app/assets/stylesheets/experiment/global/confirmation.css new file mode 100644 index 00000000..41160fbc --- /dev/null +++ b/app/assets/stylesheets/experiment/global/confirmation.css @@ -0,0 +1,14 @@ +/* License: LGPL-3.0-or-later */ +[data-ff-confirmation-prompt] { + font-weight: 500; + margin-top: 0; + font-size: 1.5rem; + margin-bottom: 2rem; + padding-right: 2rem; +} + +[data-ff-confirmation-button="yes"] { + margin-right: 1rem; +} + + diff --git a/app/assets/stylesheets/experiment/global/containers.css b/app/assets/stylesheets/experiment/global/containers.css new file mode 100644 index 00000000..45946542 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/containers.css @@ -0,0 +1,10 @@ +/* License: LGPL-3.0-or-later */ +/*[class*="container"] {*/ + /*margin-left: auto;*/ + /*margin-right: auto;*/ +/*}*/ + +/*.container { max-width: 60rem; }*/ +/*.container--medium { max-width: 50rem; }*/ +/*.container--narrow { max-width: 40rem; }*/ + diff --git a/app/assets/stylesheets/experiment/global/dashboard.css b/app/assets/stylesheets/experiment/global/dashboard.css new file mode 100644 index 00000000..ba357de2 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/dashboard.css @@ -0,0 +1,31 @@ +/* License: LGPL-3.0-or-later */ +@import 'ff-dashboard'; +@import './colors.css'; + +[data-ff-dashboard-header] { + background: white; + padding: .5rem 1rem; + border-bottom: 2px solid var(--grey-4); +} + +[data-ff-dashboard-main-panel] { + background: var(--grey-05); +} + +[data-ff-dashboard-left-panel], +[data-ff-dashboard-right-panel] { + background: white; +} + +[data-ff-dashboard-left-panel] { + border-right: 2px solid var(--grey-4); +} + +[data-ff-dashboard-right-panel] { + border-left: 2px solid var(--grey-4); +} + +[data-ff-dashboard-panel-body] { + overflow-x: auto; +} + diff --git a/app/assets/stylesheets/experiment/global/decorative.css b/app/assets/stylesheets/experiment/global/decorative.css new file mode 100644 index 00000000..bc722f89 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/decorative.css @@ -0,0 +1,29 @@ +/* License: LGPL-3.0-or-later */ +ul.tabs--v li.is-selected, +ul.tabs--h li.is-selected { + border-color: var(--green); +} + +ul.tabs--h li, +ul.tabs--v li { + color: var(--black); +} + +.border, +.border-top, +.border-bottom, +.border-left, +.border-right { + border-width: 2px; +} + +hr { + border-bottom-width: 2px; + border-color: var(--grey-4); +} + +::selection { + color: white; + background: var(--black); +} + diff --git a/app/assets/stylesheets/experiment/global/form-elements.css b/app/assets/stylesheets/experiment/global/form-elements.css new file mode 100644 index 00000000..3a3bf896 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/form-elements.css @@ -0,0 +1,84 @@ +/* License: LGPL-3.0-or-later */ +input[type='checkbox'] + label:before, +input[type='radio'] + label:before, +select, +textarea, +input { + border: 2px solid var(--grey-5); +} + +label { + margin-bottom: .25em; +} + +select, +textarea, +input { + border-radius: 3px; +} + +textarea, +select, +input { + padding: .25em .5em; +} + +select:focus, +input:focus, +textarea:focus { + border-color: var(--green); +} + +input[type='radio'] + label, +input[type='checkbox'] + label { + font-size: 1rem; +} + +input[type='radio'] + label:before, +input[type='checkbox'] + label:before { + display: inline-block; + content: "\E876"; + margin: 0; + font-family: 'Material Icons'; + padding: 0; + font-size: .85rem; + width: 1.1rem; + line-height: 1.1rem; +} + +input[type='radio'] + label:before { + border-radius: 50%; +} + +input[type='checkbox']:checked + label:before, +input[type='radio']:checked + label:before { + border-color: var(--blue); + background: var(--blue-light); +} + +input[type='checkbox']:checked + label:before, +input[type='radio']:checked + label:before { + color: white; +} + +select, +.dollar-input { + background-position: center; + background-repeat: no-repeat; +} + +select { + -moz-appearance: none !important; + width: initial; + background-image: url('/svgs/dropdown.svg'); + background-position-x: calc(100% - .25rem); + padding-right: 2rem; +} + +.dollar-input { + background-image: url('/svgs/dollar.svg'); + background-size: 1.1rem; + background-position-x: .25rem; + padding-left: 1.5rem; +} + diff --git a/client/css/global/icons.css b/app/assets/stylesheets/experiment/global/icons.css similarity index 100% rename from client/css/global/icons.css rename to app/assets/stylesheets/experiment/global/icons.css diff --git a/app/assets/stylesheets/experiment/global/loader.css b/app/assets/stylesheets/experiment/global/loader.css new file mode 100644 index 00000000..4f5a72bc --- /dev/null +++ b/app/assets/stylesheets/experiment/global/loader.css @@ -0,0 +1,24 @@ +/* License: LGPL-3.0-or-later */ +.loader { + height: 2px; + width: 100%; + position: fixed; + top: 0; + left: 0; + z-index: 2; +} + +.loader:before{ + transform: translateZ(0); + position: absolute; + content: ""; + height: 100%; + background: var(--green); + animation: loader 1s linear infinite; +} + +@keyframes loader { + from {left: -10%; width: 10%} + 50% {width: 60%; left: 40%;} + to {left: 130%; width: 10%} +} diff --git a/app/assets/stylesheets/experiment/global/modal.css b/app/assets/stylesheets/experiment/global/modal.css new file mode 100644 index 00000000..dde3476c --- /dev/null +++ b/app/assets/stylesheets/experiment/global/modal.css @@ -0,0 +1,92 @@ +/* License: LGPL-3.0-or-later */ +@import 'flimflam/ui/modal/index.css'; /* npm */ + +[data-ff-modal-backdrop] { + z-index: 2; + transform: translateZ(0); + transition: opacity 200ms ease-out, visibility 200ms ease-out; + background: var(--scrim); +} + +[data-ff-modal-backdrop=hidden] { + display: block; + opacity: 0; + visibility: hidden; +} + +[data-ff-modal-backdrop=shown] { + opacity: 1; + visibility: visible; +} + +[data-ff-modal] { + box-shadow: var(--sh-2); +} + +[data-ff-modal-header] { padding: 1rem 3rem 1rem 1rem; } + +[data-ff-modal-body], +[data-ff-modal-footer] { + padding: 1rem; +} + +[data-ff-modal-header] , +[data-ff-modal-footer] { + background: var(--grey-1); +} + +[data-ff-modal-header] h4 { margin: 0; } + +[data-ff-modal-close-button] { + top: 1rem; + right: 1rem; +} + +[data-ff-modal-close-button]:after { + line-height: 1; + font-size: 1.75rem; + color: var(--grey-6); + content: '×'; +} + +.modal-medium [data-ff-modal] { + width: 40rem; + margin-left: -20rem; +} +[data-ff-confirmation] [data-ff-modal], +.modal-small [data-ff-modal] { + width: 30rem; + margin-left: -15rem; +} + +.modal-large [data-ff-modal] { + width: 50rem; + margin-left: -25rem; +} + +@media (max-width: 40rem) { + .modal-medium [data-ff-modal] { + width: calc(100% - 1rem); + margin-left: -0.5rem; + left: 1rem; + } +} + +@media (max-width: 50rem) { + .modal-large [data-ff-modal] { + width: calc(100% - 1rem); + margin-left: -0.5rem; + left: 1rem; + } +} + +@media (max-width: 30rem) { + [data-ff-confirmation] [data-ff-modal], + .modal-small [data-ff-modal] { + width: calc(100% - 1rem); + margin-left: -0.5rem; + left: 1rem; + } +} + + diff --git a/app/assets/stylesheets/experiment/global/notification.css b/app/assets/stylesheets/experiment/global/notification.css new file mode 100644 index 00000000..a5de1019 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/notification.css @@ -0,0 +1,25 @@ +/* License: LGPL-3.0-or-later */ +[data-ff-notification] { + position: fixed; + width: 100%; + text-align: center; + box-shadow: var(--sh-2); + background: var(--black); + color: white; + font-weight: 500; + padding: 1rem; + z-index: 1; + left: 0; + bottom: -50%; + opacity: 0; + visibility: hidden; + transform: translateZ(0); + transition: 200ms ease; +} + +[data-ff-notification=shown] { + opacity: 0.9; + visibility: visible; + bottom: 0; +} + diff --git a/app/assets/stylesheets/experiment/global/page.css b/app/assets/stylesheets/experiment/global/page.css new file mode 100644 index 00000000..09389531 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/page.css @@ -0,0 +1,25 @@ +/* License: LGPL-3.0-or-later */ +@import 'commons.css'; /* npm */ +@import 'colors.css'; /* contains variables */ +@import 'shadows.css'; /* contains variables */ +/*@import 'typography.css';*/ +@import 'icons.css'; +@import 'containers.css'; +@import 'buttons.css'; +@import 'decorative.css'; +@import 'form-elements.css'; +@import 'modal.css'; +@import 'notification.css'; +@import 'confirmation.css'; +@import 'wizard.css'; +@import 'tooltip.css'; +@import 'tabswap.css'; +@import 'utils.css'; +@import 'loader.css'; +@import 'pre.css'; + +/* add to commons */ +@media (max-width: 30rem) { + .sm-left { float: left; } +} + diff --git a/app/assets/stylesheets/experiment/global/pre.css b/app/assets/stylesheets/experiment/global/pre.css new file mode 100644 index 00000000..637f4a22 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/pre.css @@ -0,0 +1,10 @@ +/* License: LGPL-3.0-or-later */ +pre { + white-space: pre-wrap; + word-break: break-all; + font-size: .8rem; + background: var(--grey-1); + padding: 1rem; + margin: 0; +} + diff --git a/app/assets/stylesheets/experiment/global/shadows.css b/app/assets/stylesheets/experiment/global/shadows.css new file mode 100644 index 00000000..55d1aa03 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/shadows.css @@ -0,0 +1,12 @@ +/* License: LGPL-3.0-or-later */ +:root { + --sh-1: 0 1px 3px 1px rgba(0,0,0,.1); + --sh-2: 0 1.5px 6px 1px rgba(0,0,0,.2); + --sh-3: 0 2px 10px 1px rgba(0,0,0,.3); + --sh-4: 0 3px 12px 1px rgba(0,0,0,.4); +} + +/* these box-shadow variables correspond with the */ +/* shadow classes in commons.css (http://yutakahoulette.com/commons.css/#shadow) */ + + diff --git a/app/assets/stylesheets/experiment/global/tabswap.css b/app/assets/stylesheets/experiment/global/tabswap.css new file mode 100644 index 00000000..2fe7823d --- /dev/null +++ b/app/assets/stylesheets/experiment/global/tabswap.css @@ -0,0 +1,49 @@ +/* License: LGPL-3.0-or-later */ +@import 'flimflam/ui/tabswap/index.css'; /* npm */ + +[data-ff-tabswap-labels] { + border-bottom: 2px solid var(--grey-4); +} + +[data-ff-tabswap-label] { + border: 2px solid var(--grey-4); +} + +[data-ff-tabswap-label], +[data-ff-tabswap-label-wrapper] { + display: inline-block; +} + +[data-ff-tabswap-label-wrapper]:first-of-type { + margin-left: 1rem; +} + +[data-ff-tabswap-label-wrapper] { + margin-right: .5rem; +} + +[data-ff-tabswap-label] { + background: var(--grey-2); + padding: .25rem 1rem; + border-radius: 3px 3px 0 0; + transform: translateY(2px); + position: relative; + color: var(--grey-6); +} + +[data-ff-tabswap-label="active"] { + font-weight: 400; + background: white; + color: var(--blue); +} + +[data-ff-tabswap-label="active"]:after { + content: ''; + position: absolute; + left: 0; + width: 100%; + height: 2px; + bottom: -2px; + background: white; +} + diff --git a/app/assets/stylesheets/experiment/global/tooltip.css b/app/assets/stylesheets/experiment/global/tooltip.css new file mode 100644 index 00000000..76f320c1 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/tooltip.css @@ -0,0 +1,51 @@ +/* License: LGPL-3.0-or-later */ +@import 'data-tooltip'; /* npm */ + +[data-ff-field] { position: relative; } + +[data-ff-field]:before, +[data-ff-field]:after { + position: absolute; + visibility: hidden; + pointer-events: none; + box-sizing: border-box; +} + +[data-ff-field='invalid']:before, +[data-ff-field='invalid']:after { + visibility: visible; +} + +[data-ff-field]:before { + content: ''; + position: absolute; + background: transparent; + height: 6px; + width: 6px; + border: 6px solid transparent; + border-top-color: #383838; + bottom: calc(100% - 6px); + left: 12px; +} + +[data-ff-field]:after { + background: #383838; + font-weight: normal; + color: white; + padding: 6px 8px; + line-height: 1.4; + white-space: nowrap; + border-radius: 3px; + text-align: left; + content: attr(data-ff-field-error); + bottom: calc(100% + 6px); + left: 0; +} + +[data-ff-field]:after, +[class*=tooltip--]:after { + font-size: .75rem; + letter-spacing: 1px; + box-shadow: 1px 1px 4px 1px rgba(0,0,0,.1); +} + diff --git a/app/assets/stylesheets/experiment/global/typography.css b/app/assets/stylesheets/experiment/global/typography.css new file mode 100644 index 00000000..9fc1fc42 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/typography.css @@ -0,0 +1,58 @@ +/* License: LGPL-3.0-or-later */ +@font-face { + font-family: 'Open Sans'; + src: url('/fonts/Open_Sans/opensans-regular-webfont.ttf') format('truetype'); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: 'Open Sans'; + src: url('/fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: 'Open Sans'; + src: url('/fonts/Open_Sans/opensans-bold-webfont.ttf') format('truetype'); + font-weight: 600; + font-style: normal; +} + +body { + font-family: + OpenSans, + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Helvetica Neue, + Helvetica, + sans-serif; + line-height: 1.5; + color: var(--black); +} + +a { + color: inherit; + text-decoration: none; +} + +p a { + background-image: linear-gradient(to right, var(--grey-5), var(--grey-5)); + background-repeat: no-repeat,no-repeat,repeat-x; + background-position: 0 100%; + background-size: 100% 1px; + display: inline; +} + +.jumbo { font-size: 4rem; } +.bump { font-size: 1.2rem; } +.sub { font-size: .9rem; } + +label { font-weight: 500; } + +strong, .bold, h1, h2, h3, h4, h5, h6 { font-weight: 600; } + +.font-weight-1 { font-weight: 400; } +.font-weight-2 { font-weight: 500; } +.font-weight-3 { font-weight: 600; } + diff --git a/app/assets/stylesheets/experiment/global/utils.css b/app/assets/stylesheets/experiment/global/utils.css new file mode 100644 index 00000000..c792c4b7 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/utils.css @@ -0,0 +1,5 @@ +/* License: LGPL-3.0-or-later */ +.transition-slow { + transition: 300ms ease; +} + diff --git a/app/assets/stylesheets/experiment/global/wizard.css b/app/assets/stylesheets/experiment/global/wizard.css new file mode 100644 index 00000000..e7abeaa9 --- /dev/null +++ b/app/assets/stylesheets/experiment/global/wizard.css @@ -0,0 +1,91 @@ +/* License: LGPL-3.0-or-later */ +[data-ff-wizard-label]:first-of-type:before { + display: none; +} + +[data-ff-wizard-label] { + opacity: 1; + transition: color 200ms ease; +} + + + +[data-ff-wizard-label='accessible'] { cursor: pointer; } + +[data-ff-wizard-label='inaccessible'] { cursor: default; } + +[data-ff-wizard-label] { + text-align: center; + font-size: .8rem; + padding: 1rem .25rem 0 .25rem; + text-align: center; + position: relative; + display: inline-block; +} + +[data-ff-wizard-label]:after, +[data-ff-wizard-label]:before { + content: ''; + position: absolute; + transition: background 200ms ease; +} + +[data-ff-wizard-label]:after { + z-index: 1; + top: 0; + right: 50%; + transform: translateX(.5rem); + height: .8rem; + width: .8rem; + border-radius: 2rem; +} + +[data-ff-wizard-label]:before { + height: .2rem; + width: 100%; + top: .35rem; + right: 50%; +} + +[data-ff-wizard-label-wrapper='complete'] [data-ff-wizard-label] { + color: var(--grey-5); + pointer-events: none; +} + + +[data-ff-wizard-label] { + color: var(--grey-5); +} + +[data-ff-wizard-label-wrapper='complete'] [data-ff-wizard-label]:after, +[data-ff-wizard-label]:after { + background: var(--grey-5); +} + +[data-ff-wizard-label-wrapper='complete'] [data-ff-wizard-label]:before, +[data-ff-wizard-label]:before { + background: var(--grey-4); +} + +[data-ff-wizard-label='accessible']:after, +[data-ff-wizard-label='current']:after { + background: var(--blue); +} + +[data-ff-wizard-label='accessible']:before, +[data-ff-wizard-label='current']:before { + background: var(--blue-light); +} + +[data-ff-wizard-label-wrapper] { + width: 100%; + padding: 1rem 1rem .5rem 1rem; + background: var(--grey-1); +} + +[data-ff-wizard-label='accessible'], +[data-ff-wizard-label='current'] { + font-weight: 500; + color: var(--blue); +} + diff --git a/public/css/donate-button.v2.css b/app/assets/stylesheets/widget/donate-button-v2.css similarity index 100% rename from public/css/donate-button.v2.css rename to app/assets/stylesheets/widget/donate-button-v2.css diff --git a/public/css/donate-button.css b/app/assets/stylesheets/widget/donate-button.css similarity index 100% rename from public/css/donate-button.css rename to app/assets/stylesheets/widget/donate-button.css diff --git a/app/controllers/activities_controller.rb b/app/controllers/activities_controller.rb deleted file mode 100644 index 07dd5f2c..00000000 --- a/app/controllers/activities_controller.rb +++ /dev/null @@ -1,10 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class ActivitiesController < ApplicationController - - before_filter :authenticate_user!, only: [:create] - - def create - json_saved Activity.create(params[:activity]) - end - -end diff --git a/app/controllers/api/api_controller.rb b/app/controllers/api/api_controller.rb new file mode 100644 index 00000000..47769648 --- /dev/null +++ b/app/controllers/api/api_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class Api::ApiController < ApplicationController + rescue_from ActiveRecord::RecordInvalid, with: :record_invalid_rescue + + protected + + def record_invalid_rescue(error) + render json:{errors: error.record.errors.messages}, status: :unprocessable_entity + end + +end \ No newline at end of file diff --git a/app/controllers/api/nonprofits_controller.rb b/app/controllers/api/nonprofits_controller.rb new file mode 100644 index 00000000..992b403a --- /dev/null +++ b/app/controllers/api/nonprofits_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class Api::NonprofitsController < Api::ApiController + # requires :nonprofit, type: Hash do + # requires :name, type: String, desc: 'Organization Name', allow_blank: false, documentation: { param_type: 'body' } + # requires :zip_code, type: String, allow_blank: false, desc: 'Organization Address ZIP Code', documentation: { param_type: 'body' } + # requires :state_code, type: String, allow_blank: false, desc: 'Organization Address State Code', documentation: { param_type: 'body' } + # requires :city, type: String, allow_blank: false, desc: 'Organization Address City', documentation: { param_type: 'body' } + # end + + # requires :user, type: Hash do + # requires :name, type: String, desc: 'Full name', allow_blank: false, documentation: { param_type: 'body' } + # requires :email, type: String, desc: 'Username', allow_blank: false, documentation: { param_type: 'body' } + # requires :password, type: String, desc: 'Password', allow_blank: false, is_equal_to: :password_confirmation, documentation: { param_type: 'body' } + def create + @nonprofit = Nonprofit.new(clean_params.merge({user_id: current_user_id})) + @nonprofit.save! + render status: :created + end + + private + + def clean_params + params.permit(:name, :zip_code, :state_code, :city, :phone, :email, :website) + end + +end + + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 76f75165..3677337f 100755 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,174 +1,90 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ApplicationController < ActionController::Base - before_filter :set_locale, :redirect_to_maintenance + include Controllers::Locale + include Controllers::Nonprofit::Authorization + before_action :set_locale, :redirect_to_maintenance + protect_from_forgery - protect_from_forgery + def redirect_to_maintenance + if Settings&.maintenance&.maintenance_mode && !current_user + unless self.class == Users::SessionsController && + ((Settings.maintenance.maintenance_token && params[:maintenance_token] == Settings.maintenance.maintenance_token) || params[:format] == 'json') + redirect_to Settings.maintenance.maintenance_page + end + end + end - helper_method \ - :current_role?, - :current_nonprofit_user?, - :administered_nonprofit, - :nonprofit_in_trial?, - :current_plan_tier #int + protected - def set_locale - if params[:locale] && Settings.available_locales.include?(params[:locale]) - I18n.locale = params[:locale] - else - I18n.locale = Settings.language - end - end - - def redirect_to_maintenance - if (Settings&.maintenance&.maintenance_mode && !current_user) - unless (self.class == Users::SessionsController && - ((Settings.maintenance.maintenance_token && params[:maintenance_token] == Settings.maintenance.maintenance_token) || params[:format] == 'json')) - redirect_to Settings.maintenance.maintenance_page - end - end - end - -protected - - def json_saved(model, msg=nil) - if model.valid? - flash[:notice] = msg if msg - render json: model, status: 200 - else - render json: model.errors.full_messages, status: :unprocessable_entity - end - end + def json_saved(model, msg = nil) + if model.valid? + flash[:notice] = msg if msg + render json: model, status: 200 + else + render json: model.errors.full_messages, status: :unprocessable_entity + end + end # A response helper for use with the param_validation gem # use like: render_json{ UpdateUsers.update(params[:user]) } # will catch and pretty print exceptions using the rails loggers def render_json(&block) begin - result = {status: 200, json: yield(block)} + result = { status: 200, json: yield(block) } rescue ParamValidation::ValidationError => e logger.info "422: #{e}".red.bold - #logger.info ">>".bold.red + " #{{'Failed key name' => e.data[:key], 'Value' => e.data[:val], 'Failed validator' => e.data[:name]}}".red - result = {status: 422, json: {error: e.message}} - rescue CCOrgError => e - logger.info "422: #{e}".red.bold - result = {status: 422, json: {error: e.message}} + # logger.info ">>".bold.red + " #{{'Failed key name' => e.data[:key], 'Value' => e.data[:val], 'Failed validator' => e.data[:name]}}".red + result = { status: 422, json: { error: e.message } } + rescue CCOrgError => e + logger.info "422: #{e}".red.bold + result = { status: 422, json: { error: e.message } } rescue ActiveRecord::RecordNotFound => e logger.info "404: #{e}".red.bold - result = {status: 404, json: {error: e.message}} - rescue AuthenticationError => e - logger.info "401: #{e}".red.bold - result = {status: 401, json: {error: e.message}} - rescue ExpiredTokenError => e - logger.info "422: #{e}".red.bold - result = {status: 422, json: {error: e.message}} + result = { status: 404, json: { error: e.message } } + rescue AuthenticationError => e + logger.info "401: #{e}".red.bold + result = { status: 401, json: { error: e.message } } + rescue ExpiredTokenError => e + logger.info "422: #{e}".red.bold + result = { status: 422, json: { error: e.message } } rescue Exception => e # a non-validation related exception logger.error "500: #{e}".red.bold - logger.error e.backtrace.take(5).map{|l| ">>".red.bold + " #{l}"}.join("\n").red - result = {status: 500, json: {error: e.message, backtrace: e.backtrace}} + logger.error e.backtrace.take(5).map { |l| '>>'.red.bold + " #{l}" }.join("\n").red + result = { status: 500, json: { error: e.message, backtrace: e.backtrace } } end render result end - # Test that within the last 5 minutes, the user has confirmed their password - def password_was_confirmed(token) - session[:pw_token] == token && Chronic.parse(session[:pw_timestamp]) >= 5.minutes.ago.utc - end - - def store_location - referrer = request.fullpath - no_redirects = ['/users', '/signup', '/signin', '/users/sign_in', '/users/sign_up', '/users/password', '/users/sign_out', /.*\.json.*/, /.*auth\/facebook.*/] - unless request.format.symbol == :json || no_redirects.map{|p| referrer.match(p)}.any? - session[:previous_url] = referrer - end - end - - def block_with_sign_in(msg=nil) - store_location - if current_user - flash[:notice] = "It looks like you're not allowed to access that page. If this seems like a mistake, please contact #{Settings.mailer.email}" - redirect_to root_path - else - msg ||= 'We need to sign you in before you can do that.' - redirect_to new_user_session_path, :flash => {:error => msg} - end - end - - def authenticate_user!(options={}) - block_with_sign_in unless current_user - end - - def authenticate_confirmed_user! - if !current_user - block_with_sign_in - elsif !current_user.confirmed? && !current_role?([:super_associate, :super_admin]) - redirect_to new_user_confirmation_path, flash: {error: 'You need to confirm your account to do that.'} - end - end - - def authenticate_super_associate! - unless current_role?(:super_admin) || current_role?(:super_associate) - block_with_sign_in 'Please login.' - end - end - - def authenticate_super_admin! - unless current_role?(:super_admin) - block_with_sign_in 'Please login.' - end - end - - def current_role?(role_names, host_id = nil) - return false unless current_user - role_names = Array(role_names) - key = "current_role_user_#{current_user_id}_names_#{role_names.join("_")}_host_#{host_id}" - QueryRoles.user_has_role?(current_user.id, role_names, host_id) - end - - def nonprofit_in_trial?(npo_id=nil) - return false if !npo_id && !administered_nonprofit - npo_id ||= administered_nonprofit.id - key = "in_trial_user_#{current_user_id}_nonprofit_#{npo_id}" - QueryBillingSubscriptions.currently_in_trial?(npo_id) + # Test that within the last 5 minutes, the user has confirmed their password + def password_was_confirmed(token) + session[:pw_token] == token && Chronic.parse(session[:pw_timestamp]) >= 5.minutes.ago.utc end - def current_plan_tier(npo_id=nil) - return 0 if !npo_id && !administered_nonprofit - npo_id ||= administered_nonprofit.id - return 2 if current_role?(:super_admin) - key = "plan_tier_user_#{current_user_id}_nonprofit_#{npo_id}" - administered_nonprofit ? QueryBillingSubscriptions.plan_tier(npo_id) : 0 - end + # devise config - def administered_nonprofit - return nil unless current_user - key = "administered_nonprofit_user_#{current_user_id}_nonprofit" - Nonprofit.where(id: QueryRoles.host_ids(current_user_id, [:nonprofit_admin, :nonprofit_associate])).last - end + def after_sign_in_path_for(_resource) + request.env['omniauth.origin'] || session[:previous_url] || root_path + end - # devise config + def after_sign_up_path_for(_resource) + request.env['omniauth.origin'] || session[:previous_url] || root_path + end - def after_sign_in_path_for(resource) - request.env['omniauth.origin'] || session[:previous_url] || root_path - end + def after_update_path_for(_resource) + profile_path(current_user.profile) + end - def after_sign_up_path_for(resource) - request.env['omniauth.origin'] || session[:previous_url] || root_path - end + def after_inactive_sign_up_path_for(_resource) + profile_path(current_user.profile) + end - def after_update_path_for(resource) - profile_path(current_user.profile) - end + # /devise config - def after_inactive_sign_up_path_for(resource) - profile_path(current_user.profile) - end - - # /devise config - -private - - def current_user_id - current_user && current_user.id - end + private + def current_user_id + current_user&.id + end end diff --git a/app/controllers/aws_presigned_posts_controller.rb b/app/controllers/aws_presigned_posts_controller.rb index bdea0839..b3ed7388 100644 --- a/app/controllers/aws_presigned_posts_controller.rb +++ b/app/controllers/aws_presigned_posts_controller.rb @@ -1,18 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class AwsPresignedPostsController < ApplicationController - before_filter :authenticate_user! + before_action :authenticate_user! # post /presigned_posts # Create some keys using the AWS gem so the user can do direct-to-S3 uploads # http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/PresignedPost.html def create uuid = SecureRandom.uuid - p = S3Bucket.presigned_post({ + p = S3Bucket.presigned_post( key: "tmp/#{uuid}/${filename}", success_action_status: 201, acl: 'public-read', expiration: 30.days.from_now - }) + ) render json: { s3_presigned_post: p.fields.to_json, @@ -20,5 +22,4 @@ class AwsPresignedPostsController < ApplicationController s3_uuid: uuid } end - end diff --git a/app/controllers/billing_subscriptions_controller.rb b/app/controllers/billing_subscriptions_controller.rb index 1f63fb4b..96d3b517 100644 --- a/app/controllers/billing_subscriptions_controller.rb +++ b/app/controllers/billing_subscriptions_controller.rb @@ -1,30 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class BillingSubscriptionsController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_admin! + before_action :authenticate_nonprofit_admin! - def create_trial - render JsonResp.new(params){|params| - requires(:nonprofit_id).as_int - requires(:stripe_plan_id).as_string - }.when_valid{|params| - InsertBillingSubscriptions.trial(params[:nonprofit_id], params[:stripe_plan_id]) - } + def create + @nonprofit ||= Nonprofit.find(params[:nonprofit_id]) + @subscription = BillingSubscription.create_with_stripe(@nonprofit, params[:billing_subscription]) + json_saved(@subscription, "Success! You are subscribed to #{Settings.general.name}.") end - def create - @nonprofit ||= Nonprofit.find(params[:nonprofit_id]) - @subscription = BillingSubscription.create_with_stripe(@nonprofit, params[:billing_subscription]) - json_saved(@subscription, "Success! You are subscribed to #{Settings.general.name}.") - end - # post /nonprofits/:nonprofit_id/billing_subscription/cancel - def cancel - @result = CancelBillingSubscription.with_stripe(@nonprofit) - flash[:notice] = "Your subscription has been cancelled. We'll email you soon with exports." + def cancel + @result = CancelBillingSubscription.with_stripe(@nonprofit) + flash[:notice] = "Your subscription has been cancelled. We'll email you soon with exports." redirect_to root_url - end + end # get nonprofits/:nonprofit_id/billing_subscription/cancellation def cancellation diff --git a/app/controllers/button_debug_controller.rb b/app/controllers/button_debug_controller.rb index 9194b0a8..53d8a892 100644 --- a/app/controllers/button_debug_controller.rb +++ b/app/controllers/button_debug_controller.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ButtonDebugController < ApplicationController def embedded @np = params[:id] || 1 - respond_to { |format| format.html{render layout: 'layouts/empty'} } + respond_to { |format| format.html { render layout: 'layouts/empty' } } end def button @np = params[:id] || 1 - respond_to { |format| format.html{render layout: 'layouts/empty'} } + respond_to { |format| format.html { render layout: 'layouts/empty' } } end end diff --git a/app/controllers/campaign_gift_options_controller.rb b/app/controllers/campaign_gift_options_controller.rb index 47b21367..82745524 100644 --- a/app/controllers/campaign_gift_options_controller.rb +++ b/app/controllers/campaign_gift_options_controller.rb @@ -1,29 +1,32 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CampaignGiftOptionsController < ApplicationController - include Controllers::CampaignHelper + include Controllers::Campaign::Current + include Controllers::Campaign::Authorization - before_filter :authenticate_campaign_editor!, only: [:create, :destroy, :update, :update_order] + before_action :authenticate_campaign_editor!, only: %i[create destroy update update_order] - def index - @gift_options = current_campaign.campaign_gift_options.order('"order", amount_recurring, amount_one_time') - render json: {data: @gift_options} - end + def index + @gift_options = current_campaign.campaign_gift_options.order('"order", amount_recurring, amount_one_time') + render json: { data: @gift_options } + end - def show - render json: {data: current_campaign.campaign_gift_options.find(params[:id])} - end + def show + render json: { data: current_campaign.campaign_gift_options.find(params[:id]) } + end - def create - campaign = current_campaign - json_saved CreateCampaignGiftOption.create(campaign, params[:campaign_gift_option]), - 'Gift option successfully created!' - end + def create + campaign = current_campaign + json_saved CreateCampaignGiftOption.create(campaign, campaign_gift_option_params), + 'Gift option successfully created!' + end - def update - @campaign = current_campaign - gift_option = @campaign.campaign_gift_options.find params[:id] - json_saved UpdateCampaignGiftOption.update(gift_option, params[:campaign_gift_option]), 'Successfully updated' - end + def update + @campaign = current_campaign + gift_option = @campaign.campaign_gift_options.find params[:id] + json_saved UpdateCampaignGiftOption.update(gift_option, campaign_gift_option_params), 'Successfully updated' + end # put /nonprofits/:nonprofit_id/campaigns/:campaign_id/campaign_gift_options/update_order # Pass in {data: [{id: 1, order: 1}]} @@ -32,9 +35,15 @@ class CampaignGiftOptionsController < ApplicationController render json: updated_gift_options end - def destroy - @campaign = current_campaign + def destroy + @campaign = current_campaign - render_json { DeleteCampaignGiftOption.delete(@campaign, params[:id])} - end + render_json { DeleteCampaignGiftOption.delete(@campaign, params[:id]) } + end + + private + + def campaign_gift_option_params + params.require(:campaign_gift_option).permit(:amount_one_time, :amount_recurring, :amount_dollars, :description, :name, :campaign, :quantity, :to_ship, :order, :hide_contributions) + end end diff --git a/app/controllers/campaign_gifts_controller.rb b/app/controllers/campaign_gifts_controller.rb index 786173ff..761761e6 100644 --- a/app/controllers/campaign_gifts_controller.rb +++ b/app/controllers/campaign_gifts_controller.rb @@ -1,8 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CampaignGiftsController < ApplicationController - # post /campaign_gifts - def create - json_saved CreateCampaignGift.create params[:campaign_gift] - end + def create + json_saved CreateCampaignGift.create campaign_gift_params + end + + private + + def campaign_gift_params + params.require(:campaign_gift).permit(:donation_id, :donation, :campaign_gift_option, :campaign_gift_option_id) + end end diff --git a/app/controllers/campaigns/campaign_gift_options_controller.rb b/app/controllers/campaigns/campaign_gift_options_controller.rb index e413855f..199391e6 100644 --- a/app/controllers/campaigns/campaign_gift_options_controller.rb +++ b/app/controllers/campaigns/campaign_gift_options_controller.rb @@ -1,15 +1,63 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Campaigns; class CampaignGiftOptionsController < ApplicationController - include Controllers::CampaignHelper +module Campaigns + class CampaignGiftOptionsController < ApplicationController + include Controllers::Campaign::Current + include Controllers::Campaign::Authorization - before_filter :authenticate_campaign_editor!, only: [:index] + before_action :authenticate_campaign_editor!, only: %i[create destroy update update_order report] - def index - respond_to do |format| - format.json do - render json: QueryCampaignGifts.report_metrics(current_campaign.id) - end - end - end + def report + respond_to do |format| + format.json do + render json: QueryCampaignGifts.report_metrics(current_campaign.id) + end + end + end -end; end + def index + @gift_options = current_campaign.campaign_gift_options.order('"order", amount_recurring, amount_one_time') + render json: { data: @gift_options } + end + + def show + render json: { data: current_campaign.campaign_gift_options.find(params[:id]) } + end + + def create + campaign = current_campaign + json_saved CreateCampaignGiftOption.create(campaign, campaign_gift_option_params), + 'Gift option successfully created!' + end + + def update + @campaign = current_campaign + gift_option = @campaign.campaign_gift_options.find params[:id] + json_saved UpdateCampaignGiftOption.update(gift_option, campaign_gift_option_params), 'Successfully updated' + end + + # put /nonprofits/:nonprofit_id/campaigns/:campaign_id/campaign_gift_options/update_order + # Pass in {data: [{id: 1, order: 1}]} + def update_order + updated_gift_options = UpdateOrder.with_data('campaign_gift_options', update_order_params) + render json: updated_gift_options + end + + def destroy + @campaign = current_campaign + + render_json { DeleteCampaignGiftOption.delete(@campaign, params[:id]) } + end + + private + + def campaign_gift_option_params + params.require(:campaign_gift_option).permit(:name, :amount_one_time, :amount_recurring, :description, :quantity, :to_ship, :order, :hide_contributions) + end + + def update_order_params + params.require(:data) + end + end +end diff --git a/app/controllers/campaigns/donations_controller.rb b/app/controllers/campaigns/donations_controller.rb index 44cb27a1..fbc73bc0 100644 --- a/app/controllers/campaigns/donations_controller.rb +++ b/app/controllers/campaigns/donations_controller.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Campaigns -class DonationsController < ApplicationController - include Controllers::CampaignHelper + class DonationsController < ApplicationController + include Controllers::Campaign::Current + include Controllers::Campaign::Authorization - before_filter :authenticate_campaign_editor!, only: [:index] - - def index - respond_to do |format| - format.csv do - file_date = Date.today.strftime("%m-%d-%Y") - donations = QueryDonations.campaign_export(current_campaign.id) - send_data(Format::Csv.from_vectors(donations), filename: "campaign-donations-#{file_date}.csv") - end - end - end + before_action :authenticate_campaign_editor!, only: [:index] -end + def index + respond_to do |format| + format.csv do + file_date = Date.today.strftime('%m-%d-%Y') + donations = QueryDonations.campaign_export(current_campaign.id) + send_data(Format::Csv.from_vectors(donations), filename: "campaign-donations-#{file_date}.csv") + end + end + end + end end diff --git a/app/controllers/campaigns/supporters_controller.rb b/app/controllers/campaigns/supporters_controller.rb index 9796e687..d504b446 100644 --- a/app/controllers/campaigns/supporters_controller.rb +++ b/app/controllers/campaigns/supporters_controller.rb @@ -1,22 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Campaigns -class SupportersController < ApplicationController - include Controllers::CampaignHelper + class SupportersController < ApplicationController + include Controllers::Campaign::Current + include Controllers::Campaign::Authorization - before_filter :authenticate_campaign_editor!, only: [:index] + before_action :authenticate_campaign_editor!, only: [:index] - def index - @panels_layout = true - @nonprofit = current_nonprofit - @campaign = current_campaign - - respond_to do |format| - format.json do - render json: QuerySupporters.campaign_list(@nonprofit.id, @campaign.id, params) - end - format.html - end - end + def index + @panels_layout = true + @nonprofit = current_nonprofit + @campaign = current_campaign -end + respond_to do |format| + format.json do + render json: QuerySupporters.campaign_list(@nonprofit.id, @campaign.id, params) + end + format.html + end + end + end end diff --git a/app/controllers/campaigns_controller.rb b/app/controllers/campaigns_controller.rb index 861370a7..74773232 100644 --- a/app/controllers/campaigns_controller.rb +++ b/app/controllers/campaigns_controller.rb @@ -1,15 +1,18 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CampaignsController < ApplicationController - include Controllers::CampaignHelper + include Controllers::Campaign::Current + include Controllers::Campaign::Authorization helper_method :current_campaign_editor? - before_filter :authenticate_confirmed_user!, only: [:create, :name_and_id, :duplicate] - before_filter :authenticate_campaign_editor!, only: [:update, :soft_delete] - before_filter :check_nonprofit_status, only: [:index, :show] + before_action :authenticate_confirmed_user!, only: %i[create name_and_id duplicate] + before_action :authenticate_campaign_editor!, only: %i[update soft_delete] + before_action :check_nonprofit_status, only: %i[index show] def index @nonprofit = current_nonprofit - if (current_nonprofit_user?) + if current_nonprofit_user? @campaigns = @nonprofit.campaigns.includes(:nonprofit).not_deleted.order('created_at desc') @deleted_campaigns = @nonprofit.campaigns.includes(:nonprofit).deleted.order('created_at desc') else @@ -48,7 +51,7 @@ class CampaignsController < ApplicationController @peer_to_peer_campaign_param = @campaign.id end - @campaign_background_image = FetchBackgroundImage.with_model(@campaign) + @campaign_background_image = @campaign.background_image.attached? && url_for(@campaign.background_image_by_size(:normal)) end def activities @@ -57,38 +60,30 @@ class CampaignsController < ApplicationController end def create - Time.use_zone(current_nonprofit.timezone || 'UTC') do - params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present? - end - - if !params[:campaign][:parent_campaign_id] - campaign = current_nonprofit.campaigns.create params[:campaign] - json_saved campaign, 'Campaign created! Well done.' + @campaign = CreateCampaign.create(params, current_nonprofit) + if (@campaign.errors.empty?) + render 'campaigns/create', campaign: @campaign else - profile_id = params[:campaign][:profile_id] - Profile.find(profile_id).update_attributes params[:profile] - render json: CreatePeerToPeerCampaign.create(params[:campaign], profile_id) + render json: { errors: @campaign.errors.messages }.as_json end + end def update Time.use_zone(current_nonprofit.timezone || 'UTC') do - params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present? + campaign_params[:end_datetime] = Chronic.parse(campaign_params[:end_datetime]) if campaign_params[:end_datetime].present? end - current_campaign.update_attributes params[:campaign] + current_campaign.update campaign_params json_saved current_campaign, 'Successfully updated!' end # post 'nonprofits/:np_id/campaigns/:campaign_id/duplicate' def duplicate - - render_json { + render_json do InsertDuplicate.campaign(current_campaign.id, current_user.profile.id) - } - + end end - def soft_delete current_campaign.update_attribute(:deleted, params[:delete]) render json: {} @@ -112,17 +107,17 @@ class CampaignsController < ApplicationController end def peer_to_peer - session[:donor_signup_url] = request.env["REQUEST_URI"] + session[:donor_signup_url] = request.env['REQUEST_URI'] @nonprofit = Nonprofit.find_by_id(params[:npo_id]) @parent_campaign = Campaign.find_by_id(params[:campaign_id]) if params[:campaign_id].present? && !@parent_campaign - raise ActionController::RoutingError.new('Not Found') + raise ActionController::RoutingError, 'Not Found' end if current_user @profile = current_user.profile - if (@parent_campaign) + if @parent_campaign @child_campaign = Campaign.where( profile_id: @profile.id, parent_campaign_id: @parent_campaign.id @@ -135,7 +130,11 @@ class CampaignsController < ApplicationController def check_nonprofit_status if !current_role?(:super_admin) && !current_nonprofit.published - raise ActionController::RoutingError.new('Not Found') + raise ActionController::RoutingError, 'Not Found' end end + + def campaign_params + params.require(:campaign).permit(:name, :tagline, :slug, :total_supporters, :goal_amount, :nonprofit_id, :profile_id, :main_image, :remove_main_image, :background_image, :remove_background_image, :banner_image, :remove_banner_image, :published, :video_url, :vimeo_video_id, :youtube_video_id, :summary, :recurring_fund, :body, :goal_amount_dollars, :show_total_raised, :show_total_count, :hide_activity_feed, :end_datetime, :deleted, :hide_goal, :hide_thermometer, :hide_title, :receipt_message, :hide_custom_amounts, :parent_campaign_id, :reason_for_supporting, :default_reason_for_supporting) + end end diff --git a/app/controllers/cards_controller.rb b/app/controllers/cards_controller.rb index 4e84fd1e..d55bc5b5 100755 --- a/app/controllers/cards_controller.rb +++ b/app/controllers/cards_controller.rb @@ -1,22 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CardsController < ApplicationController + before_action :authenticate_user!, except: [:create] - before_filter :authenticate_user!, :except => [:create] - - # post /cards - def create - acct = Supporter.find(params[:card][:holder_id]).nonprofit.stripe_account_id + # post /cards + def create + acct = Supporter.find(card_params[:holder_id]).nonprofit.stripe_account_id render( - JsonResp.new(params) do |d| + JsonResp.new(params) do |_d| requires(:card).nested do requires(:name, :stripe_card_token).as_string requires(:holder_id).as_int requires(:holder_type).one_of('Supporter') end end.when_valid do |d| - InsertCard.with_stripe(d[:card], acct, params[:event_id], current_user) + InsertCard.with_stripe(d[:card], acct, params[:event_id], current_user) end ) - end + end + private + + def card_params + params.require(:card).permit(:cardholders_name, :email, :name, :failure_message, :status, :stripe_card_token, :stripe_card_id, :stripe_customer_id, :holder, :inactive) + end end diff --git a/app/controllers/concerns/controllers/campaign/authorization.rb b/app/controllers/concerns/controllers/campaign/authorization.rb new file mode 100644 index 00000000..e1efe5e5 --- /dev/null +++ b/app/controllers/concerns/controllers/campaign/authorization.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Campaign::Authorization + extend ActiveSupport::Concern + include Controllers::Nonprofit::Authorization + + included do + private + def current_campaign_editor? + !params[:preview] && (current_nonprofit_user? || current_role?(:campaign_editor, current_campaign.id) || current_role?(:super_admin)) + end + def authenticate_campaign_editor! + unless current_campaign_editor? + reject_with_sign_in 'You need to be a campaign editor to do that.' + end + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/campaign/current.rb b/app/controllers/concerns/controllers/campaign/current.rb new file mode 100644 index 00000000..0997124c --- /dev/null +++ b/app/controllers/concerns/controllers/campaign/current.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Campaign::Current + extend ActiveSupport::Concern + include Controllers::Nonprofit::Current + + included do + private + def current_campaign + @campaign ||= FetchCampaign.with_params params, current_nonprofit + raise ActionController::RoutingError, 'Campaign not found' if @campaign.nil? + + @campaign + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/event/authorization.rb b/app/controllers/concerns/controllers/event/authorization.rb new file mode 100644 index 00000000..94e6ca6b --- /dev/null +++ b/app/controllers/concerns/controllers/event/authorization.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Event::Authorization + extend ActiveSupport::Concern + include Controllers::Nonprofit::Authorization + + included do + private + + def current_event_admin? + current_nonprofit_admin? + end + + def current_event_editor? + !params[:preview] && (current_nonprofit_user? || current_role?(:event_editor, current_event.id) || current_role?(:super_admin)) + end + + def authenticate_event_editor! + unless current_event_editor? + reject_with_sign_in 'You need to be the event organizer or a nonprofit administrator before doing that.' + end + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/event/current.rb b/app/controllers/concerns/controllers/event/current.rb new file mode 100644 index 00000000..290b18cb --- /dev/null +++ b/app/controllers/concerns/controllers/event/current.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Event::Current + extend ActiveSupport::Concern + include Controllers::Nonprofit::Current + + included do + private + def current_event + @event ||= FetchEvent.with_params params, current_nonprofit + raise ActionController::RoutingError, 'Event not found' if @event.nil? + + @event + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/locale.rb b/app/controllers/concerns/controllers/locale.rb new file mode 100644 index 00000000..78e2407b --- /dev/null +++ b/app/controllers/concerns/controllers/locale.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Locale + extend ActiveSupport::Concern + + included do + before_action :set_locale + + def set_locale + if params[:locale] && Settings.available_locales.include?(params[:locale]) + I18n.locale = params[:locale] + else + I18n.locale = Settings.language + end + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/nonprofit/authorization.rb b/app/controllers/concerns/controllers/nonprofit/authorization.rb new file mode 100644 index 00000000..1fb3e684 --- /dev/null +++ b/app/controllers/concerns/controllers/nonprofit/authorization.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Nonprofit::Authorization + extend ActiveSupport::Concern + include Controllers::User::Authorization + + included do + private + def authenticate_nonprofit_user!(type: :web) + reject_with_sign_in 'Please sign in' unless current_nonprofit_user? + end + + def authenticate_nonprofit_admin! + reject_with_sign_in 'Please sign in' unless current_nonprofit_admin? + end + + def current_nonprofit_user? + return false if params[:preview] + return false unless current_nonprofit_without_exception + + @current_user_role ||= current_role?(%i[nonprofit_admin nonprofit_associate], current_nonprofit_without_exception.id) || current_role?(:super_admin) + end + + def current_nonprofit_admin? + return false if !current_user || current_user.roles.empty? + + @current_admin_role ||= current_role?(:nonprofit_admin, current_nonprofit.id) || current_role?(:super_admin) + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/nonprofit/current.rb b/app/controllers/concerns/controllers/nonprofit/current.rb new file mode 100644 index 00000000..14940031 --- /dev/null +++ b/app/controllers/concerns/controllers/nonprofit/current.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::Nonprofit::Current + extend ActiveSupport::Concern + included do + helper_method :current_nonprofit_user? + + def current_nonprofit + @nonprofit = current_nonprofit_without_exception + raise ActionController::RoutingError, 'Nonprofit not found' if @nonprofit.nil? + + @nonprofit + end + + def current_nonprofit_without_exception + key = "current_nonprofit_#{current_user_id}_params_#{[params[:state_code], params[:city], params[:name], params[:nonprofit_id], params[:id]].join('_')}" + FetchNonprofit.with_params params, administered_nonprofit + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/controllers/user/authorization.rb b/app/controllers/concerns/controllers/user/authorization.rb new file mode 100644 index 00000000..7834760f --- /dev/null +++ b/app/controllers/concerns/controllers/user/authorization.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Controllers::User::Authorization + extend ActiveSupport::Concern + + included do + helper_method :current_role?, :administered_nonprofit + private + def authenticate_user!(type= :html) + reject_with_sign_in unless current_user + end + + def reject_with_sign_in(msg=nil, type= :html) + if type == :html + block_with_sign_in(msg) + else + render text: msg, status: :unauthorized + end + end + + def block_with_sign_in(msg = nil) + store_location + if current_user + flash[:notice] = "It looks like you're not allowed to access that page. If this seems like a mistake, please contact #{Settings.mailer.email}" + redirect_to root_path + else + msg ||= 'We need to sign you in before you can do that.' + redirect_to new_user_session_path, flash: { error: msg } + end + end + + def current_role?(role_names, host_id = nil) + return false unless current_user + + role_names = Array(role_names) + key = "current_role_user_#{current_user_id}_names_#{role_names.join('_')}_host_#{host_id}" + QueryRoles.user_has_role?(current_user.id, role_names, host_id) + end + + def authenticate_confirmed_user! + if !current_user + reject_with_sign_in + elsif !current_user.confirmed? && !current_role?(%i[super_associate super_admin]) + redirect_to new_user_confirmation_path, flash: { error: 'You need to confirm your account to do that.' } + end + end + + def authenticate_super_associate! + unless current_role?(:super_admin) || current_role?(:super_associate) + reject_with_sign_in 'Please login.' + end + end + + def authenticate_super_admin! + reject_with_sign_in 'Please login.' unless current_role?(:super_admin) + end + + def store_location + referrer = request.fullpath + no_redirects = ['/users', '/signup', '/signin', '/users/sign_in', '/users/sign_up', '/users/password', '/users/sign_out', /.*\.json.*/, %r{.*auth/facebook.*}] + unless request.format.symbol == :json || no_redirects.map { |p| referrer.match(p) }.any? + session[:previous_url] = referrer + end + end + + def administered_nonprofit + return nil unless current_user + + key = "administered_nonprofit_user_#{current_user_id}_nonprofit" + Nonprofit.where(id: QueryRoles.host_ids(current_user_id, %i[nonprofit_admin nonprofit_associate])).last + end + end +end \ No newline at end of file diff --git a/app/controllers/direct_debit_details_controller.rb b/app/controllers/direct_debit_details_controller.rb index ea9e00bb..52c7426d 100644 --- a/app/controllers/direct_debit_details_controller.rb +++ b/app/controllers/direct_debit_details_controller.rb @@ -1,19 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class DirectDebitDetailsController < ApplicationController - - # POST /sepa # This endpoint is used for saving direct debit account details # when SEPA payment is selected in the donation widget. Actual charge is # happening offline, after donations are exported to an external CRM. def create render( - JsonResp.new(params) do |data| + JsonResp.new(params) do |_data| requires(:supporter_id).as_int requires(:sepa_params).nested do requires(:iban, :name, :bic).as_string end - end.when_valid do |data| + end.when_valid do |_data| InsertDirectDebitDetail.execute(params) end ) diff --git a/app/controllers/email_settings_controller.rb b/app/controllers/email_settings_controller.rb index 5eaf43fa..b4652fd1 100644 --- a/app/controllers/email_settings_controller.rb +++ b/app/controllers/email_settings_controller.rb @@ -1,7 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EmailSettingsController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! def index user = current_role?(:super_admin) ? User.find(params[:user_id]) : current_user @@ -13,8 +16,12 @@ class EmailSettingsController < ApplicationController # post /nonprofits/:nonprofit_id/users/:user_id/email_settings for current_user def create user = current_role?(:super_admin) ? User.find(params[:user_id]) : current_user - render json: UpdateEmailSettings.save(params[:nonprofit_id], user.id, params[:email_settings]) + render json: UpdateEmailSettings.save(params[:nonprofit_id], user.id, email_settings_params) end -end + private + def email_settings_params + params.require(:email_settings).permit(:notify_payments, :notify_campaigns, :notify_events, :notify_payouts, :notify_recurring_donations) + end +end diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb index 7ece89da..bb3e057b 100644 --- a/app/controllers/emails_controller.rb +++ b/app/controllers/emails_controller.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EmailsController < ApplicationController - before_filter :authenticate_user! - - def create - email = params[:email] - GenericMailer.delay.generic_mail(email[:from_email], email[:from_name], email[:message], email[:subject], email[:to_email], email[:to_name]) - render :json => {:notification => 'Email successfully sent'}, :status => :created - end + before_action :authenticate_user! + def create + email = params[:email] + GenericMailer.generic_mail(email[:from_email], email[:from_name], email[:message], email[:subject], email[:to_email], email[:to_name]).deliver_later + render json: { notification: 'Email successfully sent' }, status: :created + end end diff --git a/app/controllers/event_discounts_controller.rb b/app/controllers/event_discounts_controller.rb index 21a4bbf5..9ea04f5e 100644 --- a/app/controllers/event_discounts_controller.rb +++ b/app/controllers/event_discounts_controller.rb @@ -1,17 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EventDiscountsController < ApplicationController - include Controllers::EventHelper - before_filter :authenticate_event_editor!, :except => [:index] + include Controllers::Event::Current + include Controllers::Event::Authorization + before_action :authenticate_event_editor!, except: [:index] def create - params[:event_discount][:event_id] = current_event.id + event_discount_params[:event_id] = current_event.id - render JsonResp.new(params[:event_discount]){|data| + render JsonResp.new(event_discount_params) do |_data| requires(:code, :name).as_string requires(:event_id, :percent).as_int - }.when_valid{|data| + end.when_valid do |data| { status: 200, json: { event_discount: current_event.event_discounts.create(data) } } - } + end end def index @@ -21,20 +24,25 @@ class EventDiscountsController < ApplicationController def update discount = Hamster.to_ruby( Psql.execute( - Qexpr.new.update(:event_discounts, params[:event_discount]) + Qexpr.new.update(:event_discounts, event_discount_params) .where('id=$id', id: params[:id]) .returning('*') ).first ) - render json: {status: 200, data: discount } + render json: { status: 200, data: discount } end def destroy Psql.execute( - Qexpr.new.delete_from("event_discounts") - .where("event_discounts.event_id=$id", id: params["event_id"]) - .where("event_discounts.id=$id", id: params["id"]) + Qexpr.new.delete_from('event_discounts') + .where('event_discounts.event_id=$id', id: params['event_id']) + .where('event_discounts.id=$id', id: params['id']) ) end + private + + def event_discount_params + params.required(:event_discount).permit(:code, :event_id, :name, :percent) + end end diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 6710adca..33d13e4c 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -1,56 +1,58 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EventsController < ApplicationController - include Controllers::EventHelper + include Controllers::Event::Current + include Controllers::Event::Authorization - helper_method :current_event_editor? - before_filter :authenticate_nonprofit_user!, only: :name_and_id - before_filter :authenticate_event_editor!, only: [:update, :soft_delete, :stats, :create, :duplicate] + helper_method :current_event_editor? + before_action :authenticate_nonprofit_user!, only: :name_and_id + before_action :authenticate_event_editor!, only: %i[update soft_delete stats create duplicate] - - def index + def index @nonprofit = current_nonprofit - end + end def listings render json: QueryEventMetrics.for_listings('nonprofit', current_nonprofit.id, params) end - def show + def show @event = params[:event_slug] ? Event.find_by_slug!(params[:event_slug]) : Event.find_by_id!(params[:id]) - @event_background_image = FetchBackgroundImage.with_model(@event) + @event_background_image = @event.background_image.attached? && url_for(@event.background_image_by_size(:normal)) @nonprofit = @event.nonprofit if @event.deleted && !current_event_editor? redirect_to nonprofit_path(current_nonprofit) flash[:notice] = "Sorry, we couldn't find that event" return end - @organizer = QueryEventOrganizer.with_event(@event.id) - end + @organizer = QueryEventOrganizer.with_event(@event.id) + end - def create + def create render_json do Time.use_zone(current_nonprofit.timezone || 'UTC') do - params[:event][:start_datetime] = Chronic.parse(params[:event][:start_datetime]) if params[:event][:start_datetime].present? - params[:event][:end_datetime] = Chronic.parse(params[:event][:end_datetime]) if params[:event][:end_datetime].present? + event_params[:start_datetime] = Chronic.parse(event_params[:start_datetime]) if event_params[:start_datetime].present? + event_params[:end_datetime] = Chronic.parse(event_params[:end_datetime]) if event_params[:end_datetime].present? end flash[:notice] = 'Your draft event has been created! Well done.' - ev = current_nonprofit.events.create(params[:event]) - {url: "/events/#{ev.slug}", event: ev} + ev = current_nonprofit.events.create(event_params) + { url: "/events/#{ev.slug}", event: ev } end - end + end - def update + def update Time.use_zone(current_nonprofit.timezone || 'UTC') do - params[:event][:start_datetime] = Chronic.parse(params[:event][:start_datetime]) if params[:event][:start_datetime].present? - params[:event][:end_datetime] = Chronic.parse(params[:event][:end_datetime]) if params[:event][:end_datetime].present? + event_params[:start_datetime] = Chronic.parse(event_params[:start_datetime]) if event_params[:start_datetime].present? + event_params[:end_datetime] = Chronic.parse(event_params[:end_datetime]) if event_params[:end_datetime].present? end - current_event.update_attributes(params[:event]) - json_saved current_event, 'Successfully updated' - end + current_event.update(event_params) + json_saved current_event, 'Successfully updated' + end # post 'nonprofits/:np_id/events/:event_id/duplicate' def duplicate - render_json { InsertDuplicate.event(current_event.id, current_user.profile.id)} + render_json { InsertDuplicate.event(current_event.id, current_user.profile.id) } end def activities @@ -58,24 +60,28 @@ class EventsController < ApplicationController end def soft_delete - current_event.update_attribute(:deleted, params[:delete]) - render json: {} - end + current_event.update_attribute(:deleted, params[:delete]) + render json: {} + end - def metrics + def metrics render json: QueryEventMetrics.with_event_ids([current_event.id]).first - end + end - def stats - @event = current_event - @url = Format::Url.concat(root_url, @event.url) - @event_background_image = FetchBackgroundImage.with_model(@event) - render layout: 'layouts/embed' - end + def stats + @event = current_event + @url = Format::Url.concat(root_url, @event.url) + @event_background_image = @event.background_image.attached? && url_for(@event.background_image_by_size(:normal)) + render layout: 'layouts/embed' + end def name_and_id render json: QueryEvents.name_and_id(current_nonprofit.id) end + private + def event_params + params.require(:event).permit(:deleted, :name, :tagline, :summary, :body, :end_datetime, :start_datetime, :latitude, :longitude, :location, :city, :state_code, :address, :zip_code, :main_image, :remove_main_image, :background_image, :remove_background_image, :published, :slug, :directions, :venue_name, :profile_id, :ticket_levels_attributes, :show_total_raised, :show_total_count, :hide_activity_feed, :nonprofit_id, :hide_title, :organizer_email, :receipt_message) + end end diff --git a/app/controllers/front_controller.rb b/app/controllers/front_controller.rb index e43cf7b7..da3b077f 100755 --- a/app/controllers/front_controller.rb +++ b/app/controllers/front_controller.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class FrontController < ApplicationController def index - if !Nonprofit.any? - redirect_to onboard_path - elsif current_role?([:nonprofit_admin,:nonprofit_associate]) - redirect_to NonprofitPath.dashboard(administered_nonprofit) - elsif current_user - redirect_to '/profiles/' + current_user.profile.id.to_s - else - redirect_to new_user_session_path - end - end + if Nonprofit.none? + redirect_to onboard_path + elsif current_role?(%i[nonprofit_admin nonprofit_associate]) + redirect_to NonprofitPath.dashboard(administered_nonprofit) + elsif current_user + redirect_to '/profiles/' + current_user.profile.id.to_s + else + redirect_to new_user_session_path + end + end end diff --git a/app/controllers/image_attachments_controller.rb b/app/controllers/image_attachments_controller.rb index aa378f5d..2d8683f1 100644 --- a/app/controllers/image_attachments_controller.rb +++ b/app/controllers/image_attachments_controller.rb @@ -1,24 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ImageAttachmentsController < ApplicationController - before_filter :authenticate_confirmed_user! - def create - # must return json with a link attr - # http://editor.froala.com/server-integrations/php-image-upload - @image = ImageAttachment.new(:file => params[:file]) - if @image.save - render :json => {:link => @image.file_url} - else - render :json => @image.errors.full_messages, :status => :unprocessable_entity - end - end + before_action :authenticate_confirmed_user! + def create + # must return json with a link attr + # http://editor.froala.com/server-integrations/php-image-upload + @image = ImageAttachment.new(clean_params_create) + if @image.save + render json: { link: url_for(@image.file) } + else + render json: @image.errors.full_messages, status: :unprocessable_entity + end + end - def remove - @image = ImageAttachment.select{|img| img.file_url == params[:src]}.first - if @image - @image.destroy - render :json => @image - else - render :json => {}, :status => :unprocessable_entity - end - end + def remove + @image = ImageAttachment.select { |img| url_for(img.file) == clean_params_remove[:src] }.first + if @image + @image.destroy + render json: @image + else + render json: {}, status: :unprocessable_entity + end + end + + private + def clean_params_create + params.require(:file) + end + + def clean_params_remove + params.require(:src) + end end diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index 8fe38819..561d11da 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -1,34 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class MapsController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_super_associate!, only: :all_supporters - before_filter :authenticate_nonprofit_user!, only: [:all_npo_supporters, :specific_npo_supporters] + before_action :authenticate_super_associate!, only: :all_supporters + before_action :authenticate_nonprofit_user!, only: %i[all_npo_supporters specific_npo_supporters] - # used on admin/nonprofits_map and front page - def all_npos - respond_to do |format| - format.html { redirect_to :root } - format.json { @map_data = Nonprofit.where("latitude IS NOT NULL").last(1000) } - end - end + # used on admin/nonprofits_map and front page + def all_npos + respond_to do |format| + format.html { redirect_to :root } + format.json { @map_data = Nonprofit.where('latitude IS NOT NULL').last(1000) } + end + end - # used on admin/supporters_map - def all_supporters - @map_data = Supporter.where("latitude IS NOT NULL").last(1000) - end - - # used on npo dashboard - def all_npo_supporters - @map_data = Nonprofit.find(params['npo_id']).supporters.where("latitude IS NOT NULL").last(100) - end - - # used on supporter dashboard - def specific_npo_supporters - supporter_ids = params['supporter_ids'].split(",").map { |s| s.to_i } - supporters = Nonprofit.find(params['npo_id']).supporters.find(supporter_ids).last(500) - @map_data = supporters.map{|s| s if s.latitude != ''} - end + # used on admin/supporters_map + def all_supporters + @map_data = Supporter.where('latitude IS NOT NULL').last(1000) + end + # used on npo dashboard + def all_npo_supporters + @map_data = Nonprofit.find(params['npo_id']).supporters.where('latitude IS NOT NULL').last(100) + end + # used on supporter dashboard + def specific_npo_supporters + supporter_ids = params['supporter_ids'].split(',').map(&:to_i) + supporters = Nonprofit.find(params['npo_id']).supporters.find(supporter_ids).last(500) + @map_data = supporters.map { |s| s if s.latitude != '' } + end end diff --git a/app/controllers/nonprofits/activities_controller.rb b/app/controllers/nonprofits/activities_controller.rb index 6824e8a7..a1435e6e 100644 --- a/app/controllers/nonprofits/activities_controller.rb +++ b/app/controllers/nonprofits/activities_controller.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class ActivitiesController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + class ActivitiesController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! # get /nonprofits/:nonprofit_id/supporters/:supporter_id/activities def index render json: QueryActivities.for_timeline(params[:nonprofit_id], params[:supporter_id]) end - - end + end end - diff --git a/app/controllers/nonprofits/bank_accounts_controller.rb b/app/controllers/nonprofits/bank_accounts_controller.rb index c68ef006..3982fa70 100644 --- a/app/controllers/nonprofits/bank_accounts_controller.rb +++ b/app/controllers/nonprofits/bank_accounts_controller.rb @@ -1,64 +1,72 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class BankAccountsController < ApplicationController - include Controllers::NonprofitHelper + class BankAccountsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_admin! + before_action :authenticate_nonprofit_admin! - # post /nonprofits/:nonprofit_id/bank_account - # must pass in the user's password as params[:password] - def create - if password_was_confirmed(params[:pw_token]) - render_json { InsertBankAccount.with_stripe(current_nonprofit, current_user, params[:bank_account]) } - else - render json: ["Please confirm your password"], status: :unprocessable_entity - end - end + # post /nonprofits/:nonprofit_id/bank_account + # must pass in the user's password as params[:password] + def create + if password_was_confirmed(params[:pw_token]) + render_json { InsertBankAccount.with_stripe(current_nonprofit, current_user, params[:bank_account]) } + else + render json: ['Please confirm your password'], status: :unprocessable_entity + end + end - # get /nonprofits/:nonprofit_id/bank_account/confirmation - def confirmation - @nonprofit = Nonprofit.find(params[:nonprofit_id]) - @bank_account = @nonprofit.bank_account - end + # get /nonprofits/:nonprofit_id/bank_account/confirmation + def confirmation + @nonprofit = Nonprofit.find(params[:nonprofit_id]) + @bank_account = @nonprofit.bank_account + end - # post /nonprofits/:nonprofit_id/bank_account/confirmation - def confirm - npo = current_nonprofit - ba = npo.bank_account - if params[:token] == ba.confirmation_token - ba.update_attribute(:pending_verification, false) - flash[:notice] = "Your bank account is now confirmed!" - redirect_to nonprofits_payouts_path(npo) - else - redirect_to(nonprofits_donations_path(npo), {:flash => {:error => "We could not confirm this bank account. Please follow the exact link provided in the confirmation email."}}) - end - end + # post /nonprofits/:nonprofit_id/bank_account/confirmation + def confirm + npo = current_nonprofit + ba = npo.bank_account + if params[:token] == ba.confirmation_token + ba.update_attribute(:pending_verification, false) + flash[:notice] = 'Your bank account is now confirmed!' + redirect_to nonprofits_payouts_path(npo) + else + redirect_to(nonprofits_donations_path(npo), flash: { error: 'We could not confirm this bank account. Please follow the exact link provided in the confirmation email.' }) + end + end - # get /nonprofits/:nonprofit_id/bank_account/cancellation - def cancellation - @nonprofit = Nonprofit.find(params[:nonprofit_id]) - @bank_account = @nonprofit.bank_account - end + # get /nonprofits/:nonprofit_id/bank_account/cancellation + def cancellation + @nonprofit = Nonprofit.find(params[:nonprofit_id]) + @bank_account = @nonprofit.bank_account + end - # post /nonprofits/:nonprofit_id/bank_account/cancel - def cancel - npo = current_nonprofit - ba = npo.bank_account - if params[:token] == ba.confirmation_token - ba.destroy - flash[:notice] = "Your bank account has been removed." - redirect_to nonprofits_donations_path(npo) - else - redirect_to(nonprofits_donations_path(npo), {:flash => {:error => "We could not remove this bank account. Please follow the exact link provided in the email."}}) - end - end + # post /nonprofits/:nonprofit_id/bank_account/cancel + def cancel + npo = current_nonprofit + ba = npo.bank_account + if params[:token] == ba.confirmation_token + ba.destroy + flash[:notice] = 'Your bank account has been removed.' + redirect_to nonprofits_donations_path(npo) + else + redirect_to(nonprofits_donations_path(npo), flash: { error: 'We could not remove this bank account. Please follow the exact link provided in the email.' }) + end + end - def resend_confirmation - npo = current_nonprofit - ba = npo.bank_account - NonprofitMailer.delay.new_bank_account_notification(ba) if ba.valid? - respond_to{|format| format.json{render json: {}}} - end + def resend_confirmation + npo = current_nonprofit + ba = npo.bank_account + BankAccountCreateJob.perform_later(ba) if ba.valid? + respond_to { |format| format.json { render json: {} } } + end -end + private + + def required_params + params.permit(:name, :confirmation_token, :account_number, :bank_name, :pending_verification, :status, :email, :deleted, :stripe_bank_account_token, :stripe_bank_account_id, :nonprofit_id) + end + end end diff --git a/app/controllers/nonprofits/button_controller.rb b/app/controllers/nonprofits/button_controller.rb index 83a6765e..caaa866b 100644 --- a/app/controllers/nonprofits/button_controller.rb +++ b/app/controllers/nonprofits/button_controller.rb @@ -1,28 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class ButtonController < ApplicationController - include Controllers::NonprofitHelper + class ButtonController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_user! - before_filter :authenticate_user! + def send_code + NonprofitMailer.button_code(current_nonprofit, params[:to_email], params[:to_name], params[:from_email], params[:message], params[:code]).deliver + render json: {}, status: 200 + end + def basic + @nonprofit = current_nonprofit + end - def send_code - NonprofitMailer.button_code(current_nonprofit, params[:to_email], params[:to_name], params[:from_email], params[:message], params[:code]).deliver - render json: {}, status: 200 - end + def guided + @nonprofit = current_nonprofit + end - def basic - @nonprofit = current_nonprofit - end - - def guided - @nonprofit = current_nonprofit - end - - def advanced - @nonprofit = current_nonprofit - end - -end + def advanced + @nonprofit = current_nonprofit + end + end end diff --git a/app/controllers/nonprofits/cards_controller.rb b/app/controllers/nonprofits/cards_controller.rb index fc0e377b..6bd0792b 100644 --- a/app/controllers/nonprofits/cards_controller.rb +++ b/app/controllers/nonprofits/cards_controller.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class CardsController < ApplicationController - include Controllers::NonprofitHelper + class CardsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! def edit @nonprofit = current_nonprofit @@ -12,7 +15,7 @@ module Nonprofits # POST /nonprofits/:nonprofit_id/card def create render( - JsonResp.new(params) do |d| + JsonResp.new(params) do |_d| requires(:nonprofit_id).as_int requires(:card).nested do requires(:name, :stripe_card_token, :stripe_card_id).as_string @@ -20,11 +23,13 @@ module Nonprofits requires(:holder_type).one_of('Supporter', 'Nonprofit') end end.when_valid do |d| - UpdateBillingSubscriptions.activate_from_trial(d[:nonprofit_id]) InsertCard.with_stripe(d[:card]) end ) end + def required_params + params.require(:nonprofit_id, card: [:name, :stripe_card_token, :stripe_card_id, :holder_id, :holder_type]) + end end end diff --git a/app/controllers/nonprofits/charges_controller.rb b/app/controllers/nonprofits/charges_controller.rb index e3d87a6c..232a6c70 100644 --- a/app/controllers/nonprofits/charges_controller.rb +++ b/app/controllers/nonprofits/charges_controller.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class ChargesController < ApplicationController - include Controllers::NonprofitHelper + class ChargesController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user!, only: :index + before_action :authenticate_nonprofit_user!, only: :index - # get /nonprofit/:nonprofit_id/charges - def index - redirect_to controller: :payments, action: :index - end # def index - - end + # get /nonprofit/:nonprofit_id/charges + def index + redirect_to controller: :payments, action: :index + end # def index + end end diff --git a/app/controllers/nonprofits/custom_field_joins_controller.rb b/app/controllers/nonprofits/custom_field_joins_controller.rb index 9c1a7fe2..57cbd114 100644 --- a/app/controllers/nonprofits/custom_field_joins_controller.rb +++ b/app/controllers/nonprofits/custom_field_joins_controller.rb @@ -1,41 +1,46 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class CustomFieldJoinsController < ApplicationController + class CustomFieldJoinsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + def index + @custom_field_joins = current_nonprofit + .supporters.find(custom_field_params[:supporter_id]) + .custom_field_joins + .order('created_at DESC') + end - def index - @custom_field_joins = current_nonprofit - .supporters.find(params[:supporter_id]) - .custom_field_joins - .order('created_at DESC') - end + # used for modify a single supporter's custom fields or a group of + # selected supporters' CFs or all supporters' CFs + def modify + if custom_field_params[:custom_fields].blank? || custom_field_params[:custom_fields].empty? + render json: {} + return + end - # used for modify a single supporter's custom fields or a group of - # selected supporters' CFs or all supporters' CFs - def modify - if params[:custom_fields].blank? || params[:custom_fields].empty? - render json: {} - return - end - - if params[:selecting_all] - supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, params[:query]).select("supporters.id").execute.map{|h| h['id']} - else - supporter_ids = params[:supporter_ids]. map(&:to_i) - end + if custom_field_params[:selecting_all] + supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, custom_field_params[:query]).select('supporters.id').execute.map { |h| h['id'] } + else + supporter_ids = custom_field_params[:supporter_ids]. map(&:to_i) + end - render InsertCustomFieldJoins.in_bulk(current_nonprofit.id, supporter_ids, params[:custom_fields]) - end + render InsertCustomFieldJoins.in_bulk(current_nonprofit.id, supporter_ids, custom_field_params[:custom_fields]) + end + def destroy + supporter = current_nonprofit.supporters.find(custom_field_params[:supporter_id]) + supporter.custom_field_joins.find(custom_field_params[:id]).destroy + render json: {}, status: :ok + end - def destroy - supporter = current_nonprofit.supporters.find(params[:supporter_id]) - supporter.custom_field_joins.find(params[:id]).destroy - render json: {}, status: :ok - end + private - end + def custom_field_params + params.permit(:selecting_all, :supporter_id, :supporter_ids, :custom_fields, :query, :id) + end + end end - diff --git a/app/controllers/nonprofits/custom_field_masters_controller.rb b/app/controllers/nonprofits/custom_field_masters_controller.rb index f4e75a14..e95691f5 100644 --- a/app/controllers/nonprofits/custom_field_masters_controller.rb +++ b/app/controllers/nonprofits/custom_field_masters_controller.rb @@ -1,27 +1,34 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class CustomFieldMastersController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + class CustomFieldMastersController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! - def index - @custom_field_masters = current_nonprofit - .custom_field_masters - .order('id DESC') - .not_deleted - end + def index + @custom_field_masters = current_nonprofit + .custom_field_masters + .order('id DESC') + .not_deleted + end - def create - json_saved CreateCustomFieldMaster.create(current_nonprofit, params[:custom_field_master]) - end + def create + json_saved CreateCustomFieldMaster.create(current_nonprofit, params[:custom_field_master]) + end - def destroy - custom_field_master = current_nonprofit.custom_field_masters.find(params[:id]) - custom_field_master.update_attribute(:deleted, true) - custom_field_master.custom_field_joins.destroy_all - render json: {}, status: :ok - end + def destroy + custom_field_master = current_nonprofit.custom_field_masters.find(params[:id]) + custom_field_master.update_attribute(:deleted, true) + custom_field_master.custom_field_joins.destroy_all + render json: {}, status: :ok + end - end + private + + def custom_field_master_params + params.require(:custom_field_master).permit(:nonprofit, :nonprofit_id, :name, :deleted, :created_at) + end + end end - diff --git a/app/controllers/nonprofits/donations_controller.rb b/app/controllers/nonprofits/donations_controller.rb index 4599788b..ea8d4bc9 100644 --- a/app/controllers/nonprofits/donations_controller.rb +++ b/app/controllers/nonprofits/donations_controller.rb @@ -1,84 +1,90 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class DonationsController < ApplicationController - include Controllers::NonprofitHelper + class DonationsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user!, only: [:index, :update] - before_filter :authenticate_campaign_editor!, only: [:create_offsite] + before_action :authenticate_nonprofit_user!, only: %i[index update] + before_action :authenticate_campaign_editor!, only: [:create_offsite] - # get /nonprofit/:nonprofit_id/donations - def index - redirect_to controller: :payments, action: :index - end # def index + # get /nonprofit/:nonprofit_id/donations + def index + redirect_to controller: :payments, action: :index + end # def index - # post /nonprofits/:nonprofit_id/donations - def create + # post /nonprofits/:nonprofit_id/donations + def create + if params[:token] + donations_params[:token] = params[:token] + render_json { InsertDonation.with_stripe(donations_params, current_user) } + elsif params[:direct_debit_detail_id] + render JsonResp.new(donations_params) do |_data| + requires(:amount).as_int + requires(:supporter_id, :nonprofit_id) + # TODO + # requires_either(:card_id, :direct_debit_detail_id).as_int + optional(:dedication, :designation).as_string + optional(:campaign_id, :event_id).as_int + end.when_valid do |data| - if params[:token] - params[:donation][:token] = params[:token] - return render_json{ InsertDonation.with_stripe(params[:donation], current_user) } - elsif params[:direct_debit_detail_id] - render JsonResp.new(params[:donation]){|data| - requires(:amount).as_int - requires(:supporter_id, :nonprofit_id) - # TODO - # requires_either(:card_id, :direct_debit_detail_id).as_int - optional(:dedication, :designation).as_string - optional(:campaign_id, :event_id).as_int - }.when_valid{|data| + InsertDonation.with_sepa(data) + end + end + end - - InsertDonation.with_sepa(data) - - } - end - end - - # post /nonprofits/:nonprofit_id/donations/create_offsite - def create_offsite - render JsonResp.new(params[:donation]){|data| + # post /nonprofits/:nonprofit_id/donations/create_offsite + def create_offsite + render JsonResp.new(donations_params) do |_data| requires(:amount).as_int.min(1) requires(:supporter_id, :nonprofit_id).as_int optional(:dedication, :designation).as_string optional(:campaign_id, :event_id).as_int optional(:date).as_date - optional(:offsite_payment).nested{ + optional(:offsite_payment).nested do optional(:kind).one_of('cash', 'check') optional(:check_number) - } - }.when_valid{|data| InsertDonation.offsite(data)} - end + end + end.when_valid { |data| InsertDonation.offsite(data) } + end - def update - render_json{ UpdateDonation.update_payment(params[:id], params[:donation]) } - end + def update + render_json { UpdateDonation.update_payment(params[:id], donations_params) } + end - # put /nonprofits/:nonprofit_id/donations/:id - # update designation, dedication, or comment on a donation in the followup - def followup - nonprofit = Nonprofit.find(params[:nonprofit_id]) - donation = nonprofit.donations.find(params[:id]) - json_saved UpdateDonation.from_followup(donation, params[:donation]) - end + # put /nonprofits/:nonprofit_id/donations/:id + # update designation, dedication, or comment on a donation in the followup + def followup + nonprofit = Nonprofit.find(params[:nonprofit_id]) + donation = nonprofit.donations.find(params[:id]) + json_saved UpdateDonation.from_followup(donation, donations_params) + end - # this is a special, weird case - private + # this is a special, weird case + private - def current_campaign - if !@campaign && params[:donation] && params[:donation][:campaign_id] - @campaign = Campaign.where('id = ? ', params[:donation][:campaign_id]).first - end - return @campaign - end + def current_campaign + if !@campaign && donations_params && donations_params[:campaign_id] + @campaign = Campaign.where('id = ? ', donations_params[:campaign_id]).first + end + @campaign + end - def current_campaign_editor? - !params[:preview] && (current_nonprofit_user? || (current_campaign && current_role?(:campaign_editor, current_campaign.id)) || current_role?(:super_admin)) - end + def current_campaign_editor? + !params[:preview] && (current_nonprofit_user? || (current_campaign && current_role?(:campaign_editor, current_campaign.id)) || current_role?(:super_admin)) + end - def authenticate_campaign_editor! - unless current_campaign_editor? - block_with_sign_in 'You need to be a campaign editor to do that.' - end - end - end + def authenticate_campaign_editor! + unless current_campaign_editor? + block_with_sign_in 'You need to be a campaign editor to do that.' + end + end + + private + + def donations_params + params.require(:donation).permit(:date, :amount, :recurring, :anonymous, :email, :designation, :dedication, :comment, :origin_url, :nonprofit_id, :card_id, :supporter_id, :profile_id, :campaign_id, :payment_id, :event_id, :direct_debit_detail_id, :payment_provider) + end + end end diff --git a/app/controllers/nonprofits/email_lists_controller.rb b/app/controllers/nonprofits/email_lists_controller.rb index 1f3a5af0..0cb88813 100644 --- a/app/controllers/nonprofits/email_lists_controller.rb +++ b/app/controllers/nonprofits/email_lists_controller.rb @@ -1,17 +1,26 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class EmailListsController < ApplicationController - include Controllers::NonprofitHelper + class EmailListsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! - def index - render_json{ Qx.fetch(:email_lists, nonprofit_id: params[:nonprofit_id]) } - end + def index + render_json { Qx.fetch(:email_lists, nonprofit_id: email_list_params[:nonprofit_id]) } + end - def create - tag_master_ids = params['tag_masters'].values.map(&:to_i) - render_json{ InsertEmailLists.for_mailchimp(params[:nonprofit_id], tag_master_ids) } + def create + tag_master_ids = email_list_params[:tag_masters].values.map(&:to_i) + render_json { InsertEmailLists.for_mailchimp(email_list_params[:nonprofit_id], tag_master_ids) } + end + + private + + def email_list_params + params.permit(:nonprofit_id, :tag_masters) + end end end -end diff --git a/app/controllers/nonprofits/imports_controller.rb b/app/controllers/nonprofits/imports_controller.rb index b942bfc0..dfb212ea 100644 --- a/app/controllers/nonprofits/imports_controller.rb +++ b/app/controllers/nonprofits/imports_controller.rb @@ -1,20 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class ImportsController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! # post /nonprofits/:nonprofit_id/imports def create - render_json{ - InsertImport.delay.from_csv_safe({ - nonprofit_id: params[:nonprofit_id], - user_id: current_user.id, - user_email: current_user.email, - file_uri: params[:file_uri], - header_matches: params[:header_matches] - }) - } + render_json do + ImportCreationJob.perform_later(import_params, current_user) + end + end + + private + + def import_params + params.permit(:nonprofit_id, :file_uri, :header_matches) end end end diff --git a/app/controllers/nonprofits/miscellaneous_np_infos_controller.rb b/app/controllers/nonprofits/miscellaneous_np_infos_controller.rb index e0e7df41..59f1392b 100644 --- a/app/controllers/nonprofits/miscellaneous_np_infos_controller.rb +++ b/app/controllers/nonprofits/miscellaneous_np_infos_controller.rb @@ -1,10 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class MiscellaneousNpInfosController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization helper_method :current_nonprofit_user? - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! def show respond_to do |format| @@ -12,18 +15,17 @@ module Nonprofits render_json { FetchMiscellaneousNpInfo.fetch(params[:nonprofit_id]) } end end - end def update respond_to do |format| - format.json { - render_json { + format.json do + render_json do update = UpdateMiscellaneousNpInfo.update(params[:nonprofit_id], params[:miscellaneous_np_info]) - #flash[:notice] = "Your Miscellaneous Settings have been saved" + # flash[:notice] = "Your Miscellaneous Settings have been saved" update - } - } + end + end end end end diff --git a/app/controllers/nonprofits/nonprofit_keys_controller.rb b/app/controllers/nonprofits/nonprofit_keys_controller.rb index efde5950..62b5023b 100644 --- a/app/controllers/nonprofits/nonprofit_keys_controller.rb +++ b/app/controllers/nonprofits/nonprofit_keys_controller.rb @@ -1,38 +1,40 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class NonprofitKeysController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + class NonprofitKeysController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! - # get /nonprofits/:nonprofit_id/nonprofit_keys - # pass in the :select query param, which is the name of the column of the specific token you want - def index - render_json{QueryNonprofitKeys.get_key(current_nonprofit.id, params[:select])} - end - - # Redirects to the mailchimp OAuth2 landing page, first setting the nonprofit id in the session - # GET /nonprofits/:nonprofit_id/nonprofit_keys/mailchimp_login - def mailchimp_login - session[:current_mailchimp_nonprofit_id] = current_nonprofit.id - redirect_to "https://login.mailchimp.com/oauth2/authorize?response_type=code&client_id=#{ENV['MAILCHIMP_OAUTH_CLIENT_ID']}" - end - - # After the user OAuths mailchimp, they are redirected to /mailchimp-landing - # This action then redirects them back to /settings - # GET /mailchimp-landing - def mailchimp_landing - @nonprofit = Nonprofit.find(session[:current_mailchimp_nonprofit_id]) - session.delete(:current_mailchimp_nonprofit_id) - begin - session[:mailchimp_access_token] = InsertNonprofitKeys.insert_mailchimp_access_token(@nonprofit.id, params[:code]) - rescue Exception => e - flash[:notice] = "Unable to connect to your Mailchimp account, please try again. (Error: #{e})" - redirect_to '/settings' - return + # get /nonprofits/:nonprofit_id/nonprofit_keys + # pass in the :select query param, which is the name of the column of the specific token you want + def index + render_json { QueryNonprofitKeys.get_key(current_nonprofit.id, params[:select]) } end - redirect_to nonprofits_supporters_path @nonprofit, 'show-modal' => 'mailchimpSettingsModal' - end -end + # Redirects to the mailchimp OAuth2 landing page, first setting the nonprofit id in the session + # GET /nonprofits/:nonprofit_id/nonprofit_keys/mailchimp_login + def mailchimp_login + session[:current_mailchimp_nonprofit_id] = current_nonprofit.id + redirect_to "https://login.mailchimp.com/oauth2/authorize?response_type=code&client_id=#{ENV['MAILCHIMP_OAUTH_CLIENT_ID']}" + end + + # After the user OAuths mailchimp, they are redirected to /mailchimp-landing + # This action then redirects them back to /settings + # GET /mailchimp-landing + def mailchimp_landing + @nonprofit = Nonprofit.find(session[:current_mailchimp_nonprofit_id]) + session.delete(:current_mailchimp_nonprofit_id) + begin + session[:mailchimp_access_token] = InsertNonprofitKeys.insert_mailchimp_access_token(@nonprofit.id, params[:code]) + rescue Exception => e + flash[:notice] = "Unable to connect to your Mailchimp account, please try again. (Error: #{e})" + redirect_to '/settings' + return + end + redirect_to nonprofits_supporters_path @nonprofit, 'show-modal' => 'mailchimpSettingsModal' + end + end end diff --git a/app/controllers/nonprofits/payments_controller.rb b/app/controllers/nonprofits/payments_controller.rb index 62a96bc5..63a03ab7 100644 --- a/app/controllers/nonprofits/payments_controller.rb +++ b/app/controllers/nonprofits/payments_controller.rb @@ -1,32 +1,34 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class PaymentsController < ApplicationController - include Controllers::NonprofitHelper + class PaymentsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! - - # get /nonprofit/:nonprofit_id/payments - def index - @nonprofit = current_nonprofit - respond_to do |format| - format.html do + # get /nonprofit/:nonprofit_id/payments + def index + @nonprofit = current_nonprofit + respond_to do |format| + format.html do @panels_layout = true end - format.json do - @response = QueryPayments.full_search(params[:nonprofit_id], params) + format.json do + @response = QueryPayments.full_search(params[:nonprofit_id], params) render json: @response, status: :ok - end - end - end # def index + end + end + end # def index def export begin @nonprofit = current_nonprofit @user = current_user_id - ExportPayments::initiate_export(@nonprofit.id, params, @user) - rescue => e + ExportPayments.initiate_export(@nonprofit.id, params, @user) + rescue StandardError => e e end if e.nil? @@ -37,14 +39,15 @@ module Nonprofits end end - def show - @nonprofit = current_nonprofit - @payment = @nonprofit.payments.find(params[:id]) - end # def show + def show + @nonprofit = current_nonprofit + @payment = @nonprofit.payments.find(params[:id]) + render locals: {payment: @payment} + end # def show def update @payment = current_nonprofit.payments.find(params[:id]) - @payment.update_attributes(params[:payment]) + @payment.update(payment_params) json_saved @payment end @@ -68,11 +71,18 @@ module Nonprofits PaymentMailer.resend_donor_receipt(params[:id]) render json: {} end + # post /nonprofits/:nonprofit_id/payments/:id/resend_admin_receipt # pass user_id of the admin to send to def resend_admin_receipt PaymentMailer.resend_admin_receipt(params[:id], current_user.id) render json: {} end - end # class PaymentsController + + private + + def payment_params + params.require(:payment).permit(:towards, :gross_amount, :refund_total, :fee_total, :kind, :date) + end + end # class PaymentsController end # module Nonprofits diff --git a/app/controllers/nonprofits/payouts_controller.rb b/app/controllers/nonprofits/payouts_controller.rb index 5b0e0b55..047a1197 100644 --- a/app/controllers/nonprofits/payouts_controller.rb +++ b/app/controllers/nonprofits/payouts_controller.rb @@ -1,49 +1,58 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class PayoutsController < ApplicationController - include Controllers::NonprofitHelper + class PayoutsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_admin!, only: :create - before_filter :authenticate_nonprofit_user!, only: [:index, :show] + before_action :authenticate_nonprofit_admin!, only: :create + before_action :authenticate_nonprofit_user!, only: %i[index show] - def create - payout = InsertPayout.with_stripe(current_nonprofit.id, { - stripe_account_id: current_nonprofit.stripe_account_id, - email: current_user.email, - user_ip: current_user.current_sign_in_ip, - bank_name: current_nonprofit.bank_account.name - }, {before_date: params[:before_date]}) + def create + payout = InsertPayout.with_stripe(current_nonprofit.id, { + stripe_account_id: current_nonprofit.stripe_account_id, + email: current_user.email, + user_ip: current_user.current_sign_in_ip, + bank_name: current_nonprofit.bank_account.name + }, before_date: payout_params[:before_date]) - if payout['failure_message'].present? - flash[:notice] = "The payout failed: #{payout['failure_message']}" - render json: payout, status: :unprocessable_entity - else - flash[:notice] = 'We successfully submitted your payout! View status and receipts below.' - render json: payout, status: :ok - end - end - - def index - @nonprofit = Nonprofit.find(params[:nonprofit_id]) - @payouts = @nonprofit.payouts.order('created_at DESC') - balances = QueryPayments.nonprofit_balances(params[:nonprofit_id]) - @available_total = balances['available_gross'] - @pending_total = balances['pending_gross'] - @can_make_payouts = @nonprofit.can_make_payouts - end - - # get /nonprofits/:nonprofit_id/payouts/:id - def show - payout = current_nonprofit.payouts.find(params[:id]) - respond_to do |format| - format.json{render json: payout} - format.csv do - payments = QueryPayments.for_payout(params[:nonprofit_id], params[:id]) - filename = "payout-#{payout.created_at.strftime("%m-%d-%Y")}" - send_data(Format::Csv.from_vectors(payments), filename: "#{filename}.csv") + if payout['failure_message'].present? + flash[:notice] = "The payout failed: #{payout['failure_message']}" + render json: payout, status: :unprocessable_entity + else + flash[:notice] = 'We successfully submitted your payout! View status and receipts below.' + render json: payout, status: :ok end end - end -end + def index + @nonprofit = Nonprofit.find(params[:nonprofit_id]) + @payouts = @nonprofit.payouts.order('created_at DESC') + balances = QueryPayments.nonprofit_balances(params[:nonprofit_id]) + @available_total = balances['available_gross'] + @pending_total = balances['pending_gross'] + @can_make_payouts = @nonprofit.can_make_payouts + end + + # get /nonprofits/:nonprofit_id/payouts/:id + def show + payout = current_nonprofit.payouts.find(params[:id]) + respond_to do |format| + format.json { render json: payout } + format.csv do + payments = QueryPayments.for_payout(params[:nonprofit_id], params[:id]) + filename = "payout-#{payout.created_at.strftime('%m-%d-%Y')}" + send_data(Format::Csv.from_vectors(payments), filename: "#{filename}.csv") + end + end + end + + private + + def payout_params + params.permit(:before_date) + end + + end end diff --git a/app/controllers/nonprofits/recurring_donations_controller.rb b/app/controllers/nonprofits/recurring_donations_controller.rb index 9aa4260e..78fed2f4 100644 --- a/app/controllers/nonprofits/recurring_donations_controller.rb +++ b/app/controllers/nonprofits/recurring_donations_controller.rb @@ -1,94 +1,104 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class RecurringDonationsController < ApplicationController - include Controllers::NonprofitHelper + class RecurringDonationsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user!, except: [:create] + before_action :authenticate_nonprofit_user!, except: [:create] - # get /nonprofits/:nonprofit_id/recurring_donations - def index - @nonprofit = current_nonprofit - @panels_layout = true - respond_to do |format| - format.html - format.json do - # set dashboard params include externally active and failed - #TODO move into javascript - params[:active] = true + # get /nonprofits/:nonprofit_id/recurring_donations + def index + @nonprofit = current_nonprofit + @panels_layout = true + respond_to do |format| + format.html + format.json do + # set dashboard params include externally active and failed + # TODO move into javascript + params[:active] = true - render json: QueryRecurringDonations.full_list(params[:nonprofit_id], params) + render json: QueryRecurringDonations.full_list(params[:nonprofit_id], params) + end end - end - end + end - def export - begin - @nonprofit = current_nonprofit - @user = current_user_id - #TODO move into javascript - if params.key?(:active_and_not_failed) - params.delete(:active) if params.key?(:active) - params.delete(:failed) if params.key?(:failed) - end + def export + begin + @nonprofit = current_nonprofit + @user = current_user_id + # TODO: move into javascript + if params.key?(:active_and_not_failed) + params.delete(:active) if params.key?(:active) + params.delete(:failed) if params.key?(:failed) + end - [:active_and_not_failed, :active, :failed].each do |k| - if (params.key?(k)) - params[k] = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(params[k]) - end - end + %i[active_and_not_failed active failed].each do |k| + if params.key?(k) + params[k] = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(params[k]) + end + end - params[:root_url] = root_url + params[:root_url] = root_url - ExportRecurringDonations::initiate_export(@nonprofit.id, params, current_user.id) - rescue => e - e - end - if e.nil? - flash[:notice] = "Your export was successfully initiated and you'll be emailed at #{current_user.email} as soon as it's available. Feel free to use the site in the meantime." - render json: {}, status: :ok - else - render json: e, status: :ok - end - end + ExportRecurringDonations.initiate_export(@nonprofit.id, params, current_user.id) + rescue StandardError => e + e + end + if e.nil? + flash[:notice] = "Your export was successfully initiated and you'll be emailed at #{current_user.email} as soon as it's available. Feel free to use the site in the meantime." + render json: {}, status: :ok + else + render json: e, status: :ok + end + end - def show - @recurring_donation = current_recurring_donation - respond_to {|format| format.json} - end + def show + @recurring_donation = current_recurring_donation + respond_to do |format| + format.json do + render locals: {recurring_donation: @recurring_donation} + end + end + end - def destroy - UpdateRecurringDonations.cancel(params[:id], current_user.email) - json_saved current_recurring_donation - end + def destroy + UpdateRecurringDonations.cancel(params[:id], current_user.email) + json_saved current_recurring_donation + end - def update - json_saved UpdateRecurringDonations - .update(current_recurring_donation, params[:recurring_donation]) - end + def update + json_saved UpdateRecurringDonations + .update(current_recurring_donation, recurring_donation_params) + end - # post /nonprofits/:nonprofit_id/recurring_donations - def create - if params[:recurring_donation][:token] - render_json{ InsertRecurringDonation.with_stripe(params[:recurring_donation]) } - elsif params[:recurring_donation][:direct_debit_detail_id] - render JsonResp.new(params[:recurring_donation]){|data| - requires(:amount).as_int - requires(:supporter_id, :nonprofit_id, :direct_debit_detail_id).as_int - optional(:dedication, :designation).as_string - optional(:campaign_id, :event_id).as_int - }.when_valid{|data| - InsertRecurringDonation.with_sepa(data) - } - else - render json: {}, status: 422 + # post /nonprofits/:nonprofit_id/recurring_donations + def create + if recurring_donation_params[:token] + render_json { InsertRecurringDonation.with_stripe(recurring_donation_params) } + elsif recurring_donation_params[:direct_debit_detail_id] + render JsonResp.new(recurring_donation_params) do |_data| + requires(:amount).as_int + requires(:supporter_id, :nonprofit_id, :direct_debit_detail_id).as_int + optional(:dedication, :designation).as_string + optional(:campaign_id, :event_id).as_int + end.when_valid do |data| + InsertRecurringDonation.with_sepa(data) + end + else + render json: {}, status: 422 + end + end + + private + + def current_recurring_donation + @recurring_donation ||= current_nonprofit.recurring_donations.find params[:id] + end + + def recurring_donation_params + params.require(:recurring_donation).permit(:amount, :active, :paydate, :interval, :time_unit, :start_date, :end_date, :n_failures, :edit_token, :cancelled_by, :cancelled_at, :donation_id, :nonprofit_id, :supporter_id) end end - -private - - def current_recurring_donation - @recurring_donation ||= current_nonprofit.recurring_donations.find params[:id] - end - -end end diff --git a/app/controllers/nonprofits/refunds_controller.rb b/app/controllers/nonprofits/refunds_controller.rb index aecd77a5..8afa35e8 100644 --- a/app/controllers/nonprofits/refunds_controller.rb +++ b/app/controllers/nonprofits/refunds_controller.rb @@ -1,21 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class RefundsController < ApplicationController - include Controllers::NonprofitHelper + class RefundsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user! + before_action :authenticate_nonprofit_user! - # post /charges/:charge_id/refunds - def create - charge = Qx.select("*").from("charges").where(id: params[:charge_id]).execute.first - params[:refund][:user_id] = current_user.id - render_json{ InsertRefunds.with_stripe(charge, params['refund']) } - end + # post /charges/:charge_id/refunds + def create + charge = Qx.select('*').from('charges').where(id: charge_params[:charge_id]).execute.first + params[:refund][:user_id] = current_user.id + render_json { InsertRefunds.with_stripe(charge, charge_params['refund']) } + end - def index - charge = current_nonprofit.charges.find(params[:charge_id]) - @refunds = charge.refunds - end - end + def index + charge = current_nonprofit.charges.find(params[:charge_id]) + @refunds = charge.refunds + end + +private + + def charge_params + params.require(:charge_id, refund: [:amount]) + end + end end - diff --git a/app/controllers/nonprofits/reports_controller.rb b/app/controllers/nonprofits/reports_controller.rb index fd7e0346..a5fdd6f6 100644 --- a/app/controllers/nonprofits/reports_controller.rb +++ b/app/controllers/nonprofits/reports_controller.rb @@ -1,14 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class ReportsController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! def end_of_year respond_to do |format| format.csv do filename = "end-of-year-report-#{params[:year]}.csv" - data = QuerySupporters.year_aggregate_report(params[:nonprofit_id], {:year => params[:year]}) + data = QuerySupporters.year_aggregate_report(params[:nonprofit_id], year: params[:year]) send_data(Format::Csv.from_array(data), filename: filename) end end @@ -18,17 +21,15 @@ module Nonprofits respond_to do |format| format.csv do name_description = nil - if (params[:year]) + if params[:year] name_description = params[:year] - elsif (params[:start]) + elsif params[:start] name_description = "from-#{params[:start]}" - if (params[:end]) - name_description += "-to-#{params[:end]}" - end + name_description += "-to-#{params[:end]}" if params[:end] end filename = "aggregate-report-#{name_description}.csv" - data = QuerySupporters.year_aggregate_report(params[:nonprofit_id], {:year => params[:year], :start => params[:start], :end => params[:end]}) + data = QuerySupporters.year_aggregate_report(params[:nonprofit_id], year: params[:year], start: params[:start], end: params[:end]) send_data(Format::Csv.from_array(data), filename: filename) end end diff --git a/app/controllers/nonprofits/supporter_emails_controller.rb b/app/controllers/nonprofits/supporter_emails_controller.rb index 724e53be..5a3930ac 100644 --- a/app/controllers/nonprofits/supporter_emails_controller.rb +++ b/app/controllers/nonprofits/supporter_emails_controller.rb @@ -1,25 +1,27 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class SupporterEmailsController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! def create if params[:selecting_all] ids = QuerySupporters.full_filter_expr(params[:nonprofit_id], params[:query]) - .select("supporters.id") - .execute(format: 'csv')[1..-1].flatten + .select('supporters.id') + .execute(format: 'csv')[1..-1].flatten elsif params[:supporter_ids] ids = params[:supporter_ids] end if ids.nil? || ids.empty? - render json: {errors: 'Supporters not found'}, status: :unprocessable_entity + render json: { errors: 'Supporters not found' }, status: :unprocessable_entity return end - DelayedJobHelper.enqueue_job(EmailSupporters, :deliver, [ids, params[:supporter_email]]) - render json: {count: ids.count}, status: :ok + render json: { count: ids.count }, status: :ok end def gmail @@ -29,4 +31,3 @@ module Nonprofits end end end - diff --git a/app/controllers/nonprofits/supporter_notes_controller.rb b/app/controllers/nonprofits/supporter_notes_controller.rb index 554e4827..1926ea32 100644 --- a/app/controllers/nonprofits/supporter_notes_controller.rb +++ b/app/controllers/nonprofits/supporter_notes_controller.rb @@ -1,27 +1,36 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class SupporterNotesController < ApplicationController - include Controllers::NonprofitHelper + class SupporterNotesController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user!, except: [:create] + before_action :authenticate_nonprofit_user!, except: [:create] - # post /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes - def create - params[:supporter_note][:user_id] ||= current_user && current_user.id - render_json{ InsertSupporterNotes.create([params[:supporter_note]]) } - end + # post /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes + def create + params[:supporter_note][:user_id] ||= current_user&.id + render_json { InsertSupporterNotes.create([supporter_params[:supporter_note]]) } + end - # put /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes/:id - def update - params[:supporter_note][:user_id] ||= current_user && current_user.id - params[:supporter_note][:id] = params[:id] - render_json{ UpdateSupporterNotes.update(params[:supporter_note]) } - end + # put /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes/:id + def update + params[:supporter_note][:user_id] ||= current_user&.id + params[:supporter_note][:id] = params[:id] + render_json { UpdateSupporterNotes.update(supporter_params[:supporter_note]) } + end - # delete /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes/:id - def destroy - render_json{ UpdateSupporterNotes.delete(params[:id]) } - end + # delete /nonprofits/:nonprofit_id/supporters/:supporter_id/supporter_notes/:id + def destroy + render_json { UpdateSupporterNotes.delete(params[:id]) } + end -end + private + + def supporter_params + params.require(:supporter_note) + + end + end end diff --git a/app/controllers/nonprofits/supporters_controller.rb b/app/controllers/nonprofits/supporters_controller.rb index 380564cc..ee2173e7 100644 --- a/app/controllers/nonprofits/supporters_controller.rb +++ b/app/controllers/nonprofits/supporters_controller.rb @@ -1,114 +1,120 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits -class SupportersController < ApplicationController - include Controllers::NonprofitHelper + class SupportersController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_user!, except: [:new, :create] - #before_filter(except: [:create, :mailchimp_landing]){authenticate_min_nonprofit_plan(2)} + before_action :authenticate_nonprofit_user!, except: %i[new create] - # get /nonprofit/:nonprofit_id/supporters - def index - @panels_layout = true - @nonprofit = current_nonprofit - respond_to do |format| - format.html - format.json do - render json: QuerySupporters.full_search(params[:nonprofit_id], params) - end - - format.csv do - file_date = Date.today.strftime("%m-%d-%Y") - supporters = QuerySupporters.for_export(params[:nonprofit_id], params) - send_data(Format::Csv.from_vectors(supporters), filename: "supporters-#{file_date}.csv") - end - end - end - - def export - begin + # get /nonprofit/:nonprofit_id/supporters + def index + @panels_layout = true @nonprofit = current_nonprofit - @user = current_user_id - ExportSupporters::initiate_export(@nonprofit.id, params, @user) - rescue => e - e + respond_to do |format| + format.html + format.json do + render json: QuerySupporters.full_search(params[:nonprofit_id], params) + end + + format.csv do + file_date = Date.today.strftime('%m-%d-%Y') + supporters = QuerySupporters.for_export(params[:nonprofit_id], params) + send_data(Format::Csv.from_vectors(supporters), filename: "supporters-#{file_date}.csv") + end + end end - if e.nil? - flash[:notice] = "Your export was successfully initiated and you'll be emailed at #{current_user.email} as soon as it's available. Feel free to use the site in the meantime." - render json: {}, status: :ok - else - render json: e, status: :ok + + def export + begin + @nonprofit = current_nonprofit + @user = current_user_id + ExportSupporters.initiate_export(@nonprofit.id, params, @user) + rescue StandardError => e + e + end + if e.nil? + flash[:notice] = "Your export was successfully initiated and you'll be emailed at #{current_user.email} as soon as it's available. Feel free to use the site in the meantime." + render json: {}, status: :ok + else + render json: e, status: :ok + end + end + + def index_metrics + render_json do + QuerySupporters.full_search_metrics(params[:nonprofit_id], params) + end + end + + def show + render json: { data: QuerySupporters.for_crm_profile(params[:nonprofit_id], [params[:id]]).first } + end + + def email_address + render json: Supporter.find(params[:supporter_id]).email + end + + def full_contact + fc = FullContactInfo.where("supporter_id=#{params[:id]}").first + if fc + render json: { full_contact: QueryFullContactInfos.fetch_associated_tables(fc.id) } + else + render json: { full_contact: nil } + end + end + + def info_card + render json: QuerySupporters.for_info_card(params[:supporter_id]) + end + + # post /nonprofits/:nonprofit_id/supporters + def create + render_json { InsertSupporter.create_or_update(params[:nonprofit_id], create_supporter_params.to_h) } + end + + # put /nonprofits/:nonprofit_id/supporters/:id + def update + @supporter = current_nonprofit.supporters.find(params[:id]) + json_saved UpdateSupporter.from_info(@supporter, update_supporter_params[:supporter]) + end + + def bulk_delete + if params[:selecting_all] + supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, params[:query]).select('supporters.id').execute.map { |h| h['id'] } + else + supporter_ids = params[:supporter_ids]. map(&:to_i) + end + render_json { UpdateSupporter.bulk_delete(current_nonprofit.id, supporter_ids) } + end + + # get /nonprofits/:nonprofit_id/supporters/merge_data + # returns the info required to merge two supporters + def merge_data + render json: QuerySupporters.merge_data(params[:ids]) + end + + # post /nonprofits/:nonprofit_id/supporters/merge + def merge + render JsonResp.new(params) do |_params| + requires(:supporter) + requires(:nonprofit_id).as_int + requires(:supporter_ids).as_array + end.when_valid do |params| + params[:supporter][:nonprofit_id] = params[:nonprofit_id] + MergeSupporters.selected(update_supporter_params[:supporter], params[:supporter_ids], params[:nonprofit_id], current_user.id) + end + end + + private + + def create_supporter_params + params.require(:supporter).permit(:name, :address, :city, :state_code, :country, :address_line2, :first_name, :last_name, :customFields) + end + + def update_supporter_params + params.require(:supporter).permit(:name, :address, :city, :state_code, :country, :address_line2) end end - - def index_metrics - render_json do - QuerySupporters.full_search_metrics(params[:nonprofit_id], params) - end - end - - def show - render json: {data: QuerySupporters.for_crm_profile(params[:nonprofit_id], [params[:id]]).first} - end - - def email_address - render json: Supporter.find(params[:supporter_id]).email - end - - def full_contact - fc = FullContactInfo.where("supporter_id=#{params[:supporter_id]}").first - if fc - render json: {full_contact: QueryFullContactInfos.fetch_associated_tables(fc.id )} - else - render json: {full_contact: nil} - end - end - - def info_card - render json: QuerySupporters.for_info_card(params[:supporter_id]) - end - - - # post /nonprofits/:nonprofit_id/supporters - def create - render_json{ InsertSupporter.create_or_update(params[:nonprofit_id], params[:supporter]) } - end - - # put /nonprofits/:nonprofit_id/supporters/:id - def update - @supporter = current_nonprofit.supporters.find(params[:id]) - json_saved UpdateSupporter.from_info(@supporter, params[:supporter]) - end - - def bulk_delete - if params[:selecting_all] - supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, params[:query]).select("supporters.id").execute.map{|h| h['id']} - else - supporter_ids = params[:supporter_ids]. map(&:to_i) - end - render_json {UpdateSupporter.bulk_delete(current_nonprofit.id, supporter_ids ) } - end - - # get /nonprofits/:nonprofit_id/supporters/merge_data - # returns the info required to merge two supporters - def merge_data - render json: QuerySupporters.merge_data(params[:ids]) - end - - # post /nonprofits/:nonprofit_id/supporters/merge - def merge - render JsonResp.new(params){|params| - requires(:supporter) - requires(:nonprofit_id).as_int - requires(:supporter_ids).as_array - }.when_valid{|params| - params[:supporter][:nonprofit_id] = params[:nonprofit_id] - MergeSupporters.selected(params[:supporter], params[:supporter_ids], params[:nonprofit_id], current_user.id) - } - end - - # def new - # @nonprofit = current_nonprofit - # end - -end end diff --git a/app/controllers/nonprofits/tag_joins_controller.rb b/app/controllers/nonprofits/tag_joins_controller.rb index 2552120c..6471927e 100644 --- a/app/controllers/nonprofits/tag_joins_controller.rb +++ b/app/controllers/nonprofits/tag_joins_controller.rb @@ -1,36 +1,33 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits - class TagJoinsController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + class TagJoinsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! def index render_json do - {data: QuerySupporters.tag_joins(params['nonprofit_id'], params['supporter_id'])} + { data: QuerySupporters.tag_joins(params['nonprofit_id'], params['supporter_id']) } end end - # used for modify a single supporter's tags or a group of - # selected supporters' tags or all supporters' tags - def modify - + # used for modify a single supporter's tags or a group of + # selected supporters' tags or all supporters' tags + def modify if params[:selecting_all] - supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, params[:query]).select("supporters.id").execute.map{|h| h['id']} + supporter_ids = QuerySupporters.full_filter_expr(current_nonprofit.id, params[:query]).select('supporters.id').execute.map { |h| h['id'] } else supporter_ids = params[:supporter_ids]. map(&:to_i) end - render InsertTagJoins.in_bulk(current_nonprofit.id, current_user.profile.id, supporter_ids, params[:tags]) + render InsertTagJoins.in_bulk(current_nonprofit.id, current_user.profile.id, supporter_ids, params[:tags]) + end - - - end - - def destroy - supporter = current_nonprofit.supporters.find(params[:supporter_id]) - supporter.tag_joins.find(params[:id]).destroy - render json: {}, status: :ok - end - - end + def destroy + supporter = current_nonprofit.supporters.find(params[:supporter_id]) + supporter.tag_joins.find(params[:id]).destroy + render json: {}, status: :ok + end + end end - diff --git a/app/controllers/nonprofits/tag_masters_controller.rb b/app/controllers/nonprofits/tag_masters_controller.rb index cd0132da..42e234bb 100644 --- a/app/controllers/nonprofits/tag_masters_controller.rb +++ b/app/controllers/nonprofits/tag_masters_controller.rb @@ -1,22 +1,25 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class TagMastersController < ApplicationController - include Controllers::NonprofitHelper - before_filter :authenticate_nonprofit_user! + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization + before_action :authenticate_nonprofit_user! def index - render json: {data: - Qx.select('id', 'name', 'created_at') + render json: { data: + Qx.select('id', 'name', 'created_at') .from('tag_masters') .where( ['tag_masters.nonprofit_id = $id', id: current_nonprofit.id], - ["coalesce(deleted, FALSE) = FALSE"]) - .execute - } + ['coalesce(deleted, FALSE) = FALSE'] + ) + .execute } end def create - json_saved CreateTagMaster.create(current_nonprofit, params[:tag_master]) + json_saved(current_nonprofit.tag_masters.create(tag_master_params[:tag_master])) end def destroy @@ -26,6 +29,12 @@ module Nonprofits render json: {}, status: :ok end + private + + def tag_master_params + params.require(:tag_master).permit(:name).tap do |tag_params| + tag_params.require(:name) # SAFER + end + end end end - diff --git a/app/controllers/nonprofits/trackings_controller.rb b/app/controllers/nonprofits/trackings_controller.rb index 208e64b7..8a9ca5ce 100644 --- a/app/controllers/nonprofits/trackings_controller.rb +++ b/app/controllers/nonprofits/trackings_controller.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Nonprofits class TrackingsController < ApplicationController # POST /nonprofits/:nonprofit_id/tracking def create - render JsonResp.new(params){|data| + render JsonResp.new(params) do |_data| requires(:donation_id).as_int optional(:utm_campaign, :utm_content, :utm_medium, :utm_source).as_string - }.when_valid{|data| + end.when_valid do |_data| InsertTracking.create(params) - } + end end end end diff --git a/app/controllers/nonprofits_controller.rb b/app/controllers/nonprofits_controller.rb index d33e46a4..d54af193 100755 --- a/app/controllers/nonprofits_controller.rb +++ b/app/controllers/nonprofits_controller.rb @@ -1,67 +1,71 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later - class NonprofitsController < ApplicationController - include Controllers::NonprofitHelper +class NonprofitsController < ApplicationController + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - helper_method :current_nonprofit_user? - before_filter :authenticate_nonprofit_user!, only: [:dashboard, :dashboard_metrics, :dashboard_todos, :payment_history, :profile_todos, :recurring_donation_stats, :update, :verify_identity] - before_filter :authenticate_super_admin!, only: [:destroy] + helper_method :current_nonprofit_user? + before_action :authenticate_nonprofit_user!, only: %i[dashboard dashboard_metrics dashboard_todos payment_history profile_todos recurring_donation_stats update verify_identity] + before_action :authenticate_super_admin!, only: [:destroy] - # get /nonprofits/:id - # get /:state_code/:city/:name - def show + # get /nonprofits/:id + # get /:state_code/:city/:name + def show if !current_nonprofit.published && !current_role?(:super_admin) - block_with_sign_in + block_with_sign_in return end - @nonprofit = current_nonprofit - @url = Format::Url.concat(root_url, @nonprofit.url) - @supporters = @nonprofit.supporters.not_deleted - @profiles = @nonprofit.profiles.order('total_raised DESC').limit(5).includes(:user).uniq + @nonprofit = current_nonprofit + @url = Format::Url.concat(root_url, @nonprofit.url) + @supporters = @nonprofit.supporters.not_deleted events = @nonprofit.events.not_deleted.order('start_datetime desc') campaigns = @nonprofit.campaigns.not_deleted.not_a_child.order('created_at desc') - @events = events.upcoming - @any_past_events = events.past.any? - @active_campaigns = campaigns.active - @any_past_campaigns = campaigns.past.any? + @events = events.upcoming + @any_past_events = events.past.any? + @active_campaigns = campaigns.active + @any_past_campaigns = campaigns.past.any? - @nonprofit_background_image = FetchBackgroundImage.with_model(@nonprofit) + @nonprofit_background_image = @nonprofit.background_image.attached? ? + url_for(@nonprofit.background_image_by_size(:normal)) : + url_for(Image::DefaultNonprofitUrl) - respond_to do |format| - format.html - format.json {render json: @nonprofit} - end - end + respond_to do |format| + format.html + format.json { @nonprofit } + end + end def recurring_donation_stats render json: QueryRecurringDonations.overall_stats(params[:nonprofit_id]) end - def profile_todos - render json: FetchTodoStatus.for_profile(current_nonprofit) - end + def profile_todos + render json: FetchTodoStatus.for_profile(current_nonprofit) + end - def dashboard_todos - render json: FetchTodoStatus.for_dashboard(current_nonprofit) - end + def dashboard_todos + render json: FetchTodoStatus.for_dashboard(current_nonprofit) + end - def create + def create current_user ||= User.find(params[:user_id]) - json_saved Nonprofit.register(current_user, params[:nonprofit]) - end + json_saved Nonprofit.register(current_user, nonprofit_params) + end - def update - flash[:notice] = 'Update successful!' - current_nonprofit.update_attributes params[:nonprofit].except(:verification_status) - json_saved current_nonprofit - end + def update + flash[:notice] = 'Update successful!' + current_nonprofit.update nonprofit_params.except(:verification_status) + json_saved current_nonprofit + end - def destroy - current_nonprofit.destroy - flash[:notice] = 'Nonprofit removed' - render json: {} - end + def destroy + current_nonprofit.destroy + flash[:notice] = 'Nonprofit removed' + render json: {} + end # get /nonprofits/:id/donate def donate @@ -69,18 +73,18 @@ @referer = params[:origin] || request.env['HTTP_REFERER'] @campaign = current_nonprofit.campaigns.find_by_id(params[:campaign_id]) if params[:campaign_id] @countries_translations = countries_list(I18n.locale) - respond_to { |format| format.html{render layout: 'layouts/embed'} } + respond_to { |format| format.html { render layout: 'layouts/embed' } } end - def btn - @nonprofit = current_nonprofit - respond_to { |format| format.html{render layout: 'layouts/embed'} } - end + def btn + @nonprofit = current_nonprofit + respond_to { |format| format.html { render layout: 'layouts/embed' } } + end # get /nonprofits/:id/supporter_form def supporter_form - @nonprofit = current_nonprofit - respond_to { |format| format.html{render layout: 'layouts/embed'} } + @nonprofit = current_nonprofit + respond_to { |format| format.html { render layout: 'layouts/embed' } } end # post /nonprofits/:id/supporter_with_tag @@ -89,21 +93,21 @@ render json: InsertSupporter.with_tags_and_fields(@nonprofit.id, params[:supporter]) end - def dashboard - @nonprofit = current_nonprofit - respond_to { |format| format.html } - end + def dashboard + @nonprofit = current_nonprofit + respond_to { |format| format.html } + end - def dashboard_metrics - render json: Hamster::Hash[data: NonprofitMetrics.all_metrics(current_nonprofit.id)] - end + def dashboard_metrics + render json: Hamster::Hash[data: NonprofitMetrics.all_metrics(current_nonprofit.id)] + end - def payment_history + def payment_history render json: NonprofitMetrics.payment_history(params) - end + end - # put /nonprofits/:id/verify_identity - def verify_identity + # put /nonprofits/:id/verify_identity + def verify_identity if params[:legal_entity][:address] tos = { ip: current_user.current_sign_in_ip, @@ -111,8 +115,8 @@ user_agent: request.user_agent } end - render_json{ UpdateNonprofit.verify_identity(params[:nonprofit_id], params[:legal_entity], tos) } - end + render_json { UpdateNonprofit.verify_identity(params[:nonprofit_id], params[:legal_entity], tos) } + end def search render json: QueryNonprofits.by_search_string(params[:npo_name]) @@ -132,13 +136,64 @@ all_countries = ISO3166::Country.translations(locale) if Settings.intntl.all_countries - countries = all_countries.select{ |code, name| Settings.intntl.all_countries.include? code } - countries = countries.map{ |code, name| [code.upcase, name] }.sort{ |a, b| a[1] <=> b[1] } + countries = all_countries.select { |code, _name| Settings.intntl.all_countries.include? code } + countries = countries.map { |code, name| [code.upcase, name] }.sort_by { |a| a[1] } countries.push([Settings.intntl.other_country.upcase, I18n.t('nonprofits.donate.info.supporter.other_country')]) if Settings.intntl.other_country countries else - all_countries.map{ |code, name| [code.upcase, name] }.sort{ |a, b| a[1] <=> b[1] } + all_countries.map { |code, name| [code.upcase, name] }.sort_by { |a| a[1] } end end + def nonprofit_params + params.require(:nonprofit).permit( + :name, + :stripe_account_id, + :summary, + :tagline, + :email, + :phone, + :main_image, + :second_image, + :third_image, + :background_image, + :remove_background_image, + :logo, + :zip_code, + :website, + :categories, + :achievements, + :full_description, + :state_code, + :statement, + :city, + :slug, + :city_slug, + :state_code_slug, + :ein, + :published, + :vetted, + :verification_status, + :latitude, + :longitude, + :timezone, + :address, + :thank_you_note, + :referrer, + :no_anon, + :roles_attributes, + :brand_font, + :brand_color, + :hide_activity_feed, + :tracking_script, + :facebook, + :twitter, + :youtube, + :instagram, + :blog, + :card_failure_message_top, + :card_failure_message_bottom, + :autocomplete_supporter_address + ) + end end diff --git a/app/controllers/onboard_controller.rb b/app/controllers/onboard_controller.rb index 7e11316c..1a88c76f 100644 --- a/app/controllers/onboard_controller.rb +++ b/app/controllers/onboard_controller.rb @@ -1,5 +1,8 @@ +# frozen_string_literal: true + class OnboardController < ApplicationController layout 'layouts/apified' + def index @theme = 'minimal' end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index d219c4e2..72e7b7eb 100755 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -1,63 +1,64 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ProfilesController < ApplicationController - helper_method :authenticate_profile_owner! - before_filter :authenticate_profile_owner!, only: [:update, :fundraisers, :donations_history] + before_action :authenticate_profile_owner!, only: %i[update fundraisers donations_history] - # get /profiles/:id - # public profile - def show - @profile = Profile.find(params[:id]) - @profile_nonprofits = Psql.execute(Qexpr.new.select("DISTINCT nonprofits.*").from(:nonprofits).join(:supporters, "supporters.nonprofit_id=nonprofits.id AND supporters.profile_id=#{@profile.id}")) + # get /profiles/:id + # public profile + def show + @profile = Profile.find(params[:id]) + @profile_nonprofits = Psql.execute(Qexpr.new.select('DISTINCT nonprofits.*').from(:nonprofits).join(:supporters, "supporters.nonprofit_id=nonprofits.id AND supporters.profile_id=#{@profile.id}")) @campaigns = @profile.campaigns.published.includes(:nonprofit) - if @profile.anonymous? && current_user_id != @profile.user_id && !:super_admin - flash[:notice] = 'That user does not have a public profile.' - redirect_to(request.env["HTTP_REFERER"] || root_url) - return - end - end + if @profile.anonymous? && current_user_id != @profile.user_id && !:super_admin + flash[:notice] = 'That user does not have a public profile.' + redirect_to(request.env['HTTP_REFERER'] || root_url) + return + end + end - # get /profiles/:id/donations_history - def donations_history + # get /profiles/:id/donations_history + def donations_history validate - @profile = Profile.find(params[:id]) - @recurring_donations = @profile.recurring_donations.where(:active => true).includes(:nonprofit) - @donations = @profile.donations.includes(:nonprofit) - end + @profile = Profile.find(params[:id]) + @recurring_donations = @profile.recurring_donations.where(active: true).includes(:nonprofit) + @donations = @profile.donations.includes(:nonprofit) + end # get /profiles/:id/fundraisers def fundraisers validate current_user = Profile.find(params[:id]).user @profile = current_user.profile - @edited_campaigns = Campaign.where("profile_id=#{@profile.id}").order("end_datetime DESC") + @edited_campaigns = Campaign.where("profile_id=#{@profile.id}").order('end_datetime DESC') end # get /profiles/:id/events def events - render json: QueryEventMetrics.for_listings('profile', params[:id], params) + render json: QueryEventMetrics.for_listings('profile', params[:id], params) end - # put /profiles/:id - def update - if current_role?(:super_admin) # can update other profiles - @profile = Profile.find(params[:id]) - else - @profile = current_user.profile - end - @profile.update_attributes(params[:profile]) - json_saved @profile, 'Profile updated' - end + # put /profiles/:id + def update + @profile = if current_role?(:super_admin) # can update other profiles + Profile.find(params[:id]) + else + current_user.profile + end + @profile.update(profile_params) + json_saved @profile, 'Profile updated' + end private - def authenticate_profile_owner!() - if (!current_role?(:super_associate) && - !current_role?(:super_admin) && - (!current_user || - !current_user.profile || - current_user.profile.id != params[:id].to_i)) + def authenticate_profile_owner! + if !current_role?(:super_associate) && + !current_role?(:super_admin) && + (!current_user || + !current_user.profile || + current_user.profile.id != params[:id].to_i) block_with_sign_in end end @@ -68,4 +69,10 @@ class ProfilesController < ApplicationController redirect_to root_url end end + + private + + def profile_params + params.require(:profile).permit(:registered, :mini_bio, :first_name, :last_name, :name, :phone, :address, :email, :city, :state_code, :zip_code, :privacy_settings, :picture, :anonymous, :city_state, :user_id) + end end diff --git a/app/controllers/recurring_donations_controller.rb b/app/controllers/recurring_donations_controller.rb index 3561dd65..bdf0e5ae 100644 --- a/app/controllers/recurring_donations_controller.rb +++ b/app/controllers/recurring_donations_controller.rb @@ -1,19 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class RecurringDonationsController < ApplicationController - def edit @data = QueryRecurringDonations.fetch_for_edit params[:id] if @data && params[:t] == @data['recurring_donation']['edit_token'] @data['change_amount_suggestions'] = CalculateSuggestedAmounts.calculate(@data['recurring_donation']['amount']) @data['miscellaneous_np_info'] = FetchMiscellaneousNpInfo.fetch(@data['nonprofit']['id']) if @data['miscellaneous_np_info']['donate_again_url'].blank? - @data['miscellaneous_np_info']['donate_again_url'] = url_for(:controller => :nonprofits, :action=> :show, :id => @data['nonprofit']['id'], :only_path => false) + @data['miscellaneous_np_info']['donate_again_url'] = url_for(controller: :nonprofits, action: :show, id: @data['nonprofit']['id'], only_path: false) end respond_to do |format| format.html end else - flash[:notice] = "Unable to find donation. Please follow the exact link provided in your email" + flash[:notice] = 'Unable to find donation. Please follow the exact link provided in your email' redirect_to root_url end end @@ -21,7 +22,7 @@ class RecurringDonationsController < ApplicationController def destroy @data = QueryRecurringDonations.fetch_for_edit params[:id] if params[:edit_token] != @data['recurring_donation']['edit_token'] - render json: {error: 'Invalid token'}, status: :unprocessable_entity + render json: { error: 'Invalid token' }, status: :unprocessable_entity else updated = UpdateRecurringDonations.cancel(params[:id], current_user ? current_user.email : @data['supporter']['email']) render json: updated @@ -37,7 +38,7 @@ class RecurringDonationsController < ApplicationController data['recurring_donation'] = UpdateRecurringDonations.update_paydate(data['recurring_donation'], params[:paydate]) if params[:paydate] render json: data, status: data.is_a?(ValidationError) ? :unprocessable_entity : :ok else - render json: {error: 'Invalid token'}, status: :unprocessable_entity + render json: { error: 'Invalid token' }, status: :unprocessable_entity end end @@ -45,15 +46,14 @@ class RecurringDonationsController < ApplicationController rd = RecurringDonation.where('id = ?', params[:id]).first if rd && params[:edit_token] == rd['edit_token'] begin - amount_response = UpdateRecurringDonations.update_amount(rd, params[:token], params[:amount]) - flash[:notice] = "Your recurring donation amount has been successfully changed to $#{(amount_response.amount/100).to_i}" + amount_response = UpdateRecurringDonations.update_amount(rd, params[:token], params[:amount]) + flash[:notice] = "Your recurring donation amount has been successfully changed to $#{(amount_response.amount / 100).to_i}" render_json { amount_response } - rescue => e + rescue StandardError => e render_json { raise e } end else - render json: {error: 'Invalid token'}, status: :unprocessable_entity + render json: { error: 'Invalid token' }, status: :unprocessable_entity end end - end diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index 5dbe31ea..74c8fad7 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -1,23 +1,32 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class RolesController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - before_filter :authenticate_nonprofit_admin! + before_action :authenticate_nonprofit_admin! - def create - role = Role.create_for_nonprofit(params[:role][:name].to_sym, params[:role][:email], FetchNonprofit.with_params(params)) - json_saved role, "User successfully added!" - end + def create + role = Role.create_for_nonprofit(role_params[:name].to_sym, role_params[:email], FetchNonprofit.with_params(params)) + json_saved role, 'User successfully added!' + end - def destroy - role = Role.find(params[:id]) - roles = role.user.roles.where(host_id: params[:nonprofit_id], name: role.name) - unless roles.empty? - roles.destroy_all - flash[:notice] = 'User successfully removed' - render json: {} - else - render json: {:error => "We couldn't find that admin"}, :status => :unprocessable_entity - end - end + def destroy + role = Role.find(params[:id]) + roles = role.user.roles.where(host_id: params[:nonprofit_id], name: role.name) + if roles.empty? + render json: { error: "We couldn't find that admin" }, status: :unprocessable_entity + else + roles.destroy_all + flash[:notice] = 'User successfully removed' + render json: {} + end + end + + private + + def role_params + params.require(:role).permit(:name, :email) + end end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index da64cf2b..f1e31798 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -1,31 +1,32 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class SettingsController < ApplicationController - include Controllers::NonprofitHelper + include Controllers::Nonprofit::Current + include Controllers::Nonprofit::Authorization - helper_method :current_nonprofit_user? - before_filter :authenticate_user! + helper_method :current_nonprofit_user? + before_action :authenticate_user! - def index - if current_role?(:super_admin) && params[:nonprofit_id] - @nonprofit = Nonprofit.find(params[:nonprofit_id]) - elsif current_role?([:nonprofit_admin, :nonprofit_associate]) - @nonprofit = administered_nonprofit - end - - if current_role?(:super_admin) && params[:user_id] - @user = User.find_by_id(params[:user_id]) - elsif current_role?(:super_admin) && params[:user_email] - @user = User.find_by_email(params[:user_email]) - else - @user = current_user + def index + if current_role?(:super_admin) && params[:nonprofit_id] + @nonprofit = Nonprofit.find(params[:nonprofit_id]) + elsif current_role?(%i[nonprofit_admin nonprofit_associate]) + @nonprofit = administered_nonprofit end - @profile = @user.profile + @user = if current_role?(:super_admin) && params[:user_id] + User.find_by_id(params[:user_id]) + elsif current_role?(:super_admin) && params[:user_email] + User.find_by_email(params[:user_email]) + else + current_user + end - if @nonprofit - @miscellaneous_np_info = FetchMiscellaneousNpInfo.fetch(@nonprofit.id) - end - - end + @profile = @user.profile + if @nonprofit + @miscellaneous_np_info = FetchMiscellaneousNpInfo.fetch(@nonprofit.id) + end + end end diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb index e0587fb0..00e729ba 100644 --- a/app/controllers/static_controller.rb +++ b/app/controllers/static_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class StaticController < ApplicationController layout 'layouts/static' @@ -8,18 +10,17 @@ class StaticController < ApplicationController def ccs ccs_method = !Settings.ccs ? 'local_tar_gz' : Settings.ccs.ccs_method - if (ccs_method == 'local_tar_gz') + if ccs_method == 'local_tar_gz' temp_file = "#{Rails.root}/tmp/#{Time.current.to_i}.tar.gz" result = Kernel.system("git archive --format=tar.gz -o #{temp_file} HEAD") if result - send_file(temp_file, :type => "application/gzip") + send_file(temp_file, type: 'application/gzip') else - render :nothing => true, :status => 500 + render body: nil, status: 500 end - elsif (ccs_method == 'github') + elsif ccs_method == 'github' git_hash = File.read("#{Rails.root}/CCS_HASH") redirect_to "https://github.com/#{Settings.ccs.options.account}/#{Settings.ccs.options.repo}/tree/#{git_hash}" end - end end diff --git a/app/controllers/super_admins_controller.rb b/app/controllers/super_admins_controller.rb index 3d20b636..cd444ceb 100644 --- a/app/controllers/super_admins_controller.rb +++ b/app/controllers/super_admins_controller.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class SuperAdminsController < ApplicationController - layout "layouts/page" + layout 'layouts/page' - before_filter :authenticate_super_associate! + before_action :authenticate_super_associate! - def index - end + def index; end def search_nonprofits render json: QueryNonprofits.for_admin(params) @@ -15,49 +16,47 @@ class SuperAdminsController < ApplicationController render json: QueryProfiles.for_admin(params) end - def search_fullcontact + def search_fullcontact begin result = FullContact.person(email: params[:search]) rescue Exception => e - result = '' + result = '' end render json: [result] end def resend_user_confirmation - ParamValidation.new(params || {}, { - profile_id: {:required => true, is_integer: true} - }) + ParamValidation.new(params || {}, + profile_id: { required: true, is_integer: true }) profile = Profile.includes(:user).where('id = ?', params[:profile_id]).first - unless (profile.user) - raise ArgumentError.new("#{params[:profile_id]} is a profile without a valid user") + unless profile.user + raise ArgumentError, "#{params[:profile_id]} is a profile without a valid user" end profile.user.send_confirmation_instructions - render json: {status: :ok} + render json: { status: :ok } end def recurring_donations_without_cards - odd_donations = QueryRecurringDonations::recurring_donations_without_cards + odd_donations = QueryRecurringDonations.recurring_donations_without_cards respond_to do |format| format.html format.csv do - csv_out = CSV.generate { |csv| + csv_out = CSV.generate do |csv| csv << ['supporter id', 'recurring donation id', 'rd created date', 'rd modified', 'donation id', 'donation card id', 'edit_token', 'nonprofit id', 'last charge succeeded id', 'last charge succeeded created at', 'last charge attempted id', 'last charge attempted created at', 'amount'] - odd_donations.each { |rd| + odd_donations.each do |rd| csv << [rd.supporter.id, rd.id, rd.created_at, rd.updated_at, rd.donation.id, rd.donation.card_id, rd.edit_token, rd.nonprofit.id, rd.most_recent_paid_charge.id, rd.most_recent_paid_charge.created_at, rd.most_recent_charge.id, rd.most_recent_charge.created_at, rd.amount] - } - } + end + end - - send_data(csv_out, filename: "recurring_donations_without_cards-#{Time.now.to_date()}.csv") + send_data(csv_out, filename: "recurring_donations_without_cards-#{Time.now.to_date}.csv") end end end @@ -65,17 +64,13 @@ class SuperAdminsController < ApplicationController def export_supporters_with_rds np = params[:np] ids = params[:ids] - results = QuerySupporters.for_export(np, {ids: ids}) - results[0].push("Management URLS") - results.drop(1).each {|row| - rds = Supporter.includes(:recurring_donations).find(row.last).recurring_donations.select{|rd| rd.active}.map{|rd| "* #{root_url}recurring_donations/#{rd.id}/edit?t=#{rd.edit_token}"}.join("\n") + results = QuerySupporters.for_export(np, ids: ids) + results[0].push('Management URLS') + results.drop(1).each do |row| + rds = Supporter.includes(:recurring_donations).find(row.last).recurring_donations.select(&:active).map { |rd| "* #{root_url}recurring_donations/#{rd.id}/edit?t=#{rd.edit_token}" }.join("\n") row.push(rds) - } + end - - send_data(Format::Csv.from_vectors(results), filename: "supporters_with_multiple_donations.csv") + send_data(Format::Csv.from_vectors(results), filename: 'supporters_with_multiple_donations.csv') end - - end - diff --git a/app/controllers/ticket_levels_controller.rb b/app/controllers/ticket_levels_controller.rb index e0082a0e..a59cef10 100644 --- a/app/controllers/ticket_levels_controller.rb +++ b/app/controllers/ticket_levels_controller.rb @@ -1,27 +1,30 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class TicketLevelsController < ApplicationController - include Controllers::EventHelper + include Controllers::Event::Current + include Controllers::Event::Authorization - before_filter :authenticate_event_editor!, :except => [:index, :show] + before_action :authenticate_event_editor!, except: %i[index show] - def index + def index ev_id = current_event.id - render json: {data: QueryTicketLevels.with_event_id(ev_id, current_role?(:event_editor, ev_id) || current_role?(:super_admin) || current_role?(:nonprofit_admin, current_event.nonprofit_id))} - end + render json: { data: QueryTicketLevels.with_event_id(ev_id, current_role?(:event_editor, ev_id) || current_role?(:super_admin) || current_role?(:nonprofit_admin, current_event.nonprofit_id)) } + end - def show - render json: current_ticket_level - end + def show + render json: current_ticket_level + end - def create - ticket_level = current_event.ticket_levels.create params[:ticket_level] - json_saved ticket_level, 'Ticket level created!' - end + def create + ticket_level = current_event.ticket_levels.create ticket_level_params + json_saved ticket_level, 'Ticket level created!' + end - def update - current_ticket_level.update_attributes params[:ticket_level] - json_saved current_ticket_level, 'Ticket level updated' - end + def update + current_ticket_level.update ticket_level_params + json_saved current_ticket_level, 'Ticket level updated' + end # put /nonprofits/:nonprofit_id/events/:event_id/ticket_levels/update_order # Pass in {data: [{id: 1, order: 1}]} @@ -31,14 +34,17 @@ class TicketLevelsController < ApplicationController end def destroy - current_ticket_level.destroy - render json: {} - end + current_ticket_level.destroy + render json: {} + end -private + private - def current_ticket_level - @ticket_level ||= current_event.ticket_levels.find params[:id] - end + def current_ticket_level + @ticket_level ||= current_event.ticket_levels.find params[:id] + end + def ticket_level_params + params.require(:ticket_level).permit(:amount, :amount_dollars, :name, :description, :quantity, :deleted, :event_id, :admin_only, :limit, :order) + end end diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 1a8b1569..72fdc605 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -1,13 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class TicketsController < ApplicationController - include Controllers::EventHelper + include Controllers::Event::Current + include Controllers::Event::Authorization - helper_method :current_event_admin?, :current_event_editor? - before_filter :authenticate_event_editor!, :except => [:create, :add_note] - before_filter :authenticate_nonprofit_user!, only: [:delete_card_for_ticket] + helper_method :current_event_admin?, :current_event_editor? + before_action :authenticate_event_editor!, except: %i[create add_note] + before_action :authenticate_nonprofit_user!, only: [:delete_card_for_ticket] - # post /nonprofits/:nonprofit_id/events/:event_id/tickets - def create + # post /nonprofits/:nonprofit_id/events/:event_id/tickets + def create authenticate_event_editor! if params[:kind] == 'offsite' render_json do params[:current_user] = current_user @@ -16,35 +19,36 @@ class TicketsController < ApplicationController end def update - params[:ticket][:ticket_id] = params[:id] - params[:ticket][:event_id] = params[:event_id] - render_json{ UpdateTickets.update(params[:ticket], current_user) } + ticket_params[:ticket_id] = params[:id] + ticket_params[:event_id] = params[:event_id] + render_json { UpdateTickets.update(ticket_params, current_user) } end # Attendees dashboard - # get /nonprofits/:nonprofit_id/events/:event_id/tickets - def index - @panels_layout = true - @nonprofit = current_nonprofit - @event = current_event - respond_to do |format| - format.html + # get /nonprofits/:nonprofit_id/events/:event_id/tickets + def index + @panels_layout = true + @nonprofit = current_nonprofit + @event = current_event + + respond_to do |format| + format.html format.csv do - file_date = Date.today.strftime("%m-%d-%Y") - filename = "tickets-#{file_date}" + file_date = Date.today.strftime('%m-%d-%Y') + filename = "tickets-#{file_date}" @tickets = QueryTickets.for_export(@event.id, params) - send_data(Format::Csv.from_vectors(@tickets), filename: "#{filename}.csv") + send_data(Format::Csv.from_vectors(@tickets), filename: "#{filename}.csv") end - format.json do + format.json do render json: QueryTickets.attendees_list(@event.id, params) - end - end - end + end + end + end # PUT nonprofits/:nonprofit_id/events/:event_id/tickets/:id/add_note def add_note - current_nonprofit.tickets.find(params[:id]).update_attributes(note: params[:ticket][:note]) + current_nonprofit.tickets.find(params[:id]).update(note: ticket_params[:note]) render json: {} end @@ -59,4 +63,10 @@ class TicketsController < ApplicationController @event = current_event render json: UpdateTickets.delete_card_for_ticket(@event.id, params[:id]) end + + private + + def ticket_params + params.require(:ticket).permit(:ticket_id, :event_id, :note, :event_discount, :event_discount_id) + end end diff --git a/app/controllers/users/confirmations_controller.rb b/app/controllers/users/confirmations_controller.rb index 97812ee8..2ff745f8 100644 --- a/app/controllers/users/confirmations_controller.rb +++ b/app/controllers/users/confirmations_controller.rb @@ -1,41 +1,41 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Users::ConfirmationsController < Devise::ConfirmationsController + # get /confirm + def show + @user = User.confirm_by_token(params[:confirmation_token]) - # get /confirm - def show - @user = User.confirm_by_token(params[:confirmation_token]) - - if !@user.auto_generated || !@user.valid? - flash[:notice] = "We successfully confirmed your account" - redirect_to session[:donor_signup_url] || root_url - else + if !@user.auto_generated || !@user.valid? + flash[:notice] = 'We successfully confirmed your account' + redirect_to session[:donor_signup_url] || root_url + else respond_to do |format| format.html end - end - end + end + end def exists render json: User.find_by_email(params[:email]) end - # post /confirm - # set account password - def confirm - @user = User.find(params[:id]) + # post /confirm + # set account password + def confirm + @user = User.find(params[:id]) - if @user.valid? && @user.update_attributes(params[:user].except(:confirmation_token)) - flash[:notice] = "Your account is all set!" - sign_in @user - redirect_to session[:donor_signup_url] || root_url - else - session[:donor_signup_url] || root_url - #render :action => "show", :layout => 'layouts/embed' - end - end - - def is_confirmed - render json: {is_confirmed: User.find(params[:user_id]).confirmed?} + if @user.valid? && @user.update(params[:user].except(:confirmation_token)) + flash[:notice] = 'Your account is all set!' + sign_in @user + redirect_to session[:donor_signup_url] || root_url + else + session[:donor_signup_url] || root_url + # render :action => "show", :layout => 'layouts/embed' + end end + def is_confirmed + render json: { is_confirmed: User.find(params[:user_id]).confirmed? } + end end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 24288ad8..2476e519 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Users::RegistrationsController < Devise::RegistrationsController respond_to :html, :json @@ -8,13 +10,13 @@ class Users::RegistrationsController < Devise::RegistrationsController # this endpoint only creates donor users def create - params[:user][:referer] = session[:referer_id] - user = User.register_donor!(params[:user]) + clean_params[:user][:referer] = session[:referer_id] + user = User.register_donor!(clean_params[:user]) if user.save sign_in user - render :json => user + render json: user else - render :json => user.errors.full_messages, :status => :unprocessable_entity + render json: user.errors.full_messages, status: :unprocessable_entity clean_up_passwords(user) end end @@ -29,11 +31,11 @@ class Users::RegistrationsController < Devise::RegistrationsController if current_user.pending_password && params[:user][:password] && params[:user][:password_confirmation] params[:user][:pending_password] = false end - success = current_user.update_attributes(params[:user]) + success = current_user.update(params[:user]) errs = current_user.errors.full_messages else success = false - errs = {:password => :incorrect} + errs = { password: :incorrect } end if success @@ -43,10 +45,15 @@ class Users::RegistrationsController < Devise::RegistrationsController flash[:notice] = 'Account updated!' end - sign_in(current_user, :bypass => true) - render :json => current_user + sign_in(current_user, bypass: true) + render json: current_user else - render :json => {:errors => errs}, :status => :unprocessable_entity + render json: { errors: errs }, status: :unprocessable_entity end end + + private + def clean_params + params.permit(:user => [:name, :email, :password_confirmation, :password]) + end end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index d9a4ad10..0271dad8 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,37 +1,36 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Users::SessionsController < Devise::SessionsController - layout 'layouts/apified', only: :new - + layout 'layouts/apified', only: :new + respond_to :json, only: :new def new @theme = 'minimal' super end - def create + def create @theme = 'minimal' - respond_to do |format| - format.json { - warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new") - render :status => 200, :json => { :status => "Success" } - } - end - end - - - # post /users/confirm_auth - # A simple action to confirm an entered password for a user who is already signed in - def confirm_auth - if current_user.valid_password?(params[:password]) - tok = SecureRandom.uuid - session[:pw_token] = tok - session[:pw_timestamp] = Time.current.to_s - render json: {token: tok}, status: :ok - else - render json: ["Incorrect password. Please enter your #{Settings.general.name} %> password."], status: :unprocessable_entity - end + respond_to do |format| + format.json do + warden.authenticate!(scope: resource_name, recall: "#{controller_path}#new") + render status: 200, json: { status: 'Success' } + end + end end - -end + # post /users/confirm_auth + # A simple action to confirm an entered password for a user who is already signed in + def confirm_auth + if current_user.valid_password?(params[:password]) + tok = SecureRandom.uuid + session[:pw_token] = tok + session[:pw_timestamp] = Time.current.to_s + render json: { token: tok }, status: :ok + else + render json: ["Incorrect password. Please enter your #{Settings.general.name} %> password."], status: :unprocessable_entity + end + end +end diff --git a/app/controllers/widget_controller.rb b/app/controllers/widget_controller.rb new file mode 100644 index 00000000..1ef940bc --- /dev/null +++ b/app/controllers/widget_controller.rb @@ -0,0 +1,20 @@ +class WidgetController < ApplicationController + def v2 + expires_in 10.minutes + head :found, location: helpers.asset_pack_url("donate-button-v2.js"), content_type: "application/javascript" + end + + def i18n + head :found, location: helpers.asset_pack_url("i18n.js"), content_type: "application/javascript" + end + + def v1_css + expires_in 10.minutes + head :found, location: helpers.stylesheet_url("widget/donate-button.css"), content_type: "text/css" + end + + def v2_css + expires_in 10.minutes + head :found, location: helpers.stylesheet_url("widget/donate-button-v2.css"), content_type: "text/css" + end +end diff --git a/app/helpers/api/nonprofits_helper.rb b/app/helpers/api/nonprofits_helper.rb new file mode 100644 index 00000000..eca003fe --- /dev/null +++ b/app/helpers/api/nonprofits_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module Api::NonprofitsHelper + def nonprofit_slug_url(nonprofit) + nonprofit_location_url city: nonprofit.city_slug, state_code: nonprofit.state_code_slug, name: nonprofit.slug + end + + def nonprofit_slug_path(nonprofit) + nonprofit_location_path city: nonprofit.city_slug, state_code: nonprofit.state_code_slug, name: nonprofit.slug + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e2f981df..d6208207 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,70 +1,71 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ApplicationHelper + def resource_name + :user + end - def resource_name - :user - end + def resource + @resource ||= User.new + end - def resource - @resource ||= User.new - end + def devise_mapping + @devise_mapping ||= Devise.mappings[:user] + end - def devise_mapping - @devise_mapping ||= Devise.mappings[:user] - end + def print_currency(cents, unit = 'EUR', sign = true) + dollars = cents.to_f / 100.0 + dollars = number_to_currency(dollars, unit: unit.to_s, precision: dollars.round == dollars ? 0 : 2) + dollars = dollars[1..-1] unless sign + dollars + end - def print_currency(cents, unit="EUR", sign=true) - - dollars = cents.to_f / 100.0 - dollars = number_to_currency(dollars, :unit => "#{unit}", :precision => (dollars.round == dollars) ? 0 : 2) - dollars = dollars[1..-1] if !sign - dollars - end + def print_percent(rate) + (rate.to_f * 100).round(2) + end - def print_percent(rate) - (rate.to_f * 100).round(2) - end + ## Dates - ## Dates + def simple_date(date_object, timezone = nil) + return '' if date_object.nil? - def simple_date date_object, timezone=nil - return '' if date_object.nil? - date_object = date_object.in_time_zone(timezone) if timezone - date_object.strftime("%m/%d/%Y") - end + date_object = date_object.in_time_zone(timezone) if timezone + date_object.strftime('%m/%d/%Y') + end - def simple_time time_object, timezone=nil - return '' if time_object.nil? - time_object = time_object.in_time_zone(timezone) if timezone - time_object.strftime("%l:%M%P") - end + def simple_time(time_object, timezone = nil) + return '' if time_object.nil? - def readable_date date_object - date_object.strftime("%B %d, %Y") - end + time_object = time_object.in_time_zone(timezone) if timezone + time_object.strftime('%l:%M%P') + end - def date_and_time date_object, timezone=nil - date_object = date_object.in_time_zone(timezone) if timezone - date_object.strftime("%m/%d/%Y %I:%M%P (%Z)") - end + def readable_date(date_object) + date_object.strftime('%B %d, %Y') + end - def us_states - [ ['Alabama', 'AL'], ['Alaska', 'AK'], ['Arizona', 'AZ'], ['Arkansas', 'AR'], ['California', 'CA'], ['Colorado', 'CO'], ['Connecticut', 'CT'], ['Delaware', 'DE'], ['District of Columbia', 'DC'], ['Florida', 'FL'], ['Georgia', 'GA'], ['Hawaii', 'HI'], ['Idaho', 'ID'], ['Illinois', 'IL'], ['Indiana', 'IN'], ['Iowa', 'IA'], ['Kansas', 'KS'], ['Kentucky', 'KY'], ['Louisiana', 'LA'], ['Maine', 'ME'], ['Maryland', 'MD'], ['Massachusetts', 'MA'], ['Michigan', 'MI'], ['Minnesota', 'MN'], ['Mississippi', 'MS'], ['Missouri', 'MO'], ['Montana', 'MT'], ['Nebraska', 'NE'], ['Nevada', 'NV'], ['New Hampshire', 'NH'], ['New Jersey', 'NJ'], ['New Mexico', 'NM'], ['New York', 'NY'], ['North Carolina', 'NC'], ['North Dakota', 'ND'], ['Ohio', 'OH'], ['Oklahoma', 'OK'], ['Oregon', 'OR'], ['Pennsylvania', 'PA'], ['Puerto Rico', 'PR'], ['Rhode Island', 'RI'], ['South Carolina', 'SC'], ['South Dakota', 'SD'], ['Tennessee', 'TN'], ['Texas', 'TX'], ['Utah', 'UT'], ['Vermont', 'VT'], ['Virginia', 'VA'], ['Washington', 'WA'], ['West Virginia', 'WV'], ['Wisconsin', 'WI'], ['Wyoming', 'WY'] ] - end + def date_and_time(date_object, timezone = nil) + date_object = date_object.in_time_zone(timezone) if timezone + date_object.strftime('%m/%d/%Y %I:%M%P (%Z)') + end - # Append a parameter to a URL string - def url_with_param(param, val, url) - url + (url.include?('?') ? '&' : '?') + param + '=' + val - end + def us_states + [%w[Alabama AL], %w[Alaska AK], %w[Arizona AZ], %w[Arkansas AR], %w[California CA], %w[Colorado CO], %w[Connecticut CT], %w[Delaware DE], ['District of Columbia', 'DC'], %w[Florida FL], %w[Georgia GA], %w[Hawaii HI], %w[Idaho ID], %w[Illinois IL], %w[Indiana IN], %w[Iowa IA], %w[Kansas KS], %w[Kentucky KY], %w[Louisiana LA], %w[Maine ME], %w[Maryland MD], %w[Massachusetts MA], %w[Michigan MI], %w[Minnesota MN], %w[Mississippi MS], %w[Missouri MO], %w[Montana MT], %w[Nebraska NE], %w[Nevada NV], ['New Hampshire', 'NH'], ['New Jersey', 'NJ'], ['New Mexico', 'NM'], ['New York', 'NY'], ['North Carolina', 'NC'], ['North Dakota', 'ND'], %w[Ohio OH], %w[Oklahoma OK], %w[Oregon OR], %w[Pennsylvania PA], ['Puerto Rico', 'PR'], ['Rhode Island', 'RI'], ['South Carolina', 'SC'], ['South Dakota', 'SD'], %w[Tennessee TN], %w[Texas TX], %w[Utah UT], %w[Vermont VT], %w[Virginia VA], %w[Washington WA], ['West Virginia', 'WV'], %w[Wisconsin WI], %w[Wyoming WY]] + end - # Prepend 'http://' if it is not present in a given url - # Used for linking to nonprofit-provided website - def add_http url - if url[/^http:\/\//] || url[/^https:\/\//] - url - else - 'http://' + url - end - end + # Append a parameter to a URL string + def url_with_param(param, val, url) + url + (url.include?('?') ? '&' : '?') + param + '=' + val + end + # Prepend 'http://' if it is not present in a given url + # Used for linking to nonprofit-provided website + def add_http(url) + if url[%r{^http://}] || url[%r{^https://}] + url + else + 'http://' + url + end + end end diff --git a/app/helpers/card_helper.rb b/app/helpers/card_helper.rb index 7a9309e6..0dd63536 100644 --- a/app/helpers/card_helper.rb +++ b/app/helpers/card_helper.rb @@ -1,23 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CardHelper + def brand_file(brand) + if brand == 'Visa' || brand == 'visa' || brand == 'VISA' + 'visa' + elsif brand == 'American Express' || brand == 'amex' + 'amex' + elsif brand == 'Discover' || brand == 'Discover Card' || brand == 'discover' + 'discover' + elsif brand == 'MasterCard' || brand == 'Mastercard' || brand == 'mastercard' + 'mastercard' + end + end - def brand_file(brand) - if brand == 'Visa' || brand == 'visa' || brand == 'VISA' - 'visa' - elsif brand == 'American Express' || brand == 'amex' - 'amex' - elsif brand == 'Discover' || brand == 'Discover Card' || brand == 'discover' - 'discover' - elsif brand == 'MasterCard' || brand == 'Mastercard' || brand == 'mastercard' - 'mastercard' - end - end + def current_card + current_user&.profile&.card + end - def current_card - current_user && current_user.profile.card - end - - def expiration_years - (0..15).map{|n| (Date.today + n.years).year} - end + def expiration_years + (0..15).map { |n| (Date.today + n.years).year } + end end diff --git a/app/helpers/devise_helper.rb b/app/helpers/devise_helper.rb index c36671b5..03359923 100644 --- a/app/helpers/devise_helper.rb +++ b/app/helpers/devise_helper.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module DeviseHelper def devise_error_messages! if resource && !resource.errors.empty? - resource.errors.first.first.to_s + ' ' + + resource.errors.first.first.to_s + ' ' + resource.errors.first.second end end diff --git a/app/helpers/nonprofits_helper.rb b/app/helpers/nonprofits_helper.rb index 5d9ea441..c721b02f 100644 --- a/app/helpers/nonprofits_helper.rb +++ b/app/helpers/nonprofits_helper.rb @@ -1,16 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module NonprofitsHelper - - def managed_npo_card_json - if current_user - if params[:nonprofit_id] && current_role?(:super_admin) - raw(Nonprofit.find(params[:nonprofit_id]).active_card.to_json) - elsif administered_nonprofit && administered_nonprofit.active_card - raw(administered_nonprofit.active_card.to_json) - end - else - 'undefined' - end - end - + def managed_npo_card_json + if current_user + if params[:nonprofit_id] && current_role?(:super_admin) + raw(Nonprofit.find(params[:nonprofit_id]).active_card.to_json) + elsif administered_nonprofit&.active_card + raw(administered_nonprofit.active_card.to_json) + end + else + 'undefined' + end + end end diff --git a/app/helpers/onboard_helper.rb b/app/helpers/onboard_helper.rb index 1cb832a5..ebc34eda 100644 --- a/app/helpers/onboard_helper.rb +++ b/app/helpers/onboard_helper.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + module OnboardHelper end diff --git a/app/helpers/pricing_helper.rb b/app/helpers/pricing_helper.rb index db1d7b48..e6022535 100644 --- a/app/helpers/pricing_helper.rb +++ b/app/helpers/pricing_helper.rb @@ -1,8 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module PricingHelper -private - def nonprofit_email - return nil if @nonprofit.nil? - @nonprofit.email || GetData.chain(@nonprofit.users.first, :email) - end + private + + def nonprofit_email + return nil if @nonprofit.nil? + + @nonprofit.email || GetData.chain(@nonprofit.users.first, :email) + end end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb index e40303e4..dfca3351 100755 --- a/app/helpers/profiles_helper.rb +++ b/app/helpers/profiles_helper.rb @@ -1,12 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ProfilesHelper - - def get_shortened_name name + def get_shortened_name(name) if name name.length > 18 ? name[0..18] + '...' : name else 'Your Account' end end - end diff --git a/app/javascript/donate-button/details.js.erb b/app/javascript/donate-button/details.js.erb new file mode 100644 index 00000000..a8a31c50 --- /dev/null +++ b/app/javascript/donate-button/details.js.erb @@ -0,0 +1,9 @@ + +<% +cdn_url = URI(Settings.cdn.url) +cdn_url = cdn_url.to_s +if Settings.button_config&.url + cdn_url = URI(Settings.button_config.url).to_s +end +%> +module.exports = "<%= cdn_url %>" diff --git a/app/javascript/donate-button/donate-button.v2.ts b/app/javascript/donate-button/donate-button.v2.ts new file mode 100644 index 00000000..b86c8416 --- /dev/null +++ b/app/javascript/donate-button/donate-button.v2.ts @@ -0,0 +1,232 @@ +const donate_css = require('../../assets/stylesheets/donate-button/donate-button.v2.css'); + +const iframeHost = require('./details.js.erb') + +function on_ios11() { + var userAgent = window.navigator.userAgent; + var has11 = userAgent.search("OS 11_\\d") > 0 + var hasMacOS = userAgent.search(" like Mac OS X") > 0 + + return has11 && hasMacOS; + } + + const windowAsAny = window as any; + windowAsAny.commitchange = { + iframes: [] + , modalIframe: null + } + const commitchange = windowAsAny.commitchange; + commitchange.getParamsFromUrl = (whitelist:any) => { + var result:any = {}, + tmp = []; + var items = location.search.substr(1).split("&"); + for (var index = 0; index < items.length; index++) { + tmp = items[index].split("="); + if (whitelist.indexOf(tmp[0])) result[tmp[0]] = decodeURIComponent(tmp[1]); + } + return result; + } + + commitchange.openDonationModal = (iframe:HTMLIFrameElement, overlay:HTMLElement) => { + return (event:Event) => { + overlay.className = 'commitchange-overlay commitchange-open' + iframe.className = 'commitchange-iframe commitchange-open' + if (on_ios11()) { + iframe.style.position = 'absolute' + } + commitchange.setParams(commitchange.getParamsFromButton(event.currentTarget), iframe) + if (on_ios11()) { + iframe.scrollIntoView() + } + + commitchange.open_iframe = iframe + commitchange.open_overlay = overlay + } + } + + // Dynamically set the params of the appended iframe donate window + commitchange.setParams = (params:any, iframe:HTMLIFrameElement) => { + params.command = 'setDonationParams' + params.sender = 'commitchange' + iframe.contentWindow.postMessage(JSON.stringify(params), fullHost) + } + + commitchange.hideDonation = () => { + if(!commitchange.open_overlay || !commitchange.open_iframe) return + commitchange.open_overlay.className = 'commitchange-overlay commitchange-closed' + commitchange.open_iframe.className = 'commitchange-iframe commitchange-closed' + if (on_ios11()) { + commitchange.open_iframe.style.position = 'fixed' + } + commitchange.open_overlay = undefined + commitchange.open_iframe = undefined + } + + const fullHost = iframeHost + + commitchange.overlay = () => { + let div = document.createElement('div') + div.setAttribute('class', 'commitchange-closed commitchange-overlay') + return div + } + + commitchange.createIframe = (source:string) => { + let i = document.createElement('iframe') + const url = document.location.href + i.setAttribute('class', 'commitchange-closed commitchange-iframe') + i.src = source + "&origin=" + url + return i + } + + // Given a button with a bunch of data parameters + // return an object of key/vals corresponing to each param + commitchange.getParamsFromButton = (elem:HTMLElement) => { + let options: {[props:string]:any} = { + offsite: 't' + , type: elem.getAttribute('data-type') + , custom_amounts: elem.getAttribute('data-custom-amounts') || elem.getAttribute('data-amounts') + , amount: elem.getAttribute('data-amount') + , minimal: elem.getAttribute('data-minimal') + , weekly: elem.getAttribute('data-weekly') + , default: elem.getAttribute('data-default') + , custom_fields: elem.getAttribute('data-custom-fields') + , campaign_id: elem.getAttribute('data-campaign-id') + , gift_option_id: elem.getAttribute('data-gift-option-id') + , redirect: elem.getAttribute('data-redirect') + , designation: elem.getAttribute('data-designation') + , multiple_designations: elem.getAttribute('data-multiple-designations') + , hide_dedication: elem.getAttribute('data-hide-dedication')? true : false + , designations_prompt: elem.getAttribute('data-designations-prompt') + , single_amount: elem.getAttribute('data-single-amount') + , designation_desc: elem.getAttribute('data-designation-desc') || elem.getAttribute('data-description') + , locale: elem.getAttribute('data-locale') + , "utm_source": elem.getAttribute('data-utm_source') + , "utm_campaign": elem.getAttribute('data-utm_campaign') + , "utm_medium": elem.getAttribute('data-utm_medium') + , "utm_content": elem.getAttribute('data-utm_content') + , "first_name": elem.getAttribute('data-first_name') + , "last_name": elem.getAttribute('data-last_name') + , "country": elem.getAttribute('data-country') + , "postal_code": elem.getAttribute('data-postal_code') + + + } + // Remove false values from the options + for(let key in options) { + if(!options[key]) delete options[key] + } + return options + } + + commitchange.appendMarkup = () => { + if(commitchange.alreadyAppended) return + else commitchange.alreadyAppended = true + let script = document.getElementById('commitchange-donation-script') || document.getElementById('commitchange-script') + const nonprofitID = script.getAttribute('data-npo-id') + const baseSource = fullHost + "/nonprofits/" + nonprofitID + "/donate?offsite=t" + let elems = document.querySelectorAll('.commitchange-donate') + + for(let i = 0; i < elems.length; ++i) { + let elem:any = elems[i] + let source = baseSource + + let optionsButton = commitchange.getParamsFromButton(elem) + let options = commitchange.getParamsFromUrl(["utm_campaign","utm_content","utm_source","utm_medium","first_name","last_name","country","postal_code","address","city"]) + for (var attr in optionsButton) { options[attr] = optionsButton[attr]; } + let params = [] + for(let key in options) { + params.push(key + '=' + options[key]) + } + source += "&" + params.join("&") + + if(elem.hasAttribute('data-embedded')) { + source += '&mode=embedded' + let iframe = commitchange.createIframe(source) + elem.appendChild(iframe) + iframe.setAttribute('class', 'commitchange-iframe-embedded') + commitchange.iframes.push(iframe) + } else { + let overlay = commitchange.overlay() + let iframe + // Show the CommitChange-branded button if it's not set to custom. + if(!elem.hasAttribute('data-custom') && !elem.hasAttribute('data-custom-button')) { + let btn_iframe = document.createElement('iframe') + let btn_src = fullHost + "/nonprofits/" + nonprofitID + "/btn" + if(elem.hasAttribute('data-fixed')) { btn_src += '?fixed=t' } + btn_iframe.src = btn_src + btn_iframe.className = 'commitchange-btn-iframe' + btn_iframe.setAttribute('scrolling', 'no') + btn_iframe.setAttribute('seamless', 'seamless') + elem.appendChild(btn_iframe) + btn_iframe.onclick = commitchange.openDonationModal(iframe, overlay) + } + // Create the iframe overlay for this button + let modal = document.createElement('div') + modal.className = 'commitchange-modal' + + if(commitchange.modalIframe) { + iframe = commitchange.modalIframe + } else { + iframe = commitchange.createIframe(source) + commitchange.iframes.push(iframe) + commitchange.modalIframe = iframe + } + modal.appendChild(overlay) + document.body.appendChild(iframe) + elem.parentNode.appendChild(modal) + overlay.onclick = commitchange.hideDonation + elem.onclick = commitchange.openDonationModal(iframe, overlay) + } // end else + } // end for loop + } + + // Load the CSS for the parent page element from our AWS server + commitchange.loadStylesheet = () => { + if(commitchange.alreadyStyled) return + else commitchange.alreadyStyled = true + // let stylesheet = document.createElement('style') + // stylesheet.rel = 'stylesheet' + // stylesheet.type = 'text/css' + // stylesheet.innerText = donate_css + // document.getElementsByTagName('head')[0].appendChild(stylesheet) + } + + + // Handle iframe post messages + if(window.addEventListener) { + window.addEventListener('message', (e) => { + // Close the modal + if(e.data === 'commitchange:close') { + commitchange.hideDonation() + } + // Redirect on donation completion using the redirect param + else if(e.data.match(/^commitchange:redirect/)) { + const matches = e.data.match(/^commitchange:redirect:(.+)$/) + if(matches.length === 2) window.location.href = matches[1] + } + }) + } + + // Make initialization calls on document load + if(document.addEventListener) { + document.addEventListener("DOMContentLoaded", (event) => { + commitchange.loadStylesheet() + commitchange.appendMarkup() + }) + } else if(windowAsAny.jQuery) { + windowAsAny.jQuery(document).ready(() => { + commitchange.loadStylesheet() + commitchange.appendMarkup() + }) + } else { + window.onload = () => { + commitchange.loadStylesheet() + commitchange.appendMarkup() + } + } + + if(document.querySelector('.commitchange-donate')) { + commitchange.loadStylesheet() + commitchange.appendMarkup() + } + \ No newline at end of file diff --git a/app/javascript/i18n.js.erb b/app/javascript/i18n.js.erb new file mode 100644 index 00000000..b54f2db7 --- /dev/null +++ b/app/javascript/i18n.js.erb @@ -0,0 +1,7 @@ +const I18n = require('i18n-js'); + +I18n.translations = I18n.translations || {}; +<% I18n::JS::translations.each do |k,v| %> +I18n.translations['<%= k %>'] = I18n.extend((I18n.translations['<%= k %>'] || {}),<%= JSON.generate(v) %>); +<% end %> +module.exports = I18n; \ No newline at end of file diff --git a/client/js/bank_accounts/confirm/index.es6 b/app/javascript/legacy/bank_accounts/confirm/index.es6 similarity index 100% rename from client/js/bank_accounts/confirm/index.es6 rename to app/javascript/legacy/bank_accounts/confirm/index.es6 diff --git a/client/js/bank_accounts/confirm/page.js b/app/javascript/legacy/bank_accounts/confirm/page.js similarity index 100% rename from client/js/bank_accounts/confirm/page.js rename to app/javascript/legacy/bank_accounts/confirm/page.js diff --git a/client/js/bank_accounts/create.es6 b/app/javascript/legacy/bank_accounts/create.es6 similarity index 100% rename from client/js/bank_accounts/create.es6 rename to app/javascript/legacy/bank_accounts/create.es6 diff --git a/client/js/bank_accounts/resend_confirmation_email.js b/app/javascript/legacy/bank_accounts/resend_confirmation_email.js similarity index 100% rename from client/js/bank_accounts/resend_confirmation_email.js rename to app/javascript/legacy/bank_accounts/resend_confirmation_email.js diff --git a/client/js/campaigns/index/page.js b/app/javascript/legacy/campaigns/index/page.js similarity index 100% rename from client/js/campaigns/index/page.js rename to app/javascript/legacy/campaigns/index/page.js diff --git a/client/js/campaigns/new/peer_to_peer_wizard.js b/app/javascript/legacy/campaigns/new/peer_to_peer_wizard.js similarity index 100% rename from client/js/campaigns/new/peer_to_peer_wizard.js rename to app/javascript/legacy/campaigns/new/peer_to_peer_wizard.js diff --git a/client/js/campaigns/new/wizard.js b/app/javascript/legacy/campaigns/new/wizard.js similarity index 100% rename from client/js/campaigns/new/wizard.js rename to app/javascript/legacy/campaigns/new/wizard.js diff --git a/client/js/campaigns/peer_to_peer/page.js b/app/javascript/legacy/campaigns/peer_to_peer/page.js similarity index 100% rename from client/js/campaigns/peer_to_peer/page.js rename to app/javascript/legacy/campaigns/peer_to_peer/page.js diff --git a/client/js/campaigns/show/admin.js b/app/javascript/legacy/campaigns/show/admin.js similarity index 100% rename from client/js/campaigns/show/admin.js rename to app/javascript/legacy/campaigns/show/admin.js diff --git a/client/js/campaigns/show/choose-gift-options-modal.js b/app/javascript/legacy/campaigns/show/choose-gift-options-modal.js similarity index 100% rename from client/js/campaigns/show/choose-gift-options-modal.js rename to app/javascript/legacy/campaigns/show/choose-gift-options-modal.js diff --git a/client/js/campaigns/show/gift-option-button.js b/app/javascript/legacy/campaigns/show/gift-option-button.js similarity index 100% rename from client/js/campaigns/show/gift-option-button.js rename to app/javascript/legacy/campaigns/show/gift-option-button.js diff --git a/client/js/campaigns/show/gift-option-list.js b/app/javascript/legacy/campaigns/show/gift-option-list.js similarity index 100% rename from client/js/campaigns/show/gift-option-list.js rename to app/javascript/legacy/campaigns/show/gift-option-list.js diff --git a/client/js/campaigns/show/gift-option-quantity-left.js b/app/javascript/legacy/campaigns/show/gift-option-quantity-left.js similarity index 100% rename from client/js/campaigns/show/gift-option-quantity-left.js rename to app/javascript/legacy/campaigns/show/gift-option-quantity-left.js diff --git a/client/js/campaigns/show/is-sold-out.js b/app/javascript/legacy/campaigns/show/is-sold-out.js similarity index 100% rename from client/js/campaigns/show/is-sold-out.js rename to app/javascript/legacy/campaigns/show/is-sold-out.js diff --git a/client/js/campaigns/show/metrics-and-contribute-box.js b/app/javascript/legacy/campaigns/show/metrics-and-contribute-box.js similarity index 100% rename from client/js/campaigns/show/metrics-and-contribute-box.js rename to app/javascript/legacy/campaigns/show/metrics-and-contribute-box.js diff --git a/client/js/campaigns/show/page.js b/app/javascript/legacy/campaigns/show/page.js similarity index 100% rename from client/js/campaigns/show/page.js rename to app/javascript/legacy/campaigns/show/page.js diff --git a/client/js/campaigns/show/tour.js b/app/javascript/legacy/campaigns/show/tour.js similarity index 100% rename from client/js/campaigns/show/tour.js rename to app/javascript/legacy/campaigns/show/tour.js diff --git a/client/js/campaigns/supporters/index/index.es6 b/app/javascript/legacy/campaigns/supporters/index/index.es6 similarity index 100% rename from client/js/campaigns/supporters/index/index.es6 rename to app/javascript/legacy/campaigns/supporters/index/index.es6 diff --git a/client/js/campaigns/supporters/index/meta.es6 b/app/javascript/legacy/campaigns/supporters/index/meta.es6 similarity index 100% rename from client/js/campaigns/supporters/index/meta.es6 rename to app/javascript/legacy/campaigns/supporters/index/meta.es6 diff --git a/client/js/campaigns/supporters/index/metrics.es6 b/app/javascript/legacy/campaigns/supporters/index/metrics.es6 similarity index 100% rename from client/js/campaigns/supporters/index/metrics.es6 rename to app/javascript/legacy/campaigns/supporters/index/metrics.es6 diff --git a/client/js/campaigns/supporters/index/page.js b/app/javascript/legacy/campaigns/supporters/index/page.js similarity index 100% rename from client/js/campaigns/supporters/index/page.js rename to app/javascript/legacy/campaigns/supporters/index/page.js diff --git a/client/js/campaigns/supporters/index/supporter-list.es6 b/app/javascript/legacy/campaigns/supporters/index/supporter-list.es6 similarity index 100% rename from client/js/campaigns/supporters/index/supporter-list.es6 rename to app/javascript/legacy/campaigns/supporters/index/supporter-list.es6 diff --git a/client/js/campaigns/supporters/index/supporter-table.es6 b/app/javascript/legacy/campaigns/supporters/index/supporter-table.es6 similarity index 100% rename from client/js/campaigns/supporters/index/supporter-table.es6 rename to app/javascript/legacy/campaigns/supporters/index/supporter-table.es6 diff --git a/client/js/campaigns/timeline.js b/app/javascript/legacy/campaigns/timeline.js similarity index 100% rename from client/js/campaigns/timeline.js rename to app/javascript/legacy/campaigns/timeline.js diff --git a/client/js/campaigns/totals.js b/app/javascript/legacy/campaigns/totals.js similarity index 100% rename from client/js/campaigns/totals.js rename to app/javascript/legacy/campaigns/totals.js diff --git a/client/js/cards/create-frp.es6 b/app/javascript/legacy/cards/create-frp.es6 similarity index 100% rename from client/js/cards/create-frp.es6 rename to app/javascript/legacy/cards/create-frp.es6 diff --git a/client/js/cards/create.js b/app/javascript/legacy/cards/create.js similarity index 100% rename from client/js/cards/create.js rename to app/javascript/legacy/cards/create.js diff --git a/client/js/common/ajax/check_campaign_or_event_name.js b/app/javascript/legacy/common/ajax/check_campaign_or_event_name.js similarity index 100% rename from client/js/common/ajax/check_campaign_or_event_name.js rename to app/javascript/legacy/common/ajax/check_campaign_or_event_name.js diff --git a/client/js/common/ajax/get_campaign_and_event_names_and_ids.js b/app/javascript/legacy/common/ajax/get_campaign_and_event_names_and_ids.js similarity index 100% rename from client/js/common/ajax/get_campaign_and_event_names_and_ids.js rename to app/javascript/legacy/common/ajax/get_campaign_and_event_names_and_ids.js diff --git a/client/js/common/application_view.js b/app/javascript/legacy/common/application_view.js similarity index 98% rename from client/js/common/application_view.js rename to app/javascript/legacy/common/application_view.js index bf8c362d..a60cbdd9 100644 --- a/client/js/common/application_view.js +++ b/app/javascript/legacy/common/application_view.js @@ -15,14 +15,6 @@ module.exports = appl appl.is_loading = function() {appl.def('loading', true)} appl.not_loading = function() {appl.def('loading', false)} appl.not_loading() - -// Define the current payment plan tier for a signed-in nonprofit -appl.def('current_plan_tier', app.current_plan_tier) - -appl.def("is_at_least_plan", function(tier) { - return app.current_plan_tier >= tier -}) - // Open a modal given by its modal id (uses the modal div's 'id' attribute) appl.def('open_modal', function(modalId) { $('.modal').removeClass('inView') diff --git a/client/js/common/apply-pikaday.js b/app/javascript/legacy/common/apply-pikaday.js similarity index 100% rename from client/js/common/apply-pikaday.js rename to app/javascript/legacy/common/apply-pikaday.js diff --git a/client/js/common/autosubmit.js b/app/javascript/legacy/common/autosubmit.js similarity index 100% rename from client/js/common/autosubmit.js rename to app/javascript/legacy/common/autosubmit.js diff --git a/client/js/common/brand-fonts.js b/app/javascript/legacy/common/brand-fonts.js similarity index 100% rename from client/js/common/brand-fonts.js rename to app/javascript/legacy/common/brand-fonts.js diff --git a/client/js/common/class-object.js b/app/javascript/legacy/common/class-object.js similarity index 100% rename from client/js/common/class-object.js rename to app/javascript/legacy/common/class-object.js diff --git a/client/js/common/client.js b/app/javascript/legacy/common/client.js similarity index 100% rename from client/js/common/client.js rename to app/javascript/legacy/common/client.js diff --git a/client/js/common/colors.js b/app/javascript/legacy/common/colors.js similarity index 100% rename from client/js/common/colors.js rename to app/javascript/legacy/common/colors.js diff --git a/client/js/common/confirmation.js b/app/javascript/legacy/common/confirmation.js similarity index 100% rename from client/js/common/confirmation.js rename to app/javascript/legacy/common/confirmation.js diff --git a/client/js/common/credit-card-validator.js b/app/javascript/legacy/common/credit-card-validator.js similarity index 100% rename from client/js/common/credit-card-validator.js rename to app/javascript/legacy/common/credit-card-validator.js diff --git a/client/js/common/css-gradient.js b/app/javascript/legacy/common/css-gradient.js similarity index 100% rename from client/js/common/css-gradient.js rename to app/javascript/legacy/common/css-gradient.js diff --git a/client/js/common/direct-to-s3-upload.es6 b/app/javascript/legacy/common/direct-to-s3-upload.es6 similarity index 100% rename from client/js/common/direct-to-s3-upload.es6 rename to app/javascript/legacy/common/direct-to-s3-upload.es6 diff --git a/client/js/common/dynamic_form.js b/app/javascript/legacy/common/dynamic_form.js similarity index 100% rename from client/js/common/dynamic_form.js rename to app/javascript/legacy/common/dynamic_form.js diff --git a/client/js/common/editable.js b/app/javascript/legacy/common/editable.js similarity index 100% rename from client/js/common/editable.js rename to app/javascript/legacy/common/editable.js diff --git a/client/js/common/editor/froala.es6 b/app/javascript/legacy/common/editor/froala.es6 similarity index 100% rename from client/js/common/editor/froala.es6 rename to app/javascript/legacy/common/editor/froala.es6 diff --git a/client/js/common/editor/quill.es6 b/app/javascript/legacy/common/editor/quill.es6 similarity index 100% rename from client/js/common/editor/quill.es6 rename to app/javascript/legacy/common/editor/quill.es6 diff --git a/client/js/common/el_swapo.js b/app/javascript/legacy/common/el_swapo.js similarity index 100% rename from client/js/common/el_swapo.js rename to app/javascript/legacy/common/el_swapo.js diff --git a/client/js/common/event.js b/app/javascript/legacy/common/event.js similarity index 100% rename from client/js/common/event.js rename to app/javascript/legacy/common/event.js diff --git a/client/js/common/ff-form-validation/index.es6 b/app/javascript/legacy/common/ff-form-validation/index.es6 similarity index 100% rename from client/js/common/ff-form-validation/index.es6 rename to app/javascript/legacy/common/ff-form-validation/index.es6 diff --git a/client/js/common/ff-form-validation/lib/currency-regex.es6 b/app/javascript/legacy/common/ff-form-validation/lib/currency-regex.es6 similarity index 100% rename from client/js/common/ff-form-validation/lib/currency-regex.es6 rename to app/javascript/legacy/common/ff-form-validation/lib/currency-regex.es6 diff --git a/client/js/common/ff-form-validation/lib/email-regex.es6 b/app/javascript/legacy/common/ff-form-validation/lib/email-regex.es6 similarity index 100% rename from client/js/common/ff-form-validation/lib/email-regex.es6 rename to app/javascript/legacy/common/ff-form-validation/lib/email-regex.es6 diff --git a/client/js/common/ff-form-validation/lib/readable-prop.es6 b/app/javascript/legacy/common/ff-form-validation/lib/readable-prop.es6 similarity index 100% rename from client/js/common/ff-form-validation/lib/readable-prop.es6 rename to app/javascript/legacy/common/ff-form-validation/lib/readable-prop.es6 diff --git a/client/js/common/file-input-stream.js b/app/javascript/legacy/common/file-input-stream.js similarity index 100% rename from client/js/common/file-input-stream.js rename to app/javascript/legacy/common/file-input-stream.js diff --git a/client/js/common/form-to-object.js b/app/javascript/legacy/common/form-to-object.js similarity index 100% rename from client/js/common/form-to-object.js rename to app/javascript/legacy/common/form-to-object.js diff --git a/client/js/common/form.js b/app/javascript/legacy/common/form.js similarity index 100% rename from client/js/common/form.js rename to app/javascript/legacy/common/form.js diff --git a/client/js/common/format.js b/app/javascript/legacy/common/format.js similarity index 100% rename from client/js/common/format.js rename to app/javascript/legacy/common/format.js diff --git a/client/js/common/format_response_error.js b/app/javascript/legacy/common/format_response_error.js similarity index 100% rename from client/js/common/format_response_error.js rename to app/javascript/legacy/common/format_response_error.js diff --git a/client/js/common/fundraiser_metrics.js b/app/javascript/legacy/common/fundraiser_metrics.js similarity index 100% rename from client/js/common/fundraiser_metrics.js rename to app/javascript/legacy/common/fundraiser_metrics.js diff --git a/client/js/common/geography.js b/app/javascript/legacy/common/geography.js similarity index 100% rename from client/js/common/geography.js rename to app/javascript/legacy/common/geography.js diff --git a/client/js/common/get-valid-data.js b/app/javascript/legacy/common/get-valid-data.js similarity index 100% rename from client/js/common/get-valid-data.js rename to app/javascript/legacy/common/get-valid-data.js diff --git a/client/js/common/image_uploader.js b/app/javascript/legacy/common/image_uploader.js similarity index 100% rename from client/js/common/image_uploader.js rename to app/javascript/legacy/common/image_uploader.js diff --git a/client/js/common/jquery_additions.js b/app/javascript/legacy/common/jquery_additions.js similarity index 100% rename from client/js/common/jquery_additions.js rename to app/javascript/legacy/common/jquery_additions.js diff --git a/client/js/common/notification.js b/app/javascript/legacy/common/notification.js similarity index 100% rename from client/js/common/notification.js rename to app/javascript/legacy/common/notification.js diff --git a/client/js/common/on-change-sanitize-slug.js b/app/javascript/legacy/common/on-change-sanitize-slug.js similarity index 100% rename from client/js/common/on-change-sanitize-slug.js rename to app/javascript/legacy/common/on-change-sanitize-slug.js diff --git a/client/js/common/on-ios11.js b/app/javascript/legacy/common/on-ios11.js similarity index 100% rename from client/js/common/on-ios11.js rename to app/javascript/legacy/common/on-ios11.js diff --git a/client/js/common/onboard.js b/app/javascript/legacy/common/onboard.js similarity index 100% rename from client/js/common/onboard.js rename to app/javascript/legacy/common/onboard.js diff --git a/client/js/common/panels_layout.js b/app/javascript/legacy/common/panels_layout.js similarity index 100% rename from client/js/common/panels_layout.js rename to app/javascript/legacy/common/panels_layout.js diff --git a/client/js/common/pikaday-timepicker.js b/app/javascript/legacy/common/pikaday-timepicker.js similarity index 100% rename from client/js/common/pikaday-timepicker.js rename to app/javascript/legacy/common/pikaday-timepicker.js diff --git a/client/js/common/polyfills.js b/app/javascript/legacy/common/polyfills.js similarity index 100% rename from client/js/common/polyfills.js rename to app/javascript/legacy/common/polyfills.js diff --git a/client/js/common/post-form-data.es6 b/app/javascript/legacy/common/post-form-data.es6 similarity index 100% rename from client/js/common/post-form-data.es6 rename to app/javascript/legacy/common/post-form-data.es6 diff --git a/client/js/common/post-form-data.js b/app/javascript/legacy/common/post-form-data.js similarity index 100% rename from client/js/common/post-form-data.js rename to app/javascript/legacy/common/post-form-data.js diff --git a/client/js/common/request.js b/app/javascript/legacy/common/request.js similarity index 100% rename from client/js/common/request.js rename to app/javascript/legacy/common/request.js diff --git a/client/js/common/restful_resource.js b/app/javascript/legacy/common/restful_resource.js similarity index 100% rename from client/js/common/restful_resource.js rename to app/javascript/legacy/common/restful_resource.js diff --git a/client/js/common/sanitize-slug.js b/app/javascript/legacy/common/sanitize-slug.js similarity index 100% rename from client/js/common/sanitize-slug.js rename to app/javascript/legacy/common/sanitize-slug.js diff --git a/client/js/common/scroll_toggle_class.js b/app/javascript/legacy/common/scroll_toggle_class.js similarity index 100% rename from client/js/common/scroll_toggle_class.js rename to app/javascript/legacy/common/scroll_toggle_class.js diff --git a/client/js/common/search-data.js b/app/javascript/legacy/common/search-data.js similarity index 100% rename from client/js/common/search-data.js rename to app/javascript/legacy/common/search-data.js diff --git a/client/js/common/super-agent-frp.js b/app/javascript/legacy/common/super-agent-frp.js similarity index 100% rename from client/js/common/super-agent-frp.js rename to app/javascript/legacy/common/super-agent-frp.js diff --git a/client/js/common/super-agent-promise.js b/app/javascript/legacy/common/super-agent-promise.js similarity index 100% rename from client/js/common/super-agent-promise.js rename to app/javascript/legacy/common/super-agent-promise.js diff --git a/client/js/common/time-remaining.js b/app/javascript/legacy/common/time-remaining.js similarity index 100% rename from client/js/common/time-remaining.js rename to app/javascript/legacy/common/time-remaining.js diff --git a/client/js/common/utilities.js b/app/javascript/legacy/common/utilities.js similarity index 100% rename from client/js/common/utilities.js rename to app/javascript/legacy/common/utilities.js diff --git a/client/js/common/vendor/Chart.min.js b/app/javascript/legacy/common/vendor/Chart.min.js similarity index 100% rename from client/js/common/vendor/Chart.min.js rename to app/javascript/legacy/common/vendor/Chart.min.js diff --git a/client/js/common/vendor/bootstrap-tour-standalone.js b/app/javascript/legacy/common/vendor/bootstrap-tour-standalone.js similarity index 100% rename from client/js/common/vendor/bootstrap-tour-standalone.js rename to app/javascript/legacy/common/vendor/bootstrap-tour-standalone.js diff --git a/client/js/common/vendor/bootstrap.js b/app/javascript/legacy/common/vendor/bootstrap.js similarity index 100% rename from client/js/common/vendor/bootstrap.js rename to app/javascript/legacy/common/vendor/bootstrap.js diff --git a/client/js/common/vendor/colpick.js b/app/javascript/legacy/common/vendor/colpick.js similarity index 100% rename from client/js/common/vendor/colpick.js rename to app/javascript/legacy/common/vendor/colpick.js diff --git a/client/js/common/vendor/jquery.cookie.js b/app/javascript/legacy/common/vendor/jquery.cookie.js similarity index 100% rename from client/js/common/vendor/jquery.cookie.js rename to app/javascript/legacy/common/vendor/jquery.cookie.js diff --git a/client/js/common/vendor/masonry.js b/app/javascript/legacy/common/vendor/masonry.js similarity index 100% rename from client/js/common/vendor/masonry.js rename to app/javascript/legacy/common/vendor/masonry.js diff --git a/client/js/components/activity_feed.js b/app/javascript/legacy/components/activity_feed.js similarity index 100% rename from client/js/components/activity_feed.js rename to app/javascript/legacy/components/activity_feed.js diff --git a/client/js/components/address-autocomplete-fields.js b/app/javascript/legacy/components/address-autocomplete-fields.js similarity index 100% rename from client/js/components/address-autocomplete-fields.js rename to app/javascript/legacy/components/address-autocomplete-fields.js diff --git a/client/js/components/address-autocomplete.js b/app/javascript/legacy/components/address-autocomplete.js similarity index 100% rename from client/js/components/address-autocomplete.js rename to app/javascript/legacy/components/address-autocomplete.js diff --git a/client/js/components/ajax/toggle_soft_delete.js b/app/javascript/legacy/components/ajax/toggle_soft_delete.js similarity index 100% rename from client/js/components/ajax/toggle_soft_delete.js rename to app/javascript/legacy/components/ajax/toggle_soft_delete.js diff --git a/client/js/components/b64.js b/app/javascript/legacy/components/b64.js similarity index 100% rename from client/js/components/b64.js rename to app/javascript/legacy/components/b64.js diff --git a/client/js/components/branded_fundraising.js b/app/javascript/legacy/components/branded_fundraising.js similarity index 100% rename from client/js/components/branded_fundraising.js rename to app/javascript/legacy/components/branded_fundraising.js diff --git a/client/js/components/card-form.es6 b/app/javascript/legacy/components/card-form.es6 similarity index 100% rename from client/js/components/card-form.es6 rename to app/javascript/legacy/components/card-form.es6 diff --git a/client/js/components/chart-options.js b/app/javascript/legacy/components/chart-options.js similarity index 100% rename from client/js/components/chart-options.js rename to app/javascript/legacy/components/chart-options.js diff --git a/client/js/components/checkbox.js b/app/javascript/legacy/components/checkbox.js similarity index 100% rename from client/js/components/checkbox.js rename to app/javascript/legacy/components/checkbox.js diff --git a/client/js/components/color-picker.es6 b/app/javascript/legacy/components/color-picker.es6 similarity index 100% rename from client/js/components/color-picker.es6 rename to app/javascript/legacy/components/color-picker.es6 diff --git a/client/js/components/confirmation-modal.js b/app/javascript/legacy/components/confirmation-modal.js similarity index 100% rename from client/js/components/confirmation-modal.js rename to app/javascript/legacy/components/confirmation-modal.js diff --git a/client/js/components/date-range.js b/app/javascript/legacy/components/date-range.js similarity index 100% rename from client/js/components/date-range.js rename to app/javascript/legacy/components/date-range.js diff --git a/client/js/components/date_range_picker.js b/app/javascript/legacy/components/date_range_picker.js similarity index 100% rename from client/js/components/date_range_picker.js rename to app/javascript/legacy/components/date_range_picker.js diff --git a/client/js/components/dollar-input.js b/app/javascript/legacy/components/dollar-input.js similarity index 100% rename from client/js/components/dollar-input.js rename to app/javascript/legacy/components/dollar-input.js diff --git a/client/js/components/drag-to-reorder.js b/app/javascript/legacy/components/drag-to-reorder.js similarity index 100% rename from client/js/components/drag-to-reorder.js rename to app/javascript/legacy/components/drag-to-reorder.js diff --git a/client/js/components/duplicate_fundraiser.js b/app/javascript/legacy/components/duplicate_fundraiser.js similarity index 100% rename from client/js/components/duplicate_fundraiser.js rename to app/javascript/legacy/components/duplicate_fundraiser.js diff --git a/client/js/components/encode-plain-email.js b/app/javascript/legacy/components/encode-plain-email.js similarity index 100% rename from client/js/components/encode-plain-email.js rename to app/javascript/legacy/components/encode-plain-email.js diff --git a/client/js/components/field-with-error.js b/app/javascript/legacy/components/field-with-error.js similarity index 100% rename from client/js/components/field-with-error.js rename to app/javascript/legacy/components/field-with-error.js diff --git a/client/js/components/fundraising/add_header_image.js b/app/javascript/legacy/components/fundraising/add_header_image.js similarity index 100% rename from client/js/components/fundraising/add_header_image.js rename to app/javascript/legacy/components/fundraising/add_header_image.js diff --git a/client/js/components/maps/cc_map.js b/app/javascript/legacy/components/maps/cc_map.js similarity index 100% rename from client/js/components/maps/cc_map.js rename to app/javascript/legacy/components/maps/cc_map.js diff --git a/client/js/components/maps/default_options.js b/app/javascript/legacy/components/maps/default_options.js similarity index 100% rename from client/js/components/maps/default_options.js rename to app/javascript/legacy/components/maps/default_options.js diff --git a/client/js/components/maps/npo_coordinates.js b/app/javascript/legacy/components/maps/npo_coordinates.js similarity index 100% rename from client/js/components/maps/npo_coordinates.js rename to app/javascript/legacy/components/maps/npo_coordinates.js diff --git a/client/js/components/maps/styles.js b/app/javascript/legacy/components/maps/styles.js similarity index 100% rename from client/js/components/maps/styles.js rename to app/javascript/legacy/components/maps/styles.js diff --git a/client/js/components/modal.js b/app/javascript/legacy/components/modal.js similarity index 100% rename from client/js/components/modal.js rename to app/javascript/legacy/components/modal.js diff --git a/app/javascript/legacy/components/nonprofit-branding.js b/app/javascript/legacy/components/nonprofit-branding.js new file mode 100644 index 00000000..26501110 --- /dev/null +++ b/app/javascript/legacy/components/nonprofit-branding.js @@ -0,0 +1,5 @@ +// License: LGPL-3.0-or-later +import nonprofitBranding from '../../legacy_react/src/lib/nonprofitBranding'; + +export default nonprofitBranding(app.nonprofit.brand_color) + diff --git a/client/js/components/number-input.js b/app/javascript/legacy/components/number-input.js similarity index 100% rename from client/js/components/number-input.js rename to app/javascript/legacy/components/number-input.js diff --git a/client/js/components/progress-bar.js b/app/javascript/legacy/components/progress-bar.js similarity index 100% rename from client/js/components/progress-bar.js rename to app/javascript/legacy/components/progress-bar.js diff --git a/client/js/components/public-activities.js b/app/javascript/legacy/components/public-activities.js similarity index 100% rename from client/js/components/public-activities.js rename to app/javascript/legacy/components/public-activities.js diff --git a/client/js/components/radio-and-label-wrapper.js b/app/javascript/legacy/components/radio-and-label-wrapper.js similarity index 100% rename from client/js/components/radio-and-label-wrapper.js rename to app/javascript/legacy/components/radio-and-label-wrapper.js diff --git a/client/js/components/radios.js b/app/javascript/legacy/components/radios.js similarity index 100% rename from client/js/components/radios.js rename to app/javascript/legacy/components/radios.js diff --git a/client/js/components/render-activities.js b/app/javascript/legacy/components/render-activities.js similarity index 100% rename from client/js/components/render-activities.js rename to app/javascript/legacy/components/render-activities.js diff --git a/client/js/components/saving_indicator.js b/app/javascript/legacy/components/saving_indicator.js similarity index 100% rename from client/js/components/saving_indicator.js rename to app/javascript/legacy/components/saving_indicator.js diff --git a/client/js/components/search-table.js b/app/javascript/legacy/components/search-table.js similarity index 100% rename from client/js/components/search-table.js rename to app/javascript/legacy/components/search-table.js diff --git a/client/js/components/search.js b/app/javascript/legacy/components/search.js similarity index 100% rename from client/js/components/search.js rename to app/javascript/legacy/components/search.js diff --git a/client/js/components/select.js b/app/javascript/legacy/components/select.js similarity index 100% rename from client/js/components/select.js rename to app/javascript/legacy/components/select.js diff --git a/client/js/components/sepa-form.es6 b/app/javascript/legacy/components/sepa-form.es6 similarity index 100% rename from client/js/components/sepa-form.es6 rename to app/javascript/legacy/components/sepa-form.es6 diff --git a/client/js/components/set-state-from-value.js b/app/javascript/legacy/components/set-state-from-value.js similarity index 100% rename from client/js/components/set-state-from-value.js rename to app/javascript/legacy/components/set-state-from-value.js diff --git a/client/js/components/show-more-button.es6 b/app/javascript/legacy/components/show-more-button.es6 similarity index 100% rename from client/js/components/show-more-button.es6 rename to app/javascript/legacy/components/show-more-button.es6 diff --git a/client/js/components/state-selector.js b/app/javascript/legacy/components/state-selector.js similarity index 100% rename from client/js/components/state-selector.js rename to app/javascript/legacy/components/state-selector.js diff --git a/client/js/components/styles/branded-wizard.js b/app/javascript/legacy/components/styles/branded-wizard.js similarity index 100% rename from client/js/components/styles/branded-wizard.js rename to app/javascript/legacy/components/styles/branded-wizard.js diff --git a/client/js/components/styles/render-styles.js b/app/javascript/legacy/components/styles/render-styles.js similarity index 100% rename from client/js/components/styles/render-styles.js rename to app/javascript/legacy/components/styles/render-styles.js diff --git a/client/js/components/supporter-address-form.es6 b/app/javascript/legacy/components/supporter-address-form.es6 similarity index 100% rename from client/js/components/supporter-address-form.es6 rename to app/javascript/legacy/components/supporter-address-form.es6 diff --git a/client/js/components/supporter-fields.js b/app/javascript/legacy/components/supporter-fields.js similarity index 100% rename from client/js/components/supporter-fields.js rename to app/javascript/legacy/components/supporter-fields.js diff --git a/client/js/components/tables/filtering/apply_filter.js b/app/javascript/legacy/components/tables/filtering/apply_filter.js similarity index 100% rename from client/js/components/tables/filtering/apply_filter.js rename to app/javascript/legacy/components/tables/filtering/apply_filter.js diff --git a/client/js/components/tables/search.es6 b/app/javascript/legacy/components/tables/search.es6 similarity index 100% rename from client/js/components/tables/search.es6 rename to app/javascript/legacy/components/tables/search.es6 diff --git a/client/js/components/text-input.js b/app/javascript/legacy/components/text-input.js similarity index 100% rename from client/js/components/text-input.js rename to app/javascript/legacy/components/text-input.js diff --git a/client/js/components/textarea.js b/app/javascript/legacy/components/textarea.js similarity index 100% rename from client/js/components/textarea.js rename to app/javascript/legacy/components/textarea.js diff --git a/client/js/components/todos.js b/app/javascript/legacy/components/todos.js similarity index 100% rename from client/js/components/todos.js rename to app/javascript/legacy/components/todos.js diff --git a/client/js/components/top-nav.js b/app/javascript/legacy/components/top-nav.js similarity index 100% rename from client/js/components/top-nav.js rename to app/javascript/legacy/components/top-nav.js diff --git a/client/js/components/wizard.js b/app/javascript/legacy/components/wizard.js similarity index 100% rename from client/js/components/wizard.js rename to app/javascript/legacy/components/wizard.js diff --git a/client/js/donations/create.js b/app/javascript/legacy/donations/create.js similarity index 100% rename from client/js/donations/create.js rename to app/javascript/legacy/donations/create.js diff --git a/client/js/donations/create_offline.js b/app/javascript/legacy/donations/create_offline.js similarity index 100% rename from client/js/donations/create_offline.js rename to app/javascript/legacy/donations/create_offline.js diff --git a/client/js/events/discounts/index.js b/app/javascript/legacy/events/discounts/index.js similarity index 100% rename from client/js/events/discounts/index.js rename to app/javascript/legacy/events/discounts/index.js diff --git a/client/js/events/discounts/manage.js b/app/javascript/legacy/events/discounts/manage.js similarity index 100% rename from client/js/events/discounts/manage.js rename to app/javascript/legacy/events/discounts/manage.js diff --git a/client/js/events/index/page.js b/app/javascript/legacy/events/index/page.js similarity index 100% rename from client/js/events/index/page.js rename to app/javascript/legacy/events/index/page.js diff --git a/client/js/events/listing-item/index.js b/app/javascript/legacy/events/listing-item/index.js similarity index 100% rename from client/js/events/listing-item/index.js rename to app/javascript/legacy/events/listing-item/index.js diff --git a/client/js/events/listings/index.js b/app/javascript/legacy/events/listings/index.js similarity index 100% rename from client/js/events/listings/index.js rename to app/javascript/legacy/events/listings/index.js diff --git a/client/js/events/new/wizard.js b/app/javascript/legacy/events/new/wizard.js similarity index 100% rename from client/js/events/new/wizard.js rename to app/javascript/legacy/events/new/wizard.js diff --git a/client/js/events/show/editor.js b/app/javascript/legacy/events/show/editor.js similarity index 100% rename from client/js/events/show/editor.js rename to app/javascript/legacy/events/show/editor.js diff --git a/client/js/events/show/event_donation.js b/app/javascript/legacy/events/show/event_donation.js similarity index 100% rename from client/js/events/show/event_donation.js rename to app/javascript/legacy/events/show/event_donation.js diff --git a/client/js/events/show/page.js b/app/javascript/legacy/events/show/page.js similarity index 100% rename from client/js/events/show/page.js rename to app/javascript/legacy/events/show/page.js diff --git a/client/js/events/show/tour.js b/app/javascript/legacy/events/show/tour.js similarity index 100% rename from client/js/events/show/tour.js rename to app/javascript/legacy/events/show/tour.js diff --git a/client/js/events/stats/page.js b/app/javascript/legacy/events/stats/page.js similarity index 100% rename from client/js/events/stats/page.js rename to app/javascript/legacy/events/stats/page.js diff --git a/client/js/gift_options/admin.js b/app/javascript/legacy/gift_options/admin.js similarity index 100% rename from client/js/gift_options/admin.js rename to app/javascript/legacy/gift_options/admin.js diff --git a/client/js/gift_options/index.js b/app/javascript/legacy/gift_options/index.js similarity index 100% rename from client/js/gift_options/index.js rename to app/javascript/legacy/gift_options/index.js diff --git a/client/js/nonprofits/btn/page.js b/app/javascript/legacy/nonprofits/btn/page.js similarity index 100% rename from client/js/nonprofits/btn/page.js rename to app/javascript/legacy/nonprofits/btn/page.js diff --git a/client/js/nonprofits/button/amounts.js b/app/javascript/legacy/nonprofits/button/amounts.js similarity index 100% rename from client/js/nonprofits/button/amounts.js rename to app/javascript/legacy/nonprofits/button/amounts.js diff --git a/client/js/nonprofits/button/appearance.js b/app/javascript/legacy/nonprofits/button/appearance.js similarity index 100% rename from client/js/nonprofits/button/appearance.js rename to app/javascript/legacy/nonprofits/button/appearance.js diff --git a/client/js/nonprofits/button/designations.js b/app/javascript/legacy/nonprofits/button/designations.js similarity index 100% rename from client/js/nonprofits/button/designations.js rename to app/javascript/legacy/nonprofits/button/designations.js diff --git a/client/js/nonprofits/button/footer.js b/app/javascript/legacy/nonprofits/button/footer.js similarity index 100% rename from client/js/nonprofits/button/footer.js rename to app/javascript/legacy/nonprofits/button/footer.js diff --git a/client/js/nonprofits/button/hide-dedication.js b/app/javascript/legacy/nonprofits/button/hide-dedication.js similarity index 100% rename from client/js/nonprofits/button/hide-dedication.js rename to app/javascript/legacy/nonprofits/button/hide-dedication.js diff --git a/client/js/nonprofits/button/page.js b/app/javascript/legacy/nonprofits/button/page.js similarity index 100% rename from client/js/nonprofits/button/page.js rename to app/javascript/legacy/nonprofits/button/page.js diff --git a/client/js/nonprofits/button/preview.js b/app/javascript/legacy/nonprofits/button/preview.js similarity index 100% rename from client/js/nonprofits/button/preview.js rename to app/javascript/legacy/nonprofits/button/preview.js diff --git a/client/js/nonprofits/button/thank-you.js b/app/javascript/legacy/nonprofits/button/thank-you.js similarity index 100% rename from client/js/nonprofits/button/thank-you.js rename to app/javascript/legacy/nonprofits/button/thank-you.js diff --git a/client/js/nonprofits/button/type.js b/app/javascript/legacy/nonprofits/button/type.js similarity index 100% rename from client/js/nonprofits/button/type.js rename to app/javascript/legacy/nonprofits/button/type.js diff --git a/client/js/nonprofits/cards/edit/index.es6 b/app/javascript/legacy/nonprofits/cards/edit/index.es6 similarity index 88% rename from client/js/nonprofits/cards/edit/index.es6 rename to app/javascript/legacy/nonprofits/cards/edit/index.es6 index 626f34b3..7403cf75 100644 --- a/client/js/nonprofits/cards/edit/index.es6 +++ b/app/javascript/legacy/nonprofits/cards/edit/index.es6 @@ -38,9 +38,6 @@ const view = state => h('div.u-centered.u-maxWidth--600.u-margin--auto.u-marginTop--50.u-padding--15.js-view-confirm', [ h('h4', `Payment Method for ${app.nonprofit.name}`) , state.card.name ? h('p', `Current card: ${state.card.name}`) : '' - , h('p', [ - state.subscription.status === 'trialing' ? `You have ${state.daysLeft} days left in your free trial. If you add a payment method now, your account will stay active after your trial, and you will get your remaining trial days for free.` : '' - ]) , h('p.u-strong', `Tier: ${state.plan.name} ($${format.centsToDollars(state.plan.amount)} ${state.plan.interval})`) , h('hr') , h('h5', 'Update Your Card:') diff --git a/client/js/nonprofits/cards/edit/page.js b/app/javascript/legacy/nonprofits/cards/edit/page.js similarity index 100% rename from client/js/nonprofits/cards/edit/page.js rename to app/javascript/legacy/nonprofits/cards/edit/page.js diff --git a/client/js/nonprofits/dashboard/page.js b/app/javascript/legacy/nonprofits/dashboard/page.js similarity index 100% rename from client/js/nonprofits/dashboard/page.js rename to app/javascript/legacy/nonprofits/dashboard/page.js diff --git a/client/js/nonprofits/dashboard/tour.js b/app/javascript/legacy/nonprofits/dashboard/tour.js similarity index 100% rename from client/js/nonprofits/dashboard/tour.js rename to app/javascript/legacy/nonprofits/dashboard/tour.js diff --git a/client/js/nonprofits/donate/amount-step.js b/app/javascript/legacy/nonprofits/donate/amount-step.js similarity index 100% rename from client/js/nonprofits/donate/amount-step.js rename to app/javascript/legacy/nonprofits/donate/amount-step.js diff --git a/client/js/nonprofits/donate/dedication-form.js b/app/javascript/legacy/nonprofits/donate/dedication-form.js similarity index 100% rename from client/js/nonprofits/donate/dedication-form.js rename to app/javascript/legacy/nonprofits/donate/dedication-form.js diff --git a/client/js/nonprofits/donate/followup-step.js b/app/javascript/legacy/nonprofits/donate/followup-step.js similarity index 100% rename from client/js/nonprofits/donate/followup-step.js rename to app/javascript/legacy/nonprofits/donate/followup-step.js diff --git a/client/js/nonprofits/donate/get-params.js b/app/javascript/legacy/nonprofits/donate/get-params.js similarity index 100% rename from client/js/nonprofits/donate/get-params.js rename to app/javascript/legacy/nonprofits/donate/get-params.js diff --git a/client/js/nonprofits/donate/info-step.js b/app/javascript/legacy/nonprofits/donate/info-step.js similarity index 100% rename from client/js/nonprofits/donate/info-step.js rename to app/javascript/legacy/nonprofits/donate/info-step.js diff --git a/client/js/nonprofits/donate/page.js b/app/javascript/legacy/nonprofits/donate/page.js similarity index 88% rename from client/js/nonprofits/donate/page.js rename to app/javascript/legacy/nonprofits/donate/page.js index f256fd7b..09fb6c87 100644 --- a/client/js/nonprofits/donate/page.js +++ b/app/javascript/legacy/nonprofits/donate/page.js @@ -72,14 +72,14 @@ function setGiftOptionParams(campaign_id, gift_id) { var state = donate.init(params$) var container = document.querySelector('.js-donateForm') -if(app.nonprofit.plan_tier > 0) { - $(".donationWizard").trigger("render:pre"); - var event = new CustomEvent('render:pre'); - container.parentNode.dispatchEvent(event); - render({patch, view: donate.view, state, container}) - jQuery(function($){ - $(".donationWizard").trigger("render:post").addClass("displayed-updated"); - }); + +$(".donationWizard").trigger("render:pre"); +var event = new CustomEvent('render:pre'); +container.parentNode.dispatchEvent(event); +render({patch, view: donate.view, state, container}) +jQuery(function($){ +$(".donationWizard").trigger("render:post").addClass("displayed-updated"); +}); // event = new CustomEvent('render:post'); // container.parentNode.dispatchEvent(event); -} + diff --git a/client/js/nonprofits/donate/payment-step.js b/app/javascript/legacy/nonprofits/donate/payment-step.js similarity index 100% rename from client/js/nonprofits/donate/payment-step.js rename to app/javascript/legacy/nonprofits/donate/payment-step.js diff --git a/client/js/nonprofits/donate/plugins-available/alwaysAnonymous.js b/app/javascript/legacy/nonprofits/donate/plugins-available/alwaysAnonymous.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/alwaysAnonymous.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/alwaysAnonymous.js diff --git a/client/js/nonprofits/donate/plugins-available/default-recurring.js b/app/javascript/legacy/nonprofits/donate/plugins-available/default-recurring.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/default-recurring.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/default-recurring.js diff --git a/client/js/nonprofits/donate/plugins-available/dummy.js b/app/javascript/legacy/nonprofits/donate/plugins-available/dummy.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/dummy.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/dummy.js diff --git a/client/js/nonprofits/donate/plugins-available/ibanonly.js b/app/javascript/legacy/nonprofits/donate/plugins-available/ibanonly.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/ibanonly.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/ibanonly.js diff --git a/client/js/nonprofits/donate/plugins-available/minamount.js b/app/javascript/legacy/nonprofits/donate/plugins-available/minamount.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/minamount.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/minamount.js diff --git a/client/js/nonprofits/donate/plugins-available/minimalForm.js b/app/javascript/legacy/nonprofits/donate/plugins-available/minimalForm.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/minimalForm.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/minimalForm.js diff --git a/client/js/nonprofits/donate/plugins-available/piwik.js b/app/javascript/legacy/nonprofits/donate/plugins-available/piwik.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/piwik.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/piwik.js diff --git a/client/js/nonprofits/donate/plugins-available/prefill-identity.js b/app/javascript/legacy/nonprofits/donate/plugins-available/prefill-identity.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/prefill-identity.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/prefill-identity.js diff --git a/client/js/nonprofits/donate/plugins-available/prettify.js b/app/javascript/legacy/nonprofits/donate/plugins-available/prettify.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/prettify.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/prettify.js diff --git a/client/js/nonprofits/donate/plugins-available/select-amount.js b/app/javascript/legacy/nonprofits/donate/plugins-available/select-amount.js similarity index 100% rename from client/js/nonprofits/donate/plugins-available/select-amount.js rename to app/javascript/legacy/nonprofits/donate/plugins-available/select-amount.js diff --git a/client/js/nonprofits/donate/wizard.js b/app/javascript/legacy/nonprofits/donate/wizard.js similarity index 99% rename from client/js/nonprofits/donate/wizard.js rename to app/javascript/legacy/nonprofits/donate/wizard.js index 5993868f..dde2ad11 100644 --- a/client/js/nonprofits/donate/wizard.js +++ b/app/javascript/legacy/nonprofits/donate/wizard.js @@ -160,7 +160,7 @@ const view = state => { , class: {'u-hide': !state.params$().offsite || !state.params$().embedded} }) , h('div.titleRow', [ - h('img', {props: {src: app.nonprofit.logo.normal.url}}) + h('img', {props: {src: app.nonprofit.logo.normal}}) , h('div.titleRow-info', [ h('h2', app.campaign.name || app.nonprofit.name ) , h('p', [ diff --git a/client/js/nonprofits/edit/page.js b/app/javascript/legacy/nonprofits/edit/page.js similarity index 100% rename from client/js/nonprofits/edit/page.js rename to app/javascript/legacy/nonprofits/edit/page.js diff --git a/client/js/nonprofits/payments/index/page.js b/app/javascript/legacy/nonprofits/payments/index/page.js similarity index 100% rename from client/js/nonprofits/payments/index/page.js rename to app/javascript/legacy/nonprofits/payments/index/page.js diff --git a/client/js/nonprofits/payments/index/payment_details.js b/app/javascript/legacy/nonprofits/payments/index/payment_details.js similarity index 100% rename from client/js/nonprofits/payments/index/payment_details.js rename to app/javascript/legacy/nonprofits/payments/index/payment_details.js diff --git a/client/js/nonprofits/payments/index/tour.js b/app/javascript/legacy/nonprofits/payments/index/tour.js similarity index 100% rename from client/js/nonprofits/payments/index/tour.js rename to app/javascript/legacy/nonprofits/payments/index/tour.js diff --git a/client/js/nonprofits/payments_chart.js b/app/javascript/legacy/nonprofits/payments_chart.js similarity index 100% rename from client/js/nonprofits/payments_chart.js rename to app/javascript/legacy/nonprofits/payments_chart.js diff --git a/client/js/nonprofits/payouts/create.js b/app/javascript/legacy/nonprofits/payouts/create.js similarity index 100% rename from client/js/nonprofits/payouts/create.js rename to app/javascript/legacy/nonprofits/payouts/create.js diff --git a/client/js/nonprofits/payouts/index/identity-verification-form.es6 b/app/javascript/legacy/nonprofits/payouts/index/identity-verification-form.es6 similarity index 100% rename from client/js/nonprofits/payouts/index/identity-verification-form.es6 rename to app/javascript/legacy/nonprofits/payouts/index/identity-verification-form.es6 diff --git a/client/js/nonprofits/payouts/index/page.js b/app/javascript/legacy/nonprofits/payouts/index/page.js similarity index 100% rename from client/js/nonprofits/payouts/index/page.js rename to app/javascript/legacy/nonprofits/payouts/index/page.js diff --git a/client/js/nonprofits/payouts/index/verify_identity.js b/app/javascript/legacy/nonprofits/payouts/index/verify_identity.js similarity index 100% rename from client/js/nonprofits/payouts/index/verify_identity.js rename to app/javascript/legacy/nonprofits/payouts/index/verify_identity.js diff --git a/client/js/nonprofits/recurring_donations/index/create.js b/app/javascript/legacy/nonprofits/recurring_donations/index/create.js similarity index 100% rename from client/js/nonprofits/recurring_donations/index/create.js rename to app/javascript/legacy/nonprofits/recurring_donations/index/create.js diff --git a/client/js/nonprofits/recurring_donations/index/delete.js b/app/javascript/legacy/nonprofits/recurring_donations/index/delete.js similarity index 100% rename from client/js/nonprofits/recurring_donations/index/delete.js rename to app/javascript/legacy/nonprofits/recurring_donations/index/delete.js diff --git a/client/js/nonprofits/recurring_donations/index/index.es6 b/app/javascript/legacy/nonprofits/recurring_donations/index/index.es6 similarity index 100% rename from client/js/nonprofits/recurring_donations/index/index.es6 rename to app/javascript/legacy/nonprofits/recurring_donations/index/index.es6 diff --git a/client/js/nonprofits/recurring_donations/index/page.js b/app/javascript/legacy/nonprofits/recurring_donations/index/page.js similarity index 100% rename from client/js/nonprofits/recurring_donations/index/page.js rename to app/javascript/legacy/nonprofits/recurring_donations/index/page.js diff --git a/client/js/nonprofits/recurring_donations/index/tour.js b/app/javascript/legacy/nonprofits/recurring_donations/index/tour.js similarity index 100% rename from client/js/nonprofits/recurring_donations/index/tour.js rename to app/javascript/legacy/nonprofits/recurring_donations/index/tour.js diff --git a/client/js/nonprofits/recurring_donations/index/update.js b/app/javascript/legacy/nonprofits/recurring_donations/index/update.js similarity index 100% rename from client/js/nonprofits/recurring_donations/index/update.js rename to app/javascript/legacy/nonprofits/recurring_donations/index/update.js diff --git a/client/js/nonprofits/recurring_donations/readable_interval.js b/app/javascript/legacy/nonprofits/recurring_donations/readable_interval.js similarity index 100% rename from client/js/nonprofits/recurring_donations/readable_interval.js rename to app/javascript/legacy/nonprofits/recurring_donations/readable_interval.js diff --git a/client/js/nonprofits/reports/modal.js b/app/javascript/legacy/nonprofits/reports/modal.js similarity index 100% rename from client/js/nonprofits/reports/modal.js rename to app/javascript/legacy/nonprofits/reports/modal.js diff --git a/client/js/nonprofits/show/page.js b/app/javascript/legacy/nonprofits/show/page.js similarity index 100% rename from client/js/nonprofits/show/page.js rename to app/javascript/legacy/nonprofits/show/page.js diff --git a/client/js/nonprofits/show/tour.js b/app/javascript/legacy/nonprofits/show/tour.js similarity index 100% rename from client/js/nonprofits/show/tour.js rename to app/javascript/legacy/nonprofits/show/tour.js diff --git a/client/js/nonprofits/supporter_form/index.es6 b/app/javascript/legacy/nonprofits/supporter_form/index.es6 similarity index 100% rename from client/js/nonprofits/supporter_form/index.es6 rename to app/javascript/legacy/nonprofits/supporter_form/index.es6 diff --git a/client/js/nonprofits/supporter_form/page.js b/app/javascript/legacy/nonprofits/supporter_form/page.js similarity index 100% rename from client/js/nonprofits/supporter_form/page.js rename to app/javascript/legacy/nonprofits/supporter_form/page.js diff --git a/client/js/nonprofits/supporters/create.js b/app/javascript/legacy/nonprofits/supporters/create.js similarity index 100% rename from client/js/nonprofits/supporters/create.js rename to app/javascript/legacy/nonprofits/supporters/create.js diff --git a/client/js/nonprofits/supporters/get_name.js b/app/javascript/legacy/nonprofits/supporters/get_name.js similarity index 100% rename from client/js/nonprofits/supporters/get_name.js rename to app/javascript/legacy/nonprofits/supporters/get_name.js diff --git a/client/js/nonprofits/supporters/import/index.es6 b/app/javascript/legacy/nonprofits/supporters/import/index.es6 similarity index 100% rename from client/js/nonprofits/supporters/import/index.es6 rename to app/javascript/legacy/nonprofits/supporters/import/index.es6 diff --git a/client/js/nonprofits/supporters/import/regex-header-matchers.js b/app/javascript/legacy/nonprofits/supporters/import/regex-header-matchers.js similarity index 100% rename from client/js/nonprofits/supporters/import/regex-header-matchers.js rename to app/javascript/legacy/nonprofits/supporters/import/regex-header-matchers.js diff --git a/client/js/nonprofits/supporters/index/action_recipient.js b/app/javascript/legacy/nonprofits/supporters/index/action_recipient.js similarity index 100% rename from client/js/nonprofits/supporters/index/action_recipient.js rename to app/javascript/legacy/nonprofits/supporters/index/action_recipient.js diff --git a/client/js/nonprofits/supporters/index/bulk_delete.js b/app/javascript/legacy/nonprofits/supporters/index/bulk_delete.js similarity index 100% rename from client/js/nonprofits/supporters/index/bulk_delete.js rename to app/javascript/legacy/nonprofits/supporters/index/bulk_delete.js diff --git a/client/js/nonprofits/supporters/index/import.js b/app/javascript/legacy/nonprofits/supporters/index/import.js similarity index 100% rename from client/js/nonprofits/supporters/index/import.js rename to app/javascript/legacy/nonprofits/supporters/index/import.js diff --git a/client/js/nonprofits/supporters/index/list_supporters.js b/app/javascript/legacy/nonprofits/supporters/index/list_supporters.js similarity index 100% rename from client/js/nonprofits/supporters/index/list_supporters.js rename to app/javascript/legacy/nonprofits/supporters/index/list_supporters.js diff --git a/client/js/nonprofits/supporters/index/manage_custom_fields.js b/app/javascript/legacy/nonprofits/supporters/index/manage_custom_fields.js similarity index 100% rename from client/js/nonprofits/supporters/index/manage_custom_fields.js rename to app/javascript/legacy/nonprofits/supporters/index/manage_custom_fields.js diff --git a/client/js/nonprofits/supporters/index/manage_tags.js b/app/javascript/legacy/nonprofits/supporters/index/manage_tags.js similarity index 100% rename from client/js/nonprofits/supporters/index/manage_tags.js rename to app/javascript/legacy/nonprofits/supporters/index/manage_tags.js diff --git a/client/js/nonprofits/supporters/index/merge_supporters.js b/app/javascript/legacy/nonprofits/supporters/index/merge_supporters.js similarity index 100% rename from client/js/nonprofits/supporters/index/merge_supporters.js rename to app/javascript/legacy/nonprofits/supporters/index/merge_supporters.js diff --git a/client/js/nonprofits/supporters/index/page.js b/app/javascript/legacy/nonprofits/supporters/index/page.js similarity index 100% rename from client/js/nonprofits/supporters/index/page.js rename to app/javascript/legacy/nonprofits/supporters/index/page.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/generate-content.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/generate-content.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/generate-content.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/generate-content.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/index.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/index.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/index.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/index.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/offsite-donation-form.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/offsite-donation-form.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/offsite-donation-form.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-actions.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/supporter-actions.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-actions.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/supporter-activities.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-activities.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/supporter-activities.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-activities.js diff --git a/client/js/nonprofits/supporters/index/sidepanel/supporter-note-form.js b/app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-note-form.js similarity index 100% rename from client/js/nonprofits/supporters/index/sidepanel/supporter-note-form.js rename to app/javascript/legacy/nonprofits/supporters/index/sidepanel/supporter-note-form.js diff --git a/client/js/nonprofits/supporters/index/supporter_details.js b/app/javascript/legacy/nonprofits/supporters/index/supporter_details.js similarity index 100% rename from client/js/nonprofits/supporters/index/supporter_details.js rename to app/javascript/legacy/nonprofits/supporters/index/supporter_details.js diff --git a/client/js/nonprofits/supporters/index/tags_and_fields_shared_methods.js b/app/javascript/legacy/nonprofits/supporters/index/tags_and_fields_shared_methods.js similarity index 100% rename from client/js/nonprofits/supporters/index/tags_and_fields_shared_methods.js rename to app/javascript/legacy/nonprofits/supporters/index/tags_and_fields_shared_methods.js diff --git a/client/js/nonprofits/supporters/index/timeline.js b/app/javascript/legacy/nonprofits/supporters/index/timeline.js similarity index 100% rename from client/js/nonprofits/supporters/index/timeline.js rename to app/javascript/legacy/nonprofits/supporters/index/timeline.js diff --git a/client/js/nonprofits/supporters/index/tour.js b/app/javascript/legacy/nonprofits/supporters/index/tour.js similarity index 100% rename from client/js/nonprofits/supporters/index/tour.js rename to app/javascript/legacy/nonprofits/supporters/index/tour.js diff --git a/client/js/nonprofits/supporters/new/page.js b/app/javascript/legacy/nonprofits/supporters/new/page.js similarity index 100% rename from client/js/nonprofits/supporters/new/page.js rename to app/javascript/legacy/nonprofits/supporters/new/page.js diff --git a/client/js/page.js b/app/javascript/legacy/page.js similarity index 100% rename from client/js/page.js rename to app/javascript/legacy/page.js diff --git a/client/js/pages/show/index.js b/app/javascript/legacy/pages/show/index.js similarity index 100% rename from client/js/pages/show/index.js rename to app/javascript/legacy/pages/show/index.js diff --git a/client/js/recurring_donations/edit/amount-step.es6 b/app/javascript/legacy/recurring_donations/edit/amount-step.es6 similarity index 100% rename from client/js/recurring_donations/edit/amount-step.es6 rename to app/javascript/legacy/recurring_donations/edit/amount-step.es6 diff --git a/client/js/recurring_donations/edit/branded-wizard.es6 b/app/javascript/legacy/recurring_donations/edit/branded-wizard.es6 similarity index 100% rename from client/js/recurring_donations/edit/branded-wizard.es6 rename to app/javascript/legacy/recurring_donations/edit/branded-wizard.es6 diff --git a/client/js/recurring_donations/edit/card-form.es6 b/app/javascript/legacy/recurring_donations/edit/card-form.es6 similarity index 100% rename from client/js/recurring_donations/edit/card-form.es6 rename to app/javascript/legacy/recurring_donations/edit/card-form.es6 diff --git a/client/js/recurring_donations/edit/change-amount-wizard.es6 b/app/javascript/legacy/recurring_donations/edit/change-amount-wizard.es6 similarity index 100% rename from client/js/recurring_donations/edit/change-amount-wizard.es6 rename to app/javascript/legacy/recurring_donations/edit/change-amount-wizard.es6 diff --git a/app/javascript/legacy/recurring_donations/edit/custom-nonprofit-branding.es6 b/app/javascript/legacy/recurring_donations/edit/custom-nonprofit-branding.es6 new file mode 100644 index 00000000..3b2861c3 --- /dev/null +++ b/app/javascript/legacy/recurring_donations/edit/custom-nonprofit-branding.es6 @@ -0,0 +1,4 @@ +// License: LGPL-3.0-or-later +import nonprofitBranding from '../../../legacy_react/src/lib/nonprofitBranding'; + +module.exports = nonprofitBranding diff --git a/client/js/recurring_donations/edit/followup-step.js b/app/javascript/legacy/recurring_donations/edit/followup-step.js similarity index 100% rename from client/js/recurring_donations/edit/followup-step.js rename to app/javascript/legacy/recurring_donations/edit/followup-step.js diff --git a/client/js/recurring_donations/edit/get-params.js b/app/javascript/legacy/recurring_donations/edit/get-params.js similarity index 100% rename from client/js/recurring_donations/edit/get-params.js rename to app/javascript/legacy/recurring_donations/edit/get-params.js diff --git a/client/js/recurring_donations/edit/index.es6 b/app/javascript/legacy/recurring_donations/edit/index.es6 similarity index 100% rename from client/js/recurring_donations/edit/index.es6 rename to app/javascript/legacy/recurring_donations/edit/index.es6 diff --git a/client/js/recurring_donations/edit/page.js b/app/javascript/legacy/recurring_donations/edit/page.js similarity index 100% rename from client/js/recurring_donations/edit/page.js rename to app/javascript/legacy/recurring_donations/edit/page.js diff --git a/client/js/recurring_donations/edit/payment-step.es6 b/app/javascript/legacy/recurring_donations/edit/payment-step.es6 similarity index 100% rename from client/js/recurring_donations/edit/payment-step.es6 rename to app/javascript/legacy/recurring_donations/edit/payment-step.es6 diff --git a/client/js/recurring_donations/index.js b/app/javascript/legacy/recurring_donations/index.js similarity index 100% rename from client/js/recurring_donations/index.js rename to app/javascript/legacy/recurring_donations/index.js diff --git a/client/js/refunds/create.js b/app/javascript/legacy/refunds/create.js similarity index 100% rename from client/js/refunds/create.js rename to app/javascript/legacy/refunds/create.js diff --git a/client/js/settings/index/branding/index.js b/app/javascript/legacy/settings/index/branding/index.js similarity index 96% rename from client/js/settings/index/branding/index.js rename to app/javascript/legacy/settings/index/branding/index.js index 2337abfd..10ee8272 100644 --- a/client/js/settings/index/branding/index.js +++ b/app/javascript/legacy/settings/index/branding/index.js @@ -15,7 +15,7 @@ const colorPicker = require('../../../components/color-picker.es6') const view = require('./view') function init() { - var np = R.merge(app.nonprofit, {tier: app.current_plan_tier}) + var np = R.merge(app.nonprofit, {}) var state = { nonprofit: np , font$: flyd.stream({ diff --git a/client/js/settings/index/branding/view.js b/app/javascript/legacy/settings/index/branding/view.js similarity index 98% rename from client/js/settings/index/branding/view.js rename to app/javascript/legacy/settings/index/branding/view.js index a3eb33ed..ff177fe2 100644 --- a/client/js/settings/index/branding/view.js +++ b/app/javascript/legacy/settings/index/branding/view.js @@ -52,7 +52,6 @@ const fontRow = R.curry((state, key, font) => ) const form = state => { - var tier = state.nonprofit.tier var btn = button({ buttonText: 'Save Branding' , loading$: state.loading$ }) return h('form.branding-form', { diff --git a/client/js/settings/index/email-settings/index.js b/app/javascript/legacy/settings/index/email-settings/index.js similarity index 100% rename from client/js/settings/index/email-settings/index.js rename to app/javascript/legacy/settings/index/email-settings/index.js diff --git a/client/js/settings/index/email-settings/view.js b/app/javascript/legacy/settings/index/email-settings/view.js similarity index 100% rename from client/js/settings/index/email-settings/view.js rename to app/javascript/legacy/settings/index/email-settings/view.js diff --git a/client/js/settings/index/integrations/index.js b/app/javascript/legacy/settings/index/integrations/index.js similarity index 100% rename from client/js/settings/index/integrations/index.js rename to app/javascript/legacy/settings/index/integrations/index.js diff --git a/client/js/settings/index/page.js b/app/javascript/legacy/settings/index/page.js similarity index 100% rename from client/js/settings/index/page.js rename to app/javascript/legacy/settings/index/page.js diff --git a/client/js/stripe_wrapper/index.es6 b/app/javascript/legacy/stripe_wrapper/index.es6 similarity index 100% rename from client/js/stripe_wrapper/index.es6 rename to app/javascript/legacy/stripe_wrapper/index.es6 diff --git a/client/js/stripe_wrapper/page.js b/app/javascript/legacy/stripe_wrapper/page.js similarity index 100% rename from client/js/stripe_wrapper/page.js rename to app/javascript/legacy/stripe_wrapper/page.js diff --git a/client/js/super-admin/fullcontact-table.js b/app/javascript/legacy/super-admin/fullcontact-table.js similarity index 100% rename from client/js/super-admin/fullcontact-table.js rename to app/javascript/legacy/super-admin/fullcontact-table.js diff --git a/client/js/super-admin/nonprofits-table.js b/app/javascript/legacy/super-admin/nonprofits-table.js similarity index 100% rename from client/js/super-admin/nonprofits-table.js rename to app/javascript/legacy/super-admin/nonprofits-table.js diff --git a/client/js/super-admin/page.js b/app/javascript/legacy/super-admin/page.js similarity index 100% rename from client/js/super-admin/page.js rename to app/javascript/legacy/super-admin/page.js diff --git a/client/js/super-admin/profiles-table.js b/app/javascript/legacy/super-admin/profiles-table.js similarity index 100% rename from client/js/super-admin/profiles-table.js rename to app/javascript/legacy/super-admin/profiles-table.js diff --git a/client/js/supporters/index.js b/app/javascript/legacy/supporters/index.js similarity index 100% rename from client/js/supporters/index.js rename to app/javascript/legacy/supporters/index.js diff --git a/client/js/supporters/info-card.es6 b/app/javascript/legacy/supporters/info-card.es6 similarity index 100% rename from client/js/supporters/info-card.es6 rename to app/javascript/legacy/supporters/info-card.es6 diff --git a/client/js/supporters/settings/mailchimp-integration-settings.js b/app/javascript/legacy/supporters/settings/mailchimp-integration-settings.js similarity index 100% rename from client/js/supporters/settings/mailchimp-integration-settings.js rename to app/javascript/legacy/supporters/settings/mailchimp-integration-settings.js diff --git a/client/js/ticket_levels/get_totals.js b/app/javascript/legacy/ticket_levels/get_totals.js similarity index 100% rename from client/js/ticket_levels/get_totals.js rename to app/javascript/legacy/ticket_levels/get_totals.js diff --git a/client/js/ticket_levels/manage.js b/app/javascript/legacy/ticket_levels/manage.js similarity index 100% rename from client/js/ticket_levels/manage.js rename to app/javascript/legacy/ticket_levels/manage.js diff --git a/client/js/tickets/index/delete-ticket.js b/app/javascript/legacy/tickets/index/delete-ticket.js similarity index 100% rename from client/js/tickets/index/delete-ticket.js rename to app/javascript/legacy/tickets/index/delete-ticket.js diff --git a/client/js/tickets/index/page.js b/app/javascript/legacy/tickets/index/page.js similarity index 100% rename from client/js/tickets/index/page.js rename to app/javascript/legacy/tickets/index/page.js diff --git a/client/js/tickets/new.js b/app/javascript/legacy/tickets/new.js similarity index 100% rename from client/js/tickets/new.js rename to app/javascript/legacy/tickets/new.js diff --git a/client/js/tickets/wizard.js b/app/javascript/legacy/tickets/wizard.js similarity index 100% rename from client/js/tickets/wizard.js rename to app/javascript/legacy/tickets/wizard.js diff --git a/client/js/widget/donate-button.v2.js b/app/javascript/legacy/widget/donate-button.v2.js similarity index 100% rename from client/js/widget/donate-button.v2.js rename to app/javascript/legacy/widget/donate-button.v2.js diff --git a/app/javascript/legacy/wip.txt b/app/javascript/legacy/wip.txt new file mode 100644 index 00000000..465d618b --- /dev/null +++ b/app/javascript/legacy/wip.txt @@ -0,0 +1,27 @@ +./bank_accounts/confirm/page.js +./nonprofits/btn/page.js +./nonprofits/button/page.js +./nonprofits/donate/page.js +./nonprofits/supporters/index/page.js +./nonprofits/supporters/new/page.js +./nonprofits/payouts/index/page.js +./nonprofits/dashboard/page.js +./nonprofits/supporter_form/page.js +./nonprofits/edit/page.js +./nonprofits/cards/edit/page.js +./nonprofits/recurring_donations/index/page.js +./nonprofits/payments/index/page.js +./nonprofits/show/page.js +./settings/index/page.js +./events/stats/page.js +./events/index/page.js +./events/show/page.js +./page.js +./recurring_donations/edit/page.js +./super-admin/page.js +./tickets/index/page.js +./stripe_wrapper/page.js +./campaigns/supporters/index/page.js +./campaigns/index/page.js +./campaigns/peer_to_peer/page.js +./campaigns/show/page.js \ No newline at end of file diff --git a/app/javascript/legacy_react/api/api/NonprofitsApi.ts b/app/javascript/legacy_react/api/api/NonprofitsApi.ts new file mode 100644 index 00000000..eb145a49 --- /dev/null +++ b/app/javascript/legacy_react/api/api/NonprofitsApi.ts @@ -0,0 +1,197 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +import * as $ from 'jquery'; +import * as models from '../model/models'; +import { Configuration } from '../configuration'; + +const page_info = require('../../../page_info.js.erb') + +/* tslint:disable:no-unused-variable member-ordering */ + + + + +export class NonprofitsApi { + protected basePath = `${page_info.apiDomain}/api`; + public defaultHeaders: Array = []; + public defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings = null; + public configuration: Configuration = new Configuration(); + + constructor(basePath?: string, configuration?: Configuration, defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings) { + if (basePath) { + this.basePath = basePath; + } + if (configuration) { + this.configuration = configuration; + } + if (defaultExtraJQueryAjaxSettings) { + this.defaultExtraJQueryAjaxSettings = defaultExtraJQueryAjaxSettings; + } + } + + private extendObj(objA: T2, objB: T2): T1|T2 { + for (let key in objB) { + if (objB.hasOwnProperty(key)) { + objA[key] = objB[key]; + } + } + return objA; + } + + + /** + * Return a nonprofit. + * @summary Return a nonprofit. + * @param id Status id. + */ + public getNonprofitId(id: number, extraJQueryAjaxSettings?: JQueryAjaxSettings): JQueryPromise { + let localVarPath = this.basePath + '/nonprofit/{id}'.replace('{' + 'id' + '}', encodeURIComponent(String(id))); + + let queryParameters: any = {}; + let headerParams: any = {}; + // verify required parameter 'id' is not null or undefined + if (id === null || id === undefined) { + throw new Error('Required parameter id was null or undefined when calling getNonprofitId.'); + } + + + localVarPath = localVarPath + "?" + $.param(queryParameters); + // to determine the Content-Type header + let consumes: string[] = [ + ]; + + // to determine the Accept header + let produces: string[] = [ + 'application/json' + ]; + + + let requestOptions: JQueryAjaxSettings = { + url: localVarPath, + type: 'GET', + headers: headerParams, + processData: false + }; + + if (headerParams['Content-Type']) { + requestOptions.contentType = headerParams['Content-Type']; + } + + if (extraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); + } + + if (this.defaultExtraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + } + + let dfd = $.Deferred(); + $.ajax(requestOptions).then( + (data: models.Nonprofit, textStatus: string, jqXHR: JQueryXHR) => + dfd.resolve(jqXHR, data), + (xhr: JQueryXHR, textStatus: string, errorThrown: string) => { + if(false){} + else if (xhr.status == 200 && 200 >= 400) + { + dfd.reject(new models.NonprofitException(xhr.responseJSON as models.Nonprofit)) + } + + else + { + + dfd.reject(errorThrown) + } + } + ); + return dfd.promise(); + } + + + /** + * Register a nonprofit + * @summary Register a nonprofit + * @param Nonprofit + */ + public postNonprofit(Nonprofit: models.PostNonprofit, extraJQueryAjaxSettings?: JQueryAjaxSettings): JQueryPromise { + let localVarPath = this.basePath + '/nonprofits'; + + let queryParameters: any = {}; + let headerParams: any = {}; + // verify required parameter 'Nonprofit' is not null or undefined + if (Nonprofit === null || Nonprofit === undefined) { + throw new Error('Required parameter Nonprofit was null or undefined when calling postNonprofit.'); + } + + + localVarPath = localVarPath + "?" + $.param(queryParameters); + // to determine the Content-Type header + let consumes: string[] = [ + 'application/json' + ]; + + // to determine the Accept header + let produces: string[] = [ + 'application/json' + ]; + + + headerParams['Content-Type'] = 'application/json'; + + let requestOptions: JQueryAjaxSettings = { + url: localVarPath, + type: 'POST', + headers: headerParams, + processData: false + }; + + requestOptions.data = JSON.stringify(Nonprofit); + if (headerParams['Content-Type']) { + requestOptions.contentType = headerParams['Content-Type']; + } + + if (extraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); + } + + if (this.defaultExtraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + } + + let dfd = $.Deferred(); + $.ajax(requestOptions).then( + (data: models.Nonprofit, textStatus: string, jqXHR: JQueryXHR) => + dfd.resolve(jqXHR, data), + (xhr: JQueryXHR, textStatus: string, errorThrown: string) => { + if(false){} + else if (xhr.status == 201 && 201 >= 400) + { + dfd.reject(new models.NonprofitException(xhr.responseJSON as models.Nonprofit)) + } + + else if (xhr.status == 400 && 400 >= 400) + { + dfd.reject(new models.ValidationErrorsException(xhr.responseJSON as models.ValidationErrors)) + } + + else + { + + dfd.reject(errorThrown) + } + } + ); + return dfd.promise(); + } + +} diff --git a/app/javascript/legacy_react/api/api/UsersApi.ts b/app/javascript/legacy_react/api/api/UsersApi.ts new file mode 100644 index 00000000..21f4f669 --- /dev/null +++ b/app/javascript/legacy_react/api/api/UsersApi.ts @@ -0,0 +1,123 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +import * as $ from 'jquery'; +import * as models from '../model/models'; +import { Configuration } from '../configuration'; + +const page_info = require('../../../page_info.js.erb') + +/* tslint:disable:no-unused-variable member-ordering */ + + + + +export class UsersApi { + protected basePath = `${page_info.apiDomain}`; + public defaultHeaders: Array = []; + public defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings = null; + public configuration: Configuration = new Configuration(); + + constructor(basePath?: string, configuration?: Configuration, defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings) { + if (basePath) { + this.basePath = basePath; + } + if (configuration) { + this.configuration = configuration; + } + if (defaultExtraJQueryAjaxSettings) { + this.defaultExtraJQueryAjaxSettings = defaultExtraJQueryAjaxSettings; + } + } + + private extendObj(objA: T2, objB: T2): T1|T2 { + for (let key in objB) { + if (objB.hasOwnProperty(key)) { + objA[key] = objB[key]; + } + } + return objA; + } + + + /** + * Register a nonprofit + * @summary Register a nonprofit + * @param Nonprofit + */ + public postUser(User: models.PostUser, extraJQueryAjaxSettings?: JQueryAjaxSettings): JQueryPromise { + let localVarPath = this.basePath + '/users'; + + let queryParameters: any = {}; + let headerParams: any = {}; + // verify required parameter 'Nonprofit' is not null or undefined + if (User === null || User === undefined) { + throw new Error('Required parameter Nonprofit was null or undefined when calling postNonprofit.'); + } + + + localVarPath = localVarPath + "?" + $.param(queryParameters); + // to determine the Content-Type header + let consumes: string[] = [ + 'application/json' + ]; + + // to determine the Accept header + let produces: string[] = [ + 'application/json' + ]; + + + headerParams['Content-Type'] = 'application/json'; + + let requestOptions: JQueryAjaxSettings = { + url: localVarPath, + type: 'POST', + headers: headerParams, + processData: false + }; + + requestOptions.data = JSON.stringify(User); + if (headerParams['Content-Type']) { + requestOptions.contentType = headerParams['Content-Type']; + } + + if (extraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); + } + + if (this.defaultExtraJQueryAjaxSettings) { + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + } + + let dfd = $.Deferred(); + $.ajax(requestOptions).then( + (data: any, textStatus: string, jqXHR: JQueryXHR) => + dfd.resolve(jqXHR, data), + (xhr: JQueryXHR, textStatus: string, errorThrown: string) => { + if (xhr.status == 422) + { + dfd.reject(new models.ValidationErrorsException(xhr.responseJSON as models.ValidationErrors)) + } + + else + { + + dfd.reject(errorThrown) + } + } + ); + return dfd.promise(); + } + +} diff --git a/app/javascript/legacy_react/api/api/api.ts b/app/javascript/legacy_react/api/api/api.ts new file mode 100644 index 00000000..2c3cb78e --- /dev/null +++ b/app/javascript/legacy_react/api/api/api.ts @@ -0,0 +1,5 @@ +export * from './NonprofitsApi'; +export * from './UsersApi'; +import { NonprofitsApi } from './NonprofitsApi'; +import { UsersApi } from './UsersApi'; +export const APIS = [NonprofitsApi, UsersApi]; diff --git a/lib/swagger-typescript-jquery/configuration.mustache b/app/javascript/legacy_react/api/configuration.ts similarity index 100% rename from lib/swagger-typescript-jquery/configuration.mustache rename to app/javascript/legacy_react/api/configuration.ts diff --git a/app/javascript/legacy_react/api/index.ts b/app/javascript/legacy_react/api/index.ts new file mode 100644 index 00000000..2ecff6bd --- /dev/null +++ b/app/javascript/legacy_react/api/index.ts @@ -0,0 +1,3 @@ +export * from './api/api'; +export * from './model/models'; +export * from './configuration'; \ No newline at end of file diff --git a/app/javascript/legacy_react/api/model/Nonprofit.ts b/app/javascript/legacy_react/api/model/Nonprofit.ts new file mode 100644 index 00000000..9ff50643 --- /dev/null +++ b/app/javascript/legacy_react/api/model/Nonprofit.ts @@ -0,0 +1,36 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +/** + * Return a nonprofit. + */ +export interface Nonprofit { + id?: string; + +} +export class NonprofitException implements Error{ + + constructor(obj:Nonprofit, message?:string){ + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: Nonprofit; +} + + diff --git a/app/javascript/legacy_react/api/model/PostNonprofit.ts b/app/javascript/legacy_react/api/model/PostNonprofit.ts new file mode 100644 index 00000000..616161e1 --- /dev/null +++ b/app/javascript/legacy_react/api/model/PostNonprofit.ts @@ -0,0 +1,36 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +/** + * Register a nonprofit + */ +export interface PostNonprofit extends models.PostNonprofitNonprofit { + +} +export class PostNonprofitException implements Error{ + + constructor(obj:PostNonprofit, message?:string){ + this.item = obj; + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: PostNonprofit; +} + + diff --git a/app/javascript/legacy_react/api/model/PostNonprofitNonprofit.ts b/app/javascript/legacy_react/api/model/PostNonprofitNonprofit.ts new file mode 100644 index 00000000..1b84fccb --- /dev/null +++ b/app/javascript/legacy_react/api/model/PostNonprofitNonprofit.ts @@ -0,0 +1,72 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +export interface PostNonprofitNonprofit { + /** + * Organization Name + */ + name: string; + + /** + * Organization website URL + */ + website?: string; + + /** + * Organization Address ZIP Code + */ + zip_code: string; + + /** + * Organization Address State Code + */ + state_code: string; + + /** + * Organization Address City + */ + city: string; + + /** + * Organization email (public) + */ + email?: string; + + /** + * Organization phone (public) + */ + phone?: string; + +} +export class PostNonprofitNonprofitException implements Error{ + + constructor(obj:PostNonprofitNonprofit, message?:string){ + this.item = obj; + this.item = obj; + this.item = obj; + this.item = obj; + this.item = obj; + this.item = obj; + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: PostNonprofitNonprofit; +} + + diff --git a/app/javascript/legacy_react/api/model/PostNonprofitUser.ts b/app/javascript/legacy_react/api/model/PostNonprofitUser.ts new file mode 100644 index 00000000..0a1e7358 --- /dev/null +++ b/app/javascript/legacy_react/api/model/PostNonprofitUser.ts @@ -0,0 +1,54 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +export interface PostNonprofitUser { + /** + * Full name + */ + name: string; + + /** + * Username + */ + email: string; + + /** + * Password + */ + password: string; + + /** + * Password confirmation + */ + password_confirmation: string; + +} +export class PostNonprofitUserException implements Error{ + + constructor(obj:PostNonprofitUser, message?:string){ + this.item = obj; + this.item = obj; + this.item = obj; + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: PostNonprofitUser; +} + + diff --git a/app/javascript/legacy_react/api/model/PostUser.ts b/app/javascript/legacy_react/api/model/PostUser.ts new file mode 100644 index 00000000..39e37d7f --- /dev/null +++ b/app/javascript/legacy_react/api/model/PostUser.ts @@ -0,0 +1,24 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +/** + * Register a nonprofit + */ +export interface PostUser { + + user?: models.PostNonprofitUser; + +} + + diff --git a/app/javascript/legacy_react/api/model/ValidationError.ts b/app/javascript/legacy_react/api/model/ValidationError.ts new file mode 100644 index 00000000..dfdcbf5c --- /dev/null +++ b/app/javascript/legacy_react/api/model/ValidationError.ts @@ -0,0 +1,42 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +export interface ValidationError { + /** + * Params where the following had an error. + */ + params?: Array; + + /** + * The validation messages for the params + */ + messages?: Array; + +} +export class ValidationErrorException implements Error{ + + constructor(obj:ValidationError, message?:string){ + this.item = obj; + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: ValidationError; +} + + diff --git a/app/javascript/legacy_react/api/model/ValidationErrors.ts b/app/javascript/legacy_react/api/model/ValidationErrors.ts new file mode 100644 index 00000000..13bb42a4 --- /dev/null +++ b/app/javascript/legacy_react/api/model/ValidationErrors.ts @@ -0,0 +1,39 @@ +/** + * API title + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: 0.0.1 + * + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +import * as models from './models'; + +/** + * Register a nonprofit + */ +export interface ValidationErrors { + /** + * errors + */ + errors?: Array; + +} +export class ValidationErrorsException implements Error{ + + constructor(obj:ValidationErrors, message?:string){ + this.item = obj; + + } + + message: string; + stack: string; + name: string; + + item: ValidationErrors; +} + + diff --git a/app/javascript/legacy_react/api/model/models.ts b/app/javascript/legacy_react/api/model/models.ts new file mode 100644 index 00000000..b871d490 --- /dev/null +++ b/app/javascript/legacy_react/api/model/models.ts @@ -0,0 +1,7 @@ +export * from './Nonprofit'; +export * from './PostNonprofit'; +export * from './PostNonprofitNonprofit'; +export * from './PostNonprofitUser'; +export * from './PostUser'; +export * from './ValidationError'; +export * from './ValidationErrors'; diff --git a/javascripts/app/create_new_offsite_payment_pane.tsx b/app/javascript/legacy_react/app/create_new_offsite_payment_pane.tsx similarity index 100% rename from javascripts/app/create_new_offsite_payment_pane.tsx rename to app/javascript/legacy_react/app/create_new_offsite_payment_pane.tsx diff --git a/javascripts/app/edit_payment_pane.tsx b/app/javascript/legacy_react/app/edit_payment_pane.tsx similarity index 100% rename from javascripts/app/edit_payment_pane.tsx rename to app/javascript/legacy_react/app/edit_payment_pane.tsx diff --git a/javascripts/app/loading_indicator.ts b/app/javascript/legacy_react/app/loading_indicator.ts similarity index 100% rename from javascripts/app/loading_indicator.ts rename to app/javascript/legacy_react/app/loading_indicator.ts diff --git a/javascripts/app/registration_page.tsx b/app/javascript/legacy_react/app/registration_page.tsx similarity index 100% rename from javascripts/app/registration_page.tsx rename to app/javascript/legacy_react/app/registration_page.tsx diff --git a/javascripts/app/session_login_page.tsx b/app/javascript/legacy_react/app/session_login_page.tsx similarity index 100% rename from javascripts/app/session_login_page.tsx rename to app/javascript/legacy_react/app/session_login_page.tsx diff --git a/app/javascript/legacy_react/javascripts/app/create_new_offsite_payment_pane.tsx b/app/javascript/legacy_react/javascripts/app/create_new_offsite_payment_pane.tsx new file mode 100644 index 00000000..f3b49926 --- /dev/null +++ b/app/javascript/legacy_react/javascripts/app/create_new_offsite_payment_pane.tsx @@ -0,0 +1,33 @@ +// License: LGPL-3.0-or-later +// require a root component here. This will be treated as the root of a webpack package +import Root from "../../src/components/common/Root" +import CreateOffsitePaymentPane from "../../src/components/create_offsite_payment_pane/CreateOffsitePaymentPane" + +import * as ReactDOM from 'react-dom' +import * as React from 'react' + +export interface FundraiserInfo { + id: number + name: string +} + +function LoadReactPage(element:HTMLElement, campaigns: FundraiserInfo[], + events: FundraiserInfo[], + nonprofitId: number, + supporterId:number, + preupdateDonationAction:() => void, + postUpdateSuccess: () => void, + + //from ModalProps + onClose: () => void, + modalActive: boolean, + nonprofitTimezone?: string) { + ReactDOM.render(, element) +} + + +(window as any).LoadReactCreateOffsiteDonationPane = LoadReactPage \ No newline at end of file diff --git a/app/javascript/legacy_react/javascripts/app/edit_payment_pane.tsx b/app/javascript/legacy_react/javascripts/app/edit_payment_pane.tsx new file mode 100644 index 00000000..e5378acd --- /dev/null +++ b/app/javascript/legacy_react/javascripts/app/edit_payment_pane.tsx @@ -0,0 +1,27 @@ +// License: LGPL-3.0-or-later +// require a root component here. This will be treated as the root of a webpack package +import Root from "../../src/components/common/Root" +import EditPaymentPane, {FundraiserInfo} from "../../src/components/edit_payment_pane/EditPaymentPane" + +import * as ReactDOM from 'react-dom' +import * as React from 'react' + +function LoadReactPage(element:HTMLElement, data:any, campaigns:FundraiserInfo[], + events:FundraiserInfo[], + onClose:() => void, + modalActive:boolean, + preupdateDonationAction: () => void, + postUpdateSuccess: () => void, + nonprofitTimezone?:string + + ) { + ReactDOM.render(, element) +} + + +(window as any).LoadReactEditPaymentPane = LoadReactPage \ No newline at end of file diff --git a/app/javascript/legacy_react/javascripts/app/registration_page.tsx b/app/javascript/legacy_react/javascripts/app/registration_page.tsx new file mode 100644 index 00000000..086d632e --- /dev/null +++ b/app/javascript/legacy_react/javascripts/app/registration_page.tsx @@ -0,0 +1,16 @@ +// License: LGPL-3.0-or-later + +// require a root component here. This will be treated as the root of a webpack package +import Root from "../../src/components/common/Root" +import RegistrationPage from "../../src/components/registration_page/RegistrationPage" + +import * as ReactDOM from 'react-dom' +import * as React from 'react' + +function LoadReactPage(element:HTMLElement) { + ReactDOM.render(, element) +} + + +(window as any).LoadReactPage = LoadReactPage + diff --git a/javascripts/src/components/common/BootstrapWrapper.tsx b/app/javascript/legacy_react/src/components/common/BootstrapWrapper.tsx similarity index 100% rename from javascripts/src/components/common/BootstrapWrapper.tsx rename to app/javascript/legacy_react/src/components/common/BootstrapWrapper.tsx diff --git a/javascripts/src/components/common/DefaultCloseButton.tsx b/app/javascript/legacy_react/src/components/common/DefaultCloseButton.tsx similarity index 97% rename from javascripts/src/components/common/DefaultCloseButton.tsx rename to app/javascript/legacy_react/src/components/common/DefaultCloseButton.tsx index 2ce37e3e..0a5a3c1b 100644 --- a/javascripts/src/components/common/DefaultCloseButton.tsx +++ b/app/javascript/legacy_react/src/components/common/DefaultCloseButton.tsx @@ -1,8 +1,8 @@ -import React = require("react"); +import * as React from "react"; import { action, observable } from "mobx"; import { Transition } from "react-transition-group"; import { CloseButton } from "./svg/CloseButton"; -import color = require("color"); +import color from "color"; import { observer } from "mobx-react"; import ScreenReaderOnlyText from "./ScreenReaderOnlyText"; diff --git a/javascripts/src/components/common/LabeledFieldComponent.spec.tsx b/app/javascript/legacy_react/src/components/common/LabeledFieldComponent.spec.tsx similarity index 100% rename from javascripts/src/components/common/LabeledFieldComponent.spec.tsx rename to app/javascript/legacy_react/src/components/common/LabeledFieldComponent.spec.tsx diff --git a/javascripts/src/components/common/LabeledFieldComponent.tsx b/app/javascript/legacy_react/src/components/common/LabeledFieldComponent.tsx similarity index 100% rename from javascripts/src/components/common/LabeledFieldComponent.tsx rename to app/javascript/legacy_react/src/components/common/LabeledFieldComponent.tsx diff --git a/javascripts/src/components/common/Modal.spec.tsx b/app/javascript/legacy_react/src/components/common/Modal.spec.tsx similarity index 100% rename from javascripts/src/components/common/Modal.spec.tsx rename to app/javascript/legacy_react/src/components/common/Modal.spec.tsx diff --git a/javascripts/src/components/common/Modal.tsx b/app/javascript/legacy_react/src/components/common/Modal.tsx similarity index 61% rename from javascripts/src/components/common/Modal.tsx rename to app/javascript/legacy_react/src/components/common/Modal.tsx index 7c04dc1d..1c3fae1e 100644 --- a/javascripts/src/components/common/Modal.tsx +++ b/app/javascript/legacy_react/src/components/common/Modal.tsx @@ -1,10 +1,7 @@ // License: LGPL-3.0-or-later import * as React from 'react'; import { observer } from 'mobx-react'; -import AriaModal = require('react-aria-modal'); -import { VelocityTransitionGroup } from 'velocity-react'; -import 'velocity-animate'; -import 'velocity-animate/velocity.ui'; +import AriaModal from 'react-aria-modal'; import { DefaultCloseButton } from './DefaultCloseButton'; import BootstrapWrapper from './BootstrapWrapper'; import { Row, Column } from './layout'; @@ -27,7 +24,7 @@ class Modal extends React.Component { } render() { - const innerModal = this.props.modalActive ?
{ : false - const modal = - - {innerModal} - ; - return modal } diff --git a/javascripts/src/components/common/ProgressableButton.spec.tsx b/app/javascript/legacy_react/src/components/common/ProgressableButton.spec.tsx similarity index 100% rename from javascripts/src/components/common/ProgressableButton.spec.tsx rename to app/javascript/legacy_react/src/components/common/ProgressableButton.spec.tsx diff --git a/javascripts/src/components/common/ProgressableButton.tsx b/app/javascript/legacy_react/src/components/common/ProgressableButton.tsx similarity index 100% rename from javascripts/src/components/common/ProgressableButton.tsx rename to app/javascript/legacy_react/src/components/common/ProgressableButton.tsx diff --git a/javascripts/src/components/common/Root.tsx b/app/javascript/legacy_react/src/components/common/Root.tsx similarity index 80% rename from javascripts/src/components/common/Root.tsx rename to app/javascript/legacy_react/src/components/common/Root.tsx index 22aa3b22..ce4696ff 100644 --- a/javascripts/src/components/common/Root.tsx +++ b/app/javascript/legacy_react/src/components/common/Root.tsx @@ -10,10 +10,13 @@ import {CSRFInterceptor} from "../../lib/csrf_interceptor"; import * as CustomAPIS from "../../lib/apis" const enLocaleData = require('react-intl/locale-data/en'); -const deLocaleData = require('react-intl/locale-data/de'); -const I18n = require('i18n') - -addLocaleData([...enLocaleData, ...deLocaleData]) +const I18n = require('../../../../i18n.js.erb') +const localeData = [...enLocaleData] +Object.keys(I18n.translations).filter((i:string) => i !== 'en').forEach((i:string) => { + const data = [...require(`react-intl/locale-data/${i}`)] + localeData.concat(data) +}) +addLocaleData(localeData) interface RootProps { diff --git a/javascripts/src/components/common/ScreenReaderOnlyText.spec.tsx b/app/javascript/legacy_react/src/components/common/ScreenReaderOnlyText.spec.tsx similarity index 100% rename from javascripts/src/components/common/ScreenReaderOnlyText.spec.tsx rename to app/javascript/legacy_react/src/components/common/ScreenReaderOnlyText.spec.tsx diff --git a/javascripts/src/components/common/ScreenReaderOnlyText.tsx b/app/javascript/legacy_react/src/components/common/ScreenReaderOnlyText.tsx similarity index 100% rename from javascripts/src/components/common/ScreenReaderOnlyText.tsx rename to app/javascript/legacy_react/src/components/common/ScreenReaderOnlyText.tsx diff --git a/javascripts/src/components/common/Spinner.spec.tsx b/app/javascript/legacy_react/src/components/common/Spinner.spec.tsx similarity index 100% rename from javascripts/src/components/common/Spinner.spec.tsx rename to app/javascript/legacy_react/src/components/common/Spinner.spec.tsx diff --git a/javascripts/src/components/common/Spinner.tsx b/app/javascript/legacy_react/src/components/common/Spinner.tsx similarity index 81% rename from javascripts/src/components/common/Spinner.tsx rename to app/javascript/legacy_react/src/components/common/Spinner.tsx index dc530466..0d11033f 100644 --- a/javascripts/src/components/common/Spinner.tsx +++ b/app/javascript/legacy_react/src/components/common/Spinner.tsx @@ -1,7 +1,6 @@ // License: LGPL-3.0-or-later import { Color } from 'csstype'; import * as React from 'react'; -import { VelocityComponent } from 'velocity-react'; import ScreenReaderOnlyText from './ScreenReaderOnlyText'; export interface SpinnerProps { @@ -41,11 +40,9 @@ class Spinner extends React.Component { } render() { - return -
+ return
Loading...
- ; } } diff --git a/javascripts/src/components/common/StandardFieldComponent.spec.tsx b/app/javascript/legacy_react/src/components/common/StandardFieldComponent.spec.tsx similarity index 100% rename from javascripts/src/components/common/StandardFieldComponent.spec.tsx rename to app/javascript/legacy_react/src/components/common/StandardFieldComponent.spec.tsx diff --git a/javascripts/src/components/common/StandardFieldComponent.tsx b/app/javascript/legacy_react/src/components/common/StandardFieldComponent.tsx similarity index 100% rename from javascripts/src/components/common/StandardFieldComponent.tsx rename to app/javascript/legacy_react/src/components/common/StandardFieldComponent.tsx diff --git a/javascripts/src/components/common/__snapshots__/LabeledFieldComponent.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/LabeledFieldComponent.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/__snapshots__/LabeledFieldComponent.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/__snapshots__/LabeledFieldComponent.spec.tsx.snap diff --git a/app/javascript/legacy_react/src/components/common/__snapshots__/Modal.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/Modal.spec.tsx.snap new file mode 100644 index 00000000..f00df333 --- /dev/null +++ b/app/javascript/legacy_react/src/components/common/__snapshots__/Modal.spec.tsx.snap @@ -0,0 +1,370 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Modal active modal displays matches snapshot 1`] = ` + + + +
+
+ +
+
+ } + > + + +
+
+ +
+ + + + + +`; + +exports[`Modal nothing displayed if inactive 1`] = `""`; diff --git a/javascripts/src/components/common/__snapshots__/ProgressableButton.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/ProgressableButton.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/__snapshots__/ProgressableButton.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/__snapshots__/ProgressableButton.spec.tsx.snap diff --git a/javascripts/src/components/common/__snapshots__/ScreenReaderOnlyText.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/ScreenReaderOnlyText.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/__snapshots__/ScreenReaderOnlyText.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/__snapshots__/ScreenReaderOnlyText.spec.tsx.snap diff --git a/app/javascript/legacy_react/src/components/common/__snapshots__/Spinner.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/Spinner.spec.tsx.snap new file mode 100644 index 00000000..9014bd65 --- /dev/null +++ b/app/javascript/legacy_react/src/components/common/__snapshots__/Spinner.spec.tsx.snap @@ -0,0 +1,165 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Spinner has custom color 1`] = ` + +
+ + + Loading... + + +
+
+`; + +exports[`Spinner is large 1`] = ` + +
+ + + Loading... + + +
+
+`; + +exports[`Spinner is normal 1`] = ` + +
+ + + Loading... + + +
+
+`; + +exports[`Spinner is small 1`] = ` + +
+ + + Loading... + + +
+
+`; diff --git a/javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/__snapshots__/StandardFieldComponent.spec.tsx.snap diff --git a/javascripts/src/components/common/__snapshots__/layout.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/__snapshots__/layout.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/__snapshots__/layout.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/__snapshots__/layout.spec.tsx.snap diff --git a/javascripts/src/components/common/fields.tsx b/app/javascript/legacy_react/src/components/common/fields.tsx similarity index 98% rename from javascripts/src/components/common/fields.tsx rename to app/javascript/legacy_react/src/components/common/fields.tsx index fa8be668..fe151380 100644 --- a/javascripts/src/components/common/fields.tsx +++ b/app/javascript/legacy_react/src/components/common/fields.tsx @@ -1,7 +1,7 @@ // License: LGPL-3.0-or-later import * as React from 'react'; import { observer } from "mobx-react"; -import { Field } from "../../../../types/mobx-react-form"; +import { Field } from "../../../../../../types/mobx-react-form"; import LabeledFieldComponent from "./LabeledFieldComponent"; import { HoudiniField } from "../../lib/houdini_form"; import ReactInput from "./form/ReactInput"; diff --git a/javascripts/src/components/common/form/ReactForm.tsx b/app/javascript/legacy_react/src/components/common/form/ReactForm.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactForm.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactForm.tsx diff --git a/javascripts/src/components/common/form/ReactInput.spec.tsx b/app/javascript/legacy_react/src/components/common/form/ReactInput.spec.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactInput.spec.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactInput.spec.tsx diff --git a/javascripts/src/components/common/form/ReactInput.tsx b/app/javascript/legacy_react/src/components/common/form/ReactInput.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactInput.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactInput.tsx diff --git a/javascripts/src/components/common/form/ReactMaskedInput.tsx b/app/javascript/legacy_react/src/components/common/form/ReactMaskedInput.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactMaskedInput.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactMaskedInput.tsx diff --git a/javascripts/src/components/common/form/ReactSelect.spec.tsx b/app/javascript/legacy_react/src/components/common/form/ReactSelect.spec.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactSelect.spec.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactSelect.spec.tsx diff --git a/javascripts/src/components/common/form/ReactSelect.tsx b/app/javascript/legacy_react/src/components/common/form/ReactSelect.tsx similarity index 96% rename from javascripts/src/components/common/form/ReactSelect.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactSelect.tsx index acba6f6e..c3e0bb42 100644 --- a/javascripts/src/components/common/form/ReactSelect.tsx +++ b/app/javascript/legacy_react/src/components/common/form/ReactSelect.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import {InjectedIntlProps, injectIntl} from 'react-intl'; -import {Field} from "../../../../../types/mobx-react-form"; +import {Field} from "../../../../../../../types/mobx-react-form"; import {InputHTMLAttributes} from "react"; import {action, observable} from "mobx"; import {SelectHTMLAttributes} from "react"; diff --git a/javascripts/src/components/common/form/ReactTextarea.spec.tsx b/app/javascript/legacy_react/src/components/common/form/ReactTextarea.spec.tsx similarity index 100% rename from javascripts/src/components/common/form/ReactTextarea.spec.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactTextarea.spec.tsx diff --git a/javascripts/src/components/common/form/ReactTextarea.tsx b/app/javascript/legacy_react/src/components/common/form/ReactTextarea.tsx similarity index 95% rename from javascripts/src/components/common/form/ReactTextarea.tsx rename to app/javascript/legacy_react/src/components/common/form/ReactTextarea.tsx index a948a2af..3e8a826a 100644 --- a/javascripts/src/components/common/form/ReactTextarea.tsx +++ b/app/javascript/legacy_react/src/components/common/form/ReactTextarea.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import {InjectedIntlProps, injectIntl} from 'react-intl'; -import {Field} from "../../../../../types/mobx-react-form"; +import {Field} from "../../../../../../../types/mobx-react-form"; import {InputHTMLAttributes, ReactText, TextareaHTMLAttributes} from "react"; import {action, observable} from "mobx"; import {ReactInputProps} from "./react_input_props"; diff --git a/javascripts/src/components/common/form/__snapshots__/ReactInput.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactInput.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/form/__snapshots__/ReactInput.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactInput.spec.tsx.snap diff --git a/javascripts/src/components/common/form/__snapshots__/ReactSelect.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactSelect.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/form/__snapshots__/ReactSelect.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactSelect.spec.tsx.snap diff --git a/javascripts/src/components/common/form/__snapshots__/ReactTextarea.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactTextarea.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/form/__snapshots__/ReactTextarea.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/form/__snapshots__/ReactTextarea.spec.tsx.snap diff --git a/javascripts/src/components/common/form/react_input_props.ts b/app/javascript/legacy_react/src/components/common/form/react_input_props.ts similarity index 100% rename from javascripts/src/components/common/form/react_input_props.ts rename to app/javascript/legacy_react/src/components/common/form/react_input_props.ts diff --git a/javascripts/src/components/common/layout.spec.tsx b/app/javascript/legacy_react/src/components/common/layout.spec.tsx similarity index 100% rename from javascripts/src/components/common/layout.spec.tsx rename to app/javascript/legacy_react/src/components/common/layout.spec.tsx diff --git a/javascripts/src/components/common/layout.tsx b/app/javascript/legacy_react/src/components/common/layout.tsx similarity index 100% rename from javascripts/src/components/common/layout.tsx rename to app/javascript/legacy_react/src/components/common/layout.tsx diff --git a/javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx b/app/javascript/legacy_react/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx similarity index 100% rename from javascripts/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx rename to app/javascript/legacy_react/src/components/common/selectable_table_row/SelectableTableRow.spec.tsx diff --git a/javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx b/app/javascript/legacy_react/src/components/common/selectable_table_row/SelectableTableRow.tsx similarity index 100% rename from javascripts/src/components/common/selectable_table_row/SelectableTableRow.tsx rename to app/javascript/legacy_react/src/components/common/selectable_table_row/SelectableTableRow.tsx diff --git a/javascripts/src/components/common/selectable_table_row/connect.tsx b/app/javascript/legacy_react/src/components/common/selectable_table_row/connect.tsx similarity index 100% rename from javascripts/src/components/common/selectable_table_row/connect.tsx rename to app/javascript/legacy_react/src/components/common/selectable_table_row/connect.tsx diff --git a/javascripts/src/components/common/svg/CloseButton.tsx b/app/javascript/legacy_react/src/components/common/svg/CloseButton.tsx similarity index 96% rename from javascripts/src/components/common/svg/CloseButton.tsx rename to app/javascript/legacy_react/src/components/common/svg/CloseButton.tsx index d8e23f00..5167c93c 100644 --- a/javascripts/src/components/common/svg/CloseButton.tsx +++ b/app/javascript/legacy_react/src/components/common/svg/CloseButton.tsx @@ -1,5 +1,5 @@ // License: LGPL-3.0-or-later -import React = require("react"); +import * as React from "react"; interface CloseButtonProps { backgroundCircleStyle:React.CSSProperties foregroundCircleStyle:React.CSSProperties diff --git a/javascripts/src/components/common/svg/checkbox.tsx b/app/javascript/legacy_react/src/components/common/svg/checkbox.tsx similarity index 100% rename from javascripts/src/components/common/svg/checkbox.tsx rename to app/javascript/legacy_react/src/components/common/svg/checkbox.tsx diff --git a/javascripts/src/components/common/test/react_test_helpers.tsx b/app/javascript/legacy_react/src/components/common/test/react_test_helpers.tsx similarity index 97% rename from javascripts/src/components/common/test/react_test_helpers.tsx rename to app/javascript/legacy_react/src/components/common/test/react_test_helpers.tsx index ad6ab99b..888f01e7 100644 --- a/javascripts/src/components/common/test/react_test_helpers.tsx +++ b/app/javascript/legacy_react/src/components/common/test/react_test_helpers.tsx @@ -33,7 +33,7 @@ export function mountForMobx(props:TProps, return mount() + __childrenCreator={rootComponentCreator as any} />) } /** @@ -45,7 +45,7 @@ export function mountForMobx(props:TProps, export function mountForMobxWithIntl(props:TProps, rootComponentCreator:(props:TProps) => React.ReactNode): ReactWrapper { return mountWithIntl() + __childrenCreator={rootComponentCreator as any} />) } diff --git a/javascripts/src/components/common/test/unique_id_mock.ts b/app/javascript/legacy_react/src/components/common/test/unique_id_mock.ts similarity index 100% rename from javascripts/src/components/common/test/unique_id_mock.ts rename to app/javascript/legacy_react/src/components/common/test/unique_id_mock.ts diff --git a/javascripts/src/components/common/wizard/RAT/Tab.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/Tab.ts similarity index 100% rename from javascripts/src/components/common/wizard/RAT/Tab.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/Tab.ts diff --git a/javascripts/src/components/common/wizard/RAT/TabList.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/TabList.ts similarity index 100% rename from javascripts/src/components/common/wizard/RAT/TabList.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/TabList.ts diff --git a/javascripts/src/components/common/wizard/RAT/TabPanel.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/TabPanel.ts similarity index 100% rename from javascripts/src/components/common/wizard/RAT/TabPanel.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/TabPanel.ts diff --git a/javascripts/src/components/common/wizard/RAT/Wrapper.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/RAT/Wrapper.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/RAT/Wrapper.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/RAT/Wrapper.spec.tsx diff --git a/javascripts/src/components/common/wizard/RAT/Wrapper.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/Wrapper.ts similarity index 96% rename from javascripts/src/components/common/wizard/RAT/Wrapper.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/Wrapper.ts index 54f0f9d8..66f1e3d5 100644 --- a/javascripts/src/components/common/wizard/RAT/Wrapper.ts +++ b/app/javascript/legacy_react/src/components/common/wizard/RAT/Wrapper.ts @@ -5,7 +5,7 @@ import {TabManagerParent} from "./abstract_tabcomponent_state"; import {observer} from 'mobx-react'; import specialAssign from "./specialAssign"; -import PropTypes = require('prop-types'); +const PropTypes = require ('prop-types'); interface WrapperProps { manager: TabManagerParent diff --git a/javascripts/src/components/common/wizard/RAT/__snapshots__/Wrapper.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/wizard/RAT/__snapshots__/Wrapper.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/wizard/RAT/__snapshots__/Wrapper.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/wizard/RAT/__snapshots__/Wrapper.spec.tsx.snap diff --git a/javascripts/src/components/common/wizard/RAT/abstract_tabcomponent_state.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/RAT/abstract_tabcomponent_state.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/RAT/abstract_tabcomponent_state.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/RAT/abstract_tabcomponent_state.spec.tsx diff --git a/javascripts/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts similarity index 99% rename from javascripts/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts index d55d3b89..c3c6cd3d 100644 --- a/javascripts/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts +++ b/app/javascript/legacy_react/src/components/common/wizard/RAT/abstract_tabcomponent_state.ts @@ -1,6 +1,6 @@ // License: LGPL-3.0-or-later import {action, computed, observable, reaction, runInAction} from "mobx"; -import _ = require("lodash"); +import * as _ from "lodash"; const createFocusGroup = require('focus-group'); diff --git a/javascripts/src/components/common/wizard/RAT/specialAssign.ts b/app/javascript/legacy_react/src/components/common/wizard/RAT/specialAssign.ts similarity index 100% rename from javascripts/src/components/common/wizard/RAT/specialAssign.ts rename to app/javascript/legacy_react/src/components/common/wizard/RAT/specialAssign.ts diff --git a/javascripts/src/components/common/wizard/Wizard.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/Wizard.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/Wizard.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/Wizard.spec.tsx diff --git a/javascripts/src/components/common/wizard/Wizard.tsx b/app/javascript/legacy_react/src/components/common/wizard/Wizard.tsx similarity index 100% rename from javascripts/src/components/common/wizard/Wizard.tsx rename to app/javascript/legacy_react/src/components/common/wizard/Wizard.tsx diff --git a/javascripts/src/components/common/wizard/WizardPanel.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/WizardPanel.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/WizardPanel.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/WizardPanel.spec.tsx diff --git a/javascripts/src/components/common/wizard/WizardPanel.tsx b/app/javascript/legacy_react/src/components/common/wizard/WizardPanel.tsx similarity index 100% rename from javascripts/src/components/common/wizard/WizardPanel.tsx rename to app/javascript/legacy_react/src/components/common/wizard/WizardPanel.tsx diff --git a/javascripts/src/components/common/wizard/WizardTab.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/WizardTab.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/WizardTab.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/WizardTab.spec.tsx diff --git a/javascripts/src/components/common/wizard/WizardTab.tsx b/app/javascript/legacy_react/src/components/common/wizard/WizardTab.tsx similarity index 100% rename from javascripts/src/components/common/wizard/WizardTab.tsx rename to app/javascript/legacy_react/src/components/common/wizard/WizardTab.tsx diff --git a/javascripts/src/components/common/wizard/WizardTabList.tsx b/app/javascript/legacy_react/src/components/common/wizard/WizardTabList.tsx similarity index 100% rename from javascripts/src/components/common/wizard/WizardTabList.tsx rename to app/javascript/legacy_react/src/components/common/wizard/WizardTabList.tsx diff --git a/javascripts/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap similarity index 95% rename from javascripts/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap index 9112ff40..5e7aaba8 100644 --- a/javascripts/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap +++ b/app/javascript/legacy_react/src/components/common/wizard/__snapshots__/Wizard.spec.tsx.snap @@ -43,21 +43,21 @@ exports[`Wizard Move back on disabled make first invalid so move back there 1`]
-
-
-
@@ -112,21 +112,21 @@ exports[`Wizard Move back on disabled make first invalid so move back there 2`]
-
-
-
@@ -181,21 +181,21 @@ exports[`Wizard Move back on disabled make second invalid so move back there 1`]
-
-
-
@@ -250,21 +250,21 @@ exports[`Wizard first tab is active 1`] = `
-
-
-
@@ -319,21 +319,21 @@ exports[`Wizard go to the second tab go to next via backend 1`] = `
-
-
-
@@ -388,21 +388,21 @@ exports[`Wizard go to the second tab set via next click 1`] = `
-
-
-
@@ -457,21 +457,21 @@ exports[`Wizard go to the second tab set via tab click 1`] = `
-
-
-
diff --git a/javascripts/src/components/common/wizard/__snapshots__/WizardPanel.spec.tsx.snap b/app/javascript/legacy_react/src/components/common/wizard/__snapshots__/WizardPanel.spec.tsx.snap similarity index 100% rename from javascripts/src/components/common/wizard/__snapshots__/WizardPanel.spec.tsx.snap rename to app/javascript/legacy_react/src/components/common/wizard/__snapshots__/WizardPanel.spec.tsx.snap diff --git a/javascripts/src/components/common/wizard/abstract_wizard_state.spec.tsx b/app/javascript/legacy_react/src/components/common/wizard/abstract_wizard_state.spec.tsx similarity index 100% rename from javascripts/src/components/common/wizard/abstract_wizard_state.spec.tsx rename to app/javascript/legacy_react/src/components/common/wizard/abstract_wizard_state.spec.tsx diff --git a/javascripts/src/components/common/wizard/abstract_wizard_state.ts b/app/javascript/legacy_react/src/components/common/wizard/abstract_wizard_state.ts similarity index 98% rename from javascripts/src/components/common/wizard/abstract_wizard_state.ts rename to app/javascript/legacy_react/src/components/common/wizard/abstract_wizard_state.ts index a6ab3c78..ab5a05f4 100644 --- a/javascripts/src/components/common/wizard/abstract_wizard_state.ts +++ b/app/javascript/legacy_react/src/components/common/wizard/abstract_wizard_state.ts @@ -1,7 +1,7 @@ // License: LGPL-3.0-or-later import {computed, reaction} from "mobx"; import {AbstractTabComponentState, AbstractTabPanelState} from "./RAT/abstract_tabcomponent_state"; -import _ = require("lodash"); +import * as _ from "lodash"; export abstract class AbstractWizardState extends AbstractTabComponentState { diff --git a/javascripts/src/components/common/wizard/wizard_state.spec.ts b/app/javascript/legacy_react/src/components/common/wizard/wizard_state.spec.ts similarity index 100% rename from javascripts/src/components/common/wizard/wizard_state.spec.ts rename to app/javascript/legacy_react/src/components/common/wizard/wizard_state.spec.ts diff --git a/javascripts/src/components/common/wizard/wizard_state.ts b/app/javascript/legacy_react/src/components/common/wizard/wizard_state.ts similarity index 99% rename from javascripts/src/components/common/wizard/wizard_state.ts rename to app/javascript/legacy_react/src/components/common/wizard/wizard_state.ts index 843baa39..5b6b383f 100644 --- a/javascripts/src/components/common/wizard/wizard_state.ts +++ b/app/javascript/legacy_react/src/components/common/wizard/wizard_state.ts @@ -1,7 +1,7 @@ // License: LGPL-3.0-or-later import {observable, action, computed, toJS, reaction, runInAction} from "mobx"; import {Field, Form, FieldDefinition, FieldHandlers, FieldHooks} from "mobx-react-form"; -import _ = require("lodash"); +import * as _ from "lodash"; import {AbstractWizardState, AbstractWizardTabPanelState} from "./abstract_wizard_state"; interface SubFormDefinition { diff --git a/javascripts/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx b/app/javascript/legacy_react/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx similarity index 97% rename from javascripts/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx rename to app/javascript/legacy_react/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx index b31b2b14..751bbbbd 100644 --- a/javascripts/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx +++ b/app/javascript/legacy_react/src/components/create_offsite_payment_pane/CreateOffsitePaymentPane.tsx @@ -3,13 +3,13 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import {InjectedIntlProps, injectIntl} from 'react-intl'; import Modal from "../common/Modal"; -import { FundraiserInfo} from "../edit_payment_pane/EditPaymentPane"; +//import { FundraiserInfo} from "../edit_payment_pane/EditPaymentPane"; import {HoudiniForm} from "../../lib/houdini_form"; import {BasicField, CurrencyField, SelectField, TextareaField} from "../common/fields"; import ProgressableButton from "../common/ProgressableButton"; import {action, computed} from "mobx"; import {NonprofitTimezonedDates} from "../../lib/date"; -import {Field, FieldDefinition} from "../../../../types/mobx-react-form"; +import {Field, FieldDefinition} from "../../../../../../types/mobx-react-form"; import {createFieldDefinition} from "../../lib/mobx_utils"; import {centsToDollars, dollarsToCents} from "../../lib/format"; import {Validations} from "../../lib/vjf_rules"; @@ -18,9 +18,9 @@ import {ApiManager} from "../../lib/api_manager"; import * as CustomAPIS from "../../lib/apis"; import {CSRFInterceptor} from "../../lib/csrf_interceptor"; import {CreateOffsiteDonation, CreateOffsiteDonationModel} from "../../lib/api/create_offsite_donation"; -import blacklist = require("validator/lib/blacklist"); -import * as _ from 'lodash' -import moment = require('moment'); +import blacklist from "validator/lib/blacklist"; +import * as _ from 'lodash'; +import moment from 'moment'; import { castToUndefinedIfBlank } from '../../lib/utils'; import ReactInput from "../common/form/ReactInput"; diff --git a/javascripts/src/components/edit_payment_pane/EditPaymentPane.tsx b/app/javascript/legacy_react/src/components/edit_payment_pane/EditPaymentPane.tsx similarity index 99% rename from javascripts/src/components/edit_payment_pane/EditPaymentPane.tsx rename to app/javascript/legacy_react/src/components/edit_payment_pane/EditPaymentPane.tsx index 6b54a46b..ce9d0260 100644 --- a/javascripts/src/components/edit_payment_pane/EditPaymentPane.tsx +++ b/app/javascript/legacy_react/src/components/edit_payment_pane/EditPaymentPane.tsx @@ -15,9 +15,9 @@ import {CSRFInterceptor} from "../../lib/csrf_interceptor"; import {BasicField, CurrencyField, SelectField, TextareaField} from '../common/fields'; import {TwoColumnFields} from "../common/layout"; import {Validations} from "../../lib/vjf_rules"; -import _ = require("lodash"); +import * as _ from 'lodash' import {Dedication, parseDedication, serializeDedication} from '../../lib/dedication'; -import blacklist = require("validator/lib/blacklist"); +import blacklist from "validator/lib/blacklist"; import {createFieldDefinition} from "../../lib/mobx_utils"; import Modal from "../common/Modal"; import ReactInput from "../common/form/ReactInput"; diff --git a/javascripts/src/components/registration_page/NonprofitInfoForm.spec.tsx b/app/javascript/legacy_react/src/components/registration_page/NonprofitInfoForm.spec.tsx similarity index 100% rename from javascripts/src/components/registration_page/NonprofitInfoForm.spec.tsx rename to app/javascript/legacy_react/src/components/registration_page/NonprofitInfoForm.spec.tsx diff --git a/javascripts/src/components/registration_page/NonprofitInfoForm.tsx b/app/javascript/legacy_react/src/components/registration_page/NonprofitInfoForm.tsx similarity index 98% rename from javascripts/src/components/registration_page/NonprofitInfoForm.tsx rename to app/javascript/legacy_react/src/components/registration_page/NonprofitInfoForm.tsx index b82bc1bc..51dc4914 100644 --- a/javascripts/src/components/registration_page/NonprofitInfoForm.tsx +++ b/app/javascript/legacy_react/src/components/registration_page/NonprofitInfoForm.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer } from 'mobx-react'; import {InjectedIntlProps, injectIntl} from 'react-intl'; -import {Field, FieldDefinition} from "../../../../types/mobx-react-form"; +import {Field, FieldDefinition} from "../../../../../../types/mobx-react-form"; import {BasicField} from "../common/fields"; import {ThreeColumnFields, TwoColumnFields} from "../common/layout"; import {Validations} from "../../lib/vjf_rules"; diff --git a/javascripts/src/components/registration_page/NonprofitInfoPanel.spec.tsx b/app/javascript/legacy_react/src/components/registration_page/NonprofitInfoPanel.spec.tsx similarity index 100% rename from javascripts/src/components/registration_page/NonprofitInfoPanel.spec.tsx rename to app/javascript/legacy_react/src/components/registration_page/NonprofitInfoPanel.spec.tsx diff --git a/javascripts/src/components/registration_page/NonprofitInfoPanel.tsx b/app/javascript/legacy_react/src/components/registration_page/NonprofitInfoPanel.tsx similarity index 100% rename from javascripts/src/components/registration_page/NonprofitInfoPanel.tsx rename to app/javascript/legacy_react/src/components/registration_page/NonprofitInfoPanel.tsx diff --git a/javascripts/src/components/registration_page/RegistrationPage.tsx b/app/javascript/legacy_react/src/components/registration_page/RegistrationPage.tsx similarity index 100% rename from javascripts/src/components/registration_page/RegistrationPage.tsx rename to app/javascript/legacy_react/src/components/registration_page/RegistrationPage.tsx diff --git a/javascripts/src/components/registration_page/RegistrationWizard.tsx b/app/javascript/legacy_react/src/components/registration_page/RegistrationWizard.tsx similarity index 84% rename from javascripts/src/components/registration_page/RegistrationWizard.tsx rename to app/javascript/legacy_react/src/components/registration_page/RegistrationWizard.tsx index 6abd2223..2303da37 100644 --- a/javascripts/src/components/registration_page/RegistrationWizard.tsx +++ b/app/javascript/legacy_react/src/components/registration_page/RegistrationWizard.tsx @@ -12,12 +12,15 @@ import {WizardState, WizardTabPanelState} from "../common/wizard/wizard_state"; import UserInfoPanel, * as UserInfo from "./UserInfoPanel"; import { Nonprofit, - NonprofitApi, + NonprofitsApi, PostNonprofit, - ValidationErrorsException + ValidationErrorsException, + UsersApi, + PostUser, + PostNonprofitUser } from "../../../api"; -import {initializationDefinition} from "../../../../types/mobx-react-form"; +import {initializationDefinition} from "../../../../../../types/mobx-react-form"; import {ApiManager} from "../../lib/api_manager"; import {HoudiniForm, StaticFormToErrorAndBackConverter} from "../../lib/houdini_form"; import {WebUserSignInOut} from "../../lib/api/sign_in"; @@ -36,16 +39,22 @@ const setTourCookies = (nonprofit:Nonprofit) => { document.cookie = `tour_supporters=${nonprofit.id};path=/` document.cookie = `tour_subscribers=${nonprofit.id};path=/` } +/** this is just here to allow compilation. */ +interface TemporaryHackyInterface { + nonprofit: PostNonprofit + user: PostNonprofitUser +} export class RegistrationPageForm extends HoudiniForm { - converter: StaticFormToErrorAndBackConverter + converter: StaticFormToErrorAndBackConverter constructor(definition: initializationDefinition, options?: any) { super(definition, options) - this.converter = new StaticFormToErrorAndBackConverter(this.inputToForm) + this.converter = new StaticFormToErrorAndBackConverter(this.inputToForm) } - nonprofitApi: NonprofitApi + nonprofitApi: NonprofitsApi + usersApi: UsersApi signinApi: WebUserSignInOut options() { @@ -78,9 +87,11 @@ export class RegistrationPageForm extends HoudiniForm { try { - let r = await this.nonprofitApi.postNonprofit(input) + const userMessage = {user: input.user} + let user = await this.usersApi.postUser(userMessage) + this.signinApi.postLogin({email: input.user.email, password: input.user.password}) + let r = await this.nonprofitApi.postNonprofit(input.nonprofit) setTourCookies(r) - await this.signinApi.postLogin({email: input.user.email, password: input.user.password}) window.location.href = `/nonprofits/${r.id}/dashboard` } @@ -155,8 +166,9 @@ export class InnerRegistrationWizard extends React.Component { - this.form.nonprofitApi = this.props.ApiManager.get(NonprofitApi) + this.form.nonprofitApi = this.props.ApiManager.get(NonprofitsApi) this.form.signinApi = this.props.ApiManager.get(WebUserSignInOut) + this.form.usersApi = this.props.ApiManager.get(UsersApi) }) } diff --git a/javascripts/src/components/registration_page/UserInfoForm.tsx b/app/javascript/legacy_react/src/components/registration_page/UserInfoForm.tsx similarity index 100% rename from javascripts/src/components/registration_page/UserInfoForm.tsx rename to app/javascript/legacy_react/src/components/registration_page/UserInfoForm.tsx diff --git a/javascripts/src/components/registration_page/UserInfoPanel.spec.tsx b/app/javascript/legacy_react/src/components/registration_page/UserInfoPanel.spec.tsx similarity index 100% rename from javascripts/src/components/registration_page/UserInfoPanel.spec.tsx rename to app/javascript/legacy_react/src/components/registration_page/UserInfoPanel.spec.tsx diff --git a/javascripts/src/components/registration_page/UserInfoPanel.tsx b/app/javascript/legacy_react/src/components/registration_page/UserInfoPanel.tsx similarity index 100% rename from javascripts/src/components/registration_page/UserInfoPanel.tsx rename to app/javascript/legacy_react/src/components/registration_page/UserInfoPanel.tsx diff --git a/javascripts/src/components/session_login_page/SessionLoginForm.tsx b/app/javascript/legacy_react/src/components/session_login_page/SessionLoginForm.tsx similarity index 98% rename from javascripts/src/components/session_login_page/SessionLoginForm.tsx rename to app/javascript/legacy_react/src/components/session_login_page/SessionLoginForm.tsx index 3d7d3ca8..d098a8ce 100644 --- a/javascripts/src/components/session_login_page/SessionLoginForm.tsx +++ b/app/javascript/legacy_react/src/components/session_login_page/SessionLoginForm.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { observer, inject} from 'mobx-react'; import {InjectedIntlProps, injectIntl, FormattedMessage} from 'react-intl'; -import {Field, FieldDefinition, Form, initializationDefinition} from "../../../../types/mobx-react-form"; +import {Field, FieldDefinition, Form, initializationDefinition} from "../../../../../../types/mobx-react-form"; import {Validations} from "../../lib/vjf_rules"; import {WebLoginModel, WebUserSignInOut} from "../../lib/api/sign_in"; diff --git a/javascripts/src/components/session_login_page/SessionLoginPage.tsx b/app/javascript/legacy_react/src/components/session_login_page/SessionLoginPage.tsx similarity index 100% rename from javascripts/src/components/session_login_page/SessionLoginPage.tsx rename to app/javascript/legacy_react/src/components/session_login_page/SessionLoginPage.tsx diff --git a/javascripts/src/lib/api/create_offsite_donation.ts b/app/javascript/legacy_react/src/lib/api/create_offsite_donation.ts similarity index 92% rename from javascripts/src/lib/api/create_offsite_donation.ts rename to app/javascript/legacy_react/src/lib/api/create_offsite_donation.ts index 9abe4d3f..d54ba985 100644 --- a/javascripts/src/lib/api/create_offsite_donation.ts +++ b/app/javascript/legacy_react/src/lib/api/create_offsite_donation.ts @@ -58,11 +58,11 @@ export class CreateOffsiteDonation { } if (extraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, extraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); } if (this.defaultExtraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); } let dfd = $.Deferred(); @@ -76,7 +76,7 @@ export class CreateOffsiteDonation { } ); - return dfd.promise(); + return dfd.promise() as any; } } diff --git a/javascripts/src/lib/api/put_donation.ts b/app/javascript/legacy_react/src/lib/api/put_donation.ts similarity index 92% rename from javascripts/src/lib/api/put_donation.ts rename to app/javascript/legacy_react/src/lib/api/put_donation.ts index c1cd81cc..e573f113 100644 --- a/javascripts/src/lib/api/put_donation.ts +++ b/app/javascript/legacy_react/src/lib/api/put_donation.ts @@ -58,11 +58,11 @@ export class PutDonation { } if (extraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, extraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); } if (this.defaultExtraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); } let dfd = $.Deferred(); @@ -76,7 +76,7 @@ export class PutDonation { } ); - return dfd.promise(); + return dfd.promise() as any; } } diff --git a/javascripts/src/lib/api/sign_in.ts b/app/javascript/legacy_react/src/lib/api/sign_in.ts similarity index 91% rename from javascripts/src/lib/api/sign_in.ts rename to app/javascript/legacy_react/src/lib/api/sign_in.ts index 55dce752..e5fb4724 100644 --- a/javascripts/src/lib/api/sign_in.ts +++ b/app/javascript/legacy_react/src/lib/api/sign_in.ts @@ -58,11 +58,11 @@ export class WebUserSignInOut { } if (extraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, extraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, extraJQueryAjaxSettings); } if (this.defaultExtraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, this.defaultExtraJQueryAjaxSettings); + requestOptions = Object.assign(requestOptions, this.defaultExtraJQueryAjaxSettings); } let dfd = $.Deferred(); @@ -76,7 +76,7 @@ export class WebUserSignInOut { } ); - return dfd.promise(); + return dfd.promise() as any; } } diff --git a/javascripts/src/lib/api_manager.spec.ts b/app/javascript/legacy_react/src/lib/api_manager.spec.ts similarity index 100% rename from javascripts/src/lib/api_manager.spec.ts rename to app/javascript/legacy_react/src/lib/api_manager.spec.ts diff --git a/javascripts/src/lib/api_manager.ts b/app/javascript/legacy_react/src/lib/api_manager.ts similarity index 94% rename from javascripts/src/lib/api_manager.ts rename to app/javascript/legacy_react/src/lib/api_manager.ts index 512fa6d6..c15aa3eb 100644 --- a/javascripts/src/lib/api_manager.ts +++ b/app/javascript/legacy_react/src/lib/api_manager.ts @@ -27,10 +27,10 @@ export class ApiManager { let newed = new i() if (beforeSendInterceptors && beforeSendInterceptors.length > 0) { let a: JQuery.AjaxSettings = { - beforeSend: ((jqXHR:JQuery.jqXHR, settings:JQuery.AjaxSettings) : false|void => { + beforeSend: ((jqXHR:JQuery.jqXHR, settings:JQuery.AjaxSettings) : false|void => { _.forEach(beforeSendInterceptors, (i:Interceptor) => i(jqXHR, settings)) return - }) + }) as any } newed.defaultExtraJQueryAjaxSettings = a } diff --git a/javascripts/src/lib/apis.ts b/app/javascript/legacy_react/src/lib/apis.ts similarity index 100% rename from javascripts/src/lib/apis.ts rename to app/javascript/legacy_react/src/lib/apis.ts diff --git a/javascripts/src/lib/createNumberMask.spec.ts b/app/javascript/legacy_react/src/lib/createNumberMask.spec.ts similarity index 100% rename from javascripts/src/lib/createNumberMask.spec.ts rename to app/javascript/legacy_react/src/lib/createNumberMask.spec.ts diff --git a/javascripts/src/lib/createNumberMask.ts b/app/javascript/legacy_react/src/lib/createNumberMask.ts similarity index 100% rename from javascripts/src/lib/createNumberMask.ts rename to app/javascript/legacy_react/src/lib/createNumberMask.ts diff --git a/javascripts/src/lib/csrf_interceptor.ts b/app/javascript/legacy_react/src/lib/csrf_interceptor.ts similarity index 83% rename from javascripts/src/lib/csrf_interceptor.ts rename to app/javascript/legacy_react/src/lib/csrf_interceptor.ts index dea0db9c..ff129012 100644 --- a/javascripts/src/lib/csrf_interceptor.ts +++ b/app/javascript/legacy_react/src/lib/csrf_interceptor.ts @@ -6,6 +6,6 @@ * @returns {false | void} */ export function CSRFInterceptor(this:any, jqXHR:JQuery.jqXHR, settings: JQuery.AjaxSettings): false|void { - jqXHR.setRequestHeader('X-CSRF-Token', (window)._csrf) + jqXHR.setRequestHeader('X-CSRF-Token', (window as any)._csrf) } diff --git a/javascripts/src/lib/date.ts b/app/javascript/legacy_react/src/lib/date.ts similarity index 98% rename from javascripts/src/lib/date.ts rename to app/javascript/legacy_react/src/lib/date.ts index 86ecdb7d..98695365 100644 --- a/javascripts/src/lib/date.ts +++ b/app/javascript/legacy_react/src/lib/date.ts @@ -1,5 +1,5 @@ // License: LGPL-3.0-or-later -import * as moment from 'moment'; +import moment from 'moment'; import 'moment-timezone' function momentTz(date:string, timezone:string='UTC'):moment.Moment { diff --git a/javascripts/src/lib/dedication.ts b/app/javascript/legacy_react/src/lib/dedication.ts similarity index 100% rename from javascripts/src/lib/dedication.ts rename to app/javascript/legacy_react/src/lib/dedication.ts diff --git a/javascripts/src/lib/deprecated_format.ts b/app/javascript/legacy_react/src/lib/deprecated_format.ts similarity index 100% rename from javascripts/src/lib/deprecated_format.ts rename to app/javascript/legacy_react/src/lib/deprecated_format.ts diff --git a/javascripts/src/lib/format.spec.ts b/app/javascript/legacy_react/src/lib/format.spec.ts similarity index 100% rename from javascripts/src/lib/format.spec.ts rename to app/javascript/legacy_react/src/lib/format.spec.ts diff --git a/javascripts/src/lib/format.ts b/app/javascript/legacy_react/src/lib/format.ts similarity index 100% rename from javascripts/src/lib/format.ts rename to app/javascript/legacy_react/src/lib/format.ts diff --git a/javascripts/src/lib/houdini_form.ts b/app/javascript/legacy_react/src/lib/houdini_form.ts similarity index 99% rename from javascripts/src/lib/houdini_form.ts rename to app/javascript/legacy_react/src/lib/houdini_form.ts index a40c2cbd..726d7a15 100644 --- a/javascripts/src/lib/houdini_form.ts +++ b/app/javascript/legacy_react/src/lib/houdini_form.ts @@ -3,7 +3,7 @@ import {Field, FieldDefinition, Form, initializationDefinition} from "mobx-react import {action, computed, IValueDidChange, observable, runInAction} from 'mobx' import * as _ from 'lodash' import {ValidationErrorsException} from "../../api"; -import validator = require("validator"); +import validator from "validator"; export class HoudiniForm extends Form { diff --git a/javascripts/src/lib/mobx_utils.ts b/app/javascript/legacy_react/src/lib/mobx_utils.ts similarity index 100% rename from javascripts/src/lib/mobx_utils.ts rename to app/javascript/legacy_react/src/lib/mobx_utils.ts diff --git a/javascripts/src/lib/nonprofitBranding.ts b/app/javascript/legacy_react/src/lib/nonprofitBranding.ts similarity index 95% rename from javascripts/src/lib/nonprofitBranding.ts rename to app/javascript/legacy_react/src/lib/nonprofitBranding.ts index b05f5d43..d9ee335c 100644 --- a/javascripts/src/lib/nonprofitBranding.ts +++ b/app/javascript/legacy_react/src/lib/nonprofitBranding.ts @@ -1,5 +1,5 @@ // License: LGPL-3.0-or-later -import color = require('color') +import color from 'color'; import { Color } from 'csstype'; interface CustomBrandColors { diff --git a/javascripts/src/lib/payments/credit_card.spec.ts b/app/javascript/legacy_react/src/lib/payments/credit_card.spec.ts similarity index 99% rename from javascripts/src/lib/payments/credit_card.spec.ts rename to app/javascript/legacy_react/src/lib/payments/credit_card.spec.ts index 8edfcabf..29bb2bc4 100644 --- a/javascripts/src/lib/payments/credit_card.spec.ts +++ b/app/javascript/legacy_react/src/lib/payments/credit_card.spec.ts @@ -191,7 +191,7 @@ describe('CreditCardTypeManager', () => { }) it('should support year shorthand', () => { - expect(cc.validateCardExpiry('05', '20')).toBeTruthy() + expect(cc.validateCardExpiry('05', '25')).toBeTruthy() }) }) describe('Validating a CVC number', () => { diff --git a/javascripts/src/lib/payments/credit_card.ts b/app/javascript/legacy_react/src/lib/payments/credit_card.ts similarity index 100% rename from javascripts/src/lib/payments/credit_card.ts rename to app/javascript/legacy_react/src/lib/payments/credit_card.ts diff --git a/javascripts/src/lib/regex.spec.ts b/app/javascript/legacy_react/src/lib/regex.spec.ts similarity index 100% rename from javascripts/src/lib/regex.spec.ts rename to app/javascript/legacy_react/src/lib/regex.spec.ts diff --git a/javascripts/src/lib/regex.ts b/app/javascript/legacy_react/src/lib/regex.ts similarity index 100% rename from javascripts/src/lib/regex.ts rename to app/javascript/legacy_react/src/lib/regex.ts diff --git a/javascripts/src/lib/tests/helpers.ts b/app/javascript/legacy_react/src/lib/tests/helpers.ts similarity index 100% rename from javascripts/src/lib/tests/helpers.ts rename to app/javascript/legacy_react/src/lib/tests/helpers.ts diff --git a/javascripts/src/lib/utils.ts b/app/javascript/legacy_react/src/lib/utils.ts similarity index 100% rename from javascripts/src/lib/utils.ts rename to app/javascript/legacy_react/src/lib/utils.ts diff --git a/javascripts/src/lib/vjf_rules.ts b/app/javascript/legacy_react/src/lib/vjf_rules.ts similarity index 98% rename from javascripts/src/lib/vjf_rules.ts rename to app/javascript/legacy_react/src/lib/vjf_rules.ts index 26eef445..b9f2db7f 100644 --- a/javascripts/src/lib/vjf_rules.ts +++ b/app/javascript/legacy_react/src/lib/vjf_rules.ts @@ -1,7 +1,7 @@ // License: LGPL-3.0-or-later import * as Regex from './regex' import {Field, Form} from "mobx-react-form"; -import moment = require("moment"); +import moment from "moment"; interface ValidationInput { diff --git a/app/javascript/packs/create_new_offsite_payment_pane.js b/app/javascript/packs/create_new_offsite_payment_pane.js new file mode 100644 index 00000000..71a6323c --- /dev/null +++ b/app/javascript/packs/create_new_offsite_payment_pane.js @@ -0,0 +1,4 @@ +// License: LGPL-3.0-or-later +// require a root component here. This will be treated as the root of a webpack package +require('bootstrap-loader'); +require('../legacy_react/app/create_new_offsite_payment_pane') \ No newline at end of file diff --git a/app/javascript/packs/donate-button-v2.js b/app/javascript/packs/donate-button-v2.js new file mode 100644 index 00000000..83d5dfe6 --- /dev/null +++ b/app/javascript/packs/donate-button-v2.js @@ -0,0 +1 @@ +require('../donate-button/donate-button.v2') \ No newline at end of file diff --git a/app/javascript/packs/edit_payment_pane.js b/app/javascript/packs/edit_payment_pane.js new file mode 100644 index 00000000..3b85237a --- /dev/null +++ b/app/javascript/packs/edit_payment_pane.js @@ -0,0 +1,4 @@ +// License: LGPL-3.0-or-later +// require a root component here. This will be treated as the root of a webpack package +require('bootstrap-loader'); +require('../legacy_react/app/edit_payment_pane') \ No newline at end of file diff --git a/app/javascript/packs/i18n.js b/app/javascript/packs/i18n.js new file mode 100644 index 00000000..521ee32c --- /dev/null +++ b/app/javascript/packs/i18n.js @@ -0,0 +1,3 @@ +const i18n = require('../i18n.js.erb') + +window.I18n = i18n; \ No newline at end of file diff --git a/app/javascript/packs/loading_indicator.ts b/app/javascript/packs/loading_indicator.ts new file mode 100644 index 00000000..86d287c4 --- /dev/null +++ b/app/javascript/packs/loading_indicator.ts @@ -0,0 +1,8 @@ +// License: LGPL-3.0-or-later +require('../legacy_react/app/loading_indicator') + + + + + + diff --git a/app/javascript/packs/page__bank_accounts__confirm.js b/app/javascript/packs/page__bank_accounts__confirm.js new file mode 100644 index 00000000..7a8a575e --- /dev/null +++ b/app/javascript/packs/page__bank_accounts__confirm.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/bank_accounts/confirm/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__campaigns__index.js b/app/javascript/packs/page__campaigns__index.js new file mode 100644 index 00000000..7d8d84b6 --- /dev/null +++ b/app/javascript/packs/page__campaigns__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/campaigns/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__campaigns__peer_to_peer.js b/app/javascript/packs/page__campaigns__peer_to_peer.js new file mode 100644 index 00000000..9c88a1be --- /dev/null +++ b/app/javascript/packs/page__campaigns__peer_to_peer.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/campaigns/peer_to_peer/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__campaigns__show.js b/app/javascript/packs/page__campaigns__show.js new file mode 100644 index 00000000..abff9f6e --- /dev/null +++ b/app/javascript/packs/page__campaigns__show.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/campaigns/show/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__campaigns__supporters__index.js b/app/javascript/packs/page__campaigns__supporters__index.js new file mode 100644 index 00000000..201c25c0 --- /dev/null +++ b/app/javascript/packs/page__campaigns__supporters__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/campaigns/supporters/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__events__index.js b/app/javascript/packs/page__events__index.js new file mode 100644 index 00000000..95ee1fd0 --- /dev/null +++ b/app/javascript/packs/page__events__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/events/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__events__show.js b/app/javascript/packs/page__events__show.js new file mode 100644 index 00000000..989c1498 --- /dev/null +++ b/app/javascript/packs/page__events__show.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/events/show/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__events__stats.js b/app/javascript/packs/page__events__stats.js new file mode 100644 index 00000000..f3f17718 --- /dev/null +++ b/app/javascript/packs/page__events__stats.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/events/stats/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__btn.js b/app/javascript/packs/page__nonprofits__btn.js new file mode 100644 index 00000000..51cf0b41 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__btn.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/btn/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__button.js b/app/javascript/packs/page__nonprofits__button.js new file mode 100644 index 00000000..78f16c5b --- /dev/null +++ b/app/javascript/packs/page__nonprofits__button.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/button/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__cards__edit.js b/app/javascript/packs/page__nonprofits__cards__edit.js new file mode 100644 index 00000000..e3be4ae0 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__cards__edit.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/cards/edit/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__dashboard.js b/app/javascript/packs/page__nonprofits__dashboard.js new file mode 100644 index 00000000..fc7ef4f1 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__dashboard.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/dashboard/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__donate.js b/app/javascript/packs/page__nonprofits__donate.js new file mode 100644 index 00000000..b36bf4cc --- /dev/null +++ b/app/javascript/packs/page__nonprofits__donate.js @@ -0,0 +1,2 @@ +require('../legacy/page.js') +require('../legacy/nonprofits/donate/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__edit.js b/app/javascript/packs/page__nonprofits__edit.js new file mode 100644 index 00000000..aacb30d1 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__edit.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/edit/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__payments__index.js b/app/javascript/packs/page__nonprofits__payments__index.js new file mode 100644 index 00000000..866269b8 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__payments__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/payments/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__payouts__index.js b/app/javascript/packs/page__nonprofits__payouts__index.js new file mode 100644 index 00000000..5bbbd972 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__payouts__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/payouts/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__recurring_donations__index.js b/app/javascript/packs/page__nonprofits__recurring_donations__index.js new file mode 100644 index 00000000..8f60a736 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__recurring_donations__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/recurring_donations/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__show.js b/app/javascript/packs/page__nonprofits__show.js new file mode 100644 index 00000000..7035699f --- /dev/null +++ b/app/javascript/packs/page__nonprofits__show.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/show/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__supporter_form.js b/app/javascript/packs/page__nonprofits__supporter_form.js new file mode 100644 index 00000000..94f8e768 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__supporter_form.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/supporter_form/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__supporters__index.js b/app/javascript/packs/page__nonprofits__supporters__index.js new file mode 100644 index 00000000..f408b7c8 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__supporters__index.js @@ -0,0 +1,2 @@ +require('../legacy/page.js') +require('../legacy/nonprofits/supporters/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__nonprofits__supporters__new.js b/app/javascript/packs/page__nonprofits__supporters__new.js new file mode 100644 index 00000000..693ff9c2 --- /dev/null +++ b/app/javascript/packs/page__nonprofits__supporters__new.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/nonprofits/supporters/new/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__profiles__donations_history.js b/app/javascript/packs/page__profiles__donations_history.js new file mode 100644 index 00000000..10da02c3 --- /dev/null +++ b/app/javascript/packs/page__profiles__donations_history.js @@ -0,0 +1 @@ +require('../legacy/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__profiles__fundraisers.js b/app/javascript/packs/page__profiles__fundraisers.js new file mode 100644 index 00000000..10da02c3 --- /dev/null +++ b/app/javascript/packs/page__profiles__fundraisers.js @@ -0,0 +1 @@ +require('../legacy/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__profiles__show.js b/app/javascript/packs/page__profiles__show.js new file mode 100644 index 00000000..10da02c3 --- /dev/null +++ b/app/javascript/packs/page__profiles__show.js @@ -0,0 +1 @@ +require('../legacy/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__recurring_donations__edit.js b/app/javascript/packs/page__recurring_donations__edit.js new file mode 100644 index 00000000..d77b9aa5 --- /dev/null +++ b/app/javascript/packs/page__recurring_donations__edit.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/recurring_donations/edit/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__settings__index.js b/app/javascript/packs/page__settings__index.js new file mode 100644 index 00000000..767aca08 --- /dev/null +++ b/app/javascript/packs/page__settings__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/settings/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__stripe_wrapper.js b/app/javascript/packs/page__stripe_wrapper.js new file mode 100644 index 00000000..a27ca2c2 --- /dev/null +++ b/app/javascript/packs/page__stripe_wrapper.js @@ -0,0 +1 @@ +require('../legacy/stripe_wrapper/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__super-admin.js b/app/javascript/packs/page__super-admin.js new file mode 100644 index 00000000..fb362949 --- /dev/null +++ b/app/javascript/packs/page__super-admin.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/super-admin/page.js') \ No newline at end of file diff --git a/app/javascript/packs/page__tickets__index.js b/app/javascript/packs/page__tickets__index.js new file mode 100644 index 00000000..5fe6f413 --- /dev/null +++ b/app/javascript/packs/page__tickets__index.js @@ -0,0 +1,3 @@ +require('../../../client/css/global/page.css') +require('../legacy/page.js') +require('../legacy/tickets/index/page.js') \ No newline at end of file diff --git a/app/javascript/packs/registration_page.js b/app/javascript/packs/registration_page.js new file mode 100644 index 00000000..7c93fb67 --- /dev/null +++ b/app/javascript/packs/registration_page.js @@ -0,0 +1,4 @@ +// License: LGPL-3.0-or-later +require('bootstrap-loader'); +require('../legacy_react/app/registration_page') + diff --git a/app/javascript/packs/session_login_page.js b/app/javascript/packs/session_login_page.js new file mode 100644 index 00000000..7e7147b2 --- /dev/null +++ b/app/javascript/packs/session_login_page.js @@ -0,0 +1,3 @@ +// License: LGPL-3.0-or-later +require('bootstrap-loader'); +require('../legacy_react/app/session_login_page') \ No newline at end of file diff --git a/app/javascript/page_info.js.erb b/app/javascript/page_info.js.erb new file mode 100644 index 00000000..dba524ba --- /dev/null +++ b/app/javascript/page_info.js.erb @@ -0,0 +1,5 @@ +const pageInfo = { + apiDomain: '<%= Settings.api_domain.url %>' +} + +module.exports = pageInfo; \ No newline at end of file diff --git a/app/jobs/admin_failed_gift_job.rb b/app/jobs/admin_failed_gift_job.rb new file mode 100644 index 00000000..15f38693 --- /dev/null +++ b/app/jobs/admin_failed_gift_job.rb @@ -0,0 +1,6 @@ +class AdminFailedGiftJob < EmailJob + + def perform(donation, campaign_gift_option) + AdminMailer.notify_failed_gift(donation, campaign_gift_option).deliver_now + end +end diff --git a/app/api/houdini/v1/entities/nonprofit.rb b/app/jobs/application_job.rb similarity index 54% rename from app/api/houdini/v1/entities/nonprofit.rb rename to app/jobs/application_job.rb index 55620d7e..d4046c71 100644 --- a/app/api/houdini/v1/entities/nonprofit.rb +++ b/app/jobs/application_job.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Entities::Nonprofit < Grape::Entity - expose :id -end \ No newline at end of file + +class ApplicationJob < ActiveJob::Base +end diff --git a/app/jobs/bank_account_create_job.rb b/app/jobs/bank_account_create_job.rb new file mode 100644 index 00000000..f30c5c23 --- /dev/null +++ b/app/jobs/bank_account_create_job.rb @@ -0,0 +1,6 @@ +class BankAccountCreateJob < EmailJob + + def perform(bank_account) + NonprofitMailer.new_bank_account_notification(bank_account).deliver_now + end +end diff --git a/app/jobs/campaign_creation_email_followup_job.rb b/app/jobs/campaign_creation_email_followup_job.rb new file mode 100644 index 00000000..2ed45890 --- /dev/null +++ b/app/jobs/campaign_creation_email_followup_job.rb @@ -0,0 +1,6 @@ +class CampaignCreationEmailFollowupJob < EmailJob + + def perform(campaign) + CampaignMailer.creation_followup(campaign).deliver_now + end +end diff --git a/app/jobs/campaign_creation_federated_email_job.rb b/app/jobs/campaign_creation_federated_email_job.rb new file mode 100644 index 00000000..c80d8f90 --- /dev/null +++ b/app/jobs/campaign_creation_federated_email_job.rb @@ -0,0 +1,6 @@ +class CampaignCreationFederatedEmailJob < EmailJob + + def perform(campaign) + CampaignMailer.federated_creation_followup(campaign).deliver_now + end +end diff --git a/app/jobs/direct_debit_create_notify_donor_job.rb b/app/jobs/direct_debit_create_notify_donor_job.rb new file mode 100644 index 00000000..d720fc32 --- /dev/null +++ b/app/jobs/direct_debit_create_notify_donor_job.rb @@ -0,0 +1,6 @@ +class DirectDebitCreateNotifyDonorJob < EmailJob + + def perform(donation_id, locale) + DonationMailer.donor_direct_debit_notification(donation_id, locale).deliver_now + end +end diff --git a/app/jobs/direct_debit_create_notify_nonprofit_job.rb b/app/jobs/direct_debit_create_notify_nonprofit_job.rb new file mode 100644 index 00000000..56767564 --- /dev/null +++ b/app/jobs/direct_debit_create_notify_nonprofit_job.rb @@ -0,0 +1,6 @@ +class DirectDebitCreateNotifyNonprofitJob < EmailJob + + def perform(donation_id) + DonationMailer.nonprofit_payment_notification(donation_id).deliver_now + end +end diff --git a/app/jobs/email_job.rb b/app/jobs/email_job.rb new file mode 100644 index 00000000..e30bd65c --- /dev/null +++ b/app/jobs/email_job.rb @@ -0,0 +1,5 @@ +class EmailJob < ApplicationJob + queue_as :email_queue + + retry_on Exception, wait: ->(executions) { executions **2.195 }, attempts: MAX_EMAIL_JOB_ATTEMPTS || 1 +end diff --git a/app/jobs/email_list_create_job.rb b/app/jobs/email_list_create_job.rb new file mode 100644 index 00000000..21795976 --- /dev/null +++ b/app/jobs/email_list_create_job.rb @@ -0,0 +1,7 @@ +class EmailListCreateJob < ApplicationJob + queue_as :default + + def perform(npo_id) + UpdateEmailLists.populate_lists_on_mailchimp(npo_id) + end +end diff --git a/app/jobs/event_create_creator_email_job.rb b/app/jobs/event_create_creator_email_job.rb new file mode 100644 index 00000000..391b936e --- /dev/null +++ b/app/jobs/event_create_creator_email_job.rb @@ -0,0 +1,6 @@ +class EventCreateCreatorEmailJob < EmailJob + + def perform(event) + EventMailer.creation_followup(event).deliver_now + end +end diff --git a/app/jobs/event_create_job.rb b/app/jobs/event_create_job.rb new file mode 100644 index 00000000..561a775a --- /dev/null +++ b/app/jobs/event_create_job.rb @@ -0,0 +1,7 @@ +class EventCreateJob < ApplicationJob + queue_as :default + + def perform(event) + EventCreateCreatorEmailJob.perform_later(event) + end +end diff --git a/app/jobs/export_payments_completed_job.rb b/app/jobs/export_payments_completed_job.rb new file mode 100644 index 00000000..49619415 --- /dev/null +++ b/app/jobs/export_payments_completed_job.rb @@ -0,0 +1,6 @@ +class ExportPaymentsCompletedJob < EmailJob + + def perform(export) + ExportMailer.export_payments_completed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_payments_failed_job.rb b/app/jobs/export_payments_failed_job.rb new file mode 100644 index 00000000..8acbcb2a --- /dev/null +++ b/app/jobs/export_payments_failed_job.rb @@ -0,0 +1,6 @@ +class ExportPaymentsFailedJob < EmailJob + + def perform(export) + ExportMailer.export_payments_failed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_recurring_donations_completed_job.rb b/app/jobs/export_recurring_donations_completed_job.rb new file mode 100644 index 00000000..245dfdda --- /dev/null +++ b/app/jobs/export_recurring_donations_completed_job.rb @@ -0,0 +1,6 @@ +class ExportRecurringDonationsCompletedJob < EmailJob + + def perform(export) + ExportMailer.export_recurring_donations_completed_notification(@export).deliver_now + end +end diff --git a/app/jobs/export_recurring_donations_failed_job.rb b/app/jobs/export_recurring_donations_failed_job.rb new file mode 100644 index 00000000..b1dd904e --- /dev/null +++ b/app/jobs/export_recurring_donations_failed_job.rb @@ -0,0 +1,6 @@ +class ExportRecurringDonationsFailedJob < EmailJob + + def perform(export) + ExportMailer.export_recurring_donations_failed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_supporter_notes_completed_job.rb b/app/jobs/export_supporter_notes_completed_job.rb new file mode 100644 index 00000000..bfcf9993 --- /dev/null +++ b/app/jobs/export_supporter_notes_completed_job.rb @@ -0,0 +1,6 @@ +class ExportSupporterNotesCompletedJob < EmailJob + + def perform(export) + ExportMailer.export_supporter_notes_completed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_supporter_notes_failed_job.rb b/app/jobs/export_supporter_notes_failed_job.rb new file mode 100644 index 00000000..ba59a7ba --- /dev/null +++ b/app/jobs/export_supporter_notes_failed_job.rb @@ -0,0 +1,6 @@ +class ExportSupporterNotesFailedJob < EmailJob + + def perform(export) + ExportMailer.export_supporter_notes_failed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_supporters_completed_job.rb b/app/jobs/export_supporters_completed_job.rb new file mode 100644 index 00000000..0d06045f --- /dev/null +++ b/app/jobs/export_supporters_completed_job.rb @@ -0,0 +1,6 @@ +class ExportSupportersCompletedJob < EmailJob + + def perform(export) + ExportMailer.export_supporters_completed_notification(export).deliver_now + end +end diff --git a/app/jobs/export_supporters_failed_job.rb b/app/jobs/export_supporters_failed_job.rb new file mode 100644 index 00000000..4da0f375 --- /dev/null +++ b/app/jobs/export_supporters_failed_job.rb @@ -0,0 +1,6 @@ +class ExportSupportersFailedJob < EmailJob + + def perform(export) + ExportMailer.export_supporters_failed_notification(export).deliver_now + end +end diff --git a/app/jobs/failed_recurring_donation_payment_donor_email_job.rb b/app/jobs/failed_recurring_donation_payment_donor_email_job.rb new file mode 100644 index 00000000..90054cd4 --- /dev/null +++ b/app/jobs/failed_recurring_donation_payment_donor_email_job.rb @@ -0,0 +1,6 @@ +class FailedRecurringDonationPaymentDonorEmailJob < EmailJob + + def perform(donation) + DonationMailer.donor_failed_recurring_donation(donation.id).deliver_now + end +end diff --git a/app/jobs/failed_recurring_donation_payment_nonprofit_email_job.rb b/app/jobs/failed_recurring_donation_payment_nonprofit_email_job.rb new file mode 100644 index 00000000..c9ed53a7 --- /dev/null +++ b/app/jobs/failed_recurring_donation_payment_nonprofit_email_job.rb @@ -0,0 +1,6 @@ +class FailedRecurringDonationPaymentNonprofitEmailJob < EmailJob + + def perform(donation) + DonationMailer.nonprofit_failed_recurring_donation(donation.id).deliver_now + end +end diff --git a/app/jobs/import_completed_job.rb b/app/jobs/import_completed_job.rb new file mode 100644 index 00000000..82fb6336 --- /dev/null +++ b/app/jobs/import_completed_job.rb @@ -0,0 +1,6 @@ +class ImportCompletedJob < EmailJob + + def perform(import) + ImportMailer.import_completed_notification(import.id).deliver_now + end +end diff --git a/app/jobs/import_creation_job.rb b/app/jobs/import_creation_job.rb new file mode 100644 index 00000000..97c24f0d --- /dev/null +++ b/app/jobs/import_creation_job.rb @@ -0,0 +1,13 @@ +class ImportCreationJob < ApplicationJob + queue_as :default + + def perform(import_params, current_user) + InsertImport.from_csv_safe( + nonprofit_id: import_params[:nonprofit_id], + user_id: current_user.id, + user_email: current_user.email, + file_uri: import_params[:file_uri], + header_matches: import_params[:header_matches] + ) + end +end diff --git a/app/jobs/mailchimp_supporter_sync_job.rb b/app/jobs/mailchimp_supporter_sync_job.rb new file mode 100644 index 00000000..557c9368 --- /dev/null +++ b/app/jobs/mailchimp_supporter_sync_job.rb @@ -0,0 +1,7 @@ +class MailchimpSupporterSyncJob < ApplicationJob + queue_as :default + + def perform(np_id, supporter_ids, tag_data) + Mailchimp.sync_supporters_to_list_from_tag_joins(np_id, supporter_ids, tag_data) + end +end diff --git a/app/jobs/nonprofit_create_job.rb b/app/jobs/nonprofit_create_job.rb new file mode 100644 index 00000000..10a3c2e2 --- /dev/null +++ b/app/jobs/nonprofit_create_job.rb @@ -0,0 +1,6 @@ +class NonprofitCreateJob < EmailJob + + def perform(nonprofit) + NonprofitMailer.welcome(nonprofit.id).deliver_now + end +end diff --git a/app/jobs/pay_recurring_donation_job.rb b/app/jobs/pay_recurring_donation_job.rb new file mode 100644 index 00000000..7a64e372 --- /dev/null +++ b/app/jobs/pay_recurring_donation_job.rb @@ -0,0 +1,7 @@ +class PayRecurringDonationJob < ApplicationJob + queue_as :rec_don_payments + + def perform(id) + PayRecurringDonation.with_stripe(id) + end +end diff --git a/app/jobs/pay_recurring_donations_job.rb b/app/jobs/pay_recurring_donations_job.rb new file mode 100644 index 00000000..a1b51bdc --- /dev/null +++ b/app/jobs/pay_recurring_donations_job.rb @@ -0,0 +1,9 @@ +class PayRecurringDonationsJob < ApplicationJob + queue_as :default + + def perform(*ids) + ids.each do |id| + PayRecurringDonationJob.perform_later(id) + end + end +end diff --git a/app/jobs/payment_export_create_job.rb b/app/jobs/payment_export_create_job.rb new file mode 100644 index 00000000..eb55e566 --- /dev/null +++ b/app/jobs/payment_export_create_job.rb @@ -0,0 +1,7 @@ +class PaymentExportCreateJob < ApplicationJob + queue_as :default + + def perform(npo_id, params, user_id, export_id) + ExportPayments.run_export(npo_id, params, user_id, export_id) + end +end diff --git a/app/jobs/payment_notification_email_donor_job.rb b/app/jobs/payment_notification_email_donor_job.rb new file mode 100644 index 00000000..5c49b97b --- /dev/null +++ b/app/jobs/payment_notification_email_donor_job.rb @@ -0,0 +1,6 @@ +class PaymentNotificationEmailDonorJob < EmailJob + + def perform(donation, locale) + DonationMailer.donor_payment_notification(donation.id, locale).deliver_now + end +end diff --git a/app/jobs/payment_notification_email_nonprofit_job.rb b/app/jobs/payment_notification_email_nonprofit_job.rb new file mode 100644 index 00000000..0b246b02 --- /dev/null +++ b/app/jobs/payment_notification_email_nonprofit_job.rb @@ -0,0 +1,6 @@ +class PaymentNotificationEmailNonprofitJob < EmailJob + + def perform(donation, user=nil) + DonationMailer.nonprofit_payment_notification(donation.id, user&.id).deliver_now + end +end diff --git a/app/jobs/payout_pending_job.rb b/app/jobs/payout_pending_job.rb new file mode 100644 index 00000000..240cfe7d --- /dev/null +++ b/app/jobs/payout_pending_job.rb @@ -0,0 +1,6 @@ +class PayoutPendingJob < EmailJob + + def perform(payout) + NonprofitMailer.pending_payout_notification(payout.id).deliver_now + end +end diff --git a/app/jobs/recurring_donation_cancelled_job.rb b/app/jobs/recurring_donation_cancelled_job.rb new file mode 100644 index 00000000..e23699c2 --- /dev/null +++ b/app/jobs/recurring_donation_cancelled_job.rb @@ -0,0 +1,6 @@ +class RecurringDonationCancelledJob < EmailJob + + def perform(donation) + DonationMailer.nonprofit_recurring_donation_cancellation(donation.id).deliver_now + end +end diff --git a/app/jobs/recurring_donation_change_amount_donor_email_job.rb b/app/jobs/recurring_donation_change_amount_donor_email_job.rb new file mode 100644 index 00000000..24aada63 --- /dev/null +++ b/app/jobs/recurring_donation_change_amount_donor_email_job.rb @@ -0,0 +1,6 @@ +class RecurringDonationChangeAmountDonorEmailJob < EmailJob + + def perform(recurring_donation, previous_amount) + DonationMailer.donor_recurring_donation_change_amount(recurring_donation.id, previous_amount).deliver_now + end +end diff --git a/app/jobs/recurring_donation_change_amount_job.rb b/app/jobs/recurring_donation_change_amount_job.rb new file mode 100644 index 00000000..e28b4e2c --- /dev/null +++ b/app/jobs/recurring_donation_change_amount_job.rb @@ -0,0 +1,8 @@ +class RecurringDonationChangeAmountJob < ApplicationJob + queue_as :default + + def perform(recurring_donation, previous_amount) + RecurringDonationChangeAmountDonorEmailJob.perform_later(recurring_donation, previous_amount) + RecurringDonationChangeAmountNonprofitEmailJob.perform_later(recurring_donation, previous_amount) + end +end diff --git a/app/jobs/recurring_donation_change_amount_nonprofit_email_job.rb b/app/jobs/recurring_donation_change_amount_nonprofit_email_job.rb new file mode 100644 index 00000000..9039cb9d --- /dev/null +++ b/app/jobs/recurring_donation_change_amount_nonprofit_email_job.rb @@ -0,0 +1,6 @@ +class RecurringDonationChangeAmountNonprofitEmailJob < EmailJob + + def perform(recurring_donation, previous_amount) + DonationMailer.nonprofit_recurring_donation_change_amount(recurring_donation.id, previous_amount).deliver_now + end +end diff --git a/app/jobs/recurring_donations_export_create_job.rb b/app/jobs/recurring_donations_export_create_job.rb new file mode 100644 index 00000000..d66558d2 --- /dev/null +++ b/app/jobs/recurring_donations_export_create_job.rb @@ -0,0 +1,7 @@ +class RecurringDonationsExportCreateJob < ApplicationJob + queue_as :default + + def perform(*args) + ExportRecurringDonations.run_export(*args) + end +end diff --git a/app/jobs/refund_notification_donor_email_job.rb b/app/jobs/refund_notification_donor_email_job.rb new file mode 100644 index 00000000..946eb462 --- /dev/null +++ b/app/jobs/refund_notification_donor_email_job.rb @@ -0,0 +1,6 @@ +class RefundNotificationDonorEmailJob < EmailJob + + def perform(refund) + UserMailer.refund_receipt(refund).deliver_now + end +end diff --git a/app/jobs/refund_notification_job.rb b/app/jobs/refund_notification_job.rb new file mode 100644 index 00000000..0c7632d0 --- /dev/null +++ b/app/jobs/refund_notification_job.rb @@ -0,0 +1,7 @@ +class RefundNotificationJob < EmailJob + + def perform(refund) + RefundNotificationDonorEmailJob.perform_later(refund) + RefundNotificationNonprofitEmailJob.perform_later(refund) + end +end diff --git a/app/jobs/refund_notification_nonprofit_email_job.rb b/app/jobs/refund_notification_nonprofit_email_job.rb new file mode 100644 index 00000000..bef576f1 --- /dev/null +++ b/app/jobs/refund_notification_nonprofit_email_job.rb @@ -0,0 +1,6 @@ +class RefundNotificationNonprofitEmailJob < EmailJob + + def perform(refund) + NonprofitMailer.refund_notification(refund.id).deliver_now + end +end diff --git a/app/jobs/role_added_job.rb b/app/jobs/role_added_job.rb new file mode 100644 index 00000000..1706e35a --- /dev/null +++ b/app/jobs/role_added_job.rb @@ -0,0 +1,6 @@ +class RoleAddedJob < EmailJob + + def perform(role) + NonprofitAdminMailer.existing_invite(role).deliver_now + end +end diff --git a/app/jobs/stripe_account_create_job.rb b/app/jobs/stripe_account_create_job.rb new file mode 100644 index 00000000..a1e82be8 --- /dev/null +++ b/app/jobs/stripe_account_create_job.rb @@ -0,0 +1,6 @@ +class StripeAccountCreateJob < EmailJob + + def perform(nonprofit) + NonprofitMailer.setup_verification(nonprofit.id).deliver_now + end +end diff --git a/app/jobs/supporter_fundraiser_create_job.rb b/app/jobs/supporter_fundraiser_create_job.rb new file mode 100644 index 00000000..262f277f --- /dev/null +++ b/app/jobs/supporter_fundraiser_create_job.rb @@ -0,0 +1,7 @@ +class SupporterFundraiserCreateJob < ApplicationJob + queue_as :default + + def perform(fundraiser) + NonprofitAdminMailer.supporter_fundraiser(fundraiser).deliver_now unless QueryRoles.is_nonprofit_user?(fundraiser.profile.user.id, fundraiser.nonprofit.id) + end +end diff --git a/app/jobs/supporter_notes_export_create_job.rb b/app/jobs/supporter_notes_export_create_job.rb new file mode 100644 index 00000000..c4594955 --- /dev/null +++ b/app/jobs/supporter_notes_export_create_job.rb @@ -0,0 +1,7 @@ +class SupporterNotesExportCreateJob < EmailJob + queue_as :default + + def perform(npo_id, params, user_id, export_id) + ExportSupporterNotes.run_export(npo_id, params, user_id, export_id) + end +end diff --git a/app/jobs/supporters_export_create_job.rb b/app/jobs/supporters_export_create_job.rb new file mode 100644 index 00000000..8ad1f1d5 --- /dev/null +++ b/app/jobs/supporters_export_create_job.rb @@ -0,0 +1,6 @@ +class SupportersExportCreateJob < EmailJob + + def perform(*args) + ExportSupporters.run_export(*args) + end +end diff --git a/app/jobs/user_invite_create_job.rb b/app/jobs/user_invite_create_job.rb new file mode 100644 index 00000000..243c4e62 --- /dev/null +++ b/app/jobs/user_invite_create_job.rb @@ -0,0 +1,6 @@ +class UserInviteCreateJob < EmailJob + + def perform(role, raw_token) + NonprofitAdminMailer.new_invite(role, raw_token).deliver_now + end +end diff --git a/app/jobs/verification_completed_job.rb b/app/jobs/verification_completed_job.rb new file mode 100644 index 00000000..7e939161 --- /dev/null +++ b/app/jobs/verification_completed_job.rb @@ -0,0 +1,6 @@ +class VerificationCompletedJob < EmailJob + + def perform(nonprofit) + NonprofitMailer.successful_verification_notice(nonprofit).deliver_now + end +end diff --git a/app/jobs/verification_failed_job.rb b/app/jobs/verification_failed_job.rb new file mode 100644 index 00000000..708af204 --- /dev/null +++ b/app/jobs/verification_failed_job.rb @@ -0,0 +1,6 @@ +class VerificationFailedJob < EmailJob + + def perform(nonprofit) + NonprofitMailer.failed_verification_notice(onprofit).deliver_now + end +end diff --git a/app/jobs/we_move_execute_for_donations_job.rb b/app/jobs/we_move_execute_for_donations_job.rb new file mode 100644 index 00000000..ce7cba77 --- /dev/null +++ b/app/jobs/we_move_execute_for_donations_job.rb @@ -0,0 +1,7 @@ +class WeMoveExecuteForDonationsJob < ApplicationJob + queue_as :default + + def perform(donation) + QueueDonations.execute_for_donation(donation.id) + end +end diff --git a/app/listeners/application_listener.rb b/app/listeners/application_listener.rb new file mode 100644 index 00000000..90820431 --- /dev/null +++ b/app/listeners/application_listener.rb @@ -0,0 +1,5 @@ +class ApplicationListener + def name + self.class.name + end +end diff --git a/app/listeners/campaign_listener.rb b/app/listeners/campaign_listener.rb new file mode 100644 index 00000000..f8129080 --- /dev/null +++ b/app/listeners/campaign_listener.rb @@ -0,0 +1,11 @@ +class CampaignListener < ApplicationListener + def campaign_create(campaign) + if campaign.child_campaign? + CampaignCreationFederatedEmailJob.perform_later(campaign) + else + CampaignCreationEmailFollowupJob.perform_later(campaign) + end + + SupporterFundraiserCreateJob.perform_later(campaign) + end +end diff --git a/app/listeners/credit_card_payment_listener.rb b/app/listeners/credit_card_payment_listener.rb new file mode 100644 index 00000000..61129c08 --- /dev/null +++ b/app/listeners/credit_card_payment_listener.rb @@ -0,0 +1,33 @@ +class CreditCardPaymentListener < ApplicationListener + def donation_create(donation, locale, user=nil) + if donation.payment_provider == :credit_card + PaymentNotificationEmailDonorJob.perform_later donation, locale + PaymentNotificationEmailNonprofitJob.perform_later donation, user + end + end + + def recurring_donation_create(donation, locale, user=nil) + if donation.payment_provider == :credit_card + PaymentNotificationEmailDonorJob.perform_later donation, locale + PaymentNotificationEmailNonprofitJob.perform_later donation, user + end + end + + def refund_create(refund) + RefundNotificationJob.perform_later refund + end + + def recurring_donation_payment_succeeded(donation, locale, user=nil) + if donation.payment_provider == :credit_card + PaymentNotificationEmailDonorJob.perform_later donation, locale + PaymentNotificationEmailNonprofitJob.perform_later donation, user + end + end + + def recurring_donation_payment_failed(donation, locale) + FailedRecurringDonationPaymentDonorEmailJob.perform_later(donation) + if (donation.recurring_donation.n_failures >= 3) + FailedRecurringDonationPaymentNonprofitEmailJob.perform_later(donation) + end + end +end diff --git a/app/listeners/nonprofit_mailer_listener.rb b/app/listeners/nonprofit_mailer_listener.rb new file mode 100644 index 00000000..3cc2edcf --- /dev/null +++ b/app/listeners/nonprofit_mailer_listener.rb @@ -0,0 +1,5 @@ +class NonprofitMailerListener < ApplicationListener + def nonprofit_create(nonprofit) + + end +end \ No newline at end of file diff --git a/app/listeners/sepa_payment_listener.rb b/app/listeners/sepa_payment_listener.rb new file mode 100644 index 00000000..67a00ab6 --- /dev/null +++ b/app/listeners/sepa_payment_listener.rb @@ -0,0 +1,8 @@ +class SepaPaymentListener < ApplicationListener + def donation_create(donation) + if donation.payment_provider == :sepa + DirectDebitCreateNotifyNonprofitJob.perform_later(donation.id) + DirectDebitCreateNotifyDonorJob.perform_later donation.id, locale + end + end +end diff --git a/app/listeners/ticket_listener.rb b/app/listeners/ticket_listener.rb new file mode 100644 index 00000000..6cac8f0a --- /dev/null +++ b/app/listeners/ticket_listener.rb @@ -0,0 +1,6 @@ +class TicketListener < ApplicationListener + def ticket_create(tickets, charge, user=nil) + TicketMailer.followup(tickets.map{|i| i.id}, charge && charge.id).deliver_later + TicketMailer.receipt_admin(tickets.map{|i| i.id}, user && user.id).deliver_later + end +end \ No newline at end of file diff --git a/app/listeners/wemove_listener.rb b/app/listeners/wemove_listener.rb new file mode 100644 index 00000000..cbc5d3f3 --- /dev/null +++ b/app/listeners/wemove_listener.rb @@ -0,0 +1,13 @@ +class WemoveListener < ApplicationListener + def donation_create(donation) + WeMoveExecuteForDonationsJob.perform_later(donation) + end + + def offsite_donation_create(donation) + WeMoveExecuteForDonationsJob.perform_later(donation) + end + + def recurring_donation_create(donation) + WeMoveExecuteForDonationsJob.perform_later(donation) + end +end diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 11e5250b..1699f1b3 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class AdminMailer < BaseMailer - # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb index 95021647..605976a3 100644 --- a/app/mailers/base_mailer.rb +++ b/app/mailers/base_mailer.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class BaseMailer < ActionMailer::Base include Roadie::Rails::Automatic include Devise::Controllers::UrlHelpers add_template_helper(ApplicationHelper) - default :from => Settings.mailer.default_from + default from: Settings.mailer.default_from layout 'email' end diff --git a/app/mailers/billing_subscription_mailer.rb b/app/mailers/billing_subscription_mailer.rb index 93240743..971f4dbd 100644 --- a/app/mailers/billing_subscription_mailer.rb +++ b/app/mailers/billing_subscription_mailer.rb @@ -1,13 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class BillingSubscriptionMailer < BaseMailer - def failed_notice(np_id) @nonprofit = Nonprofit.find(np_id) @billing_subscription = @nonprofit.billing_subscription @card = @nonprofit.active_card @billing_plan = @billing_subscription.billing_plan - @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) - mail(to: @emails, subject: "Action Needed, Please Update Your #{Settings.general.name} Account") + @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) + mail(to: @emails, subject: "Action Needed, Please Update Your #{Settings.general.name} Account") end - end diff --git a/app/mailers/campaign_mailer.rb b/app/mailers/campaign_mailer.rb index 6919bef2..72fcb0ae 100644 --- a/app/mailers/campaign_mailer.rb +++ b/app/mailers/campaign_mailer.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CampaignMailer < BaseMailer + def creation_followup(campaign) + @creator_profile = campaign.profile + @campaign = campaign + mail(to: @creator_profile.user.email, subject: "Get your new campaign rolling! (via #{Settings.general.name})") + end - def creation_followup(campaign) - @creator_profile = campaign.profile - @campaign = campaign - mail(:to => @creator_profile.user.email, :subject => "Get your new campaign rolling! (via #{Settings.general.name})") - end - - def federated_creation_followup(campaign) - @creator_profile = campaign.profile - @campaign = campaign - mail(:to => @creator_profile.user.email, :subject => "Get your new campaign rolling! (via #{Settings.general.name})") - end + def federated_creation_followup(campaign) + @creator_profile = campaign.profile + @campaign = campaign + mail(to: @creator_profile.user.email, subject: "Get your new campaign rolling! (via #{Settings.general.name})") + end end diff --git a/app/mailers/donation_mailer.rb b/app/mailers/donation_mailer.rb index 35daa7f2..d86a54bf 100644 --- a/app/mailers/donation_mailer.rb +++ b/app/mailers/donation_mailer.rb @@ -1,36 +1,38 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class DonationMailer < BaseMailer - - # Used for both one-time and recurring donations + # Used for both one-time and recurring donations # can pass in array of admin user_ids to send to only some -- if falsey/empty, will send to all - def donor_payment_notification(donation_id, locale=I18n.locale) - @donation = Donation.find(donation_id) - @nonprofit = @donation.nonprofit - if @donation.campaign && ActionView::Base.full_sanitizer.sanitize(@donation.campaign.receipt_message).present? + def donor_payment_notification(donation_id, locale = I18n.locale) + @donation = Donation.find(donation_id) + @nonprofit = @donation.nonprofit + if @donation.campaign && ActionView::Base.full_sanitizer.sanitize(@donation.campaign.receipt_message).present? @thank_you_note = @donation.campaign.receipt_message else - @thank_you_note = Format::Interpolate.with_hash(@nonprofit.thank_you_note, {'NAME' => @donation.supporter.name}) + @thank_you_note = Format::Interpolate.with_hash(@nonprofit.thank_you_note, 'NAME' => @donation.supporter.name) end @charge = @donation.charges.last - reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email + reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email from = Format::Name.email_from_np(@nonprofit.name) I18n.with_locale(locale) do - mail( - to: @donation.supporter.email, - from: from, - reply_to: reply_to, - subject: I18n.t('mailer.donations.donor_direct_debit_notification.subject', nonprofit_name: @nonprofit.name)) + mail( + to: @donation.supporter.email, + from: from, + reply_to: reply_to, + subject: I18n.t('mailer.donations.donor_direct_debit_notification.subject', nonprofit_name: @nonprofit.name) + ) end end - def donor_direct_debit_notification(donation_id, locale=I18n.locale) + def donor_direct_debit_notification(donation_id, locale = I18n.locale) @donation = Donation.find(donation_id) @nonprofit = @donation.nonprofit - if @donation.campaign && ActionView::Base.full_sanitizer.sanitize(@donation.campaign.receipt_message).present? + if @donation.campaign && ActionView::Base.full_sanitizer.sanitize(@donation.campaign.receipt_message).present? @thank_you_note = @donation.campaign.receipt_message else - @thank_you_note = Format::Interpolate.with_hash(@nonprofit.thank_you_note, {'NAME' => @donation.supporter.name}) + @thank_you_note = Format::Interpolate.with_hash(@nonprofit.thank_you_note, 'NAME' => @donation.supporter.name) end reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email @@ -45,87 +47,86 @@ class DonationMailer < BaseMailer end end - # Used for both one-time and recurring donations - def nonprofit_payment_notification(donation_id, user_id=nil) - @donation = Donation.find(donation_id) - @charge = @donation.charges.last - @nonprofit = @donation.nonprofit + # Used for both one-time and recurring donations + def nonprofit_payment_notification(donation_id, user_id = nil) + @donation = Donation.find(donation_id) + @charge = @donation.charges.last + @nonprofit = @donation.nonprofit @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, @donation.campaign ? 'notify_campaigns' : 'notify_payments') if user_id em = User.find(user_id).email # return unless @emails.include?(em) @emails = [em] end - mail(to: @emails, subject: "Donation receipt for #{@donation.supporter.name}") - end + mail(to: @emails, subject: "Donation receipt for #{@donation.supporter.name}") + end def nonprofit_failed_recurring_donation(donation_id) - @donation = Donation.find(donation_id) - @nonprofit = @donation.nonprofit - @charge = @donation.charges.last + @donation = Donation.find(donation_id) + @nonprofit = @donation.nonprofit + @charge = @donation.charges.last @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, @donation.campaign ? 'notify_campaigns' : 'notify_payments') - mail(to: @emails, subject: "Recurring donation payment failure for #{@donation.supporter.name || @donation.supporter.email}") + mail(to: @emails, subject: "Recurring donation payment failure for #{@donation.supporter.name || @donation.supporter.email}") end def donor_failed_recurring_donation(donation_id) - @donation = Donation.find(donation_id) - @nonprofit = @donation.nonprofit - @charge = @donation.charges.last - reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email + @donation = Donation.find(donation_id) + @nonprofit = @donation.nonprofit + @charge = @donation.charges.last + reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email from = Format::Name.email_from_np(@nonprofit.name) - mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Donation payment failure for #{@nonprofit.name}") + mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Donation payment failure for #{@nonprofit.name}") end def nonprofit_recurring_donation_cancellation(donation_id) - @donation = Donation.find(donation_id) - @nonprofit = @donation.nonprofit - @charge = @donation.charges.last + @donation = Donation.find(donation_id) + @nonprofit = @donation.nonprofit + @charge = @donation.charges.last @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, @donation.campaign ? 'notify_campaigns' : 'notify_payments') - mail(to: @emails, subject: "Recurring donation cancelled for #{@donation.supporter.name || @donation.supporter.email}") - end + mail(to: @emails, subject: "Recurring donation cancelled for #{@donation.supporter.name || @donation.supporter.email}") + end - def nonprofit_recurring_donation_change_amount(donation_id, previous_amount=nil) - @donation = RecurringDonation.find(donation_id).donation - @nonprofit = @donation.nonprofit - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') - @previous_amount = previous_amount - mail(to: @emails, subject:"Recurring donation amount changed for #{@donation.supporter.name || @donation.supporter.email}") - end + def nonprofit_recurring_donation_change_amount(donation_id, previous_amount = nil) + @donation = RecurringDonation.find(donation_id).donation + @nonprofit = @donation.nonprofit + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') + @previous_amount = previous_amount + mail(to: @emails, subject: "Recurring donation amount changed for #{@donation.supporter.name || @donation.supporter.email}") + end - def donor_recurring_donation_change_amount(donation_id, previous_amount=nil) - @donation = RecurringDonation.find(donation_id).donation - @nonprofit = @donation.nonprofit - reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email - if @nonprofit.miscellaneous_np_info && ActionView::Base.full_sanitizer.sanitize(@nonprofit.miscellaneous_np_info.change_amount_message).present? - @thank_you_note = @nonprofit.miscellaneous_np_info.change_amount_message - else - @thank_you_note = nil - end - from = Format::Name.email_from_np(@nonprofit.name) - @previous_amount = previous_amount - mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Recurring donation amount changed for #{@nonprofit.name}") - end + def donor_recurring_donation_change_amount(donation_id, previous_amount = nil) + @donation = RecurringDonation.find(donation_id).donation + @nonprofit = @donation.nonprofit + reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email + if @nonprofit.miscellaneous_np_info && ActionView::Base.full_sanitizer.sanitize(@nonprofit.miscellaneous_np_info.change_amount_message).present? + @thank_you_note = @nonprofit.miscellaneous_np_info.change_amount_message + else + @thank_you_note = nil + end + from = Format::Name.email_from_np(@nonprofit.name) + @previous_amount = previous_amount + mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Recurring donation amount changed for #{@nonprofit.name}") + end - def nonprofit_recurring_donation_change_amount(donation_id, previous_amount=nil) - @donation = RecurringDonation.find(donation_id).donation - @nonprofit = @donation.nonprofit - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') - @previous_amount = previous_amount - mail(to: @emails, subject:"Recurring donation amount changed for #{@donation.supporter.name || @donation.supporter.email}") - end - - def donor_recurring_donation_change_amount(donation_id, previous_amount=nil) - @donation = RecurringDonation.find(donation_id).donation - @nonprofit = @donation.nonprofit - reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email - if @nonprofit.miscellaneous_np_info && ActionView::Base.full_sanitizer.sanitize(@nonprofit.miscellaneous_np_info.change_amount_message).present? - @thank_you_note = @nonprofit.miscellaneous_np_info.change_amount_message - else - @thank_you_note = nil - end - from = Format::Name.email_from_np(@nonprofit.name) - @previous_amount = previous_amount - mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Recurring donation amount changed for #{@nonprofit.name}") - end + def nonprofit_recurring_donation_change_amount(donation_id, previous_amount = nil) + @donation = RecurringDonation.find(donation_id).donation + @nonprofit = @donation.nonprofit + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') + @previous_amount = previous_amount + mail(to: @emails, subject: "Recurring donation amount changed for #{@donation.supporter.name || @donation.supporter.email}") + end + def donor_recurring_donation_change_amount(donation_id, previous_amount = nil) + @donation = RecurringDonation.find(donation_id).donation + @nonprofit = @donation.nonprofit + reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email + if @nonprofit.miscellaneous_np_info && ActionView::Base.full_sanitizer.sanitize(@nonprofit.miscellaneous_np_info.change_amount_message).present? + @thank_you_note = @nonprofit.miscellaneous_np_info.change_amount_message + else + @thank_you_note = nil + end + from = Format::Name.email_from_np(@nonprofit.name) + @previous_amount = previous_amount + mail(to: @donation.supporter.email, from: from, reply_to: reply_to, subject: "Recurring donation amount changed for #{@nonprofit.name}") + end end diff --git a/app/mailers/event_mailer.rb b/app/mailers/event_mailer.rb index 49a32b63..47f5ba2f 100644 --- a/app/mailers/event_mailer.rb +++ b/app/mailers/event_mailer.rb @@ -1,14 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EventMailer < BaseMailer + helper :application - helper :application - - include Devise::Controllers::UrlHelpers - - def creation_followup(event) - @creator_profile = event.profile - @event = event - mail(:to => @creator_profile.user.email, :subject => "Get your new event rolling on #{Settings.general.name}!") - end + include Devise::Controllers::UrlHelpers + def creation_followup(event) + @creator_profile = event.profile + @event = event + mail(to: @creator_profile.user.email, subject: "Get your new event rolling on #{Settings.general.name}!") + end end diff --git a/app/mailers/export_mailer.rb b/app/mailers/export_mailer.rb index 2df3d36f..477eabf1 100644 --- a/app/mailers/export_mailer.rb +++ b/app/mailers/export_mailer.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ExportMailer < BaseMailer - # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # @@ -18,7 +19,6 @@ class ExportMailer < BaseMailer mail(to: @export.user.email, subject: 'Your payment export has failed') end - def export_recurring_donations_completed_notification(export) @export = export diff --git a/app/mailers/generic_mailer.rb b/app/mailers/generic_mailer.rb index 966c5f9e..c540e55a 100644 --- a/app/mailers/generic_mailer.rb +++ b/app/mailers/generic_mailer.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class GenericMailer < BaseMailer - - def generic_mail(from_email, from_name, message, subject, to_email, to_name) + def generic_mail(from_email, from_name, message, subject, to_email, _to_name) @from_email = from_email @from_name = from_name @message = message - mail(:to => to_email, :from => "#{from_name} <#{Settings.mailer.email}>", :reply_to => from_email, :subject => "#{subject}") + mail(to: to_email, from: "#{from_name} <#{Settings.mailer.email}>", reply_to: from_email, subject: subject.to_s) end # For sending a system notice to super admins @@ -16,5 +17,4 @@ class GenericMailer < BaseMailer emails = QueryUsers.super_admin_emails mail(to: emails, from: "#{@from_name} <#{@from_email}>", reply_to: @from_email, subject: options[:subject], template_name: 'generic_mail') end - end diff --git a/app/mailers/import_mailer.rb b/app/mailers/import_mailer.rb index 92a126f1..3adb30d0 100644 --- a/app/mailers/import_mailer.rb +++ b/app/mailers/import_mailer.rb @@ -1,10 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ImportMailer < BaseMailer - - def import_completed_notification(import_id) - @import = Import.find(import_id) - @nonprofit = @import.nonprofit - mail(to: @import.user.email, subject: "Your import is complete!") - end - + def import_completed_notification(import_id) + @import = Import.find(import_id) + @nonprofit = @import.nonprofit + mail(to: @import.user.email, subject: 'Your import is complete!') + end end diff --git a/app/mailers/nonprofit_admin_mailer.rb b/app/mailers/nonprofit_admin_mailer.rb index f7cad1bb..b0198d38 100644 --- a/app/mailers/nonprofit_admin_mailer.rb +++ b/app/mailers/nonprofit_admin_mailer.rb @@ -1,28 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class NonprofitAdminMailer < BaseMailer + def new_invite(role, raw_token) + @user = role.user + @title_with_article = Format::Indefinitize.with_article(role.name.to_s.titleize) + @nonprofit = role.host + @token = raw_token + mail(to: @user.email, subject: "You're now #{@title_with_article} of #{@nonprofit.name} on #{Settings.general.name}. Let's set your password.") + end - def new_invite(role, raw_token) - @user = role.user - @title_with_article = Format::Indefinitize.with_article(role.name.to_s.titleize) - @nonprofit = role.host - @token = raw_token - mail(:to => @user.email, :subject => "You're now #{@title_with_article} of #{@nonprofit.name} on #{Settings.general.name}. Let's set your password.") - end + def existing_invite(role) + @user = role.user + @title_with_article = Format::Indefinitize.with_article(role.name.to_s.titleize) + @nonprofit = role.host + mail(to: @user.email, subject: "You're now #{@title_with_article} of #{@nonprofit.name} on #{Settings.general.name}.") + end - def existing_invite(role) - @user = role.user - @title_with_article = Format::Indefinitize.with_article(role.name.to_s.titleize) - @nonprofit = role.host - mail(:to => @user.email, :subject => "You're now #{@title_with_article} of #{@nonprofit.name} on #{Settings.general.name}.") - end - - def supporter_fundraiser(event_or_campaign) - @fundraiser = event_or_campaign - @kind = event_or_campaign.class.name.downcase || 'event' - @nonprofit = event_or_campaign.nonprofit - @profile = event_or_campaign.profile - recipients = @nonprofit.nonprofit_personnel_emails - mail(to: recipients, subject: "A Supporter has created #{Format::Indefinitize.with_article(@kind.capitalize)} for your Nonprofit!") - end + def supporter_fundraiser(event_or_campaign) + @fundraiser = event_or_campaign + @kind = event_or_campaign.class.name.downcase || 'event' + @nonprofit = event_or_campaign.nonprofit + @profile = event_or_campaign.profile + recipients = @nonprofit.nonprofit_personnel_emails + mail(to: recipients, subject: "A Supporter has created #{Format::Indefinitize.with_article(@kind.capitalize)} for your Nonprofit!") + end end - diff --git a/app/mailers/nonprofit_mailer.rb b/app/mailers/nonprofit_mailer.rb index e57bd62d..445c7b9e 100644 --- a/app/mailers/nonprofit_mailer.rb +++ b/app/mailers/nonprofit_mailer.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class NonprofitMailer < BaseMailer - def failed_verification_notice(np) @nonprofit = np @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') @@ -13,92 +14,93 @@ class NonprofitMailer < BaseMailer mail(to: @emails, subject: "Verification successful on #{Settings.general.name}!") end - def refund_notification(refund_id) - @refund = Refund.find(refund_id) - @charge = @refund.charge - @nonprofit = @refund.payment.nonprofit + def refund_notification(refund_id) + @refund = Refund.find(refund_id) + @charge = @refund.charge + @nonprofit = @refund.payment.nonprofit @supporter = @refund.payment.supporter - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payments') - mail(to: @emails, subject: "A new refund has been made for $#{Format::Currency.cents_to_dollars(@refund.amount)}") - end + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payments') + mail(to: @emails, subject: "A new refund has been made for $#{Format::Currency.cents_to_dollars(@refund.amount)}") + end - def new_bank_account_notification(ba) - @nonprofit = ba.nonprofit - @bank_account = ba - @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) - mail(to: @emails, subject: "We need to confirm the new bank account") - end + def new_bank_account_notification(ba) + @nonprofit = ba.nonprofit + @bank_account = ba + @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) + mail(to: @emails, subject: 'We need to confirm the new bank account') + end - def pending_payout_notification(payout_id) - @payout = Payout.find(payout_id) - @nonprofit = @payout.nonprofit - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') - mail(to: @emails, subject: "Payout of available balance now pending") - end + def pending_payout_notification(payout_id) + @payout = Payout.find(payout_id) + @nonprofit = @payout.nonprofit + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') + mail(to: @emails, subject: 'Payout of available balance now pending') + end - def successful_payout_notification(payout) - @nonprofit = payout.nonprofit - @payout = payout - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') - mail(to: @emails, subject: "Payout of available balance succeeded") - end + def successful_payout_notification(payout) + @nonprofit = payout.nonprofit + @payout = payout + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') + mail(to: @emails, subject: 'Payout of available balance succeeded') + end - def failed_payout_notification(payout) - @nonprofit = payout.nonprofit - @payout = payout - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') - mail(to: @emails, subject: "Payout could not be completed") - end + def failed_payout_notification(payout) + @nonprofit = payout.nonprofit + @payout = payout + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_payouts') + mail(to: @emails, subject: 'Payout could not be completed') + end - def failed_recurring_donation(recurring_donation) - @recurring_donation = recurring_donation - @nonprofit = recurring_donation.nonprofit - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') - mail(to: @emails, subject: "A recurring donation from one of your supporters had a payment failure.") - end + def failed_recurring_donation(recurring_donation) + @recurring_donation = recurring_donation + @nonprofit = recurring_donation.nonprofit + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') + mail(to: @emails, subject: 'A recurring donation from one of your supporters had a payment failure.') + end - def cancelled_recurring_donation(recurring_donation) - @recurring_donation = recurring_donation - @nonprofit = recurring_donation.nonprofit - @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') - mail(to: @emails, subject: "A recurring donation from one of your supporters was cancelled.") - end + def cancelled_recurring_donation(recurring_donation) + @recurring_donation = recurring_donation + @nonprofit = recurring_donation.nonprofit + @emails = QueryUsers.nonprofit_user_emails(@nonprofit.id, 'notify_recurring_donations') + mail(to: @emails, subject: 'A recurring donation from one of your supporters was cancelled.') + end - def verified_notification(nonprofit) - @nonprofit = nonprofit - @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) - mail(to: @emails, subject: "Your nonprofit has been verified!") - end + def verified_notification(nonprofit) + @nonprofit = nonprofit + @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id) + mail(to: @emails, subject: 'Your nonprofit has been verified!') + end - def button_code(nonprofit, to_email, to_name, from_email, message, code) - @nonprofit = nonprofit - @to_email = to_email - @to_name = to_name - @from = from_email - @message = message - @code = code + def button_code(nonprofit, to_email, to_name, from_email, message, code) + @nonprofit = nonprofit + @to_email = to_email + @to_name = to_name + @from = from_email + @message = message + @code = code from = Format::Name.email_from_np(@nonprofit.name) - mail(to: to_email, from: from, reply_to: from_email, subject: "Please include this donate button code on the website") - end + mail(to: to_email, from: from, reply_to: from_email, subject: 'Please include this donate button code on the website') + end def invoice_payment_notification(nonprofit_id, payment) @nonprofit = Nonprofit.find(nonprofit_id) @payment = payment @emails = QueryUsers.all_nonprofit_user_emails(@nonprofit.id, [:nonprofit_admin]) @month_name = Date::MONTHNAMES[payment.date.month] - mail(to: @emails, subject: "#{Settings.general.name} Subscription Receipt for #{@month_name}") + mail(to: @emails, subject: "#{Settings.general.name} Subscription Receipt for #{@month_name}") end - # pass in all of: - # {is_unsubscribed_from_emails, supporter_email, message, email_unsubscribe_uuid, nonprofit_id, from_email, subject} - def supporter_message(args) - return if args[:is_unsubscribed_from_emails] || args[:supporter_email].blank? - @message = args[:message] - @uuid = args[:email_unsubscribe_uuid] - @nonprofit = Nonprofit.find args[:nonprofit_id] + # pass in all of: + # {is_unsubscribed_from_emails, supporter_email, message, email_unsubscribe_uuid, nonprofit_id, from_email, subject} + def supporter_message(args) + return if args[:is_unsubscribed_from_emails] || args[:supporter_email].blank? + + @message = args[:message] + @uuid = args[:email_unsubscribe_uuid] + @nonprofit = Nonprofit.find args[:nonprofit_id] from = Format::Name.email_from_np(@nonprofit.name) - mail(to: args[:supporter_email], reply_to: args[:from_email], from: from, subject: args[:subject]) - end + mail(to: args[:supporter_email], reply_to: args[:from_email], from: from, subject: args[:subject]) + end def setup_verification(np_id) @nonprofit = Nonprofit.find(np_id) @@ -113,6 +115,4 @@ class NonprofitMailer < BaseMailer @emails = QueryUsers.all_nonprofit_user_emails(np_id, [:nonprofit_admin]) mail(to: @emails, reply_to: 'support@commitchange.com', from: "#{Settings.general.name} Support", subject: "A hearty welcome from the #{Settings.general.name} team") end - end - diff --git a/app/mailers/payment_mailer.rb b/app/mailers/payment_mailer.rb index 28b7d52d..398477bf 100644 --- a/app/mailers/payment_mailer.rb +++ b/app/mailers/payment_mailer.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class PaymentMailer < BaseMailer - # Send a donation receipt to a single admin # or a ticket receipt def resend_admin_receipt(payment_id, user_id) payment = Payment.find(payment_id) if payment.kind == 'Donation' || payment.kind == 'RecurringDonation' - return Delayed::Job.enqueue JobTypes::NonprofitPaymentNotificationJob.new(payment.donation.id, user_id) + PaymentNotificationEmailNonprofitJob.perform_later(payment.donation, User.find(user_id)) elsif payment.kind == 'Ticket' - return TicketMailer.receipt_admin(payment.donation.id, user_id).deliver + return TicketMailer.receipt_admin(payment.donation.id, user_id).deliver_later end end @@ -17,10 +18,9 @@ class PaymentMailer < BaseMailer def resend_donor_receipt(payment_id) payment = Payment.find(payment_id) if payment.kind == 'Donation' || payment.kind == 'RecurringDonation' - Delayed::Job.enqueue JobTypes::DonorPaymentNotificationJob.new(payment.donation.id) + PaymentNotificationEmailDonorJob.perform_later payment.donation elsif payment.kind == 'Ticket' - return TicketMailer.followup(payment.tickets.pluck(:id), payment.charge.id).deliver + return TicketMailer.followup(payment.tickets.pluck(:id), payment.charge).deliver_later end end - end diff --git a/app/mailers/recurring_donation_mailer.rb b/app/mailers/recurring_donation_mailer.rb index d1a4a649..ce64e58c 100644 --- a/app/mailers/recurring_donation_mailer.rb +++ b/app/mailers/recurring_donation_mailer.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class RecurringDonationMailer < BaseMailer + def send_cancellation_notices(recurring_donation) + UserMailer.recurring_donation_cancelled(recurring_donation).deliver + NonprofitMailer.cancelled_recurring_donation(recurring_donation).deliver + recurring_donation + end - def send_cancellation_notices(recurring_donation) - UserMailer.recurring_donation_cancelled(recurring_donation).deliver - NonprofitMailer.cancelled_recurring_donation(recurring_donation).deliver - return recurring_donation - end - - def send_failure_notifications(recurring_donation) - UserMailer.recurring_donation_failure(recurring_donation).deliver - NonprofitMailer.failed_recurring_donation(recurring_donation).deliver - end + def send_failure_notifications(recurring_donation) + UserMailer.recurring_donation_failure(recurring_donation).deliver + NonprofitMailer.failed_recurring_donation(recurring_donation).deliver + end end diff --git a/app/mailers/testing.rb b/app/mailers/testing.rb index a82ab782..9d064245 100644 --- a/app/mailers/testing.rb +++ b/app/mailers/testing.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Testing < ActionMailer::Base - default from: "from@example.com" + default from: 'from@example.com' end diff --git a/app/mailers/ticket_mailer.rb b/app/mailers/ticket_mailer.rb index 623646d6..7f39b499 100644 --- a/app/mailers/ticket_mailer.rb +++ b/app/mailers/ticket_mailer.rb @@ -1,22 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class TicketMailer < BaseMailer - helper :application # Pass in ticket_ids, event_id, and supporter - def followup(ticket_ids, charge_id=nil) + def followup(ticket_ids, charge_id = nil) @charge = charge_id ? Charge.find(charge_id) : nil - @tickets = Ticket.where("id IN(?)", ticket_ids) + @tickets = Ticket.where('id IN(?)', ticket_ids) @event = @tickets.last.event @supporter = @tickets.last.supporter @nonprofit = @supporter.nonprofit from = Format::Name.email_from_np(@nonprofit.name) reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email - mail(from: from, to: @supporter.email, reply_to: reply_to, subject: "Your tickets#{@charge ? ' and receipt ' : ' '}for: #{@event.name}") + mail(from: from, to: @supporter.email, reply_to: reply_to, subject: "Your tickets#{@charge ? ' and receipt ' : ' '}for: #{@event.name}") end - def receipt_admin(ticket_ids, user_id=nil) - @tickets = Ticket.where("id IN (?)", ticket_ids) + def receipt_admin(ticket_ids, user_id = nil) + @tickets = Ticket.where('id IN (?)', ticket_ids) @charge = @tickets.last.charge @supporter = @tickets.last.supporter @event = @tickets.last.event @@ -25,9 +26,9 @@ class TicketMailer < BaseMailer if user_id em = User.find(user_id).email return unless recipients.include?(em) + recipients = [em] end mail(to: recipients, subject: "Ticket redeemed for #{@event.name} - #{@supporter.name}") end - end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index f216cc0e..b9a01478 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,26 +1,26 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class UserMailer < BaseMailer - - def refund_receipt(refund_id) - @refund = Refund.find(refund_id) + def refund_receipt(refund_id) + @refund = Refund.find(refund_id) @nonprofit = @refund.payment.nonprofit - @charge = @refund.charge + @charge = @refund.charge @supporter = @refund.payment.supporter - reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email + reply_to = @nonprofit.email.blank? ? @nonprofit.users.first.email : @nonprofit.email from = Format::Name.email_from_np(@nonprofit.name) - mail(to: @supporter.email, from: from, reply_to: reply_to, subject: "Your refund receipt for #{@nonprofit.name}") - end + mail(to: @supporter.email, from: from, reply_to: reply_to, subject: "Your refund receipt for #{@nonprofit.name}") + end - def recurring_donation_failure(recurring_donation) - @recurring_donation = recurring_donation - mail(:to => @recurring_donation.email, - :subject => ("We couldn't process your recurring donation towards #{@recurring_donation.nonprofit.name}.")) - end - - def recurring_donation_cancelled(recurring_donation) - @recurring_donation = recurring_donation - mail(:to => @recurring_donation.email, - :subject => ("Your recurring donation towards #{@recurring_donation.nonprofit.name} was successfully cancelled.")) - end + def recurring_donation_failure(recurring_donation) + @recurring_donation = recurring_donation + mail(to: @recurring_donation.email, + subject: "We couldn't process your recurring donation towards #{@recurring_donation.nonprofit.name}.") + end + def recurring_donation_cancelled(recurring_donation) + @recurring_donation = recurring_donation + mail(to: @recurring_donation.email, + subject: "Your recurring donation towards #{@recurring_donation.nonprofit.name} was successfully cancelled.") + end end diff --git a/app/models/activity.rb b/app/models/activity.rb index c98f0888..8dd456c0 100644 --- a/app/models/activity.rb +++ b/app/models/activity.rb @@ -1,5 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Activity < ActiveRecord::Base - +class Activity < ApplicationRecord end - diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 00000000..9f8f3f0d --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/bank_account.rb b/app/models/bank_account.rb index 92265f4f..82291b55 100644 --- a/app/models/bank_account.rb +++ b/app/models/bank_account.rb @@ -1,41 +1,38 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class BankAccount < ActiveRecord::Base +class BankAccount < ApplicationRecord + # :name, # str (readable bank name identifier, eg. "Wells Fargo *1234") + # :confirmation_token, # str (randomly generated private token for email confirmation) + # :account_number, # str (last digits only) + # :bank_name, # str + # :pending_verification, # bool (whether this bank account is still awaiting email confirmation) + # :status, # str + # :email, # str (contact email associated with the user who created this bank account) + # :deleted, # bool (soft delete flag) + # :stripe_bank_account_token, # str + # :stripe_bank_account_id, # str + # :nonprofit_id, :nonprofit + validates :stripe_bank_account_token, presence: true, uniqueness: true + validates :stripe_bank_account_id, presence: true, uniqueness: true + validates :nonprofit, presence: true + validates :email, presence: true, format: {with: Email::Regex} + validate :nonprofit_must_be_vetted, on: :create + validate :nonprofit_has_stripe_account - attr_accessible \ - :name, # str (readable bank name identifier, eg. "Wells Fargo *1234") - :confirmation_token, # str (randomly generated private token for email confirmation) - :account_number, # str (last digits only) - :bank_name, # str - :pending_verification, # bool (whether this bank account is still awaiting email confirmation) - :status, # str - :email, # str (contact email associated with the user who created this bank account) - :deleted, # bool (soft delete flag) - :stripe_bank_account_token, # str - :stripe_bank_account_id, # str - :nonprofit_id, :nonprofit + has_many :payouts + belongs_to :nonprofit - #validates :stripe_bank_account_token, presence: true, uniqueness: true - # validates :stripe_bank_account_id, presence: true, uniqueness: true - #validates :nonprofit, presence: true - #validates :email, presence: true, format: {with: Email::Regex} - #validate :nonprofit_must_be_vetted, on: :create - #validate :nonprofit_has_stripe_account + def nonprofit_must_be_vetted + errors.add(:nonprofit, 'must be vetted') unless nonprofit&.vetted + end - has_many :payouts - belongs_to :nonprofit - - def nonprofit_must_be_vetted - errors.add(:nonprofit, "must be vetted") unless self.nonprofit && self.nonprofit.vetted - end - - - def nonprofit_has_stripe_account - errors.add(:nonprofit, 'must have a Stripe account id') if !self.nonprofit || self.nonprofit.stripe_account_id.blank? - end - - # Manually cause an instance to become invalid - def invalidate! - @not_valid = true - end + def nonprofit_has_stripe_account + errors.add(:nonprofit, 'must have a Stripe account id') if !nonprofit || nonprofit.stripe_account_id.blank? + end + # Manually cause an instance to become invalid + def invalidate! + @not_valid = true + end end diff --git a/app/models/base.rb b/app/models/base.rb new file mode 100644 index 00000000..97737b0c --- /dev/null +++ b/app/models/base.rb @@ -0,0 +1,41 @@ + + +class Base + include ActiveModel::Model + include ActiveModel::Validations + include ActiveModel::Validations::Callbacks + + + def self.validate_nested_attribute(*attributes) + validates_with NestedAttributesValidator, _merge_attributes(attributes) + end + + + private + + def _merge_attributes(attr_names) + options = attr_names.extract_options!.symbolize_keys + attr_names.flatten! + options[:attributes] = attr_names + options + end + + class NestedAttributesValidator < ActiveModel::EachValidator + def initialize(options) + @model_class = options[:model_class] + super + end + + def validate_each(record, attribute, value) + inner_validator = @model_class.new(value) unless value.is_a? @model_class + return if inner_validator.valid? + add_nested_errors_for(record, attribute, inner_validator) + end + + def add_nested_errors_for(record, attribute, other_validator) + record.errors.messages[attribute] = other_validator.errors.messages + record.errors.details[attribute] = other_validator.errors.details + end + end + end + diff --git a/app/models/billing_plan.rb b/app/models/billing_plan.rb index 552a273d..5b671256 100644 --- a/app/models/billing_plan.rb +++ b/app/models/billing_plan.rb @@ -1,18 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class BillingPlan < ActiveRecord::Base - Names = ['Starter', 'Fundraising', 'Supporter Management'] - DefaultAmounts = [0, 9900, 29900] # in pennies +class BillingPlan < ApplicationRecord + # :name, #str: readable name + # :tier, #int: 0-4 (0: Free, 1: Fundraising, 2: Supporter Management) + # :amount, #int (cents) + # :stripe_plan_id, #str (matches plan ID in Stripe) Not needed if it's not a paying subscription + # :interval, #str ('monthly', 'annual') + # :percentage_fee # 0.038 - attr_accessible \ - :name, #str: readable name - :tier, #int: 0-4 (0: Free, 1: Fundraising, 2: Supporter Management) - :amount, #int (cents) - :stripe_plan_id, #str (matches plan ID in Stripe) Not needed if it's not a paying subscription - :interval, #str ('monthly', 'annual') - :percentage_fee # 0.038 + Names = ['Starter', 'Fundraising', 'Supporter Management'].freeze + DefaultAmounts = [0, 9900, 29_900].freeze # in pennies - has_many :billing_subscriptions + has_many :billing_subscriptions - validates :name, :presence => true - validates :amount, :presence => true + validates :name, presence: true + validates :amount, presence: true end diff --git a/app/models/billing_subscription.rb b/app/models/billing_subscription.rb index 5e67bfea..f7f8831a 100644 --- a/app/models/billing_subscription.rb +++ b/app/models/billing_subscription.rb @@ -1,31 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class BillingSubscription < ActiveRecord::Base +class BillingSubscription < ApplicationRecord + # :nonprofit_id, :nonprofit, + # :billing_plan_id, :billing_plan, + # :stripe_subscription_id, + # :status # active, past_due, canceled, or unpaid - attr_accessible \ - :nonprofit_id, :nonprofit, - :billing_plan_id, :billing_plan, - :stripe_subscription_id, - :status # trialing, active, past_due, canceled, or unpaid + attr_accessor :stripe_plan_id, :manual + belongs_to :nonprofit + belongs_to :billing_plan - attr_accessor :stripe_plan_id, :manual - belongs_to :nonprofit - belongs_to :billing_plan + validates :nonprofit, presence: true + validates :billing_plan, presence: true - validates :nonprofit, presence: true - validates :billing_plan, presence: true - - def as_json(options={}) - h = super(options) - h[:plan_name] = self.billing_plan.name - h[:plan_amount] = self.billing_plan.amount / 100 - h - end - - def self.create_with_stripe(np, params) - bp = BillingPlan.find_by_stripe_plan_id params[:stripe_plan_id] - h = ConstructBillingSubscription.with_stripe np, bp - return np.create_billing_subscription h - end + def as_json(options = {}) + h = super(options) + h[:plan_name] = billing_plan.name + h[:plan_amount] = billing_plan.amount / 100 + h + end + def self.create_with_stripe(np, params) + bp = BillingPlan.find_by_stripe_plan_id params[:stripe_plan_id] + h = ConstructBillingSubscription.with_stripe np, bp + np.create_billing_subscription h + end end - diff --git a/app/models/campaign.rb b/app/models/campaign.rb index d4037873..fc2d3ed1 100644 --- a/app/models/campaign.rb +++ b/app/models/campaign.rb @@ -1,185 +1,184 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Campaign < ActiveRecord::Base +class Campaign < ApplicationRecord + include Image::AttachmentExtensions + # :name, + # :tagline, + # :slug, # str: url name + # :total_supporters, + # :goal_amount, + # :nonprofit_id, + # :profile_id, + # :main_image, + # :remove_main_image, # for carrierwave + # :background_image, + # :remove_background_image, #bool carrierwave + # :banner_image, + # :remove_banner_image, + # :published, + # :video_url, #str + # :vimeo_video_id, + # :youtube_video_id, + # :summary, + # :recurring_fund, # bool: whether this is a recurring campaign + # :body, + # :goal_amount_dollars, #accessor: translated into goal_amount (cents) + # :show_total_raised, # bool + # :show_total_count, # bool + # :hide_activity_feed, # bool + # :end_datetime, + # :deleted, #bool (soft delete) + # :hide_goal, # bool + # :hide_thermometer, #bool + # :hide_title, # bool + # :receipt_message, # text + # :hide_custom_amounts, # boolean + # :parent_campaign_id, + # :reason_for_supporting, + # :default_reason_for_supporting - attr_accessible \ - :name, - :tagline, - :slug, # str: url name - :total_supporters, - :goal_amount, - :nonprofit_id, - :profile_id, - :main_image, - :remove_main_image, # for carrierwave - :background_image, - :remove_background_image, #bool carrierwave - :banner_image, - :remove_banner_image, - :published, - :video_url, #str - :vimeo_video_id, - :youtube_video_id, - :summary, - :recurring_fund, # bool: whether this is a recurring campaign - :body, - :goal_amount_dollars, #accessor: translated into goal_amount (cents) - :show_total_raised, # bool - :show_total_count, # bool - :hide_activity_feed, # bool - :end_datetime, - :deleted, #bool (soft delete) - :hide_goal, # bool - :hide_thermometer, #bool - :hide_title, # bool - :receipt_message, # text - :hide_custom_amounts, # boolean - :parent_campaign_id, - :reason_for_supporting, - :default_reason_for_supporting - - validate :end_datetime_cannot_be_in_past, :on => :create - validates :profile, :presence => true - validates :nonprofit, :presence => true - validates :goal_amount, - :presence => true, - :numericality => {:only_integer => true, :greater_than => 99} - validates :name, - :presence => true, - :length => {:maximum => 60} - validates :slug, uniqueness: {scope: :nonprofit_id, message: 'You already have a campaign with that URL.'}, presence: true + validate :end_datetime_cannot_be_in_past, on: :create + validates :profile, presence: true + validates :nonprofit, presence: true + validates :goal_amount, + presence: true, + numericality: { only_integer: true, greater_than: 99 } + validates :name, + presence: true, + length: { maximum: 60 } + validates :slug, uniqueness: { scope: :nonprofit_id, message: 'You already have a campaign with that URL.' }, presence: true attr_accessor :goal_amount_dollars - mount_uploader :main_image, CampaignMainImageUploader - mount_uploader :background_image, CampaignBackgroundImageUploader - mount_uploader :banner_image, CampaignBannerImageUploader + has_one_attached :main_image + has_one_attached :background_image + has_one_attached :banner_image - has_many :donations - has_many :charges, through: :donations - has_many :payments, through: :donations - has_many :campaign_gift_options - has_many :campaign_gifts, through: :campaign_gift_options - has_many :supporters, :through => :donations - has_many :recurring_donations - has_many :roles, as: :host, dependent: :destroy - has_many :comments, as: :host, dependent: :destroy - has_many :activities, as: :host, dependent: :destroy - belongs_to :profile - belongs_to :nonprofit + has_one_attached_with_sizes(:main_image, {normal: [524, 360], thumb: [180,150]}) + has_one_attached_with_sizes(:background_image, {normal: [1000, 600]}) + + has_one_attached_with_default(:main_image, Image::DefaultProfileUrl, + filename: "main_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") + + has_many :donations + has_many :charges, through: :donations + has_many :payments, through: :donations, source: 'payment' + has_many :campaign_gift_options + has_many :campaign_gifts, through: :campaign_gift_options + has_many :supporters, through: :donations + has_many :recurring_donations + has_many :roles, as: :host, dependent: :destroy + has_many :comments, as: :host, dependent: :destroy + has_many :activities, as: :host, dependent: :destroy + belongs_to :profile + belongs_to :nonprofit belongs_to :parent_campaign, class_name: 'Campaign' has_many :children_campaigns, class_name: 'Campaign', foreign_key: 'parent_campaign_id' - scope :published, -> {where(:published => true)} - scope :active, -> {where(:published => true).where("end_datetime IS NULL OR end_datetime >= ?", Date.today)} - scope :past, -> {where(:published => true).where("end_datetime < ?", Date.today)} - scope :unpublished, -> {where(:published => [nil, false])} - scope :not_deleted, -> {where(deleted: [nil, false])} - scope :deleted, -> {where(deleted: true)} - scope :not_a_child, -> {where(parent_campaign_id: nil)} + scope :published, -> { where(published: true) } + scope :active, -> { where(published: true).where('end_datetime IS NULL OR end_datetime >= ?', Date.today) } + scope :past, -> { where(published: true).where('end_datetime < ?', Date.today) } + scope :unpublished, -> { where(published: [nil, false]) } + scope :not_deleted, -> { where(deleted: [nil, false]) } + scope :deleted, -> { where(deleted: true) } + scope :not_a_child, -> { where(parent_campaign_id: nil) } - before_validation do - if self.goal_amount_dollars.present? - self.goal_amount = (self.goal_amount_dollars.gsub(',','').to_f * 100).to_i - end - self - end + before_validation do + if goal_amount_dollars.present? + self.goal_amount = (goal_amount_dollars.delete(',').to_f * 100).to_i + end + self + end - before_validation(on: :create) do - unless self.slug - self.slug = Format::Url.convert_to_slug(name) - end - self.set_defaults - self - end + before_validation(on: :create) do + self.slug = Format::Url.convert_to_slug(name) unless slug + set_defaults + self + end - before_save do - self.parse_video_id if self.video_url && self.video_url_changed? - self - end + before_save do + parse_video_id if video_url && video_url_changed? + self + end - after_create do - user = self.profile.user - Role.create(name: :campaign_editor, user_id: user.id, host: self) - if child_campaign? - CampaignMailer.delay.federated_creation_followup(self) - else - CampaignMailer.delay.creation_followup(self) - end + after_create do + user = profile.user + Role.create(name: :campaign_editor, user_id: user.id, host: self) + HoudiniEventPublisher.announce(:campaign_create, self) + self + end - NonprofitAdminMailer.delay.supporter_fundraiser(self) unless QueryRoles.is_nonprofit_user?(user.id, self.nonprofit_id) - self - end + def set_defaults + self.total_supporters = 1 + self.published = false if published.nil? + end - def set_defaults + def parse_video_id + if video_url.include? 'vimeo' + self.vimeo_video_id = video_url.split('/').last + self.youtube_video_id = nil + elsif video_url.include? 'youtube' + match = video_url.match(/\?v=(.+)/) + return if match.nil? - self.total_supporters = 1 - self.published = false if self.published.nil? - end + self.youtube_video_id = match[1].split('&').first + self.vimeo_video_id = nil + elsif video_url.include? 'youtu.be' + self.youtube_video_id = video_url.split('/').last + self.vimeo_video_id = nil + elsif video_url.blank? + self.vimeo_video_id = nil + self.youtube_video_id = nil + end + self + end + def total_raised + payments.sum(:gross_amount) + end - def parse_video_id - if self.video_url.include? 'vimeo' - self.vimeo_video_id = self.video_url.split('/').last - self.youtube_video_id = nil - elsif self.video_url.include? 'youtube' - match = self.video_url.match(/\?v=(.+)/) - return if match.nil? - self.youtube_video_id = match[1].split('&').first - self.vimeo_video_id = nil - elsif self.video_url.include? 'youtu.be' - self.youtube_video_id = self.video_url.split('/').last - self.vimeo_video_id = nil - elsif self.video_url.blank? - self.vimeo_video_id = nil - self.youtube_video_id = nil - end - self - end + def percentage_funded + goal_amount.nil? ? 0 : total_raised * 100 / goal_amount + end - def total_raised - self.payments.sum(:gross_amount) - end + def average_donation + donations.any? ? total_raised / donations.count : 0 + end - def percentage_funded - self.goal_amount.nil? ? 0 : self.total_raised * 100 / self.goal_amount - end - - def average_donation - self.donations.any? ? self.total_raised / self.donations.count : 0 - end - - # Validations + # Validations def end_datetime_cannot_be_in_past - if self.end_datetime.present? && self.end_datetime < Time.now + if end_datetime.present? && end_datetime < Time.now errors.add(:end_datetime, "can't be in the past") - end - end + end + end - def ready_to_publish? - [(self.body && self.body.length >= 500), (self.campaign_gift_options.count >= 1)].all? - end + def ready_to_publish? + [(body && body.length >= 500), (campaign_gift_options.count >= 1)].all? + end - def url - "#{self.nonprofit.url}/campaigns/#{self.slug}" - end + def url + "#{nonprofit.url}/campaigns/#{slug}" + end - def days_left - return 0 if self.end_datetime.nil? - (self.end_datetime.to_date - Date.today).to_i - end + def days_left + return 0 if end_datetime.nil? - def finished? - self.end_datetime && self.end_datetime < Time.now - end + (end_datetime.to_date - Date.today).to_i + end + + def finished? + end_datetime && end_datetime < Time.now + end def child_params - excluded_for_peer_to_peer = %w( - id created_at updated_at slug profile_id url + excluded_for_peer_to_peer = %w[ + id created_at updated_at slug profile_id url total_raised show_recurring_amount external_identifier parent_campaign_id reason_for_supporting metadata - ) + ] attributes.except(*excluded_for_peer_to_peer) end diff --git a/app/models/campaign_gift.rb b/app/models/campaign_gift.rb index be8e76ba..300d7b09 100644 --- a/app/models/campaign_gift.rb +++ b/app/models/campaign_gift.rb @@ -1,16 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CampaignGift < ActiveRecord::Base +class CampaignGift < ApplicationRecord + # :donation_id, + # :donation, + # :campaign_gift_option, + # :campaign_gift_option_id - attr_accessible \ - :donation_id, - :donation, - :campaign_gift_option, - :campaign_gift_option_id - - belongs_to :donation - belongs_to :campaign_gift_option - - validates :donation, presence: true - validates :campaign_gift_option, presence: true + belongs_to :donation + belongs_to :campaign_gift_option + validates :donation, presence: true + validates :campaign_gift_option, presence: true end diff --git a/app/models/campaign_gift_option.rb b/app/models/campaign_gift_option.rb index f69129c4..627c6298 100644 --- a/app/models/campaign_gift_option.rb +++ b/app/models/campaign_gift_option.rb @@ -1,35 +1,34 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CampaignGiftOption < ActiveRecord::Base +class CampaignGiftOption < ApplicationRecord + # :amount_one_time, #int (cents) + # :amount_recurring, #int (cents) + # :amount_dollars, #str, gets converted to amount + # :description, # text + # :name, # str + # :campaign, #assocation + # :quantity, #int (optional) + # :to_ship, #boolean + # :order, #int (optional) + # :hide_contributions #boolean (optional) - attr_accessible \ - :amount_one_time, #int (cents) - :amount_recurring, #int (cents) - :amount_dollars, #str, gets converted to amount - :description, # text - :name, # str - :campaign, #assocation - :quantity, #int (optional) - :to_ship, #boolean - :order, #int (optional) - :hide_contributions #boolean (optional) + belongs_to :campaign + has_many :campaign_gifts + has_many :donations, through: :campaign_gifts - belongs_to :campaign - has_many :campaign_gifts - has_many :donations, through: :campaign_gifts + validates :name, presence: true + validates :campaign, presence: true + validates :amount_one_time, presence: true, numericality: { only_integer: true }, unless: :amount_recurring + validates :amount_recurring, presence: true, numericality: { only_integer: true }, unless: :amount_one_time - validates :name, presence: true - validates :campaign, presence: true - validates :amount_one_time, presence: true, numericality: { only_integer: true }, unless: :amount_recurring - validates :amount_recurring, presence: true, numericality: { only_integer: true }, unless: :amount_one_time - - def total_gifts - return self.campaign_gifts.count - end - - def as_json(options={}) - h = super(options) - h[:total_gifts] = self.total_gifts - h - end + def total_gifts + campaign_gifts.count + end + def as_json(options = {}) + h = super(options) + h[:total_gifts] = total_gifts + h + end end diff --git a/app/models/card.rb b/app/models/card.rb index d7b806f8..4a1b92d0 100755 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -1,25 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Card < ActiveRecord::Base +class Card < ApplicationRecord + # :cardholders_name, # str (name associated with this card) + # :email, # str (cache the email associated with this card) + # :name, # str (readable card name, eg. Visa *1234) + # :failure_message, # accessor for temporarily storing the stripe decline message + # :status, # str + # :stripe_card_token, # str + # :stripe_card_id, # str + # :stripe_customer_id, # str + # :holder, :holder_id, :holder_type, # polymorphic cardholder association + # :inactive # a card is inactive. This is currently only meaningful for nonprofit cards - attr_accessible \ - :cardholders_name, # str (name associated with this card) - :email, # str (cache the email associated with this card) - :name, # str (readable card name, eg. Visa *1234) - :failure_message, # accessor for temporarily storing the stripe decline message - :status, # str - :stripe_card_token, # str - :stripe_card_id, # str - :stripe_customer_id, # str - :holder, :holder_id, :holder_type, # polymorphic cardholder association - :inactive # a card is inactive. This is currently only meaningful for nonprofit cards - - - attr_accessor :failure_message - - - belongs_to :holder, polymorphic: true - has_many :charges - has_many :donations - has_many :tickets + attr_accessor :failure_message + belongs_to :holder, polymorphic: true + has_many :charges + has_many :donations + has_many :tickets end diff --git a/app/models/charge.rb b/app/models/charge.rb index d2ce3f47..371bbc94 100644 --- a/app/models/charge.rb +++ b/app/models/charge.rb @@ -1,36 +1,34 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # A Charge represents a potential debit to a nonprofit's account on a credit card donation action. -class Charge < ActiveRecord::Base +class Charge < ApplicationRecord + # :amount, + # :fee, + # :stripe_charge_id, + # :status - attr_accessible \ - :amount, - :fee, - :stripe_charge_id, - :status + has_one :campaign, through: :donation + has_one :recurring_donation, through: :donation + has_many :tickets + has_many :events, through: :tickets + has_many :refunds + has_many :disputes + belongs_to :supporter + belongs_to :card + belongs_to :direct_debit_detail + belongs_to :nonprofit + belongs_to :donation + belongs_to :payment + scope :paid, -> { where(status: %w[available pending disbursed]) } + scope :not_paid, -> { where(status: [nil, 'failed']) } + scope :available, -> { where(status: 'available') } + scope :pending, -> { where(status: 'pending') } + scope :disbursed, -> { where(status: 'disbursed') } - has_one :campaign, through: :donation - has_one :recurring_donation, through: :donation - has_many :tickets - has_many :events, through: :tickets - has_many :refunds - has_many :disputes - belongs_to :supporter - belongs_to :card - belongs_to :direct_debit_detail - belongs_to :nonprofit - belongs_to :donation - belongs_to :payment - - scope :paid, ->{where(status: ["available", "pending", "disbursed"])} - scope :not_paid, ->{where(status: [nil, "failed"])} - scope :available, ->{where(status: "available")} - scope :pending, ->{where(status: "pending")} - scope :disbursed, ->{where(status: "disbursed")} - - def paid? - self.status.in?(%w[available pending disbursed]) - end - + def paid? + status.in?(%w[available pending disbursed]) + end end diff --git a/app/models/comment.rb b/app/models/comment.rb index e0491d49..b2510703 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,35 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Comment < ActiveRecord::Base +class Comment < ApplicationRecord + # :host_id, + # :host_type, # parent: Event, Campaign, nil + # :profile_id, + # :body - attr_accessible \ - :host_id, :host_type, #parent: Event, Campaign, nil - :profile_id, - :body + validates :profile, presence: true + validates :body, presence: true, length: { maximum: 200 } - validates :profile, :presence => true - validates :body, :presence => true, :length => {:maximum => 200} + has_one :activity, as: :attachment, dependent: :destroy + belongs_to :host, polymorphic: true + belongs_to :donation + belongs_to :profile - has_one :activity, :as => :attachment, :dependent => :destroy - belongs_to :host, :polymorphic => true - belongs_to :donation - belongs_to :profile + before_validation(on: :create) do + remove_newlines + end - before_validation(:on => :create) do - remove_newlines - end - - after_create do - self.create_activity({ - :desc => 'commented', - :profile_id => self.profile_id, - :host_id => self.host_id, - :host_type => self.host_type, - :body => self.body - }) - end - - def remove_newlines - self.body = self.body && self.body.gsub(/\n/,'') - end + after_create do + create_activity( + desc: 'commented', + profile_id: profile_id, + host_id: host_id, + host_type: host_type, + body: body + ) + end + def remove_newlines + self.body = body && body.delete("\n") + end end diff --git a/app/models/concerns/image/attachment_extensions.rb b/app/models/concerns/image/attachment_extensions.rb new file mode 100644 index 00000000..296c29c0 --- /dev/null +++ b/app/models/concerns/image/attachment_extensions.rb @@ -0,0 +1,50 @@ +module Image::AttachmentExtensions + extend ActiveSupport::Concern + class_methods do + def has_one_attached_with_sizes(attribute_name, sizes) + if sizes.nil? || !sizes.is_a?(Hash) || !sizes.any? + raise ArgumentError, "You must pass a valid hash of sizes" + end + attribute = attribute_name.to_s + + # clean up sizes + sizes.keys.each do |key| + value = sizes[key] + if value.is_a?(Numeric) + sizes[key] = [value, value] + elsif value.is_a?(Array) && value.count == 1 && value.all?{|i| i.is_a?(Numeric)} + sizes[key]= [value[0], value[0]] + elsif value.is_a?(Array) && value.count == 2 && value.all?{|i| i.is_a?(Numeric)} + sizes[key] = [value[0], value[1]] + else + raise ArgumentError, "#{value.to_s} was not a valid size." + end + end + + class_eval <<-RUBY, __FILE__, __LINE__ + 1 + def #{attribute}_by_size(size) + case (size) + #{sizes.map do |k,v| + <<-INNER + when :#{k.to_sym} + return #{attribute}.variant(resize_to_limit: [#{v[0]}, #{v[1]}]) + INNER + end.join("\n")} + else + raise ArgumentError, ":" + size.to_s + " is not a valid size. Valid sizes are: #{sizes.keys.map{|i| ":" + i.to_s}.join(', ')}" + end + end + RUBY + end + + def has_one_attached_with_default(attribute_name, default_path, **options) + after_save do + attribute = send(attribute_name) + unless attribute.attached? + attribute.attach(io: File.open(default_path), **options) + end + self + end + end + end +end \ No newline at end of file diff --git a/app/models/coupon.rb b/app/models/coupon.rb index 1377d548..1318dd94 100644 --- a/app/models/coupon.rb +++ b/app/models/coupon.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Coupon < ActiveRecord::Base - attr_accessible \ - :name, - :victim_np_id, - :paid, # boolean - :nonprofit, :nonprofit_id +class Coupon < ApplicationRecord + # :name, + # :victim_np_id, + # :paid, # boolean + # :nonprofit, + # :nonprofit_id - scope :unpaid, -> {where(paid: [nil,false])} + scope :unpaid, -> { where(paid: [nil, false]) } - validates_presence_of :name, :nonprofit_id, :victim_np_id -end \ No newline at end of file + validates_presence_of :name, :nonprofit_id, :victim_np_id +end diff --git a/app/models/create_model.rb b/app/models/create_model.rb new file mode 100644 index 00000000..2c0fcc8c --- /dev/null +++ b/app/models/create_model.rb @@ -0,0 +1,48 @@ +class CreateModel < Base + attr_accessor :nonprofit, :user + validates_presence_of :user + validates_presence_of :nonprofit + validate_nested_attribute :user, model_class: User + validate_nested_attribute :nonprofit, model_class: Nonprofit + + before_validation do + nonprofit = Nonprofit.create(nonprofit) if !nonprofit.is_a? Nonprofit + user = User.create(user) if !nonprofit.is_a? Nonprofit + end + + def save + if valid? + if nonprofit.save! + if user.save! + role = user.roles.build(host: nonprofit, name: 'nonprofit_admin') + role.save! + + billing_plan = BillingPlan.find(Settings.default_bp.id) + b_sub = nonprofit.build_billing_subscription(billing_plan: billing_plan, status: 'active') + b_sub.save! + end + end + end + # rescue ActiveRecord::RecordInvalid => e + # class_to_name = { Nonprofit => 'nonprofit', User => 'user' } + # if class_to_name[e.record.class] + # errors = e.record.errors.keys.map do |k| + # errors = e.record.errors[k].uniq + # errors.map do |error| + # Grape::Exceptions::Validation.new( + # params: ["#{class_to_name[e.record.class]}[#{k}]"], + # value message: error + # ) + # end + # end + # raise Grape::Exceptions::ValidationErrors.new(errors: errors.flatten) + # else + # raise e + # end + # end + end + + def save! + raise 'runtime' unless save + end +end \ No newline at end of file diff --git a/app/models/custom_field_join.rb b/app/models/custom_field_join.rb index c38e9331..e304c262 100644 --- a/app/models/custom_field_join.rb +++ b/app/models/custom_field_join.rb @@ -1,25 +1,25 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CustomFieldJoin < ActiveRecord::Base +class CustomFieldJoin < ApplicationRecord + # :supporter, + # :supporter_id, + # :custom_field_master, + # :custom_field_master_id, + # :value - attr_accessible \ - :supporter, :supporter_id, - :custom_field_master, :custom_field_master_id, - :value + validates :custom_field_master, presence: true - validates :custom_field_master, presence: true - - belongs_to :custom_field_master + belongs_to :custom_field_master belongs_to :supporter - def self.create_with_name(nonprofit, h) - cfm = nonprofit.custom_field_masters.find_by_name(h['name']) - if cfm.nil? - cfm = nonprofit.custom_field_masters.create(name: h['name']) - end - self.create({value: h['value'], custom_field_master_id: cfm.id, supporter_id: h['supporter_id']}) - end - - def name; custom_field_master.name; end + def self.create_with_name(nonprofit, h) + cfm = nonprofit.custom_field_masters.find_by_name(h['name']) + cfm = nonprofit.custom_field_masters.create(name: h['name']) if cfm.nil? + create(value: h['value'], custom_field_master_id: cfm.id, supporter_id: h['supporter_id']) + end + def name + custom_field_master.name +end end - diff --git a/app/models/custom_field_master.rb b/app/models/custom_field_master.rb index ef6bb85c..15f5fe08 100644 --- a/app/models/custom_field_master.rb +++ b/app/models/custom_field_master.rb @@ -1,24 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CustomFieldMaster < ActiveRecord::Base +class CustomFieldMaster < ApplicationRecord + # :nonprofit, + # :nonprofit_id, + # :name, + # :deleted, + # :created_at - attr_accessible \ - :nonprofit, :nonprofit_id, - :name, - :deleted, - :created_at + validates :name, presence: true + validate :no_dupes, on: :create - validates :name, presence: true - validate :no_dupes, on: :create + belongs_to :nonprofit + has_many :custom_field_joins, dependent: :destroy - belongs_to :nonprofit - has_many :custom_field_joins, dependent: :destroy + scope :not_deleted, -> { where(deleted: [nil, false]) } - scope :not_deleted, ->{where(deleted: [nil,false])} - - def no_dupes - return self if nonprofit.nil? - errors.add(:base, "Duplicate custom field") if nonprofit.custom_field_masters.not_deleted.where(name: name).any? - end + def no_dupes + return self if nonprofit.nil? + errors.add(:base, 'Duplicate custom field') if nonprofit.custom_field_masters.not_deleted.where(name: name).any? + end end - diff --git a/app/models/direct_debit_detail.rb b/app/models/direct_debit_detail.rb index ba8fbe67..f3a7755d 100644 --- a/app/models/direct_debit_detail.rb +++ b/app/models/direct_debit_detail.rb @@ -1,8 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class DirectDebitDetail < ActiveRecord::Base - attr_accessible :iban, :account_holder_name, :bic, :supporter_id, :holder +class DirectDebitDetail < ApplicationRecord + # :iban, + # :account_holder_name, + # :bic, + # :supporter_id, + # :holder has_many :donations has_many :charges - belongs_to :holder, class_name: Supporter + belongs_to :holder, class_name: 'Supporter' end diff --git a/app/models/dispute.rb b/app/models/dispute.rb index 722768b3..fb919039 100644 --- a/app/models/dispute.rb +++ b/app/models/dispute.rb @@ -1,19 +1,18 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Dispute < ActiveRecord::Base +class Dispute < ApplicationRecord + Reasons = %i[unrecognized duplicate fraudulent subscription_canceled product_unacceptable product_not_received unrecognized credit_not_processed goods_services_returned_or_refused goods_services_cancelled incorrect_account_details insufficient_funds bank_cannot_process debit_not_authorized general].freeze - Reasons = [:unrecognized, :duplicate, :fraudulent, :subscription_canceled, :product_unacceptable, :product_not_received, :unrecognized, :credit_not_processed, :goods_services_returned_or_refused, :goods_services_cancelled, :incorrect_account_details, :insufficient_funds, :bank_cannot_process, :debit_not_authorized, :general] - - Statuses = [:needs_response, :under_review, :won, :lost, :lost_and_paid] - - attr_accessible \ - :gross_amount, # int - :charge_id, :charge, - :payment_id, :payment, - :status, - :reason + Statuses = %i[needs_response under_review won lost lost_and_paid].freeze + # :gross_amount, # int + # :charge_id, + # :charge, + # :payment_id, + # :payment, + # :status, + # :reason belongs_to :charge belongs_to :payment - end - diff --git a/app/models/donation.rb b/app/models/donation.rb index d014f737..501e2d86 100644 --- a/app/models/donation.rb +++ b/app/models/donation.rb @@ -1,48 +1,48 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Donation < ActiveRecord::Base +class Donation < ApplicationRecord + # :date, # datetime (when this donation was made) + # :amount, # int (in cents) + # :recurring, # bool + # :anonymous, # bool + # :email, # str (cached email of the donor) + # :designation, # text + # :dedication, # text + # :comment, # text + # :origin_url, # text + # :nonprofit_id, :nonprofit, + # :card_id, :card, # Card with which any charges were made + # :supporter_id, :supporter, + # :profile_id, :profile, + # :campaign_id, :campaign, + # :payment_id, :payment, + # :event_id, :event, + # :direct_debit_detail_id, :direct_debit_detail, + # :payment_provider - attr_accessible \ - :date, # datetime (when this donation was made) - :amount, # int (in cents) - :recurring, # bool - :anonymous, # bool - :email, # str (cached email of the donor) - :designation, # text - :dedication, # text - :comment, # text - :origin_url, # text - :nonprofit_id, :nonprofit, - :card_id, :card, # Card with which any charges were made - :supporter_id, :supporter, - :profile_id, :profile, - :campaign_id, :campaign, - :payment_id, :payment, - :event_id, :event, - :direct_debit_detail_id, :direct_debit_detail, - :payment_provider + validates :amount, presence: true, numericality: { only_integer: true } + validates :supporter, presence: true + validates :nonprofit, presence: true + validates_associated :charges + validates :payment_provider, inclusion: { in: %w[credit_card sepa] }, allow_blank: true - validates :amount, presence: true, numericality: { only_integer: true } - validates :supporter, presence: true - validates :nonprofit, presence: true - validates_associated :charges - validates :payment_provider, inclusion: { in: %(credit_card sepa) }, allow_blank: true + has_many :charges + has_many :campaign_gifts, dependent: :destroy + has_many :campaign_gift_options, through: :campaign_gifts + has_many :activities, as: :attachment, dependent: :destroy + has_many :payments + has_one :recurring_donation + has_one :payment + has_one :offsite_payment + has_one :tracking + belongs_to :supporter + belongs_to :card + belongs_to :direct_debit_detail + belongs_to :profile + belongs_to :nonprofit + belongs_to :campaign + belongs_to :event - has_many :charges - has_many :campaign_gifts, dependent: :destroy - has_many :campaign_gift_options, through: :campaign_gifts - has_many :activities, as: :attachment, dependent: :destroy - has_many :payments - has_one :recurring_donation - has_one :payment - has_one :offsite_payment - has_one :tracking - belongs_to :supporter - belongs_to :card - belongs_to :direct_debit_detail - belongs_to :profile - belongs_to :nonprofit - belongs_to :campaign - belongs_to :event - - scope :anonymous, -> {where(anonymous: true)} + scope :anonymous, -> { where(anonymous: true) } end diff --git a/app/models/email_draft.rb b/app/models/email_draft.rb index 9614c13c..bc4296f3 100644 --- a/app/models/email_draft.rb +++ b/app/models/email_draft.rb @@ -1,16 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EmailDraft < ActiveRecord::Base +class EmailDraft < ApplicationRecord + # :nonprofit, :nonprofit_id, + # :name, + # :deleted, + # :value, + # :created_at - attr_accessible \ - :nonprofit, :nonprofit_id, - :name, - :deleted, - :value, - :created_at - - belongs_to :nonprofit - - scope :not_deleted, ->{where(deleted: [nil,false])} + belongs_to :nonprofit + scope :not_deleted, -> { where(deleted: [nil, false]) } end - diff --git a/app/models/email_list.rb b/app/models/email_list.rb index ea64e773..30ded59d 100644 --- a/app/models/email_list.rb +++ b/app/models/email_list.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EmailList < ActiveRecord::Base - attr_accessible :list_name, :mailchimp_list_id, :nonprofit, :tag_master +class EmailList < ApplicationRecord + # :list_name, + # :mailchimp_list_id, + # :nonprofit, + # :tag_master belongs_to :nonprofit belongs_to :tag_master end diff --git a/app/models/email_setting.rb b/app/models/email_setting.rb index c4f8bf2e..8624def0 100644 --- a/app/models/email_setting.rb +++ b/app/models/email_setting.rb @@ -1,16 +1,15 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EmailSetting < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :user_id, :user, - :nonprofit_id, :nonprofit, - :notify_payments, - :notify_campaigns, - :notify_events, - :notify_payouts, - :notify_recurring_donations +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class EmailSetting < ApplicationRecord + # :user_id, :user, + # :nonprofit_id, :nonprofit, + # :notify_payments, + # :notify_campaigns, + # :notify_events, + # :notify_payouts, + # :notify_recurring_donations belongs_to :nonprofit belongs_to :user - end diff --git a/app/models/errors/active_model_error.rb b/app/models/errors/active_model_error.rb new file mode 100644 index 00000000..5963e621 --- /dev/null +++ b/app/models/errors/active_model_error.rb @@ -0,0 +1,5 @@ + + +class Errors::ActiveModelError < StandardError + +end \ No newline at end of file diff --git a/app/models/errors/message_invalid.rb b/app/models/errors/message_invalid.rb new file mode 100644 index 00000000..b4fed5a1 --- /dev/null +++ b/app/models/errors/message_invalid.rb @@ -0,0 +1,17 @@ + +class Errors::MessageInvalid < Errors::ActiveModelError + + attr_reader :record + def initialize(record=nil) + if record + @record = record + errors = @record.errors.full_messages.join(", ") + message = I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", errors: errors, default: :"errors.messages.record_invalid") + else + message = "Record invalid" + end + + super(message) + + end +end \ No newline at end of file diff --git a/app/models/event.rb b/app/models/event.rb index 4face493..1e53eca2 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,104 +1,110 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Event < ActiveRecord::Base +class Event < ApplicationRecord + include Image::AttachmentExtensions + # :deleted, #bool for soft-delete + # :name, # str + # :tagline, # str + # :summary, # text + # :body, # text (html) + # :end_datetime, + # :start_datetime, + # :latitude, # float + # :longitude, # float + # :location, # str + # :city, # str + # :state_code, # str + # :address, # str + # :zip_code, # str + # :main_image, # str + # :remove_main_image, # for carrierwave + # :background_image, # str + # :remove_background_image, # bool carrierwave + # :published, # bool + # :slug, # str + # :directions, # text + # :venue_name, # str + # :profile_id, # creator + # :ticket_levels_attributes, + # :show_total_raised, # bool + # :show_total_count, # bool + # :hide_activity_feed, # bool + # :nonprofit_id, # host + # :hide_title, # bool + # :organizer_email, # string + # :receipt_message # text - attr_accessible \ - :deleted, #bool for soft-delete - :name, # str - :tagline, # str - :summary, # text - :body, # text (html) - :end_datetime, - :start_datetime, - :latitude, # float - :longitude, # float - :location, # str - :city, # str - :state_code, # str - :address, # str - :zip_code, # str - :main_image, # str - :remove_main_image, # for carrierwave - :background_image, # str - :remove_background_image, # bool carrierwave - :published, # bool - :slug, # str - :directions, # text - :venue_name, # str - :profile_id, # creator - :ticket_levels_attributes, - :show_total_raised, # bool - :show_total_count, # bool - :hide_activity_feed, # bool - :nonprofit_id, # host - :hide_title, # bool - :organizer_email, # string - :receipt_message # text + validates :name, presence: true + validates :end_datetime, presence: true + validates :start_datetime, presence: true + validates :address, presence: true + validates :city, presence: true + validates :state_code, presence: true + validates :slug, presence: true, uniqueness: { scope: :nonprofit_id, message: 'You already have an event with that URL' } + validates :nonprofit_id, presence: true + validates :profile_id, presence: true - validates :name, :presence => true - validates :end_datetime, :presence => true - validates :start_datetime, :presence => true - validates :address, :presence => true - validates :city, :presence => true - validates :state_code, :presence => true - validates :slug, :presence => true, uniqueness: {scope: :nonprofit_id, message: 'You already have an event with that URL'} - validates :nonprofit_id, :presence => true - validates :profile_id, :presence => true - - belongs_to :nonprofit - belongs_to :profile - has_many :donations - has_many :charges, through: :tickets - has_many :supporters, through: :donations - has_many :recurring_donations - has_many :ticket_levels, :dependent => :destroy + belongs_to :nonprofit + belongs_to :profile + has_many :donations + has_many :charges, through: :tickets + has_many :supporters, through: :donations + has_many :recurring_donations + has_many :ticket_levels, dependent: :destroy has_many :event_discounts, dependent: :destroy - has_many :tickets - has_many :payments, through: :tickets - has_many :roles, as: :host, dependent: :destroy - has_many :comments, as: :host, dependent: :destroy - has_many :activities, as: :host, dependent: :destroy + has_many :tickets + has_many :payments, through: :tickets + has_many :roles, as: :host, dependent: :destroy + has_many :comments, as: :host, dependent: :destroy + has_many :activities, as: :host, dependent: :destroy + geocoded_by :full_address - geocoded_by :full_address + accepts_nested_attributes_for :ticket_levels, allow_destroy: true + has_one_attached :main_image + has_one_attached :background_image - accepts_nested_attributes_for :ticket_levels, :allow_destroy => true + has_one_attached_with_sizes :main_image, {normal: 400, thumb: 100} + has_one_attached_with_sizes :background_image, {normal: [1000, 600]} - mount_uploader :main_image, EventMainImageUploader - mount_uploader :background_image, EventBackgroundImageUploader + has_one_attached_with_default(:main_image, Image::DefaultProfileUrl, + filename: "main_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") - scope :not_deleted, -> {where(deleted: [nil,false])} - scope :deleted, -> {where(deleted: true)} - scope :published, -> {where(:published => true)} - scope :upcoming, -> {where("start_datetime >= ?", Date.today).published} - scope :past, -> {where("end_datetime < ?", Date.today).published} - scope :unpublished, -> {where("published != ?", true)} + has_one_attached_with_default(:background_image, Image::DefaultCampaignUrl, + filename: "background_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultCampaignUrl).extname}") + - validates :slug, uniqueness: {scope: :nonprofit_id, message: 'You already have a campaign with that name.'} + scope :not_deleted, -> { where(deleted: [nil, false]) } + scope :deleted, -> { where(deleted: true) } + scope :published, -> { where(published: true) } + scope :upcoming, -> { where('start_datetime >= ?', Date.today).published } + scope :past, -> { where('end_datetime < ?', Date.today).published } + scope :unpublished, -> { where('published != ?', true) } - before_validation(on: :create) do - unless self.slug - self.slug = Format::Url.convert_to_slug(name) - end - self.published = false if self.published.nil? - self.total_raised ||= 0 + validates :slug, uniqueness: { scope: :nonprofit_id, message: 'You already have a campaign with that name.' } + + before_validation(on: :create) do + self.slug = Format::Url.convert_to_slug(name) unless slug + self.published = false if published.nil? + self.total_raised ||= 0 self - end - - after_validation :geocode - - after_create do - user = self.profile.user - Role.create(name: :event_editor, user_id: user.id, host: self) - EventMailer.delay.creation_followup(self) - self - end - - def url - "#{self.nonprofit.url}/events/#{self.slug}" - end - - def full_address - Format::Address.full_address(self.address, self.city, self.state_code, self.zip_code) end + after_validation :geocode + + after_create do + user = profile.user + Role.create(name: :event_editor, user_id: user.id, host: self) + EventCreateJob.perform_later self + self + end + + def url + "#{nonprofit.url}/events/#{slug}" + end + + def full_address + Format::Address.full_address(address, city, state_code, zip_code) + end end diff --git a/app/models/event_discount.rb b/app/models/event_discount.rb index 7e6a69a0..cf6d7a1e 100644 --- a/app/models/event_discount.rb +++ b/app/models/event_discount.rb @@ -1,12 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EventDiscount < ActiveRecord::Base - attr_accessible \ - :code, - :event_id, - :name, - :percent +class EventDiscount < ApplicationRecord + # :code, + # :event_id, + # :name, + # :percent belongs_to :event has_many :tickets - end diff --git a/app/models/export.rb b/app/models/export.rb index e49d5609..a48e92b9 100644 --- a/app/models/export.rb +++ b/app/models/export.rb @@ -1,8 +1,19 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Export < ActiveRecord::Base +# frozen_string_literal: true +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class Export < ApplicationRecord + # :exception, + # :nonprofit, + # :status, + # :user, + # :export_type, + # :parameters, + # :ended, + # :url, + # :user_id, + # :nonprofit_id + STATUS = %w[queued started completed failed].freeze - attr_accessible :exception, :nonprofit, :status, :user, :export_type, :parameters, :ended, :url, :user_id, :nonprofit_id belongs_to :nonprofit belongs_to :user diff --git a/app/models/full_contact_info.rb b/app/models/full_contact_info.rb index 712579fa..ee910cb1 100644 --- a/app/models/full_contact_info.rb +++ b/app/models/full_contact_info.rb @@ -1,23 +1,24 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class FullContactInfo < ActiveRecord::Base - attr_accessible \ - :email, - :full_name, - :gender, - :city, - :county, - :state_code, - :country, - :continent, - :age, - :age_range, - :location_general, - :supporter_id, :supporter, - :websites +# frozen_string_literal: true - has_many :full_contact_photos - has_many :full_contact_social_profiles - has_many :full_contact_orgs - has_many :full_contact_topics +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class FullContactInfo < ApplicationRecord + # :email, + # :full_name, + # :gender, + # :city, + # :county, + # :state_code, + # :country, + # :continent, + # :age, + # :age_range, + # :location_general, + # :supporter_id, :supporter, + # :websites + + has_many :full_contact_photos + has_many :full_contact_social_profiles + has_many :full_contact_orgs + has_many :full_contact_topics belongs_to :supporter end diff --git a/app/models/full_contact_org.rb b/app/models/full_contact_org.rb index a8b4ce1b..a73a708a 100644 --- a/app/models/full_contact_org.rb +++ b/app/models/full_contact_org.rb @@ -1,16 +1,15 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class FullContactOrg < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :name, - :is_primary, - :name, - :start_date, - :end_date, - :title, - :current, - :full_contact_info_id, :full_contact_info +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class FullContactOrg < ApplicationRecord + # :name, + # :is_primary, + # :name, + # :start_date, + # :end_date, + # :title, + # :current, + # :full_contact_info_id, :full_contact_info belongs_to :full_contact_info - end diff --git a/app/models/full_contact_photo.rb b/app/models/full_contact_photo.rb index 258a764f..647d542a 100644 --- a/app/models/full_contact_photo.rb +++ b/app/models/full_contact_photo.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class FullContactPhoto < ActiveRecord::Base - attr_accessible \ - :full_contact_info, - :full_contact_info_id, - :type_id, # i.e. twitter, linkedin, facebook - :is_primary, #bool - :url #string +class FullContactPhoto < ApplicationRecord + # :full_contact_info, + # :full_contact_info_id, + # :type_id, # i.e. twitter, linkedin, facebook + # :is_primary, #bool + # :url #string - belongs_to :full_contact_info + belongs_to :full_contact_info - validates_presence_of :full_contact_info -end \ No newline at end of file + validates_presence_of :full_contact_info +end diff --git a/app/models/full_contact_social_profile.rb b/app/models/full_contact_social_profile.rb index 047160ea..717829fe 100644 --- a/app/models/full_contact_social_profile.rb +++ b/app/models/full_contact_social_profile.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class FullContactSocialProfile < ActiveRecord::Base - attr_accessible \ - :full_contact_info, - :full_contact_info_id, - :type_id, # i.e. twitter, linkedin, facebook - :username, #string - :uid, # string - :bio, #string - :url #string +class FullContactSocialProfile < ApplicationRecord + # :full_contact_info, + # :full_contact_info_id, + # :type_id, # i.e. twitter, linkedin, facebook + # :username, #string + # :uid, # string + # :bio, #string + # :url #string - belongs_to :full_contact_info + belongs_to :full_contact_info - validates_presence_of :full_contact_info -end \ No newline at end of file + validates_presence_of :full_contact_info +end diff --git a/app/models/full_contact_topic.rb b/app/models/full_contact_topic.rb index 475f9c27..952195a3 100644 --- a/app/models/full_contact_topic.rb +++ b/app/models/full_contact_topic.rb @@ -1,11 +1,10 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class FullContactTopic < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :provider, - :value, - :full_contact_info_id, :full_contact_info +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class FullContactTopic < ApplicationRecord + # :provider, + # :value, + # :full_contact_info_id, :full_contact_info belongs_to :full_contact_info - end diff --git a/app/models/image_attachment.rb b/app/models/image_attachment.rb index d6afb9a4..23e89904 100644 --- a/app/models/image_attachment.rb +++ b/app/models/image_attachment.rb @@ -1,10 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class ImageAttachment < ActiveRecord::Base +class ImageAttachment < ApplicationRecord + include Image::AttachmentExtensions + # :parent_id, + # :file + has_one_attached :file - attr_accessible :parent_id, :file - mount_uploader :file, ImageAttachmentUploader - - # not sure if poly parent is used on this model, as all values are nil in db - belongs_to :parent, :polymorphic => true + has_one_attached_with_sizes :file, {large: [600, 400], medium: [400, 266], small: [400,266], thumb_explore: [200,133]} + # not sure if poly parent is used on this model, as all values are nil in db + belongs_to :parent, polymorphic: true end diff --git a/app/models/import.rb b/app/models/import.rb index b4e3f681..4c6453a4 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -1,19 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Import < ActiveRecord::Base +class Import < ApplicationRecord + # :user_id, :user, + # :email, # email of the user who ma + # :nonprofit_id, :nonprofit, + # :row_count, + # :imported_count, + # :date - attr_accessible \ - :user_id, :user, - :email, # email of the user who ma - :nonprofit_id, :nonprofit, - :row_count, - :imported_count, - :date - - has_many :supporters - belongs_to :nonprofit - belongs_to :user - - validates :user, presence: true + has_many :supporters + belongs_to :nonprofit + belongs_to :user + validates :user, presence: true end - diff --git a/app/models/miscellaneous_np_info.rb b/app/models/miscellaneous_np_info.rb index 2b15029a..3bd32dbb 100644 --- a/app/models/miscellaneous_np_info.rb +++ b/app/models/miscellaneous_np_info.rb @@ -1,9 +1,9 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class MiscellaneousNpInfo < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :donate_again_url, - :change_amount_message +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class MiscellaneousNpInfo < ApplicationRecord + # :donate_again_url, + # :change_amount_message belongs_to :nonprofit end diff --git a/app/models/nonprofit.rb b/app/models/nonprofit.rb index 421b59b1..c1029141 100755 --- a/app/models/nonprofit.rb +++ b/app/models/nonprofit.rb @@ -1,56 +1,58 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Nonprofit < ActiveRecord::Base +class Nonprofit < ApplicationRecord + attr_accessor :register_np_only, :user_id, :user + Categories = ['Public Benefit', 'Human Services', 'Education', 'Civic Duty', 'Human Rights', 'Animals', 'Environment', 'Health', 'Arts, Culture, Humanities', 'International', 'Children', 'Religion', 'LGBTQ', "Women's Rights", 'Disaster Relief', 'Veterans'].freeze - Categories = ["Public Benefit", "Human Services", "Education", "Civic Duty", "Human Rights", "Animals", "Environment", "Health", "Arts, Culture, Humanities", "International", "Children", "Religion", "LGBTQ", "Women's Rights", "Disaster Relief", "Veterans"] - - attr_accessible \ - :name, # str - :stripe_account_id, # str - :summary, # text: paragraph-sized organization summary - :tagline, # str - :email, # str: public organization contact email - :phone, # str: public org contact phone - :main_image, # str: url of featured image - first image in profile carousel - :second_image, # str: url of 2nd image in carousel - :third_image, # str: url of 3rd image in carousel - :background_image, # str: url of large profile background - :remove_background_image, #bool carrierwave - :logo, # str: small logo image url for searching - :zip_code, # int - :website, # str: their own website url - :categories, # text [str]: see the constant Categories - :achievements, # text [str]: highlights about this org - :full_description, # text - :state_code, # str: two-letter state code (eg. CA) - :statement, # str: bank statement for donations towards the nonprofit - :city, # str - :slug, # str - :city_slug, #str - :state_code_slug, #str - :ein, # str: employee identification number - :published, # boolean; whether to display this profile - :vetted, # bool: Whether a super admin (one of CommitChange's employees) have approved this org - :verification_status, # str (either 'pending', 'unverified', 'escalated', 'verified' -- whether the org has submitted the identity verification form and it has been approved) - :latitude, # float: geocoder gem - :longitude, # float: geocoder gem - :timezone, # str - :address, # text - :thank_you_note, # text - :referrer, # str - :no_anon, # bool: whether to allow anonymous donations - :roles_attributes, - :brand_font, #string (lowercase key eg. 'helvetica') - :brand_color, #string (hex color value) - :hide_activity_feed, # bool - :tracking_script, - :facebook, #string (url) - :twitter, #string (url) - :youtube, #string (url) - :instagram, #string (url) - :blog, #string (url) - :card_failure_message_top, # text - :card_failure_message_bottom, # text - :autocomplete_supporter_address # boolean + include Image::AttachmentExtensions + # :name, # str + # :stripe_account_id, # str + # :summary, # text: paragraph-sized organization summary + # :tagline, # str + # :email, # str: public organization contact email + # :phone, # str: public org contact phone + # :main_image, # str: url of featured image - first image in profile carousel + # :second_image, # str: url of 2nd image in carousel + # :third_image, # str: url of 3rd image in carousel + # :background_image, # str: url of large profile background + # :remove_background_image, #bool carrierwave + # :logo, # str: small logo image url for searching + # :zip_code, # int + # :website, # str: their own website url + # :categories, # text [str]: see the constant Categories + # :achievements, # text [str]: highlights about this org + # :full_description, # text + # :state_code, # str: two-letter state code (eg. CA) + # :statement, # str: bank statement for donations towards the nonprofit + # :city, # str + # :slug, # str + # :city_slug, #str + # :state_code_slug, #str + # :ein, # str: employee identification number + # :published, # boolean; whether to display this profile + # :vetted, # bool: Whether a super admin (one of CommitChange's employees) have approved this org + # :verification_status, # str (either 'pending', 'unverified', 'escalated', 'verified' -- whether the org has submitted the identity verification form and it has been approved) + # :latitude, # float: geocoder gem + # :longitude, # float: geocoder gem + # :timezone, # str + # :address, # text + # :thank_you_note, # text + # :referrer, # str + # :no_anon, # bool: whether to allow anonymous donations + # :roles_attributes, + # :brand_font, #string (lowercase key eg. 'helvetica') + # :brand_color, #string (hex color value) + # :hide_activity_feed, # bool + # :tracking_script, + # :facebook, #string (url) + # :twitter, #string (url) + # :youtube, #string (url) + # :instagram, #string (url) + # :blog, #string (url) + # :card_failure_message_top, # text + # :card_failure_message_bottom, # text + # :autocomplete_supporter_address # boolean has_many :payouts has_many :charges @@ -64,16 +66,18 @@ class Nonprofit < ActiveRecord::Base has_many :campaigns, dependent: :destroy has_many :events, dependent: :destroy has_many :tickets, through: :events + has_many :roles, as: :host, dependent: :destroy has_many :users, through: :roles has_many :tag_masters, dependent: :destroy has_many :custom_field_masters, dependent: :destroy - has_many :roles, as: :host, dependent: :destroy + has_many :activities, as: :host, dependent: :destroy has_many :imports has_many :email_settings has_many :cards, as: :holder - has_one :bank_account, dependent: :destroy, conditions: "COALESCE(deleted, false) = false" + has_one :bank_account, -> { where('COALESCE(deleted, false) = false') }, + dependent: :destroy has_one :billing_subscription, dependent: :destroy has_one :billing_plan, through: :billing_subscription has_one :miscellaneous_np_info @@ -81,19 +85,42 @@ class Nonprofit < ActiveRecord::Base validates :name, presence: true validates :city, presence: true validates :state_code, presence: true - validates :email, format: { with: Email::Regex }, allow_blank: true - validates_uniqueness_of :slug, scope: [:city_slug, :state_code_slug] + validates_format_of :email, with: Email::Regex, allow_nil: true + validates_format_of :website, with: URI.regexp(['https', 'http']), allow_nil: true validates_presence_of :slug + validates_uniqueness_of :slug, scope: %i[city_slug state_code_slug] - scope :vetted, -> {where(vetted: true)} - scope :identity_verified, -> {where(verification_status: 'verified')} - scope :published, -> {where(published: true)} + validates_presence_of :user_id, on: :create, unless: -> {register_np_only} + validate :user_is_valid, on: :create, unless: -> {register_np_only} + validate :user_registerable_as_admin, on: :create, unless: -> {register_np_only} - mount_uploader :main_image, NonprofitUploader - mount_uploader :second_image, NonprofitUploader - mount_uploader :third_image, NonprofitUploader - mount_uploader :background_image, NonprofitBackgroundUploader - mount_uploader :logo, NonprofitLogoUploader + scope :vetted, -> { where(vetted: true) } + scope :identity_verified, -> { where(verification_status: 'verified') } + scope :published, -> { where(published: true) } + + has_one_attached :main_image + has_one_attached :second_image + has_one_attached :third_image + has_one_attached :background_image + has_one_attached :logo + + # way too wordy + has_one_attached_with_sizes(:logo, {small: 30, normal: 100, large: 180}) + has_one_attached_with_sizes(:background_image, {normal: [1000,600]}) + has_one_attached_with_sizes(:main_image, {nonprofit_carousel: [590, 338], thumb: [188, 120], thumb_explore: [100, 100]}) + has_one_attached_with_sizes(:second_image, {nonprofit_carousel: [590, 338], thumb: [188, 120], thumb_explore: [100, 100]}) + has_one_attached_with_sizes(:third_image, {nonprofit_carousel: [590, 338], thumb: [188, 120], thumb_explore: [100, 100]}) + + has_one_attached_with_default(:logo, Image::DefaultProfileUrl, + filename: "logo_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") + has_one_attached_with_default(:background_image, Image::DefaultNonprofitUrl, + filename: "background_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultNonprofitUrl).extname}") + has_one_attached_with_default(:main_image, Image::DefaultProfileUrl, + filename: "main_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") + has_one_attached_with_default(:second_image, Image::DefaultProfileUrl, + filename: "second_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") + has_one_attached_with_default(:third_image, Image::DefaultProfileUrl, + filename: "third_image_#{SecureRandom.uuid}#{Pathname.new(Image::DefaultProfileUrl).extname}") serialize :achievements, Array serialize :categories, Array @@ -101,20 +128,22 @@ class Nonprofit < ActiveRecord::Base geocoded_by :full_address before_validation(on: :create) do - self.set_slugs + set_slugs + set_user self end + after_create :build_admin_role, unless: -> {register_np_only} + # Register (create) a nonprofit with an initial admin def self.register(user, params) - np = self.create ConstructNonprofit.construct(user, params) + np = create ConstructNonprofit.construct(user, params) role = Role.create(user: user, name: 'nonprofit_admin', host: np) if np.valid? - return np + np end - def nonprofit_personnel_emails - self.roles.nonprofit_personnel.joins(:user).pluck('users.email') + roles.nonprofit_personnel.joins(:user).pluck('users.email') end def total_recurring @@ -123,62 +152,78 @@ class Nonprofit < ActiveRecord::Base def donation_history_monthly donation_history_monthly = [] - donations.order("created_at") - .group_by{|d| d.created_at.beginning_of_month} - .each{|_, ds| donation_history_monthly.push(ds.map(&:amount).sum)} + donations.order('created_at') + .group_by { |d| d.created_at.beginning_of_month } + .each { |_, ds| donation_history_monthly.push(ds.map(&:amount).sum) } donation_history_monthly end def as_json(options = {}) h = super(options) - h[:url] = self.url + h[:url] = url h end def url - "/#{self.state_code_slug}/#{self.city_slug}/#{self.slug}" + "/#{state_code_slug}/#{city_slug}/#{slug}" end def set_slugs - unless (self.slug) - self.slug = Format::Url.convert_to_slug self.name - end - unless (self.city_slug) - self.city_slug = Format::Url.convert_to_slug self.city - end + self.slug = Format::Url.convert_to_slug name unless slug + self.city_slug = Format::Url.convert_to_slug city unless city_slug - unless (self.state_code_slug) - self.state_code_slug = Format::Url.convert_to_slug self.state_code + unless state_code_slug + self.state_code_slug = Format::Url.convert_to_slug state_code + end + if Nonprofit.where(slug: slug, city_slug: city_slug, state_code_slug: state_code_slug).any? + correct_nonunique_slug + end + self + end + + def correct_nonunique_slug + begin + slug = SlugNonprofitNamingAlgorithm.new(self.state_code_slug, self.city_slug).create_copy_name(self.slug) + self.slug = slug + rescue UnableToCreateNameCopyError + errors.add(:slug, "could not be created.") + end + end + + def set_user + if (user_id && User.where(id: user_id).any?) + @user = User.find(user_id) end self end def full_address - Format::Address.full_address(self.address, self.city, self.state_code) + Format::Address.full_address(address, city, state_code) end def total_raised - QueryPayments.get_payout_totals( QueryPayments.ids_for_payout(self.id))['net_amount'] + QueryPayments.get_payout_totals(QueryPayments.ids_for_payout(id))['net_amount'] end def can_make_payouts - self.vetted && - self.verification_status == 'verified' && - self.bank_account && - !self.bank_account.pending_verification + vetted && + verification_status == 'verified' && + bank_account && + !bank_account.pending_verification end def active_cards - cards.where("COALESCE(cards.inactive, FALSE) = FALSE") + cards.where('COALESCE(cards.inactive, FALSE) = FALSE') end # @param [Card] card the new active_card def active_card=(card) unless card.class == Card - raise ArgumentError.new "Pass a card to active_card or else" + raise ArgumentError, 'Pass a card to active_card or else' end + Card.transaction do - active_cards.update_all :inactive => true + active_cards.update_all inactive: true return cards << card end end @@ -188,14 +233,38 @@ class Nonprofit < ActiveRecord::Base end def create_active_card(card_data) - if (card_data[:inactive]) - raise ArgumentError.new "This method is for creating active cards only" + if card_data[:inactive] + raise ArgumentError, 'This method is for creating active cards only' end - active_cards.update_all :inactive => true - return cards.create(card_data) + + active_cards.update_all inactive: true + cards.create(card_data) end def currency_symbol - Settings.intntl.all_currencies.find{|i| i.abbv.downcase == currency.downcase}&.symbol + Settings.intntl.all_currencies.find { |i| i.abbv.casecmp(currency).zero? }&.symbol end + +private + def build_admin_role + role = user.roles.build(host: self, name: 'nonprofit_admin') + role.save! + end + + def add_billing_subscription + billing_plan = BillingPlan.find(Settings.default_bp.id) + b_sub = build_billing_subscription(billing_plan: billing_plan, status: 'active') + b_sub.save! + end + + def user_registerable_as_admin + if user && user.roles.nonprofit_admins.any? + errors.add(:user_id, "cannot already be an admin for a nonprofit.") + end + end + + def user_is_valid + (user && user.is_a?(User)) || errors.add(:user_id, "is not a valid user") + end + end diff --git a/app/models/nonprofit_account.rb b/app/models/nonprofit_account.rb index a3bff211..c30938dd 100644 --- a/app/models/nonprofit_account.rb +++ b/app/models/nonprofit_account.rb @@ -1,13 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class NonprofitAccount < ActiveRecord::Base +class NonprofitAccount < ApplicationRecord + # :stripe_account_id, #str + # :nonprofit, :nonprofit_id #int - attr_accessible \ - :stripe_account_id, #str - :nonprofit, :nonprofit_id #int - - belongs_to :nonprofit - - validates :nonprofit, presence: true - validates :stripe_account_id, presence: true + belongs_to :nonprofit + validates :nonprofit, presence: true + validates :stripe_account_id, presence: true end diff --git a/app/models/offsite_payment.rb b/app/models/offsite_payment.rb index 4e4cf03a..12f0606b 100644 --- a/app/models/offsite_payment.rb +++ b/app/models/offsite_payment.rb @@ -1,10 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class OffsitePayment < ActiveRecord::Base - - attr_accessible :gross_amount, :kind, :date, :check_number - belongs_to :payment, dependent: :destroy - belongs_to :donation - belongs_to :nonprofit - belongs_to :supporter - +class OffsitePayment < ApplicationRecord +# :gross_amount, +# :kind, +# :date, +# :check_number + belongs_to :payment, dependent: :destroy + belongs_to :donation + belongs_to :nonprofit + belongs_to :supporter end diff --git a/app/models/payment.rb b/app/models/payment.rb index e916a588..17214de7 100644 --- a/app/models/payment.rb +++ b/app/models/payment.rb @@ -1,29 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # A payment represents the event where a nonprofit receives money from a supporter # If connected to a charge, this represents money potentially debited to the nonprofit's account # If connected to an offsite_payment, this is money the nonprofit is recording for convenience. -class Payment < ActiveRecord::Base - - attr_accessible \ - :towards, - :gross_amount, - :refund_total, - :fee_total, - :kind, - :date - - belongs_to :supporter - belongs_to :nonprofit - has_one :charge - has_one :offsite_payment - has_one :refund - has_one :dispute - belongs_to :donation - has_many :tickets - has_one :campaign, through: :donation - has_many :events, through: :tickets - has_many :payment_payouts - has_many :charges +class Payment < ApplicationRecord + # :towards, + # :gross_amount, + # :refund_total, + # :fee_total, + # :kind, + # :date + belongs_to :supporter + belongs_to :nonprofit + has_one :charge + has_one :offsite_payment + has_one :refund + has_one :dispute + belongs_to :donation + has_many :tickets + has_one :campaign, through: :donation + has_many :events, through: :tickets + has_many :payment_payouts + has_many :charges end diff --git a/app/models/payment_import.rb b/app/models/payment_import.rb index ebe96e11..244c9e69 100644 --- a/app/models/payment_import.rb +++ b/app/models/payment_import.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class PaymentImport < ActiveRecord::Base - attr_accessible :nonprofit, :user +class PaymentImport < ApplicationRecord + # :nonprofit, + # :user has_and_belongs_to_many :donations belongs_to :nonprofit belongs_to :user diff --git a/app/models/payment_payout.rb b/app/models/payment_payout.rb index a7a840d1..c817f63e 100644 --- a/app/models/payment_payout.rb +++ b/app/models/payment_payout.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # charge_payouts are a join table between charges and payouts # @@ -11,19 +13,16 @@ # It's also nice to keep a historical records of fees for individual donations # since our fees will continue to change as our transaction volume increases -class PaymentPayout < ActiveRecord::Base +class PaymentPayout < ApplicationRecord + # :payment_id, :payment, + # :charge_id, :charge, # deprecated + # :payout_id, :payout, + # :total_fees # int (cents) - attr_accessible \ - :payment_id, :payment, - :charge_id, :charge, # deprecated - :payout_id, :payout, - :total_fees # int (cents) + belongs_to :charge # deprecated + belongs_to :payment + belongs_to :payout - belongs_to :charge # deprecated - belongs_to :payment - belongs_to :payout - - validates :payment, presence: true - validates :payout, presence: true + validates :payment, presence: true + validates :payout, presence: true end - diff --git a/app/models/payout.rb b/app/models/payout.rb index aa53c033..e5911cc6 100644 --- a/app/models/payout.rb +++ b/app/models/payout.rb @@ -1,57 +1,54 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Payouts record a credit of the total pending balance on a nonprofit's account # to their bank account or debit card # # These are tied to Stripe transfers -class Payout < ActiveRecord::Base +class Payout < ApplicationRecord + # :scheduled, # bool (whether this was made automatically at the beginning of the month) + # :count, # int (number of donations for this payout) + # :ach_fee, # int (in cents, the total fee for the payout itself) + # :gross_amount, # int (in cents, total amount before fees) + # :fee_total, # int (in cents, total amount of fees) + # :net_amount, # int (in cents, total amount after fees for this payout) + # :email, # str (cache of user email who issued this) + # :user_ip, # str (ip address of the user who made this payout) + # :status, # str ('pending', 'paid', 'canceled', or 'failed') + # :failure_message, # str + # :bank_name, # str: cache of the nonprofit's bank name + # :stripe_transfer_id, # str + # :nonprofit_id, :nonprofit - attr_accessible \ - :scheduled, # bool (whether this was made automatically at the beginning of the month) - :count, # int (number of donations for this payout) - :ach_fee, # int (in cents, the total fee for the payout itself) - :gross_amount, # int (in cents, total amount before fees) - :fee_total, # int (in cents, total amount of fees) - :net_amount, # int (in cents, total amount after fees for this payout) - :email, # str (cache of user email who issued this) - :user_ip, # str (ip address of the user who made this payout) - :status, # str ('pending', 'paid', 'canceled', or 'failed') - :failure_message, # str - :bank_name, # str: cache of the nonprofit's bank name - :stripe_transfer_id, # str - :nonprofit_id, :nonprofit + belongs_to :nonprofit + has_one :bank_account, through: :nonprofit + has_many :payment_payouts + has_many :payments, through: :payment_payouts - belongs_to :nonprofit - has_one :bank_account, through: :nonprofit - has_many :payment_payouts - has_many :payments, through: :payment_payouts + validates :stripe_transfer_id, presence: true, uniqueness: true + validates :nonprofit, presence: true + validates :bank_account, presence: true + validates :email, presence: true + validates :net_amount, presence: true, numericality: { greater_than: 0 } + validate :nonprofit_must_be_vetted, on: :create + validate :nonprofit_must_have_identity_verified, on: :create + validate :bank_account_must_be_confirmed, on: :create - validates :stripe_transfer_id, presence: true, uniqueness: true - validates :nonprofit, presence: true - validates :bank_account, presence: true - validates :email, presence: true - validates :net_amount, presence: true, numericality: {greater_than: 0} - validate :nonprofit_must_be_vetted, on: :create - validate :nonprofit_must_have_identity_verified, on: :create - validate :bank_account_must_be_confirmed, on: :create + scope :pending, -> { where(status: 'pending') } + scope :paid, -> { where(status: %w[paid succeeded]) } - scope :pending, -> {where(status: 'pending')} - scope :paid, -> {where(status: ['paid', 'succeeded'])} + def bank_account_must_be_confirmed + if bank_account&.pending_verification + errors.add(:bank_account, 'must be confirmed via email') + end + end + def nonprofit_must_have_identity_verified + errors.add(:nonprofit, 'must be verified') unless nonprofit && nonprofit.verification_status == 'verified' + end - def bank_account_must_be_confirmed - if self.bank_account && self.bank_account.pending_verification - self.errors.add(:bank_account, 'must be confirmed via email') - end - end - - def nonprofit_must_have_identity_verified - self.errors.add(:nonprofit, "must be verified") unless self.nonprofit && self.nonprofit.verification_status == 'verified' - end - - def nonprofit_must_be_vetted - self.errors.add(:nonprofit, "must be vetted") unless self.nonprofit && self.nonprofit.vetted - end - + def nonprofit_must_be_vetted + errors.add(:nonprofit, 'must be vetted') unless nonprofit&.vetted + end end - diff --git a/app/models/profile.rb b/app/models/profile.rb index 5ff2f392..e53d5d3a 100755 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -1,119 +1,115 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Profile < ActiveRecord::Base +class Profile < ApplicationRecord + include Image::AttachmentExtensions + # :registered, # bool + # :mini_bio, + # :first_name, # str + # :last_name, # str + # :name, + # :phone, # str + # :address, # str + # :email, # str + # :city, # str + # :state_code, # str (eg. CA) + # :zip_code, # str + # :privacy_settings, # text [str]: XXX deprecated + # :picture, # str: either their social network pic or a stored pic on S3 + # :anonymous, # bool: negates all privacy_settings + # :city_state, + # :user_id - attr_accessible \ - :registered, # bool - :mini_bio, - :first_name, # str - :last_name, # str - :name, - :phone, # str - :address, # str - :email, # str - :city, # str - :state_code, # str (eg. CA) - :zip_code, # str - :privacy_settings, # text [str]: XXX deprecated - :picture, # str: either their social network pic or a stored pic on S3 - :anonymous, # bool: negates all privacy_settings - :city_state, - :user_id + validates :email, format: { with: Email::Regex }, allow_blank: true - validates :email, format: {with: Email::Regex}, allow_blank: true + attr_accessor :email, :city_state - attr_accessor :email, :city_state + serialize :privacy_settings, Array - serialize :privacy_settings, Array + has_one_attached :picture + has_one_attached_with_sizes(:picture, {normal: 150, medium:100, tiny: 50}) - mount_uploader :picture, ProfileUploader + belongs_to :user + has_many :activities # Activities this profile has created + has_many :supporters + has_many :donations + has_many :campaigns + has_many :events + has_many :recurring_donations + has_many :comments, as: :host, dependent: :destroy + has_many :nonprofits, through: :supporters + has_many :activities, dependent: :destroy + # has_one :card, as: :holder - belongs_to :user - has_many :activities # Activities this profile has created - has_many :supporters - has_many :donations - has_many :campaigns - has_many :events - has_many :recurring_donations - has_many :comments, as: :host, dependent: :destroy - has_many :nonprofits, through: :supporters - has_many :activities, dependent: :destroy -# has_one :card, as: :holder + # accepts_nested_attributes_for :card - #accepts_nested_attributes_for :card + scope :non_anon, -> { where(anonymous: [nil, false]) } - scope :non_anon, -> {where(anonymous: [nil, false])} + before_validation(on: :create) do + set_defaults + self + end - before_validation(on: :create) do - self.set_defaults - self - end + def set_defaults + self.name ||= user.name if user + self.email ||= user.email if user + picture.attach(io: File.open(Settings.default.image.profile), + filename: "profile-image.png") unless self.picture.attached? + if self.name.blank? && first_name.present? && last_name.present? + self.name ||= first_name + ' ' + last_name + end + end - def set_defaults - self.name ||= self.user.name if self.user - self.email ||= self.user.email if self.user - self.picture ||= self.user.picture if self.user - if self.name.blank? && self.first_name.present? && self.last_name.present? - self.name ||= self.first_name + ' ' + self.last_name - end - end + # Queries - # Queries + def recent_donations(npo_id) + donations.valid.order('created_at').where(nonprofit_id: npo_id).take(10) + end - def recent_donations(npo_id) - self.donations.valid.order("created_at").where(nonprofit_id: npo_id).take(10) - end + # Attrs - # Attrs + def total_given_to(nonprofit) + donations.valid.where(nonprofit_id: nonprofit.id).pluck(:amount).sum + end - def total_given_to(nonprofit) - self.donations.valid.where(nonprofit_id: nonprofit.id).pluck(:amount).sum - end + def monthly_giving(nonprofit_id) + donations.where(nonprofit_id: nonprofit_id).map(&:amount).sum + end - def monthly_giving(nonprofit_id) - self.donations.where(nonprofit_id: nonprofit_id).map(&:amount).sum - end + def monthly_total_giving + donations.map(&:amount).sum + end - def monthly_total_giving - self.donations.map(&:amount).sum - end + def full_name + "#{first_name} #{last_name}" + end - def full_name - "#{self.first_name} #{self.last_name}" - end + def supporter_name + self.name.blank? ? 'A Supporter' : self.name + end - def supporter_name - self.name.blank? ? "A Supporter" : self.name - end + def get_profile_picture(size = :normal) + # Can be, in order of precedence: your uploaded photo, facebook picture, or + # default image + if user.picture + return user.get_picture(size) + else + return picture_url(size) + end - def get_profile_picture(size=:normal) - # Can be, in order of precedence: your uploaded photo, facebook picture, or - # default image - if self.user.picture - return self.user.get_picture(size) - else - return self.picture_url(size) - end - # Either does not want photo shown or has none uploaded. - return Image::DefaultProfileUrl - end + # Either does not want photo shown or has none uploaded. + Image::DefaultProfileUrl + end - def url - Rails.application.routes.url_helpers.profile_path(self) - end + def url + Rails.application.routes.url_helpers.profile_path(self) + end - def as_json(options = {}) - h = super(options) - h[:pic_tiny] = self.get_profile_picture :tiny - h[:url] = self.url - h - end - - # Cache setters - - def set_caches! - self.total_raised = self.donations.pluck(:amount).sum - self.total_recurring = self.recurring_donations.active.pluck(:amount).sum - self.save! - end + # Cache setters + def set_caches! + self.total_raised = donations.pluck(:amount).sum + self.total_recurring = recurring_donations.active.pluck(:amount).sum + save! + end end diff --git a/app/models/recurring_donation.rb b/app/models/recurring_donation.rb index 1a088b1e..02c40da6 100644 --- a/app/models/recurring_donation.rb +++ b/app/models/recurring_donation.rb @@ -1,26 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class RecurringDonation < ActiveRecord::Base +require 'timespan' - attr_accessible \ - :amount, # int (cents) - :active, # bool (whether this recurring donation should still be paid) - :paydate, # int (fixed date of the month for monthly recurring donations) - :interval, # int (interval of time, ie the '3' in '3 months') - :time_unit, # str ('month', 'day', 'week', or 'year') - :start_date, # date (when to start this recurring donation) - :end_date, # date (when to deactivate this recurring donation) - :n_failures, # int (how many times the charge has failed) - :edit_token, # str / uuid to validate the editing page, linked from their email client - :cancelled_by, # str email of user/supporter who made the cancellation - :cancelled_at, # datetime of user/supporter who made the cancellation - :donation_id, :donation, - :nonprofit_id, :nonprofit, - :supporter_id #used because things are messed up in the datamodel +class RecurringDonation < ApplicationRecord + # :amount, # int (cents) + # :active, # bool (whether this recurring donation should still be paid) + # :paydate, # int (fixed date of the month for monthly recurring donations) + # :interval, # int (interval of time, ie the '3' in '3 months') + # :time_unit, # str ('month', 'day', 'week', or 'year') + # :start_date, # date (when to start this recurring donation) + # :end_date, # date (when to deactivate this recurring donation) + # :n_failures, # int (how many times the charge has failed) + # :edit_token, # str / uuid to validate the editing page, linked from their email client + # :cancelled_by, # str email of user/supporter who made the cancellation + # :cancelled_at, # datetime of user/supporter who made the cancellation + # :donation_id, :donation, + # :nonprofit_id, :nonprofit, + # :supporter_id #used because things are messed up in the datamodel - scope :active, -> {where(active: true)} - scope :inactive, -> {where(active: [false,nil])} - scope :monthly, -> {where(time_unit: 'month', interval: 1)} - scope :annual, -> {where(time_unit: 'year', interval: 1)} + scope :active, -> { where(active: true) } + scope :inactive, -> { where(active: [false, nil]) } + scope :monthly, -> { where(time_unit: 'month', interval: 1) } + scope :annual, -> { where(time_unit: 'year', interval: 1) } belongs_to :donation belongs_to :nonprofit @@ -28,37 +30,30 @@ class RecurringDonation < ActiveRecord::Base has_one :card, through: :donation has_one :supporter, through: :donation - validates :paydate, numericality: {less_than: 29}, allow_blank: true + validates :paydate, numericality: { less_than: 29 }, allow_blank: true validates :donation_id, presence: true validates :nonprofit_id, presence: true validates :start_date, presence: true - validates :interval, presence: true, numericality: {greater_than: 0} - validates :time_unit, presence: true, inclusion: {in: Timespan::Units} + validates :interval, presence: true, numericality: { greater_than: 0 } + validates :time_unit, presence: true, inclusion: { in: Timespan::Units } validates_associated :donation def most_recent_charge - if (self.charges) - return self.charges.sort_by { |c| c.created_at }.last() - end + charges&.max_by(&:created_at) end def most_recent_paid_charge - if (self.charges) - return self.charges.find_all {|c| c.paid?}.sort_by { |c| c.created_at }.last() - end + charges&.find_all(&:paid?)&.max_by(&:created_at) end def total_given - if (self.charges) - return self.charges.find_all(&:paid?).sum(&:amount) - end - + return charges.find_all(&:paid?).sum(&:amount) if charges end # XXX let's make these monthly_totals a query # Or just push it into the front-end def self.monthly_total - self.all.map(&:monthly_total).sum + all.map(&:monthly_total).sum end def monthly_total @@ -66,8 +61,7 @@ class RecurringDonation < ActiveRecord::Base 'week' => 4, 'day' => 30, 'year' => 0.0833 - }[self.interval] || 1 - return self.donation.amount * multiple + }[interval] || 1 + donation.amount * multiple end - end diff --git a/app/models/refund.rb b/app/models/refund.rb index f7fff991..faed9dff 100644 --- a/app/models/refund.rb +++ b/app/models/refund.rb @@ -1,26 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Refund < ActiveRecord::Base +class Refund < ApplicationRecord + Reasons = %i[duplicate fraudulent requested_by_customer].freeze + # :amount, # int + # :comment, # text + # :reason, # str ('duplicate', 'fraudulent', or 'requested_by_customer') + # :stripe_refund_id, + # :disbursed, # boolean (whether this refund has been counted in a payout) + # :failure_message, # str (accessor for storing the Stripe error message) + # :user_id, :user, # user who made this refund + # :payment_id, :payment, # negative payment that records this refund + # :charge_id, :charge - Reasons = [:duplicate, :fraudulent, :requested_by_customer] + attr_accessor :failure_message - attr_accessible \ - :amount, # int - :comment, # text - :reason, # str ('duplicate', 'fraudulent', or 'requested_by_customer') - :stripe_refund_id, - :disbursed, # boolean (whether this refund has been counted in a payout) - :failure_message, # str (accessor for storing the Stripe error message) - :user_id, :user, # user who made this refund - :payment_id, :payment, # negative payment that records this refund - :charge_id, :charge - - attr_accessor :failure_message - - belongs_to :charge - belongs_to :payment - - scope :not_disbursed, ->{where(disbursed: [nil, false])} - scope :disbursed, ->{where(disbursed: [true])} + belongs_to :charge + belongs_to :payment + scope :not_disbursed, -> { where(disbursed: [nil, false]) } + scope :disbursed, -> { where(disbursed: [true]) } end - diff --git a/app/models/role.rb b/app/models/role.rb index 2c8401bf..5b3002db 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,49 +1,56 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Role < ActiveRecord::Base +class Role < ApplicationRecord + Names = [ + :super_admin, # global access + :super_associate, # global access to everything except bank acct info + :nonprofit_admin, # npo scoped access to everything + :nonprofit_associate, # npo scoped access to everything except bank acct info + :campaign_editor, # fundraising tools, without dashboard access + :event_editor # // + ].freeze - Names = [ - :super_admin, # global access - :super_associate, # global access to everything except bank acct info - :nonprofit_admin, # npo scoped access to everything - :nonprofit_associate, # npo scoped access to everything except bank acct info - :campaign_editor, # fundraising tools, without dashboard access - :event_editor # // - ] + # :name, + # :user_id, :user, + # :host, :host_id, :host_type # nil, "Nonprofit", "Campaign", "Event" - attr_accessible \ - :name, - :user_id, :user, - :host, :host_id, :host_type # nil, "Nonprofit", "Campaign", "Event" + belongs_to :user + belongs_to :host, polymorphic: true - belongs_to :user - belongs_to :host, polymorphic: true + scope :super_admins, -> { where(name: :super_admin) } + scope :super_associate, -> { where(name: :super_associate) } + scope :nonprofit_admins, -> { where(name: :nonprofit_admin) } + scope :nonprofit_personnel, -> { where(name: %i[nonprofit_associate nonprofit_admin]) } + scope :campaign_editors, -> { where(name: :campaign_editor) } + scope :event_editors, -> { where(name: :event_editor) } - scope :super_admins, -> {where(name: :super_admin)} - scope :super_associate, -> {where(name: :super_associate)} - scope :nonprofit_admins, -> {where(name: :nonprofit_admin)} - scope :nonprofit_personnel, -> {where(name: [:nonprofit_associate, :nonprofit_admin])} - scope :campaign_editors, -> {where(name: :campaign_editor)} - scope :event_editors, -> {where(name: :event_editor)} - - validates :user, presence: true - validates :name, inclusion: {in: Names} - validates :host, presence: true, unless: [:super_admin?, :super_associate?] - - def name; super.to_sym; end - def super_admin?; name == :super_admin; end - def super_associate?; name == :super_associate; end - - def self.create_for_nonprofit(role_name, email, nonprofit) - user = User.find_or_create_with_email(email) - role = Role.create(user: user, name: role_name, host: nonprofit) - return role unless role.valid? - if user.confirmed? - NonprofitAdminMailer.delay.existing_invite(role) - else - NonprofitAdminMailer.delay.new_invite(role, user.make_confirmation_token!) - end - return role - end + validates :user, presence: true + validates :name, inclusion: { in: Names } + validates :host, presence: true, unless: %i[super_admin? super_associate?] + def name + super.to_sym end + def super_admin? + name == :super_admin +end + + def super_associate? + name == :super_associate +end + + def self.create_for_nonprofit(role_name, email, nonprofit) + user = User.find_or_create_with_email(email) + role = Role.create(user: user, name: role_name, host: nonprofit) + return role unless role.valid? + + if user.confirmed? + RoleAddedJob.perform_later role + else + UserInviteCreateJob.perform_later role, user.make_confirmation_token! + end + role + end +end diff --git a/app/models/source_token.rb b/app/models/source_token.rb index 2d785ce1..75e02055 100644 --- a/app/models/source_token.rb +++ b/app/models/source_token.rb @@ -1,7 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class SourceToken < ActiveRecord::Base +class SourceToken < ApplicationRecord self.primary_key = :token - attr_accessible :expiration, :token, :max_uses, :total_uses - belongs_to :tokenizable, :polymorphic => true + # :expiration, + # :token, + # :max_uses, + # :total_uses + belongs_to :tokenizable, polymorphic: true belongs_to :event end diff --git a/app/models/supporter.rb b/app/models/supporter.rb index 8d5a89de..8b115218 100644 --- a/app/models/supporter.rb +++ b/app/models/supporter.rb @@ -1,33 +1,33 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Supporter < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :search_vectors, - :profile_id, :profile, - :nonprofit_id, :nonprofit, - :full_contact_info, :full_contact_info_id, - :import_id, :import, - :name, - :first_name, - :last_name, - :email, - :address, - :city, - :state_code, - :country, - :phone, - :organization, - :latitude, - :locale, - :longitude, - :zip_code, - :total_raised, - :notes, - :fields, - :anonymous, - :deleted, # bool (flag for soft delete) - :email_unsubscribe_uuid, #string - :is_unsubscribed_from_emails #bool +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class Supporter < ApplicationRecord + # :search_vectors, + # :profile_id, :profile, + # :nonprofit_id, :nonprofit, + # :full_contact_info, :full_contact_info_id, + # :import_id, :import, + # :name, + # :first_name, + # :last_name, + # :email, + # :address, + # :city, + # :state_code, + # :country, + # :phone, + # :organization, + # :latitude, + # :locale, + # :longitude, + # :zip_code, + # :total_raised, + # :notes, + # :fields, + # :anonymous, + # :deleted, # bool (flag for soft delete) + # :email_unsubscribe_uuid, #string + # :is_unsubscribed_from_emails #bool belongs_to :profile belongs_to :nonprofit @@ -48,10 +48,10 @@ class Supporter < ActiveRecord::Base has_many :tag_masters, through: :tag_joins has_many :custom_field_joins, dependent: :destroy has_many :custom_field_masters, through: :custom_field_joins - belongs_to :merged_into, class_name: 'Supporter', :foreign_key => 'merged_into' + belongs_to :merged_into, class_name: 'Supporter', foreign_key: 'merged_into' - validates :nonprofit, :presence => true - scope :not_deleted, -> {where(deleted: false)} + validates :nonprofit, presence: true + scope :not_deleted, -> { where(deleted: false) } geocoded_by :full_address reverse_geocoded_by :latitude, :longitude do |obj, results| @@ -65,22 +65,21 @@ class Supporter < ActiveRecord::Base end end - def profile_picture size=:normal - return unless self.profile - self.profile.get_profile_picture(size) - end + def profile_picture(size = :normal) + return unless profile + profile.get_profile_picture(size) + end def as_json(options = {}) h = super(options) - h[:pic_tiny] = self.profile_picture(:tiny) - h[:pic_normal] = self.profile_picture(:normal) - h[:url] = self.profile && Rails.application.routes.url_helpers.profile_path(self.profile) - return h + h[:pic_tiny] = profile_picture(:tiny) + h[:pic_normal] = profile_picture(:normal) + h[:url] = profile && Rails.application.routes.url_helpers.profile_path(profile) + h end def full_address - Format::Address.full_address(self.address, self.city, self.state_code) + Format::Address.full_address(address, city, state_code) end - end diff --git a/app/models/supporter_email.rb b/app/models/supporter_email.rb index 5ac0d8d9..340afa3a 100644 --- a/app/models/supporter_email.rb +++ b/app/models/supporter_email.rb @@ -1,16 +1,17 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class SupporterEmail < ActiveRecord::Base - attr_accessible \ - :to, - :from, - :subject, - :body, - :recipient_count, - :supporter_id, :supporter, - :nonprofit_id, - :gmail_thread_id +# frozen_string_literal: true - belongs_to :supporter - validates_presence_of :nonprofit_id - has_many :activities, as: :attachment, dependent: :destroy +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class SupporterEmail < ApplicationRecord + # :to, + # :from, + # :subject, + # :body, + # :recipient_count, + # :supporter_id, :supporter, + # :nonprofit_id, + # :gmail_thread_id + + belongs_to :supporter + validates_presence_of :nonprofit_id + has_many :activities, as: :attachment, dependent: :destroy end diff --git a/app/models/supporter_note.rb b/app/models/supporter_note.rb index dd920f07..cbeeaea9 100644 --- a/app/models/supporter_note.rb +++ b/app/models/supporter_note.rb @@ -1,14 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class SupporterNote < ActiveRecord::Base +class SupporterNote < ApplicationRecord + # :content, + # :supporter_id, :supporter - attr_accessible \ - :content, - :supporter_id, :supporter + belongs_to :supporter + has_many :activities, as: :attachment, dependent: :destroy - belongs_to :supporter - has_many :activities, as: :attachment, dependent: :destroy - - validates :content, length: {minimum: 1} - validates :supporter_id, presence: true + validates :content, length: { minimum: 1 } + validates :supporter_id, presence: true end - diff --git a/app/models/tag_join.rb b/app/models/tag_join.rb index 5f0e402a..9cdbb154 100644 --- a/app/models/tag_join.rb +++ b/app/models/tag_join.rb @@ -1,24 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class TagJoin < ActiveRecord::Base +class TagJoin < ApplicationRecord + # :supporter, :supporter_id, + # :tag_master, :tag_master_id - attr_accessible \ - :supporter, :supporter_id, - :tag_master, :tag_master_id + validates :tag_master, presence: true - validates :tag_master, presence: true - - belongs_to :tag_master - belongs_to :supporter - - def name; self.tag_master.name; end - - def self.create_with_name(nonprofit, h) - tm = nonprofit.tag_masters.find_by_name(h['name']) - if tm.nil? - tm = nonprofit.tag_masters.create(name: h['name']) - end - self.create tag_master: tm, supporter_id: h['supporter_id'] - end + belongs_to :tag_master + belongs_to :supporter + def name + tag_master.name end + def self.create_with_name(nonprofit, h) + tm = nonprofit.tag_masters.find_by_name(h['name']) + tm = nonprofit.tag_masters.create(name: h['name']) if tm.nil? + create tag_master: tm, supporter_id: h['supporter_id'] + end +end diff --git a/app/models/tag_master.rb b/app/models/tag_master.rb index 6e9f0ffd..efca9eea 100644 --- a/app/models/tag_master.rb +++ b/app/models/tag_master.rb @@ -1,25 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class TagMaster < ActiveRecord::Base +class TagMaster < ApplicationRecord + # :nonprofit, :nonprofit_id, + # :name, + # :deleted, + # :created_at - attr_accessible \ - :nonprofit, :nonprofit_id, - :name, - :deleted, - :created_at + validates :name, presence: true + validate :no_dupes, on: :create - validates :name, presence: true - validate :no_dupes, on: :create + belongs_to :nonprofit + has_many :tag_joins, dependent: :destroy + has_one :email_list - belongs_to :nonprofit - has_many :tag_joins, dependent: :destroy - has_one :email_list + scope :not_deleted, -> { where(deleted: [nil, false]) } - scope :not_deleted, ->{where(deleted: [nil,false])} - - def no_dupes - return self if nonprofit.nil? - errors.add(:base, "Duplicate tag") if nonprofit.tag_masters.not_deleted.where(name: name).any? - end + def no_dupes + return self if nonprofit.nil? + errors.add(:base, 'Duplicate tag') if nonprofit.tag_masters.not_deleted.where(name: name).any? + end end - diff --git a/app/models/ticket.rb b/app/models/ticket.rb index e805ad89..0a19bc09 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -1,21 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Ticket < ActiveRecord::Base - - attr_accessible :note, :event_discount, :event_discount_id +class Ticket < ApplicationRecord + # :note, + # :event_discount, + # :event_discount_id belongs_to :event_discount - belongs_to :supporter - belongs_to :profile - belongs_to :ticket_level - belongs_to :event - belongs_to :charge - belongs_to :card - belongs_to :payment - belongs_to :source_token - has_one :nonprofit, through: :event - has_many :activities, as: :attachment, dependent: :destroy + belongs_to :supporter + belongs_to :profile + belongs_to :ticket_level + belongs_to :event + belongs_to :charge + belongs_to :card + belongs_to :payment + belongs_to :source_token + has_one :nonprofit, through: :event + has_many :activities, as: :attachment, dependent: :destroy - def related_tickets - payment.tickets.where('id != ?', self.id) - end + def related_tickets + payment.tickets.where('id != ?', id) + end end diff --git a/app/models/ticket_level.rb b/app/models/ticket_level.rb index 9d628782..95994901 100644 --- a/app/models/ticket_level.rb +++ b/app/models/ticket_level.rb @@ -1,31 +1,30 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class TicketLevel < ActiveRecord::Base +# frozen_string_literal: true - attr_accessible \ - :amount, #integer - :amount_dollars, #accessor, string - :name, #string - :description, #text - :quantity, #integer - :deleted, #bool for soft delete - :event_id, - :admin_only, #bool, only admins can create tickets for this level - :limit, #int: for limiting the number of tickets to be sold - :order #int: order in which to be displayed +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class TicketLevel < ApplicationRecord + # :amount, #integer + # :amount_dollars, #accessor, string + # :name, #string + # :description, #text + # :quantity, #integer + # :deleted, #bool for soft delete + # :event_id, + # :admin_only, #bool, only admins can create tickets for this level + # :limit, #int: for limiting the number of tickets to be sold + # :order #int: order in which to be displayed attr_accessor :amount_dollars has_many :tickets belongs_to :event - validates :name, :presence => true - validates :event_id, :presence => true + validates :name, presence: true + validates :event_id, presence: true - scope :not_deleted, ->{where(deleted: [false,nil])} + scope :not_deleted, -> { where(deleted: [false, nil]) } before_validation do - self.amount = Format::Currency.dollars_to_cents(self.amount_dollars) if self.amount_dollars.present? - self.amount = 0 if self.amount.nil? + self.amount = Format::Currency.dollars_to_cents(amount_dollars) if amount_dollars.present? + self.amount = 0 if amount.nil? end - end diff --git a/app/models/tracking.rb b/app/models/tracking.rb index d3a175c7..50485c58 100644 --- a/app/models/tracking.rb +++ b/app/models/tracking.rb @@ -1,6 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Tracking < ActiveRecord::Base - attr_accessible :utm_campaign, :utm_content, :utm_medium, :utm_source +class Tracking < ApplicationRecord + # :utm_campaign, + # :utm_content, + # :utm_medium, + # :utm_source belongs_to :donation end diff --git a/app/models/user.rb b/app/models/user.rb index 32f1e674..397edd83 100755 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,109 +1,100 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class User < ActiveRecord::Base +class User < ApplicationRecord + # :email, # str: balidated with Devise + # :password, # str: hashed with bcrypt + # :phone, # str + # :location, + # :city, + # :state_code, + # :password_confirmation, # accessor: used on registration + # :remember_me, # bool: don't sign user out for a while + # :provider, # str: OAuth provider + # :uid, # str: OAuth user ID + # :pending_password, # bool: User registered with oauth and did not set a password + # :name, # str: created with oauth + # :auto_generated, # bool: flag whether a password was auto-generated for this account + # :referer, # str: ID of the user who referred this account + # :latitude, + # :longitude, + # :reset_password_token, + # :reset_password_sent_at, + # :picture, # str: url for fb or twitter pic + # :current_password, # accessor: for updating pass + # :profile_attributes, + # :phone - attr_accessible \ - :email, # str: balidated with Devise - :password, # str: hashed with bcrypt - :phone, # str - :location, - :city, - :state_code, - :password_confirmation, # accessor: used on registration - :remember_me, # bool: don't sign user out for a while - :provider, # str: OAuth provider - :uid, # str: OAuth user ID - :pending_password, # bool: User registered with oauth and did not set a password - :name, # str: created with oauth - :auto_generated, # bool: flag whether a password was auto-generated for this account - :referer, # str: ID of the user who referred this account - :latitude, - :longitude, - :reset_password_token, - :reset_password_sent_at, - :picture, # str: url for fb or twitter pic - :current_password, # accessor: for updating pass - :profile_attributes, - :phone + geocoded_by :location - geocoded_by :location + devise :async, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable - devise :async, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable + attr_accessor :offsite_donation_id, :current_password - attr_accessor :offsite_donation_id, :current_password + validates :email, + presence: true, + uniqueness: { case_sensitive: false }, + format: { with: Email::Regex } - validates :email, - presence: true, - uniqueness: {case_sensitive: false}, - format: {with: Email::Regex} - - has_many :donations, through: :profile - has_many :roles, dependent: :destroy - has_one :profile, dependent: :destroy - has_many :imports + has_many :donations, through: :profile + has_many :roles, dependent: :destroy + has_one :profile, dependent: :destroy + has_many :imports has_many :email_settings - accepts_nested_attributes_for :profile + accepts_nested_attributes_for :profile - before_validation(on: :create) do - self.password = Devise.friendly_token.first(8) if self.auto_generated - self.build_profile if self.profile.nil? - self - end + before_validation(on: :create) do + self.password = Devise.friendly_token.first(8) if auto_generated + build_profile if profile.nil? + self + end # This creates the user in the normal way, but also sends the devise email confirmation email, which we don't want to send to np admins or anyone else def self.register_donor!(params) u = User.create!(params) u.send_confirmation_instructions - return u - end + u + end def self.find_or_create_with_email(em) - user = self.where("lower(email) = ?", em.downcase).first + user = where('lower(email) = ?', em.downcase).first return user if user.present? + User.create!(email: em, auto_generated: true) end - def profile_picture(size) - self.profile.picture_url(size) - end + def profile_picture(size) + profile.picture_url(size) + end + # Required by Devise for Omniauth + # https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview + def self.new_with_session(params, session) + super.tap do |user| + if data = session['devise.facebook_data'] && session['devise.facebook_data']['extra']['raw_info'] + user.email = data['email'] if user.email.blank? + end + end + end - # Required by Devise for Omniauth - # https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview - def self.new_with_session(params, session) - super.tap do |user| - if data = session['devise.facebook_data'] && session['devise.facebook_data']['extra']['raw_info'] - user.email = data['email'] if user.email.blank? - end - end - end - - # Don't require confirmation for new users -- they can still donate without confirmation - # https://github.com/plataformatec/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation - def confirmation_required? - false - end - - def as_json(options={}) - h = super(options) - h[:unconfirmed_email] = self.unconfirmed_email - h[:confirmed] = self.confirmed? - h[:profile] = self.profile.as_json - h - end + # Don't require confirmation for new users -- they can still donate without confirmation + # https://github.com/plataformatec/devise/wiki/How-To:-Override-confirmations-so-users-can-pick-their-own-passwords-as-part-of-confirmation-activation + def confirmation_required? + false + end # This is useful for manually generating a Devise user confirmation token so that we can get the confirmation URL with the correct token from anywhere def make_confirmation_token! raw, db = Devise.token_generator.generate(User, :confirmation_token) self.confirmation_token = db self.confirmation_sent_at = Time.now - self.save! - return raw + save! + raw end - def geocode! - #self.geocode - #self.save - end - + def geocode! + # self.geocode + # self.save + end end diff --git a/app/uploaders/campaign_background_image_uploader.rb b/app/uploaders/campaign_background_image_uploader.rb deleted file mode 100644 index 57b6c6cb..00000000 --- a/app/uploaders/campaign_background_image_uploader.rb +++ /dev/null @@ -1,26 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -# encoding: utf-8 - -class CampaignBackgroundImageUploader < CarrierWave::Uploader::Base - - include CarrierWave::MiniMagick - - - def store_dir - "uploads/campaigns/#{mounted_as}/#{model.id}" - end - - # Create different versions of your uploaded files: - version :normal do - process :resize_to_fill => [1000, 600] - end - - def extension_white_list - %w(jpg jpeg png) - end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end - -end diff --git a/app/uploaders/campaign_banner_image_uploader.rb b/app/uploaders/campaign_banner_image_uploader.rb deleted file mode 100644 index 1c4010d7..00000000 --- a/app/uploaders/campaign_banner_image_uploader.rb +++ /dev/null @@ -1,16 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CampaignBannerImageUploader < CarrierWave::Uploader::Base - include CarrierWave::MiniMagick - - def store_dir - "uploads/campaigns/#{mounted_as}/#{model.id}" - end - - def extension_white_list - %w(jpg jpeg png) - end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end -end diff --git a/app/uploaders/campaign_main_image_uploader.rb b/app/uploaders/campaign_main_image_uploader.rb deleted file mode 100644 index bcf66cf1..00000000 --- a/app/uploaders/campaign_main_image_uploader.rb +++ /dev/null @@ -1,56 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class CampaignMainImageUploader < CarrierWave::Uploader::Base - - # Include RMagick or MiniMagick support: - # include CarrierWave::RMagick - include CarrierWave::MiniMagick - - # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: - # include Sprockets::Helpers::RailsHelper - # include Sprockets::Helpers::IsolatedHelper - - # Override the directory where uploaded files will be stored. - # This is a sensible default for uploaders that are meant to be mounted: - def store_dir - "uploads/campaigns/#{mounted_as}/#{model.id}" - end - - # Provide a default URL as a default if there hasn't been a file uploaded: - def default_url - # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl - end - - # Process files as they are uploaded: - # process :scale => [200, 300] - # - # def scale(width, height) - # # do something - # end - - # Create different versions of your uploaded files: - version :normal do - process :resize_to_fill => [524, 360] - end - - version :thumb do - process :resize_to_fill => [180, 150] - end - - # Add a white list of extensions which are allowed to be uploaded. - # For images you might use something like this: - def extension_white_list - %w(jpg jpeg png) - end - - # Override the filename of the uploaded files: - # Avoid using model.id or version_name here, see uploader/store.rb for details. - # def filename - # "something.jpg" if original_filename - # end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end - -end diff --git a/app/uploaders/event_main_image_uploader.rb b/app/uploaders/event_main_image_uploader.rb deleted file mode 100644 index 6afc1216..00000000 --- a/app/uploaders/event_main_image_uploader.rb +++ /dev/null @@ -1,56 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EventMainImageUploader < CarrierWave::Uploader::Base - - # Include RMagick or MiniMagick support: - # include CarrierWave::RMagick - include CarrierWave::MiniMagick - - # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: - # include Sprockets::Helpers::RailsHelper - # include Sprockets::Helpers::IsolatedHelper - - # Override the directory where uploaded files will be stored. - # This is a sensible default for uploaders that are meant to be mounted: - def store_dir - "uploads/events/#{mounted_as}/#{model.id}" - end - - # Provide a default URL as a default if there hasn't been a file uploaded: - def default_url - # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl - end - - # Process files as they are uploaded: - # process :scale => [200, 300] - # - # def scale(width, height) - # # do something - # end - - # Create different versions of your uploaded files: - version :normal do - process :resize_to_fill => [400, 400] - end - - version :thumb do - process :resize_to_fill => [100,100] - end - - # Add a white list of extensions which are allowed to be uploaded. - # For images you might use something like this: - def extension_white_list - %w(jpg jpeg png) - end - - # Override the filename of the uploaded files: - # Avoid using model.id or version_name here, see uploader/store.rb for details. - # def filename - # "something.jpg" if original_filename - # end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end - -end diff --git a/app/uploaders/nonprofit_logo_uploader.rb b/app/uploaders/nonprofit_logo_uploader.rb deleted file mode 100644 index c068b73c..00000000 --- a/app/uploaders/nonprofit_logo_uploader.rb +++ /dev/null @@ -1,51 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -# encoding: utf-8 - -class NonprofitLogoUploader < CarrierWave::Uploader::Base - - # Include RMagick or MiniMagick support: - # include CarrierWave::RMagick - include CarrierWave::MiniMagick - - # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: - # include Sprockets::Helpers::RailsHelper - # include Sprockets::Helpers::IsolatedHelper - - # Override the directory where uploaded files will be stored. - # This is a sensible default for uploaders that are meant to be mounted: - def store_dir - "uploads/npo/#{mounted_as}/#{model.id}" - end - - # Provide a default URL as a default if there hasn't been a file uploaded: - def default_url - # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl - end - - # Create different versions of your uploaded files: - version :large do - process :resize_to_fit => [180, 180] - end - version :normal do - process :resize_to_fit => [100, 100] - end - version :small do - process :resize_to_fit => [30, 30] - end - - def extension_white_list - %w(jpg jpeg png gif) - end - - # Override the filename of the uploaded files: - # Avoid using model.id or version_name here, see uploader/store.rb for details. - # def filename - # "something.jpg" if original_filename - # end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end - -end diff --git a/app/uploaders/profile_uploader.rb b/app/uploaders/profile_uploader.rb deleted file mode 100644 index ec911fd4..00000000 --- a/app/uploaders/profile_uploader.rb +++ /dev/null @@ -1,60 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -# encoding: utf-8 - -class ProfileUploader < CarrierWave::Uploader::Base - - # Include RMagick or MiniMagick support: - # include CarrierWave::RMagick - include CarrierWave::MiniMagick - - # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: - # include Sprockets::Helpers::RailsHelper - # include Sprockets::Helpers::IsolatedHelper - - # Override the directory where uploaded files will be stored. - # This is a sensible default for uploaders that are meant to be mounted: - def store_dir - "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" - end - - # Provide a default URL as a default if there hasn't been a file uploaded: - def default_url - # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl - end - - # Process files as they are uploaded: - # process :scale => [200, 300] - # - # def scale(width, height) - # # do something - # end - - # Create different versions of your uploaded files: - version :normal do - process :resize_to_fill => [150, 150] - end - version :medium do - process :resize_to_fill => [100, 100] - end - version :tiny do - process :resize_to_fill => [50, 50] - end - - # Add a white list of extensions which are allowed to be uploaded. - # For images you might use something like this: - def extension_white_list - %w(jpg jpeg png) - end - - # Override the filename of the uploaded files: - # Avoid using model.id or version_name here, see uploader/store.rb for details. - # def filename - # "something.jpg" if original_filename - # end - - def cache_dir - "#{Rails.root}/tmp/uploads" - end - -end diff --git a/app/views/api/nonprofits/create.json.jbuilder b/app/views/api/nonprofits/create.json.jbuilder new file mode 100644 index 00000000..388e5ef7 --- /dev/null +++ b/app/views/api/nonprofits/create.json.jbuilder @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.id @nonprofit.id +json.name @nonprofit.name +json.city @nonprofit.city +json.state_code @nonprofit.state_code +json.zip_code @nonprofit.zip_code +json.state_code_slug @nonprofit.state_code_slug +json.city_slug @nonprofit.city_slug +json.slug @nonprofit.slug +json.email @nonprofit.email +json.website @nonprofit.website +json.phone @nonprofit.phone + +json.urls do + json.plain_url nonprofit_url(@nonprofit) + json.slug_url nonprofit_slug_url(@nonprofit) +end \ No newline at end of file diff --git a/app/views/app_data/_nonprofit.jbuilder b/app/views/app_data/_nonprofit.jbuilder new file mode 100644 index 00000000..9580e4b9 --- /dev/null +++ b/app/views/app_data/_nonprofit.jbuilder @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.extract! nonprofit, :id, :name, #basics + :brand_color, :brand_font, :tagline, #brand + :zip_code, :state_code, :city, :latitude, :longitude, #location + :slug, :state_code_slug, :city_slug, #slugs + :no_anon #options +json.url nonprofit_path(nonprofit) +json.logo do + json.normal url_for(nonprofit.logo_by_size(:normal)) +end \ No newline at end of file diff --git a/app/views/app_data/_profile.jbuilder b/app/views/app_data/_profile.jbuilder new file mode 100644 index 00000000..90db070e --- /dev/null +++ b/app/views/app_data/_profile.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.extract! profile, :id, :name, :country, :picture +json.url profile_path(profile) +json.pic_tiny url_for(profile.picture_by_size(:tiny)) if profile.picture.attached? diff --git a/app/views/app_data/_user.jbuilder b/app/views/app_data/_user.jbuilder new file mode 100644 index 00000000..a492df98 --- /dev/null +++ b/app/views/app_data/_user.jbuilder @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.extract! user, :id, :created_at, :updated_at, :email +json.unconfirmed_email user.unconfirmed_email +json.confirmed user.confirmed? + +json.partial! 'app_data/profile', profile: user.profile diff --git a/app/views/campaigns/_campaign_media.html.erb b/app/views/campaigns/_campaign_media.html.erb index afb4d256..ed2cf79c 100644 --- a/app/views/campaigns/_campaign_media.html.erb +++ b/app/views/campaigns/_campaign_media.html.erb @@ -11,7 +11,7 @@
<%= render 'components/media/youtube_iframe', id: @campaign.youtube_video_id %>
- <% elsif @campaign.main_image? %> - + <% elsif @campaign.main_image.attached? %> + <%= image_tag @campaign.main_image_by_size(:normal), class: 'campaignMedia-image' %> <% end %>
diff --git a/app/views/campaigns/_campaigns_table.html.erb b/app/views/campaigns/_campaigns_table.html.erb index d2ead6ad..2de8e5f5 100644 --- a/app/views/campaigns/_campaigns_table.html.erb +++ b/app/views/campaigns/_campaigns_table.html.erb @@ -5,7 +5,9 @@ <% metric = QueryCampaignMetrics.on_donations(campaign.id) %> - + <% if campaign.main_image.attached?%> + <%= image_tag campaign.main_image_by_size(:normal) %> + <% end %> <% if campaign.end_datetime %> diff --git a/app/views/campaigns/_create.jbuilder b/app/views/campaigns/_create.jbuilder new file mode 100644 index 00000000..a887badd --- /dev/null +++ b/app/views/campaigns/_create.jbuilder @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.extract! campaign, :id, :name, #basics + :nonprofit_id, :profile_id, :parent_campaign_id # references + :reason_for_supporting, :default_reason_for_supporting, + :published, :deleted + + +json.url campaign_url(nonprofit) + +if campaign.main_image.attached? + json.main_image do + json.full url_for(campaign.main_image) + json.normal url_for(campaign.main_image_by_size(:normal)) + json.thumb url_for(campaign.main_image_by_size(:thumb)) + end +end + +if campaign.background_image.attached? + json.background_image do + json.full url_for(campaign.background_image) + json.normal url_for(campaign.background_image_by_size(:normal)) + end +end \ No newline at end of file diff --git a/app/views/campaigns/_settings_modal.html.erb b/app/views/campaigns/_settings_modal.html.erb index f378e66f..ee8b5301 100644 --- a/app/views/campaigns/_settings_modal.html.erb +++ b/app/views/campaigns/_settings_modal.html.erb @@ -67,7 +67,8 @@

Used for previews and social media

-
+
");'> Edit
diff --git a/app/views/campaigns/index.html.erb b/app/views/campaigns/index.html.erb index 87a824f3..5e8c3fc7 100644 --- a/app/views/campaigns/index.html.erb +++ b/app/views/campaigns/index.html.erb @@ -6,14 +6,12 @@ <% end %> <%= content_for :javascripts do %> + <%= javascript_packs_with_chunks_tag 'page__', 'page__campaigns__index' %> - <%= IncludeAsset.js '/client/js/campaigns/index/page.js' %> <% end %> -<%= render '/components/trial_bar' if QueryBillingSubscriptions.currently_in_trial?(@nonprofit.id) %> - <%= render 'components/header', icon_class: 'icon-thermometer-medium', title: 'Campaigns', diff --git a/app/views/campaigns/index.jbuilder b/app/views/campaigns/index.jbuilder new file mode 100644 index 00000000..799fa599 --- /dev/null +++ b/app/views/campaigns/index.jbuilder @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +json.data @campaigns do |campaign| + json.extract! campaign, :name, :total_raised, :goal_amount, :id + json.url campaign_locateable_url(campaign) +end \ No newline at end of file diff --git a/app/views/campaigns/index.rabl b/app/views/campaigns/index.rabl deleted file mode 100644 index fe3366a8..00000000 --- a/app/views/campaigns/index.rabl +++ /dev/null @@ -1,8 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -object false - -child @campaigns => :data do - collection @campaigns, object_root: false - attributes :name, :total_raised, :goal_amount, :url, :id -end - diff --git a/app/views/campaigns/peer_to_peer.html.erb b/app/views/campaigns/peer_to_peer.html.erb index 6645d644..b7061ccc 100644 --- a/app/views/campaigns/peer_to_peer.html.erb +++ b/app/views/campaigns/peer_to_peer.html.erb @@ -35,7 +35,7 @@ }) <% end %> - <%= IncludeAsset.js '/client/js/campaigns/peer_to_peer/page.js' %> + <%= javascript_pack_tag 'i18n', 'page__peer_to_peer' %> <% end %> <% if @parent_campaign && @parent_campaign.banner_image_url %> diff --git a/app/views/campaigns/show.html.erb b/app/views/campaigns/show.html.erb index 7e3dc9f2..9eb6c0e8 100644 --- a/app/views/campaigns/show.html.erb +++ b/app/views/campaigns/show.html.erb @@ -20,19 +20,18 @@ app.is_parent_campaign = <%= @campaign.parent_campaign? %> appl.def('has_video', <%= @campaign.video_url.present? %>) appl.def('campaign_is_deleted', <%= @campaign.deleted || false %>) - appl.def('has_main_image', <%= @campaign.main_image.file.present? %>) + appl.def('has_main_image', <%= @campaign.main_image.attached? %>) <%= render 'schema', campaign: @campaign, url: @url %> <%= render 'common/froala' if current_campaign_editor? %> - <%= IncludeAsset.js '/client/js/campaigns/show/page.js' %> +<%= javascript_pack_tag 'i18n', 'page__campaigns' %> <% end %> <%= content_for :stylesheets do %> <%= stylesheet_link_tag 'campaigns/show/page' %> <%= stylesheet_link_tag 'campaigns/edit/page' %> - - - - -
-

The page you were looking for doesn't exist.

-

You may have mistyped the address or the page may have moved.

-
-

If you are the application owner check the logs for more information.

- - diff --git a/gems/grape_devise/spec/dummy/public/422.html b/gems/grape_devise/spec/dummy/public/422.html deleted file mode 100644 index fbb4b84d..00000000 --- a/gems/grape_devise/spec/dummy/public/422.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - The change you wanted was rejected (422) - - - - - -
-

The change you wanted was rejected.

-

Maybe you tried to change something you didn't have access to.

-
-

If you are the application owner check the logs for more information.

- - diff --git a/gems/grape_devise/spec/dummy/public/500.html b/gems/grape_devise/spec/dummy/public/500.html deleted file mode 100644 index e9052d35..00000000 --- a/gems/grape_devise/spec/dummy/public/500.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - We're sorry, but something went wrong (500) - - - - - -
-

We're sorry, but something went wrong.

-
-

If you are the application owner check the logs for more information.

- - diff --git a/gems/grape_devise/spec/dummy/public/favicon.ico b/gems/grape_devise/spec/dummy/public/favicon.ico deleted file mode 100644 index e69de29b..00000000 diff --git a/gems/grape_devise/spec/factories.rb b/gems/grape_devise/spec/factories.rb deleted file mode 100644 index beb87070..00000000 --- a/gems/grape_devise/spec/factories.rb +++ /dev/null @@ -1,7 +0,0 @@ -FactoryGirl.define do - factory :user do - email "john.doe@example.com" - password "12345" - password_confirmation "12345" - end -end diff --git a/gems/grape_devise/spec/requests/user_spec.rb b/gems/grape_devise/spec/requests/user_spec.rb deleted file mode 100644 index 86bc519e..00000000 --- a/gems/grape_devise/spec/requests/user_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'spec_helper' -require 'warden/test/helpers' - -RSpec.describe API, :type => :request do - include Warden::Test::Helpers - - let(:user) { build(:user) } - - after{ Warden.test_reset! } - - it "should return the current user" do - login_as user, scope: :user - - get "/me" - - response.body.should eq(user.to_json) - end - - it "should return an error if not logged in" do - login_as nil, scope: :user - - get "/me" - - response.code.should eq("401") - end - - it "should return true if logged in" do - login_as user, scope: :user - - get "/authorized" - - response.body.should eq("true") - end - - it "should return false if logged out" do - login_as nil, scope: :user - - get "/authorized" - - response.body.should eq("false") - end - - it "should log in the user" do - User.stub :find_for_database_authentication do - user - end - post "/signin", { user: { email: user.email, password: user.password } } - - response.code.should eq("201") - end - -end \ No newline at end of file diff --git a/gems/grape_devise/spec/spec_helper.rb b/gems/grape_devise/spec/spec_helper.rb deleted file mode 100644 index dbd0a052..00000000 --- a/gems/grape_devise/spec/spec_helper.rb +++ /dev/null @@ -1,36 +0,0 @@ -# Configure Rails Environment -ENV["RAILS_ENV"] = "test" - -require File.expand_path("../dummy/config/environment.rb", __FILE__) -require 'rspec' -require 'rspec/rails' -require 'factory_girl' -require 'warden' -require 'devise' -require 'capybara/dsl' -require 'factories' -require 'nulldb/rails' -Warden.test_mode! - -Rails.backtrace_cleaner.remove_silencers! - -# Requires supporting ruby files with custom matchers and macros, etc, -# in spec/support/ and its subdirectories. -Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} - -RSpec.configure do |config| - # == Mock Framework - # - # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: - # - # config.mock_with :mocha - # config.mock_with :flexmock - # config.mock_with :rr - config.mock_with :rspec - config.use_transactional_fixtures = true - config.infer_spec_type_from_file_location! - - config.include Capybara::DSL, type: :request - config.include FactoryGirl::Syntax::Methods - config.include Devise::TestHelpers, :type => :controller -end \ No newline at end of file diff --git a/gems/houdini_upgrade/AGPL-3.0.txt b/gems/houdini_upgrade/AGPL-3.0.txt new file mode 100644 index 00000000..be3f7b28 --- /dev/null +++ b/gems/houdini_upgrade/AGPL-3.0.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/gems/houdini_upgrade/GPL-3.0.txt b/gems/houdini_upgrade/GPL-3.0.txt new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/gems/houdini_upgrade/GPL-3.0.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/gems/houdini_upgrade/LGPL-3.0.txt b/gems/houdini_upgrade/LGPL-3.0.txt new file mode 100644 index 00000000..0a041280 --- /dev/null +++ b/gems/houdini_upgrade/LGPL-3.0.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/gems/houdini_upgrade/LICENSE b/gems/houdini_upgrade/LICENSE new file mode 100644 index 00000000..205ae966 --- /dev/null +++ b/gems/houdini_upgrade/LICENSE @@ -0,0 +1,86 @@ +# Free Software Licensing Information for "houdini_upgrade". + +The primary license of "houdini_upgrade" is: + + AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + +This software's license gives you freedom; you can copy, convey, +propagate, redistribute and/or modify this program under the terms of +the GNU Affero General Public License (AGPL) as published by the Free +Software Foundation (FSF), either version 3 of the License, or (at your +option) any later version of the AGPL published by the FSF. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program in a file in the toplevel directory called +"AGPL-3.0.txt". If not, see . + +In addition to the permission granted by the AGPLv3, you also receive +permissions as written in The Web Template Output Additional Permission, +Version 3.0, as published by the Software Freedom Conservancy +("Conservancy"), either version 3 of that Additional Permission, or (at your +option) any later version of the Additional Permission as published by +Conservancy. + +You should have received a copy of the Web Template Output Additional +Permission, along with this program in a file in the toplevel directory +called "Web-Template-Output-Additional-Permission.txt". If not, see +. + +## Different Licenses for some files in "houdini_upgrade" Repository. + +1. Any file in the "houdini_upgrade" repository that does not specific a license, or is + not discussed explicitly in this toplevel "LICENSE" file is licensed: + + AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + + as specified in detail above. + +2. Files that contain the information "License: LGPL-3.0-or-later" are + licensed as below: + + You can copy, convey, propagate, redistribute and/or modify this program + under the terms of the GNU General Public License (GPL) as published by + the Free Software Foundation (FSF), either version 3 of the License, or + (at your option) any later version of the GPL published by the FSF. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with this program in a file in the toplevel directory called + "GPL-3.0.txt". If not, see . + + In addition to the permission granted by the GPLv3, you also receive + permissions as written in GNU Lesser General Public License (LGPL), + Version 3.0, as published by the Free Software Foundation (FSF), either + version 3 of the License, or (at your option) any later version of the + LGPL published by the FSF. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program in a file in the toplevel directory called + "LGPL-3.0.txt". If not, see . + +3. Files that contain the information "License: CC0-1.0" are licensed under + the Creative Commons Public Domain Dedication 1.0 Universal or any later + version as published by Creative Commons, Inc. ("CC0"). + +4. All SVG, ICO and PNG files in this repository are licensed as CC-0. + + This applies to any files that are both (a) in the standard SVG, ICO, + and/or PNG format, and (b) where their names end in .svg or .ico. + + The project may in future place later versions of these files under a + copyleft license, but will discuss that with the contributor community + before doing so. diff --git a/gems/houdini_upgrade/README.md b/gems/houdini_upgrade/README.md new file mode 100644 index 00000000..4d0b0826 --- /dev/null +++ b/gems/houdini_upgrade/README.md @@ -0,0 +1,25 @@ +# HoudiniUpgrade +Short description and motivation. + +## Usage +How to use my plugin. + +## Installation +Add this line to your application's Gemfile: + +```ruby +gem 'houdini_upgrade' +``` + +And then execute: +```bash +$ bundle +``` + +Or install it yourself as: +```bash +$ gem install houdini_upgrade +``` + +## Contributing +Contribution directions go here. diff --git a/gems/houdini_upgrade/Rakefile b/gems/houdini_upgrade/Rakefile new file mode 100644 index 00000000..3039823b --- /dev/null +++ b/gems/houdini_upgrade/Rakefile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require "bundler/setup" + +load "rails/tasks/statistics.rake" + +require "bundler/gem_tasks" diff --git a/gems/houdini_upgrade/bin/rails b/gems/houdini_upgrade/bin/rails new file mode 100755 index 00000000..eb7cc6c7 --- /dev/null +++ b/gems/houdini_upgrade/bin/rails @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path('..', __dir__) +ENGINE_PATH = File.expand_path('../lib/houdini_upgrade/engine', __dir__) + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) + +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +require "active_record/railtie" +require "active_storage/engine" +require "action_controller/railtie" +# require "action_mailer/railtie" +require "action_view/railtie" +# require "action_cable/engine" +# require "sprockets/railtie" +# require "rails/test_unit/railtie" +require "rails/engine/commands" diff --git a/gems/houdini_upgrade/config/routes.rb b/gems/houdini_upgrade/config/routes.rb new file mode 100644 index 00000000..9e76a2d0 --- /dev/null +++ b/gems/houdini_upgrade/config/routes.rb @@ -0,0 +1,2 @@ +HoudiniUpgrade::Engine.routes.draw do +end diff --git a/gems/houdini_upgrade/db/migrate/20191105200033_remove_billing_plan_tiers.rb b/gems/houdini_upgrade/db/migrate/20191105200033_remove_billing_plan_tiers.rb new file mode 100644 index 00000000..cd6ba2da --- /dev/null +++ b/gems/houdini_upgrade/db/migrate/20191105200033_remove_billing_plan_tiers.rb @@ -0,0 +1,6 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class RemoveBillingPlanTiers < ActiveRecord::Migration[5.2] + def change + remove_column :billing_plans, :tier, :integer + end +end \ No newline at end of file diff --git a/gems/houdini_upgrade/db/migrate/20200602215911_remove_picture_from_profile.rb b/gems/houdini_upgrade/db/migrate/20200602215911_remove_picture_from_profile.rb new file mode 100644 index 00000000..b40f03bf --- /dev/null +++ b/gems/houdini_upgrade/db/migrate/20200602215911_remove_picture_from_profile.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class RemovePictureFromProfile < ActiveRecord::Migration[6.0] + def change + remove_column :profiles, :picture + end +end diff --git a/gems/houdini_upgrade/houdini_upgrade.gemspec b/gems/houdini_upgrade/houdini_upgrade.gemspec new file mode 100644 index 00000000..9584880f --- /dev/null +++ b/gems/houdini_upgrade/houdini_upgrade.gemspec @@ -0,0 +1,24 @@ +require_relative "lib/houdini_upgrade/version" + +Gem::Specification.new do |spec| + spec.name = "houdini_upgrade" + spec.version = HoudiniUpgrade::VERSION + spec.authors = ["The Houdini Project"] + spec.email = [""] + spec.homepage = "https://houdiniproject.org" + spec.summary = "" + spec.description = "" + spec.license = "AGPL-3.0-or-later WITH WTO-Additional-Permission-3.0-or-later" + + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' + # to allow pushing to a single host or delete this section to allow pushing to any host. + spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" + + spec.metadata["homepage_uri"] = spec.homepage + # spec.metadata["source_code_uri"] = "" + # spec.metadata["changelog_uri"] = "" + + spec.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "AGPL-3.0.txt", "GPL-3.0.txt", "LGPL-3.0.txt", "Rakefile", "README.md"] + + spec.add_dependency "rails", "~> 6.0.3" +end diff --git a/lib/generators/api/validator/USAGE b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/USAGE similarity index 69% rename from lib/generators/api/validator/USAGE rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/USAGE index 93ffa304..d93ba875 100644 --- a/lib/generators/api/validator/USAGE +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/USAGE @@ -2,7 +2,7 @@ Description: Explain the generator Example: - rails generate validator Thing - + rails generate cw_to_activestorage + This will create: what/will/it/create diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/cw_to_activestorage_generator.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/cw_to_activestorage_generator.rb new file mode 100644 index 00000000..c4226bfc --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/cw_to_activestorage_generator.rb @@ -0,0 +1,50 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class CwToActivestorageGenerator < Rails::Generators::Base + source_root File.expand_path('templates', __dir__) + class_option :aws_bucket, type: :string, required: true + class_option :aws_region, type: :string, required: false + class_option :aws_assethost, type: :string, required: false + + def add_carrierwave_gems + gem "carrierwave", "~> 1" + gem "carrierwave-aws" + end + + def copy_uploaders + if (!File.exists?('app/uploaders')) + directory 'uploaders', 'app/uploaders' + end + end + + def include_uploaders + file_and_search = [ + ["campaign.rb", "class Campaign < ApplicationRecord"], + ["profile.rb", "class Profile < ApplicationRecord"], + ['nonprofit.rb', "class Nonprofit < ApplicationRecord"], + ['image_attachment.rb', "class ImageAttachment < ApplicationRecord"], + ['event.rb', "class Event < ApplicationRecord"] + ] + file_and_search + .select{|filename, _| !File.read("app/models/#{filename}").include?('###MIGRATION_FIELDS_BEGIN')} + .each do |filename, find_string| + gsub_file("app/models/#{filename}",find_string ) do |match| + match << "\n" + match << File.read(Pathname(File.expand_path('templates', __dir__, )) + 'models' + "#{filename}") + end + end + end + + def create_column_migration_file + if (Dir.glob("db/migrate/*_rename_uploader_columns.houdini_upgrade.rb").none?) + copy_file "migrate/rename_uploader_columns.rb", + "db/migrate/#{(DateTime.now.utc + 1.second).strftime('%Y%m%d%H%M%S')}_rename_uploader_columns.houdini_upgrade.rb" + end + end + + def add_carrierwave_template + @aws_bucket = options[:aws_bucket] + @aws_region = options[:aws_region] + @aws_assethost = options[:aws_assethost] || "https://#{@aws_bucket}.s3.amazonaws.com" + template 'initializers/carrierwave.rb', 'config/initializers/carrierwave.rb' + end +end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/initializers/carrierwave.rb.tt b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/initializers/carrierwave.rb.tt new file mode 100644 index 00000000..0bb3676d --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/initializers/carrierwave.rb.tt @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + +CarrierWave.configure do |config| + config.storage = :aws + config.aws_bucket = "<%= @aws_bucket %>" + config.aws_acl = :public_read + config.asset_host = "<%= @aws_assethost %>" + config.aws_credentials = { + region: "<%= @aws_region %>" + } +end + \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/migrate/rename_uploader_columns.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/migrate/rename_uploader_columns.rb new file mode 100644 index 00000000..6cd4ad3b --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/migrate/rename_uploader_columns.rb @@ -0,0 +1,11 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class RenameUploaderColumns < ActiveRecord::Migration[5.2] + def change + require "houdini_upgrade" + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + table.fields.each do |field| + rename_column table.name, field.name, field.migrated_name + end + end + end +end \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/campaign.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/campaign.rb new file mode 100644 index 00000000..ce91277f --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/campaign.rb @@ -0,0 +1,5 @@ +###MIGRATION_FIELDS_BEGIN +mount_uploader :main_image_temp, CampaignMainImageUploader +mount_uploader :background_image_temp, CampaignBackgroundImageUploader +mount_uploader :banner_image_temp, CampaignBannerImageUploader +###MIGRATION_FIELDS_END \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/event.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/event.rb new file mode 100644 index 00000000..5d39c728 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/event.rb @@ -0,0 +1,4 @@ +###MIGRATION_FIELDS_BEGIN +mount_uploader :main_image_temp, EventMainImageUploader +mount_uploader :background_image_temp, EventBackgroundImageUploader +###MIGRATION_FIELDS_END \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/image_attachment.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/image_attachment.rb new file mode 100644 index 00000000..561ab5f3 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/image_attachment.rb @@ -0,0 +1,3 @@ +###MIGRATION_FIELDS_BEGIN +mount_uploader :file_temp, ImageAttachmentUploader +###MIGRATION_FIELDS_END \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/nonprofit.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/nonprofit.rb new file mode 100644 index 00000000..c3fe1c46 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/nonprofit.rb @@ -0,0 +1,7 @@ +###MIGRATION_FIELDS_BEGIN +mount_uploader :main_image_temp, NonprofitUploader +mount_uploader :second_image_temp, NonprofitUploader +mount_uploader :third_image_temp, NonprofitUploader +mount_uploader :background_image_temp, NonprofitBackgroundUploader +mount_uploader :logo_temp, NonprofitLogoUploader +###MIGRATION_FIELDS_END \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/profile.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/profile.rb new file mode 100644 index 00000000..31bee3d2 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/models/profile.rb @@ -0,0 +1,3 @@ +###MIGRATION_FIELDS_BEGIN +mount_uploader :picture_temp, ProfileUploader +###MIGRATION_FIELDS_END \ No newline at end of file diff --git a/app/uploaders/article_background_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_background_uploader.rb similarity index 92% rename from app/uploaders/article_background_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_background_uploader.rb index 3fbaa342..9ac0229f 100644 --- a/app/uploaders/article_background_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_background_uploader.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # encoding: utf-8 class ArticleBackgroundUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -20,7 +21,7 @@ class ArticleBackgroundUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultNonprofitUrl + Image::DefaultNonprofitUrl end # Process files as they are uploaded: @@ -32,14 +33,13 @@ class ArticleBackgroundUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :large do - process :resize_to_fill => [600, 400] + process resize_to_fill: [600, 400] end - # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -51,5 +51,4 @@ class ArticleBackgroundUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/app/uploaders/article_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_uploader.rb similarity index 92% rename from app/uploaders/article_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_uploader.rb index ac4b0d86..906c96ad 100644 --- a/app/uploaders/article_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/article_uploader.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # encoding: utf-8 class ArticleUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -20,7 +21,7 @@ class ArticleUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultNonprofitUrl + Image::DefaultNonprofitUrl end # Process files as they are uploaded: @@ -32,13 +33,13 @@ class ArticleUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :thumb do - process :resize_to_fill => [200, 200] + process resize_to_fill: [200, 200] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -50,5 +51,4 @@ class ArticleUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_background_image_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_background_image_uploader.rb new file mode 100644 index 00000000..da31b91c --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_background_image_uploader.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# encoding: utf-8 + +class CampaignBackgroundImageUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick + + def store_dir + "uploads/campaigns/#{mounted_as}/#{model.id}" + end + + # Create different versions of your uploaded files: + version :normal do + process resize_to_fill: [1000, 600] + end + + def extension_white_list + %w[jpg jpeg png] + end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_banner_image_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_banner_image_uploader.rb new file mode 100644 index 00000000..09fe0e46 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_banner_image_uploader.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class CampaignBannerImageUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick + + def store_dir + "uploads/campaigns/#{mounted_as}/#{model.id}" + end + + def extension_white_list + %w[jpg jpeg png] + end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_main_image_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_main_image_uploader.rb new file mode 100644 index 00000000..a8def8e4 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/campaign_main_image_uploader.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class CampaignMainImageUploader < CarrierWave::Uploader::Base + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/campaigns/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url + # For Rails 3.1+ asset pipeline compatibility: + Image::DefaultProfileUrl + end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + version :normal do + process resize_to_fill: [524, 360] + end + + version :thumb do + process resize_to_fill: [180, 150] + end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w[jpg jpeg png] + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/app/uploaders/event_background_image_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_background_image_uploader.rb similarity index 92% rename from app/uploaders/event_background_image_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_background_image_uploader.rb index 02a304d2..123517b2 100644 --- a/app/uploaders/event_background_image_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_background_image_uploader.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class EventBackgroundImageUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -18,7 +19,7 @@ class EventBackgroundImageUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultCampaignUrl + Image::DefaultCampaignUrl end # Process files as they are uploaded: @@ -30,13 +31,13 @@ class EventBackgroundImageUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :normal do - process :resize_to_fill => [1000, 600] + process resize_to_fill: [1000, 600] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -48,5 +49,4 @@ class EventBackgroundImageUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_main_image_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_main_image_uploader.rb new file mode 100644 index 00000000..e8b8adb1 --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/event_main_image_uploader.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class EventMainImageUploader < CarrierWave::Uploader::Base + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/events/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url + # For Rails 3.1+ asset pipeline compatibility: + Image::DefaultProfileUrl + end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + version :normal do + process resize_to_fill: [400, 400] + end + + version :thumb do + process resize_to_fill: [100, 100] + end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w[jpg jpeg png] + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/app/uploaders/image_attachment_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/image_attachment_uploader.rb similarity index 87% rename from app/uploaders/image_attachment_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/image_attachment_uploader.rb index 01b644f6..a5e6d304 100644 --- a/app/uploaders/image_attachment_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/image_attachment_uploader.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # encoding: utf-8 class ImageAttachmentUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -20,7 +21,7 @@ class ImageAttachmentUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl + Image::DefaultProfileUrl end # Process files as they are uploaded: @@ -32,23 +33,23 @@ class ImageAttachmentUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :large do - process :resize_to_fill => [600, 400] + process resize_to_fill: [600, 400] end version :medium do - process :resize_to_fill => [400, 266] + process resize_to_fill: [400, 266] end version :small do - process :resize_to_fill => [400, 266] + process resize_to_fill: [400, 266] end # slightly smaller than the normal thumb version :thumb_explore do - process :resize_to_fill => [200, 133] + process resize_to_fill: [200, 133] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -60,5 +61,4 @@ class ImageAttachmentUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/app/uploaders/nonprofit_background_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_background_uploader.rb similarity index 92% rename from app/uploaders/nonprofit_background_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_background_uploader.rb index b46f6284..e6134f68 100644 --- a/app/uploaders/nonprofit_background_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_background_uploader.rb @@ -1,9 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # encoding: utf-8 class NonprofitBackgroundUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -21,7 +22,7 @@ class NonprofitBackgroundUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultNonprofitUrl + Image::DefaultNonprofitUrl end # Process files as they are uploaded: @@ -33,13 +34,13 @@ class NonprofitBackgroundUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :normal do - process :resize_to_fill => [1000, 600] + process resize_to_fill: [1000, 600] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -51,5 +52,4 @@ class NonprofitBackgroundUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_logo_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_logo_uploader.rb new file mode 100644 index 00000000..55dd242e --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_logo_uploader.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# encoding: utf-8 + +class NonprofitLogoUploader < CarrierWave::Uploader::Base + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/npo/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url + # For Rails 3.1+ asset pipeline compatibility: + Image::DefaultProfileUrl + end + + # Create different versions of your uploaded files: + version :large do + process resize_to_fit: [180, 180] + end + version :normal do + process resize_to_fit: [100, 100] + end + version :small do + process resize_to_fit: [30, 30] + end + + def extension_white_list + %w[jpg jpeg png gif] + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/app/uploaders/nonprofit_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_uploader.rb similarity index 88% rename from app/uploaders/nonprofit_uploader.rb rename to gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_uploader.rb index fe37a30c..334d3df6 100755 --- a/app/uploaders/nonprofit_uploader.rb +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/nonprofit_uploader.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # encoding: utf-8 class NonprofitUploader < CarrierWave::Uploader::Base - # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick @@ -20,7 +21,7 @@ class NonprofitUploader < CarrierWave::Uploader::Base # Provide a default URL as a default if there hasn't been a file uploaded: def default_url # For Rails 3.1+ asset pipeline compatibility: - return Image::DefaultProfileUrl + Image::DefaultProfileUrl end # Process files as they are uploaded: @@ -32,20 +33,20 @@ class NonprofitUploader < CarrierWave::Uploader::Base # Create different versions of your uploaded files: version :nonprofit_carousel do - process :resize_to_fill => [590, 338] + process resize_to_fill: [590, 338] end version :thumb do - process :resize_to_fill => [188, 120] + process resize_to_fill: [188, 120] end # slightly smaller than the normal thumb version :thumb_explore do - process :resize_to_fill => [100, 100] + process resize_to_fill: [100, 100] end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_white_list - %w(jpg jpeg png) + %w[jpg jpeg png] end # Override the filename of the uploaded files: @@ -57,5 +58,4 @@ class NonprofitUploader < CarrierWave::Uploader::Base def cache_dir "#{Rails.root}/tmp/uploads" end - end diff --git a/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/profile_uploader.rb b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/profile_uploader.rb new file mode 100644 index 00000000..1abd9e4c --- /dev/null +++ b/gems/houdini_upgrade/lib/generators/cw_to_activestorage/templates/uploaders/profile_uploader.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# encoding: utf-8 + +class ProfileUploader < CarrierWave::Uploader::Base + # Include RMagick or MiniMagick support: + # include CarrierWave::RMagick + include CarrierWave::MiniMagick + + # Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility: + # include Sprockets::Helpers::RailsHelper + # include Sprockets::Helpers::IsolatedHelper + + # Override the directory where uploaded files will be stored. + # This is a sensible default for uploaders that are meant to be mounted: + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + # Provide a default URL as a default if there hasn't been a file uploaded: + def default_url + # For Rails 3.1+ asset pipeline compatibility: + Image::DefaultProfileUrl + end + + # Process files as they are uploaded: + # process :scale => [200, 300] + # + # def scale(width, height) + # # do something + # end + + # Create different versions of your uploaded files: + version :normal do + process resize_to_fill: [150, 150] + end + version :medium do + process resize_to_fill: [100, 100] + end + version :tiny do + process resize_to_fill: [50, 50] + end + + # Add a white list of extensions which are allowed to be uploaded. + # For images you might use something like this: + def extension_white_list + %w[jpg jpeg png] + end + + # Override the filename of the uploaded files: + # Avoid using model.id or version_name here, see uploader/store.rb for details. + # def filename + # "something.jpg" if original_filename + # end + + def cache_dir + "#{Rails.root}/tmp/uploads" + end +end diff --git a/gems/houdini_upgrade/lib/houdini_upgrade.rb b/gems/houdini_upgrade/lib/houdini_upgrade.rb new file mode 100644 index 00000000..c167eea3 --- /dev/null +++ b/gems/houdini_upgrade/lib/houdini_upgrade.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require "houdini_upgrade/engine" + +module HoudiniUpgrade + require 'houdini_upgrade/table_to_migrate' + UPLOADERS_TO_MIGRATE = [ + TableToMigrate.new(:nonprofits, [:main_image, + :second_image, + :third_image, + :background_image, + :logo]), + TableToMigrate.new(:campaigns, [ + :main_image, + :background_image, + :banner_image + ]), + TableToMigrate.new(:events, [ + :main_image, + :background_image + ]), + TableToMigrate.new(:image_attachments, [ + :file + ]), + TableToMigrate.new(:profiles, [ + :picture + ]) + ] +end diff --git a/gems/houdini_upgrade/lib/houdini_upgrade/column_to_migrate.rb b/gems/houdini_upgrade/lib/houdini_upgrade/column_to_migrate.rb new file mode 100644 index 00000000..e092ae25 --- /dev/null +++ b/gems/houdini_upgrade/lib/houdini_upgrade/column_to_migrate.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module HoudiniUpgrade + class ColumnToMigrate + attr_reader :name + def initialize(original_column_name) + @name = original_column_name.to_s + end + + def migrated_name + @name + "_temp" + end + end +end \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/houdini_upgrade/engine.rb b/gems/houdini_upgrade/lib/houdini_upgrade/engine.rb new file mode 100644 index 00000000..d4c01f0a --- /dev/null +++ b/gems/houdini_upgrade/lib/houdini_upgrade/engine.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module HoudiniUpgrade + class Engine < ::Rails::Engine + isolate_namespace HoudiniUpgrade + end +end diff --git a/gems/houdini_upgrade/lib/houdini_upgrade/table_to_migrate.rb b/gems/houdini_upgrade/lib/houdini_upgrade/table_to_migrate.rb new file mode 100644 index 00000000..3389fd13 --- /dev/null +++ b/gems/houdini_upgrade/lib/houdini_upgrade/table_to_migrate.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require_relative "./column_to_migrate" +module HoudiniUpgrade + class TableToMigrate + attr_reader :fields, :name + def initialize(original_table_name, original_field_names) + @name = original_table_name.to_s + @fields = original_field_names.map{|i| ColumnToMigrate.new(i)} + end + + def class_name + @name.classify + end + + def backup_table + @name + "_uploader_backups" + end + + def foreign_key + @name + "_id" + end + end +end \ No newline at end of file diff --git a/config/initializers/delayed_job_config.rb b/gems/houdini_upgrade/lib/houdini_upgrade/version.rb similarity index 53% rename from config/initializers/delayed_job_config.rb rename to gems/houdini_upgrade/lib/houdini_upgrade/version.rb index 08ff944b..3f190054 100644 --- a/config/initializers/delayed_job_config.rb +++ b/gems/houdini_upgrade/lib/houdini_upgrade/version.rb @@ -1,2 +1,6 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -Delayed::Worker.max_attempts = 1 +module HoudiniUpgrade + VERSION = '0.1.0' +end diff --git a/gems/houdini_upgrade/lib/tasks/houdini_upgrade_tasks.rake b/gems/houdini_upgrade/lib/tasks/houdini_upgrade_tasks.rake new file mode 100644 index 00000000..278b8f18 --- /dev/null +++ b/gems/houdini_upgrade/lib/tasks/houdini_upgrade_tasks.rake @@ -0,0 +1,173 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# from https://coderwall.com/p/ijr6jq/rake-progress-bar +class ProgressBar + attr_reader :total, :counter + def initialize(total, description) + @description = description + @total = total + @counter = 1 + end + + def increment + print_out + @counter += 1 + end + + def print_out + complete = sprintf("%#.2f%%", ((@counter.to_f / @total.to_f) * 100)) + print "\r\e[0K#{@description} #{@counter}/#{@total} (#{complete})" + end + + def increment_total(by_amount) + @total += by_amount + print_out + end +end + + namespace :houdini_upgrade do + Rake::Task["install:migrations"].clear_comments + desc <<-RUBY +Run houdini upgrade to v2 +DESC: + This task automates some of the process of upgrading to Houdini v2. It does + the following: + * installs and runs database migrations + * migrates images from Carrierwave on AWS to your default ActiveStorage service + +USAGE: bin/rails houdini_upgrade:run[aws_bucket,aws_region] + +TASK ARGUMENTS: + aws_bucket (required): the name of your AWS bucket where all your carrierwave images are hosted + aws_region (required): AWS region of your AWS bucket. Carrierwave won\'t work + without it. + aws_assethost: protocol and domain where your AWS bucket contents can be + accessed. For example, this could be through Cloudfront + shorter_test: a testing only argument which cuts down the number of images we copy +RUBY + task :run, [:aws_bucket, :aws_region, :aws_assethost, :shorter_test] do |t, args| + if args[:aws_bucket].blank? || args[:aws_region].blank? + puts "You must set aws_bucket and aws_region for houdini_upgrade:run like:" + puts " houdini_upgrade:run[aws_bucket,aws_region]" + puts "" + puts "See the full task description by running `bin/rails -D` or " + puts "visit `docs/houdini_upgrade.md`" + else + Rake::Task["houdini_upgrade:install:migrations"].invoke + sh 'rails active_storage:install' + Rake::Task["houdini_upgrade:cw_to_activestorage"].invoke(*args) + sh 'bundle' + Rake::Task["db:migrate"].invoke + migrate_upload_command = "rails houdini_upgrade:migrate_uploads" + if args[:shorter_test] + migrate_upload_command += "[true]" + end + sh migrate_upload_command + Rake::Task["houdini_upgrade:create_backup_uploader_migration"].invoke + sh "rails db:migrate" + Rake::Task["houdini_upgrade:cleanup_upgrade_files"].invoke + sh 'bundle' + end + end + + task :cw_to_activestorage, [:aws_bucket, :aws_region, :aws_assethost] do |t, args| + tail = ["--aws-bucket=#{args[:aws_bucket]}", "--aws-region=#{args[:aws_region]}"] + + tail.push("--aws-assethost=#{args[:aws_assethost]}") if args[:aws_assethost] + sh "rails generate cw_to_activestorage #{tail.join(' ')}" + end + + desc "Migrate your CarrierWave uploads to activestorage" + task :migrate_uploads, [:shorter_test] => [:environment] do |t, args| + progress_bar = ProgressBar.new(0, "Upload migration progress") + results = [] + Rails.application.eager_load! + # find activerecord descendents + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + klass = table.class_name.constantize + items_to_migrate = klass.where(table.fields.map{|i| i.migrated_name + " IS NOT NULL"}.join(" OR ")) + if args[:shorter_test] + items_to_migrate = items_to_migrate.limit(10) + end + progress_bar.increment_total(items_to_migrate.count * table.fields.count) + items_to_migrate + .find_each do |record| + table.fields.each do |field| + + if record.send(field.migrated_name + "?") + results << process(upload_url: record.send(field.migrated_name).url.gsub(/\/#{field.migrated_name}\//, "/#{field.name}/"), attachment_name: field.name, record: record) + end + progress_bar.increment + end + end + end + + copied = results.select{|i| i[:success]}.map{|i| i[:value]} + errors = results.select{|i| !i[:success]}.map{|i| i[:value]} + + CSV.open("#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_copied.csv", 'wb') do |csv| + csv << ['Class Name', 'Id', "UploaderName", "FileToOpen"] + copied.each {|row| csv << row} + end + + CSV.open("#{DateTime.now.utc.strftime('%Y%m%d%H%M%S')}_errored.csv", 'wb') do |csv| + csv << ['Class Name', 'Id', "UploaderName", "FileToOpen", "Error"] + errors.each {|row| csv << row} + end + + puts "Copied: #{copied.count}" + puts "Errored: #{errors.count}" + end + + def process(**args) + file_to_open = nil + begin + if args[:upload_url] + filename = File.basename(URI.parse(args[:upload_url]).to_s) + file_to_open = args[:upload_url].start_with?('/') ? "." + args[:upload_url] : args[:upload_url] + + if (!args[:simulate]) + attachment_relation = args[:record].send("#{args[:attachment_name].to_s}") + attachment_relation.attach(io: open(file_to_open), filename: filename) + end + return {success: true, value: [args[:record].class.name, args[:record].id, args[:attachment_name],file_to_open]} + end + return nil + rescue => e + return {success: false, value: [args[:record].class.name, args[:record].id, args[:attachment_name], file_to_open, e]} + end + end + + task :create_backup_uploader_migration do + if (Dir.glob("db/migrate/*_backup_uploader_columns.houdini_upgrade.rb").none?) + FileUtils.cp __dir__ + "/templates/backup_uploader_columns.rb", + "db/migrate/#{(DateTime.now.utc + 1.second).strftime('%Y%m%d%H%M%S')}_backup_uploader_columns.houdini_upgrade.rb" + end + end + + task :delete_uploader_backup_tables_migration do + if (Dir.glob("db/migrate/*_backup_uploader_columns.houdini_upgrade.rb").none?) + FileUtils.cp __dir + "/templates/delete_uploader_backup_tables.rb", + "db/migrate/#{(DateTime.now.utc + 1.second).strftime('%Y%m%d%H%M%S')}_delete_uploader_backup_tables.houdini_upgrade.rb" + end + end + + task :cleanup_upgrade_files do + FileUtils.rm_r "app/uploaders" if (File.exists?("app/uploaders")) + FileUtils.rm "config/initializers/carrierwave.rb" if (File.exists?("config/initializers/carrierwave.rb")) + gemfile_lines = File.readlines("Gemfile").select{|i| !i.include?("gem 'carrierwave'") && !i.include?("gem 'carrierwave-aws'")} + File.write('Gemfile', gemfile_lines.join()) + cleanup_model_files + end + + def cleanup_model_files + filename_roots = HoudiniUpgrade::UPLOADERS_TO_MIGRATE.map{|i| i.name.singularize} + filename_roots.each do |f| + filename = "app/models/#{f}.rb" + file_contents = File.read(filename) + file_contents = file_contents.sub(/\n\#\#\#MIGRATION_FIELDS_BEGIN(.*)\#\#\#MIGRATION_FIELDS_END/mx, '') + File.write(filename, file_contents) + end + end +end diff --git a/gems/houdini_upgrade/lib/tasks/templates/backup_uploader_columns.rb b/gems/houdini_upgrade/lib/tasks/templates/backup_uploader_columns.rb new file mode 100644 index 00000000..3943fb31 --- /dev/null +++ b/gems/houdini_upgrade/lib/tasks/templates/backup_uploader_columns.rb @@ -0,0 +1,51 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class BackupUploaderColumns < ActiveRecord::Migration[5.2] + def up + # copy all + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |uploader_table| + create_table uploader_table.backup_table do |table| + table.references uploader_table.name, foreign_key: true, index: { name: "idx_#{uploader_table.backup_table}_on_fk"} + uploader_table.fields.each do |f| + table.string f.migrated_name + end + end + end + + # copy all + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + execute <<-SQL + INSERT INTO #{table.backup_table} (#{table.foreign_key}, #{table.fields.map(&:migrated_name).join(', ')}) + ( SELECT id, #{table.fields.map(&:migrated_name).join(', ')} FROM #{table.name}) + SQL + end + + # delete columns + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + table.fields.each do |f| + remove_column table.name, f.migrated_name + end + end + end + + def down + ## readd columns + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + table.fields.each do |f| + add_column table.name, f.migrated_name, :string + end + end + + # copy all + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each do |table| + execute <<-SQL + UPDATE #{table.name} SET (#{table.fields.map(&:migrated_name).join(', ')}) = ( + SELECT #{table.fields.map{|f| table.backup_table + "." + f.migrated_name}.join(', ')} FROM #{table.backup_table} + WHERE #{table.backup_table}.#{table.foreign_key} = #{table.name}.id + ) + SQL + end + # delete tables + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each{ |entity, _| drop_table entity.backup_table} + + end +end \ No newline at end of file diff --git a/gems/houdini_upgrade/lib/tasks/templates/delete_uploader_backup_tables.rb b/gems/houdini_upgrade/lib/tasks/templates/delete_uploader_backup_tables.rb new file mode 100644 index 00000000..3ad763ec --- /dev/null +++ b/gems/houdini_upgrade/lib/tasks/templates/delete_uploader_backup_tables.rb @@ -0,0 +1,7 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class DeleteUploaderBackupTables < ActiveRecord::Migration[5.2] + def up + # delete tables + HoudiniUpgrade::UPLOADERS_TO_MIGRATE.each{ |table| drop_table table.backup_table} + end +end \ No newline at end of file diff --git a/gems/ruby-param-validation/Rakefile b/gems/ruby-param-validation/Rakefile index 1f33d053..dc5d5818 100644 --- a/gems/ruby-param-validation/Rakefile +++ b/gems/ruby-param-validation/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rake/testtask' Rake::TestTask.new do |t| @@ -5,5 +7,5 @@ Rake::TestTask.new do |t| t.test_files = FileList['test/test*.rb', 'test/*test.rb'] end -desc "Run tests" -task :default => :test \ No newline at end of file +desc 'Run tests' +task default: :test diff --git a/gems/ruby-param-validation/lib/param_validation.rb b/gems/ruby-param-validation/lib/param_validation.rb index 131ffc31..2835763d 100644 --- a/gems/ruby-param-validation/lib/param_validation.rb +++ b/gems/ruby-param-validation/lib/param_validation.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + require 'json' require 'chronic' class ParamValidation - # Given a hash of data and a validation hash, check all the validations, raising an Error on the first invalid key # @raise [ValidationError] if one or more of the validations fail. def initialize(data, validations) @@ -10,34 +11,46 @@ class ParamValidation validations.each do |key, validators| val = key === :root ? data : (data[key] || data[key.to_s] || data[key.to_sym]) next if validators[:required].nil? && val.nil? + validators.each do |name, arg| validator = @@validators[name] msg = validations[key][:message] next unless validator + is_valid = @@validators[name].call(val, arg, data) msg_proc = @@messages[name] - msg ||= @@messages[name].call({key: key, data: data, val: val, arg: arg}) if msg_proc - errors.push({msg: msg, data: {key: key, val: val, name: name, msg: msg}}) unless is_valid + msg ||= @@messages[name].call(key: key, data: data, val: val, arg: arg) if msg_proc + errors.push(msg: msg, data: { key: key, val: val, name: name, msg: msg }) unless is_valid end end if errors.length == 1 raise ValidationError.new(errors[0][:msg], errors[0][:data]) elsif errors.length > 1 - msg = errors.collect {|e| e[:msg]}.join('\n') - raise ValidationError.new(msg, errors.collect{|e| e[:data]}) + msg = errors.collect { |e| e[:msg] }.join('\n') + raise ValidationError.new(msg, errors.collect { |e| e[:data] }) end end - def self.messages; @@messages; end + def self.messages + @@messages + end + def self.set_message(name, &block) @@messages[name] = block end - def self.validators; @@validators; end + def self.validators + @@validators + end + def self.add_validator(name, &block) @@validators[name] = block end - def self.structure_validators; @@structure_validators; end + + def self.structure_validators + @@structure_validators + end + def self.add_structure_validator(name, &block) @@structure_validators[name] = block end @@ -47,64 +60,122 @@ class ParamValidation # - arg is the argument passed into the validator (eg for {required: true}, it is `true`) # - data is the entire set of data @@validators = { - required: lambda {|val, arg, data| !val.nil?}, - absent: lambda {|val, arg, data| val.nil?}, - not_blank: lambda {|val, arg, data| val.is_a?(String) && val.length > 0}, - not_included_in: lambda {|val, arg, data| !arg.include?(val) rescue false}, - included_in: lambda {|val, arg, data| arg.include?(val) rescue false}, - format: lambda {|val, arg, data| val =~ arg rescue false}, - is_integer: lambda {|val, arg, data| val.is_a?(Integer) || val =~ /\A[+-]?\d+\Z/}, - is_float: lambda {|val, arg, data| val.is_a?(Float) || (!!Float(val) rescue false) }, - min_length: lambda {|val, arg, data| val.length >= arg rescue false}, - max_length: lambda {|val, arg, data| val.length <= arg rescue false}, - length_range: lambda {|val, arg, data| arg.cover?(val.length) rescue false}, - length_equals: lambda {|val, arg, data| val.length == arg}, - is_reference: lambda{|val, arg, data| (val.is_a?(Integer)&& val >=0) || val =~ /\A\d+\Z/ || val == ''}, - equals: lambda {|val, arg, data| val == arg}, - min: lambda {|val, arg, data| val >= arg rescue false}, - max: lambda {|val, arg, data| val <= arg rescue false}, - is_array: lambda {|val, arg, data| val.is_a?(Array)}, - is_hash: lambda {|val, arg, data| val.is_a?(Hash)}, - is_json: lambda {|val, arg, data| ParamValidation.is_valid_json?(val)}, - in_range: lambda {|val, arg, data| arg.cover?(val) rescue false}, - is_a: lambda {|val, arg, data| arg.kind_of?(Enumerable) ? arg.any? {|i| val.is_a?(i)} : val.is_a?(arg)}, - can_be_date: lambda {|val, arg, data| val.is_a?(Date) || val.is_a?(DateTime) || Chronic.parse(val)}, - array_of_hashes: lambda {|val, arg, data| data.is_a?(Array) && data.map{|pair| ParamValidation.new(pair.to_h, arg)}.all?} + required: ->(val, _arg, _data) { !val.nil? }, + absent: ->(val, _arg, _data) { val.nil? }, + not_blank: ->(val, _arg, _data) { val.is_a?(String) && !val.empty? }, + not_included_in: lambda { |val, arg, _data| + begin + !arg.include?(val) + rescue StandardError + false + end + }, + included_in: lambda { |val, arg, _data| + begin + arg.include?(val) + rescue StandardError + false + end + }, + format: lambda { |val, arg, _data| + begin + val =~ arg + rescue StandardError + false + end + }, + is_integer: ->(val, _arg, _data) { val.is_a?(Integer) || val =~ /\A[+-]?\d+\Z/ }, + is_float: lambda { |val, _arg, _data| + val.is_a?(Float) || (begin + !!Float(val) + rescue StandardError + false + end) + }, + min_length: lambda { |val, arg, _data| + begin + val.length >= arg + rescue StandardError + false + end + }, + max_length: lambda { |val, arg, _data| + begin + val.length <= arg + rescue StandardError + false + end + }, + length_range: lambda { |val, arg, _data| + begin + arg.cover?(val.length) + rescue StandardError + false + end + }, + length_equals: ->(val, arg, _data) { val.length == arg }, + is_reference: ->(val, _arg, _data) { (val.is_a?(Integer) && val >= 0) || val =~ /\A\d+\Z/ || val == '' }, + equals: ->(val, arg, _data) { val == arg }, + min: lambda { |val, arg, _data| + begin + val >= arg + rescue StandardError + false + end + }, + max: lambda { |val, arg, _data| + begin + val <= arg + rescue StandardError + false + end + }, + is_array: ->(val, _arg, _data) { val.is_a?(Array) }, + is_hash: ->(val, _arg, _data) { val.is_a?(Hash) }, + is_json: ->(val, _arg, _data) { ParamValidation.is_valid_json?(val) }, + in_range: lambda { |val, arg, _data| + begin + arg.cover?(val) + rescue StandardError + false + end + }, + is_a: ->(val, arg, _data) { arg.is_a?(Enumerable) ? arg.any? { |i| val.is_a?(i) } : val.is_a?(arg) }, + can_be_date: ->(val, _arg, _data) { val.is_a?(Date) || val.is_a?(DateTime) || Chronic.parse(val) }, + array_of_hashes: ->(_val, arg, data) { data.is_a?(Array) && data.map { |pair| ParamValidation.new(pair.to_h, arg) }.all? } } @@messages = { - required: lambda {|h| "#{h[:key]} is required"}, - absent: lambda {|h| "#{h[:key]} must not be present"}, - not_blank: lambda {|h| "#{h[:key]} must not be blank"}, - not_included_in: lambda {|h| "#{h[:key]} must not be included in #{h[:arg].join(", ")}"}, - included_in: lambda {|h|"#{h[:key]} must be one of #{h[:arg].join(", ")}"}, - format: lambda {|h|"#{h[:key]} doesn't have the right format"}, - is_integer: lambda {|h|"#{h[:key]} should be an integer"}, - is_float: lambda {|h|"#{h[:key]} should be a float"}, - min_length: lambda {|h|"#{h[:key]} has a minimum length of #{h[:arg]}"}, - max_length: lambda {|h|"#{h[:key]} has a maximum length of #{h[:arg]}"}, - length_range: lambda {|h|"#{h[:key]} should have a length within #{h[:arg]}"}, - length_equals: lambda {|h|"#{h[:key]} should have a length of #{h[:arg]}"}, - is_reference: lambda{|h| "#{h[:key]} should be an integer or blank"}, - equals: lambda {|h|"#{h[:key]} should equal #{h[:arg]}"}, - min: lambda {|h|"#{h[:key]} must be at least #{h[:arg]}"}, - max: lambda {|h|"#{h[:key]} cannot be more than #{h[:arg]}"}, - in_range: lambda {|h|"#{h[:key]} should be within #{h[:arg]}"}, - is_json: lambda {|h| "#{h[:key]} should be valid JSON"}, - is_hash: lambda {|h| "#{h[:key]} should be a hash"}, - is_a: lambda {|h| "#{h[:key]} should be of the type(s): #{h[:arg].kind_of?(Enumerable) ? h[:arg].join(', '): h[:arg]}"}, - can_be_date: lambda {|h| "#{h[:key]} should be a datetime or be parsable as one"}, - array_of_hashes: lambda {|h| "Please pass in an array of hashes"} + required: ->(h) { "#{h[:key]} is required" }, + absent: ->(h) { "#{h[:key]} must not be present" }, + not_blank: ->(h) { "#{h[:key]} must not be blank" }, + not_included_in: ->(h) { "#{h[:key]} must not be included in #{h[:arg].join(', ')}" }, + included_in: ->(h) { "#{h[:key]} must be one of #{h[:arg].join(', ')}" }, + format: ->(h) { "#{h[:key]} doesn't have the right format" }, + is_integer: ->(h) { "#{h[:key]} should be an integer" }, + is_float: ->(h) { "#{h[:key]} should be a float" }, + min_length: ->(h) { "#{h[:key]} has a minimum length of #{h[:arg]}" }, + max_length: ->(h) { "#{h[:key]} has a maximum length of #{h[:arg]}" }, + length_range: ->(h) { "#{h[:key]} should have a length within #{h[:arg]}" }, + length_equals: ->(h) { "#{h[:key]} should have a length of #{h[:arg]}" }, + is_reference: ->(h) { "#{h[:key]} should be an integer or blank" }, + equals: ->(h) { "#{h[:key]} should equal #{h[:arg]}" }, + min: ->(h) { "#{h[:key]} must be at least #{h[:arg]}" }, + max: ->(h) { "#{h[:key]} cannot be more than #{h[:arg]}" }, + in_range: ->(h) { "#{h[:key]} should be within #{h[:arg]}" }, + is_json: ->(h) { "#{h[:key]} should be valid JSON" }, + is_hash: ->(h) { "#{h[:key]} should be a hash" }, + is_a: ->(h) { "#{h[:key]} should be of the type(s): #{h[:arg].is_a?(Enumerable) ? h[:arg].join(', ') : h[:arg]}" }, + can_be_date: ->(h) { "#{h[:key]} should be a datetime or be parsable as one" }, + array_of_hashes: ->(_h) { 'Please pass in an array of hashes' } } # small utility for testing json validity def self.is_valid_json?(str) - begin - JSON.parse(str) - return true - rescue => e - return false - end + JSON.parse(str) + true + rescue StandardError => e + false end # Special error class that holds all the error data for reference @@ -125,6 +196,4 @@ class ParamValidation super(msg) end end - end - diff --git a/gems/ruby-param-validation/param_validation.gemspec b/gems/ruby-param-validation/param_validation.gemspec index 56d8931f..751a4c8f 100644 --- a/gems/ruby-param-validation/param_validation.gemspec +++ b/gems/ruby-param-validation/param_validation.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Gem::Specification.new do |s| s.name = 'param_validation' s.version = '0.0.2' diff --git a/gems/ruby-param-validation/test/param_validation_test.rb b/gems/ruby-param-validation/test/param_validation_test.rb index a58262ef..cafe8435 100644 --- a/gems/ruby-param-validation/test/param_validation_test.rb +++ b/gems/ruby-param-validation/test/param_validation_test.rb @@ -1,173 +1,193 @@ +# frozen_string_literal: true + require './lib/param_validation.rb' require 'minitest/autorun' class ParamValidationTest < Minitest::Test - - def setup - end + def setup; end def test_required - begin; ParamValidation.new({}, {x: {required: true}}) + begin; ParamValidation.new({}, x: { required: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + # If a key is not required, then don't run the tests on it def test_not_required_and_absent_then_tests_do_not_run - ParamValidation.new({}, {x: {max: 100}}) + ParamValidation.new({}, x: { max: 100 }) assert true end + def test_not_blank_fail - begin; ParamValidation.new({x: ''}, {x: {not_blank: true}}) + begin; ParamValidation.new({ x: '' }, x: { not_blank: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_not_blank_fail_nil - begin; ParamValidation.new({x: nil}, {x: {not_blank: true, required: true}}) + begin; ParamValidation.new({ x: nil }, x: { not_blank: true, required: true }) rescue ParamValidation::ValidationError => e; e; end - assert(e.data.one?{|i| i[:name] == :not_blank && i[:key] == :x}) - assert(e.data.one?{|i| i[:name] == :required && i[:key] == :x}) + assert(e.data.one? { |i| i[:name] == :not_blank && i[:key] == :x }) + assert(e.data.one? { |i| i[:name] == :required && i[:key] == :x }) end + def test_not_blank_succeed - ParamValidation.new({x: 'x'}, {x: {not_blank: true}}) + ParamValidation.new({ x: 'x' }, x: { not_blank: true }) assert true end + def test_require_no_err - begin; ParamValidation.new({x: 1}, {x: {required: true}}) + begin; ParamValidation.new({ x: 1 }, x: { required: true }) rescue ParamValidation::ValidationError => e; end assert e.nil? end + def test_absent - begin; ParamValidation.new({x: 1}, {x: {absent: true}}) + begin; ParamValidation.new({ x: 1 }, x: { absent: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_not_included_in - begin; ParamValidation.new({x: 1}, {x: {not_included_in: [1]}}) + begin; ParamValidation.new({ x: 1 }, x: { not_included_in: [1] }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_included_in - begin; ParamValidation.new({x: 1}, {x: {included_in: [2]}}) + begin; ParamValidation.new({ x: 1 }, x: { included_in: [2] }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_format - begin; ParamValidation.new({x: 'x'}, {x: {format: /y/}}) + begin; ParamValidation.new({ x: 'x' }, x: { format: /y/ }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end def test_is_reference_string - begin; ParamValidation.new({x: '-0'}, {x: {is_reference: true}}) + begin; ParamValidation.new({ x: '-0' }, x: { is_reference: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end def test_is_reference_negative_integer - begin; ParamValidation.new({x: -1}, {x: {is_reference: true}}) + begin; ParamValidation.new({ x: -1 }, x: { is_reference: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end def test_is_reference_passes - ParamValidation.new({x: '0'}, {x: {is_reference: true}}) - ParamValidation.new({x: 1}, {x: {is_reference: true}}) - ParamValidation.new({x: ''}, {x: {is_reference: true}}) - pass() + ParamValidation.new({ x: '0' }, x: { is_reference: true }) + ParamValidation.new({ x: 1 }, x: { is_reference: true }) + ParamValidation.new({ x: '' }, x: { is_reference: true }) + pass end def test_is_integer - begin; ParamValidation.new({x: 'x'}, {x: {is_integer: true}}) + begin; ParamValidation.new({ x: 'x' }, x: { is_integer: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_is_float - begin; ParamValidation.new({x: 'x'}, {x: {is_float: true}}) + begin; ParamValidation.new({ x: 'x' }, x: { is_float: true }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_min_length - begin; ParamValidation.new({x: []}, {x: {min_length: 2}}) + begin; ParamValidation.new({ x: [] }, x: { min_length: 2 }) rescue ParamValidation::ValidationError => e; e; end assert_equal :x, e.data[:key] end + def test_max_length - begin; ParamValidation.new({x: [1,2,3]}, {x: {max_length: 2}}) + begin; ParamValidation.new({ x: [1, 2, 3] }, x: { max_length: 2 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:key], :x end + def test_length_range - begin; ParamValidation.new({x: [1,2,3,4]}, {x: {length_range: 1..3}}) + begin; ParamValidation.new({ x: [1, 2, 3, 4] }, x: { length_range: 1..3 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:key], :x end + def test_length_equals - begin; ParamValidation.new({x: [1,2]}, {x: {length_equals: 1}}) + begin; ParamValidation.new({ x: [1, 2] }, x: { length_equals: 1 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:key], :x end + def test_min - begin; ParamValidation.new({x: 1}, {x: {min: 2}}) + begin; ParamValidation.new({ x: 1 }, x: { min: 2 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:key], :x end + def test_max - begin; ParamValidation.new({x: 4}, {x: {max: 2}}) + begin; ParamValidation.new({ x: 4 }, x: { max: 2 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:name], :max end + def test_in_range - begin; ParamValidation.new({x: 1}, {x: {in_range: 2..4}}) + begin; ParamValidation.new({ x: 1 }, x: { in_range: 2..4 }) rescue ParamValidation::ValidationError => e; e; end assert_equal e.data[:val], 1 end + def test_equals - begin; ParamValidation.new({x: 1}, {x: {equals: 2}}) + begin; ParamValidation.new({ x: 1 }, x: { equals: 2 }) rescue ParamValidation::ValidationError => e; e; end - assert_equal "x should equal #{2}", e.to_s + assert_equal 'x should equal 2', e.to_s end + def test_root_array_of_hashes - begin; ParamValidation.new({x: 1}, {root: {array_of_hashes: {x: {required: true}}}}) + begin; ParamValidation.new({ x: 1 }, root: { array_of_hashes: { x: { required: true } } }) rescue ParamValidation::ValidationError => e; e; end - assert_equal "Please pass in an array of hashes", e.to_s + assert_equal 'Please pass in an array of hashes', e.to_s end + def test_root_array_of_hashes_with_nesting_ok - v = ParamValidation.new([{'x' => 1}, {x: 1}], {root: {array_of_hashes: {x: {is_integer: true}}}}) + v = ParamValidation.new([{ 'x' => 1 }, { x: 1 }], root: { array_of_hashes: { x: { is_integer: true } } }) assert_equal v, v # test that it does not raise end + def test_root_array_of_hashes_with_nesting - begin; ParamValidation.new([{x: 1}, {x: 'hi'}], {root: {array_of_hashes: {x: {is_integer: true}}}}) + begin; ParamValidation.new([{ x: 1 }, { x: 'hi' }], root: { array_of_hashes: { x: { is_integer: true } } }) rescue ParamValidation::ValidationError => e; e; end - assert_equal "x should be an integer", e.to_s + assert_equal 'x should be an integer', e.to_s end def test_is_json_with_string - begin; ParamValidation.new({x: '[[[[[[['}, {x: {is_json: true}}) + begin; ParamValidation.new({ x: '[[[[[[[' }, x: { is_json: true }) rescue ParamValidation::ValidationError => e; e; end - assert_equal "x should be valid JSON", e.to_s + assert_equal 'x should be valid JSON', e.to_s end def test_is_json_without_string - begin; ParamValidation.new({x: {}}, {x: {is_json: true}}) + begin; ParamValidation.new({ x: {} }, x: { is_json: true }) rescue ParamValidation::ValidationError => e; e; end - assert_equal "x should be valid JSON", e.to_s + assert_equal 'x should be valid JSON', e.to_s end def test_is_a_single - ParamValidation.new({x: 5.6}, {x: {is_a: Float}}) + ParamValidation.new({ x: 5.6 }, x: { is_a: Float }) begin - ParamValidation.new({x: 5.6}, {x: {is_a: Integer}}) + ParamValidation.new({ x: 5.6 }, x: { is_a: Integer }) rescue ParamValidation::ValidationError => e e end assert_equal 'x should be of the type(s): Integer', e.to_s end - def test_is_a_multiple - ParamValidation.new({x: 5.6}, {x: {is_a: [Integer,Float]}}) + ParamValidation.new({ x: 5.6 }, x: { is_a: [Integer, Float] }) begin - ParamValidation.new({x: 5.6}, {x: {is_a: [Integer, Array]}}) + ParamValidation.new({ x: 5.6 }, x: { is_a: [Integer, Array] }) rescue ParamValidation::ValidationError => e e end @@ -176,13 +196,13 @@ class ParamValidationTest < Minitest::Test end def test_can_be_date - ParamValidation.new({x: Date.new()}, {x: {can_be_date: true}}) - ParamValidation.new({x: DateTime.new()}, {x: {can_be_date: true}}) - ParamValidation.new({x: '2017-05-15T12:00:00.000Z'}, {x: {can_be_date: true}}) - ParamValidation.new({x: '2017-05-15'}, {x: {can_be_date: true}}) + ParamValidation.new({ x: Date.new }, x: { can_be_date: true }) + ParamValidation.new({ x: DateTime.new }, x: { can_be_date: true }) + ParamValidation.new({ x: '2017-05-15T12:00:00.000Z' }, x: { can_be_date: true }) + ParamValidation.new({ x: '2017-05-15' }, x: { can_be_date: true }) begin - ParamValidation.new({x: 'not_a _date'}, {x: {can_be_date: true}}) + ParamValidation.new({ x: 'not_a _date' }, x: { can_be_date: true }) rescue ParamValidation::ValidationError => e e end @@ -191,25 +211,25 @@ class ParamValidationTest < Minitest::Test end def test_add_validator - ParamValidation.add_validator(:dollars){|val, arg, data| val =~ /^\d+(\.\d\d)?$/} + ParamValidation.add_validator(:dollars) { |val, _arg, _data| val =~ /^\d+(\.\d\d)?$/ } begin - ParamValidation.new({x: 'hi'}, {x: {dollars: true}}) + ParamValidation.new({ x: 'hi' }, x: { dollars: true }) rescue ParamValidation::ValidationError => e e end assert_equal :dollars, e.data[:name] end + def test_set_message - ParamValidation.add_validator(:dollars){|val, arg, data| val =~ /^\d+(\.\d\d)?$/} - ParamValidation.set_message(:dollars){|h| "#{h[:key]} must be a dollar amount"} + ParamValidation.add_validator(:dollars) { |val, _arg, _data| val =~ /^\d+(\.\d\d)?$/ } + ParamValidation.set_message(:dollars) { |h| "#{h[:key]} must be a dollar amount" } begin - ParamValidation.new({x: 'hi'}, {x: {dollars: true}}) + ParamValidation.new({ x: 'hi' }, x: { dollars: true }) rescue ParamValidation::ValidationError => e e end - assert_equal "x must be a dollar amount", e.to_s + assert_equal 'x must be a dollar amount', e.to_s end - def test_custom_validator - end + def test_custom_validator; end end diff --git a/gems/ruby-qx/lib/qx.rb b/gems/ruby-qx/lib/qx.rb index 1db46b59..f5ee40e1 100644 --- a/gems/ruby-qx/lib/qx.rb +++ b/gems/ruby-qx/lib/qx.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record' require 'colorize' @@ -39,9 +41,9 @@ class Qx str += expr[:JOIN].map { |from, cond| " JOIN #{from} ON #{cond}" }.join if expr[:JOIN] str += expr[:LEFT_JOIN].map { |from, cond| " LEFT JOIN #{from} ON #{cond}" }.join if expr[:LEFT_JOIN] str += expr[:LEFT_OUTER_JOIN].map { |from, cond| " LEFT OUTER JOIN #{from} ON #{cond}" }.join if expr[:LEFT_OUTER_JOIN] - str += expr[:JOIN_LATERAL].map {|i| " JOIN LATERAL (#{i[:select_statement]}) #{i[:join_name]} ON #{i[:success_condition]}"}.join if expr[:JOIN_LATERAL] + str += expr[:JOIN_LATERAL].map { |i| " JOIN LATERAL (#{i[:select_statement]}) #{i[:join_name]} ON #{i[:success_condition]}" }.join if expr[:JOIN_LATERAL] - str += expr[:LEFT_JOIN_LATERAL].map {|i| " LEFT JOIN LATERAL (#{i[:select_statement]}) #{i[:join_name]} ON #{i[:success_condition]}"}.join if expr[:LEFT_JOIN_LATERAL] + str += expr[:LEFT_JOIN_LATERAL].map { |i| " LEFT JOIN LATERAL (#{i[:select_statement]}) #{i[:join_name]} ON #{i[:success_condition]}" }.join if expr[:LEFT_JOIN_LATERAL] str += ' WHERE ' + expr[:WHERE].map { |w| "(#{w})" }.join(' AND ') if expr[:WHERE] str += ' GROUP BY ' + expr[:GROUP_BY].join(', ') if expr[:GROUP_BY] str += ' HAVING ' + expr[:HAVING].map { |h| "(#{h})" }.join(' AND ') if expr[:HAVING] @@ -77,9 +79,9 @@ class Qx elsif expr[:ON_CONSTRAINT] str += " ON CONSTRAINT #{expr[:ON_CONSTRAINT]}" end - str += ' DO NOTHING' if !expr[:CONFLICT_UPSERT] + str += ' DO NOTHING' unless expr[:CONFLICT_UPSERT] if expr[:CONFLICT_UPSERT] - set_str = expr[:INSERT_COLUMNS].select{|i| i != 'created_at'}.map{|i| "#{i} = EXCLUDED.#{i}" } + set_str = expr[:INSERT_COLUMNS].reject { |i| i == 'created_at' }.map { |i| "#{i} = EXCLUDED.#{i}" } str += " DO UPDATE SET #{set_str.join(', ')}" end end @@ -101,6 +103,7 @@ class Qx str += ' ' + expr[:ON_CONFLICT] if expr[:ON_CONFLICT] str += ' RETURNING ' + expr[:RETURNING].join(', ') if expr[:RETURNING] end + str end @@ -121,6 +124,7 @@ class Qx # Qx.execute(Qx.select("id").from("table_name")) def self.execute(expr, data = {}, options = {}) return expr.execute(data) if expr.is_a?(Qx) + interpolated = Qx.interpolate_expr(expr, data) execute_raw(interpolated, options) end @@ -247,7 +251,7 @@ class Qx def order_by(*cols) orders = /(asc)|(desc)( nulls (first)|(last))?/i # Sanitize out invalid order keywords - @tree[:ORDER_BY] = cols.map { |col, order| [col.to_s, order.to_s.downcase.strip.match(order.to_s.downcase) ? order.to_s.upcase : nil] } + @tree[:ORDER_BY] = cols.map { |col, order| [col.to_s, order.to_s.downcase.strip.match?(order.to_s.downcase) ? order.to_s.upcase : nil] } self end @@ -310,18 +314,15 @@ class Qx self end - def join_lateral(join_name, select_statement, success_condition=true) - + def join_lateral(join_name, select_statement, success_condition = true) @tree[:JOIN_LATERAL] ||= [] - @tree[:JOIN_LATERAL].concat([{join_name: join_name, select_statement: select_statement, success_condition: success_condition}]) + @tree[:JOIN_LATERAL].concat([{ join_name: join_name, select_statement: select_statement, success_condition: success_condition }]) self end - - def left_join_lateral(join_name, select_statement, success_condition=true) - + def left_join_lateral(join_name, select_statement, success_condition = true) @tree[:LEFT_JOIN_LATERAL] ||= [] - @tree[:LEFT_JOIN_LATERAL].concat([{join_name: join_name, select_statement: select_statement, success_condition: success_condition}]) + @tree[:LEFT_JOIN_LATERAL].concat([{ join_name: join_name, select_statement: select_statement, success_condition: success_condition }]) self end @@ -384,7 +385,7 @@ class Qx self end - def on_conflict() + def on_conflict @tree[:ON_CONFLICT] = true self end @@ -399,8 +400,8 @@ class Qx self end - def upsert(on_index, columns=nil) - @tree[:CONFLICT_UPSERT] = {index: on_index, cols: columns} + def upsert(on_index, columns = nil) + @tree[:CONFLICT_UPSERT] = { index: on_index, cols: columns } self end @@ -455,7 +456,7 @@ class Qx # Quote a string for use in sql to prevent injection or weird errors # Always use this for all values! # Just uses double-dollar quoting universally. Should be generally safe and easy. - # Will return an unquoted value it it's a Fixnum + # Will return an unquoted value it it's a Integer def self.quote(val) if val.is_a?(Qx) val.parse diff --git a/gems/ruby-qx/qx.gemspec b/gems/ruby-qx/qx.gemspec index dcfcd007..195b126b 100644 --- a/gems/ruby-qx/qx.gemspec +++ b/gems/ruby-qx/qx.gemspec @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Gem::Specification.new do |s| s.name = 'qx' s.version = '0.1.1' @@ -9,7 +11,7 @@ Gem::Specification.new do |s| s.files = 'lib/qx.rb' s.homepage = 'https://github.com/jayrbolton/qx' s.license = 'MIT' - s.add_runtime_dependency 'colorize', '~> 0.8' s.add_runtime_dependency 'activerecord', '>= 3.0' + s.add_runtime_dependency 'colorize', '~> 0.8' s.add_development_dependency 'minitest', '~> 5.9' end diff --git a/gems/ruby-qx/test/UpsertTest.rb b/gems/ruby-qx/test/UpsertTest.rb index 1638669f..909d2fb7 100644 --- a/gems/ruby-qx/test/UpsertTest.rb +++ b/gems/ruby-qx/test/UpsertTest.rb @@ -1,20 +1,20 @@ +# frozen_string_literal: true + require './lib/qx.rb' require 'minitest/autorun' class UpsertTest < Minitest::Test - def setup - - end + def setup; end def test_upsert table = 'x' - column1 = "a" + column1 = 'a' column2 = 'b' - idx = "idx_something_more" + idx = 'idx_something_more' - result = Qx.insert_into(table).values({column1: column1, column2: column2}).on_conflict.upsert(idx).parse + result = Qx.insert_into(table).values(column1: column1, column2: column2).on_conflict.upsert(idx).parse - expected = %Q(INSERT INTO "#{table}" ("column1", "column2") VALUES ($Q$#{column1}$Q$, $Q$#{column2}$Q$) ON CONFLICT ON CONSTRAINT #{idx} DO UPDATE SET "column1" = EXCLUDED."column1", "column2" = EXCLUDED."column2") + expected = %(INSERT INTO "#{table}" ("column1", "column2") VALUES ($Q$#{column1}$Q$, $Q$#{column2}$Q$) ON CONFLICT ON CONSTRAINT #{idx} DO UPDATE SET "column1" = EXCLUDED."column1", "column2" = EXCLUDED."column2") assert_equal(expected, result) end -end \ No newline at end of file +end diff --git a/gems/ruby-qx/test/qx_test.rb b/gems/ruby-qx/test/qx_test.rb index 74fd2ee8..71c497ae 100644 --- a/gems/ruby-qx/test/qx_test.rb +++ b/gems/ruby-qx/test/qx_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require './lib/qx.rb' require 'pg' require 'minitest/autorun' @@ -10,9 +12,7 @@ Qx.config(type_map: tm) Qx.execute_file('./test/test_schema.sql') class QxTest < Minitest::Test - - def setup - end + def setup; end # Let's just test that the schema was executed def test_execute_file @@ -23,241 +23,260 @@ class QxTest < Minitest::Test end def test_select_from - parsed = Qx.select(:id, "name").from(:table_name).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name) + parsed = Qx.select(:id, 'name').from(:table_name).parse + assert_equal parsed, %(SELECT id, name FROM table_name) end + def test_select_distinct_on - parsed = Qx.select(:id, "name").distinct_on(:distinct_col1, :distinct_col2).from(:table_name).parse - assert_equal parsed, %Q(SELECT DISTINCT ON (distinct_col1, distinct_col2) id, name FROM table_name) + parsed = Qx.select(:id, 'name').distinct_on(:distinct_col1, :distinct_col2).from(:table_name).parse + assert_equal parsed, %(SELECT DISTINCT ON (distinct_col1, distinct_col2) id, name FROM table_name) end + def test_select_distinct - parsed = Qx.select(:id, "name").distinct.from(:table_name).parse - assert_equal parsed, %Q(SELECT DISTINCT id, name FROM table_name) + parsed = Qx.select(:id, 'name').distinct.from(:table_name).parse + assert_equal parsed, %(SELECT DISTINCT id, name FROM table_name) end def test_select_as - parsed = Qx.select(:id, "name").from(:table_name).as(:alias).parse - assert_equal parsed, %Q((SELECT id, name FROM table_name) AS "alias") + parsed = Qx.select(:id, 'name').from(:table_name).as(:alias).parse + assert_equal parsed, %((SELECT id, name FROM table_name) AS "alias") end - + def test_select_where - parsed = Qx.select(:id, "name").from(:table_name).where("x = $y OR a = $b", y: 1, b: 2).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name WHERE (x = 1 OR a = 2)) + parsed = Qx.select(:id, 'name').from(:table_name).where('x = $y OR a = $b', y: 1, b: 2).parse + assert_equal parsed, %(SELECT id, name FROM table_name WHERE (x = 1 OR a = 2)) end + def test_select_where_hash_array - parsed = Qx.select(:id, "name").from(:table_name).where([x: 1], ["y = $n", {n: 2}]).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name WHERE ("x" IN (1)) AND (y = 2)) + parsed = Qx.select(:id, 'name').from(:table_name).where([x: 1], ['y = $n', { n: 2 }]).parse + assert_equal parsed, %(SELECT id, name FROM table_name WHERE ("x" IN (1)) AND (y = 2)) end + def test_select_and_where - parsed = Qx.select(:id, "name").from(:table_name).where("x = $y", y: 1).and_where("a = $b", b: 2).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name WHERE (x = 1) AND (a = 2)) + parsed = Qx.select(:id, 'name').from(:table_name).where('x = $y', y: 1).and_where('a = $b', b: 2).parse + assert_equal parsed, %(SELECT id, name FROM table_name WHERE (x = 1) AND (a = 2)) end + def test_select_and_where_hash - parsed = Qx.select(:id, "name").from(:table_name).where("x = $y", y: 1).and_where(a: 2).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name WHERE (x = 1) AND ("a" IN (2))) + parsed = Qx.select(:id, 'name').from(:table_name).where('x = $y', y: 1).and_where(a: 2).parse + assert_equal parsed, %(SELECT id, name FROM table_name WHERE (x = 1) AND ("a" IN (2))) end - + def test_select_and_group_by - parsed = Qx.select(:id, "name").from(:table_name).group_by("col1", "col2").parse - assert_equal parsed, %Q(SELECT id, name FROM table_name GROUP BY col1, col2) + parsed = Qx.select(:id, 'name').from(:table_name).group_by('col1', 'col2').parse + assert_equal parsed, %(SELECT id, name FROM table_name GROUP BY col1, col2) end - + def test_select_and_order_by - parsed = Qx.select(:id, "name").from(:table_name).order_by("col1", ["col2", "DESC NULLS LAST"]).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name ORDER BY col1 , col2 DESC NULLS LAST) + parsed = Qx.select(:id, 'name').from(:table_name).order_by('col1', ['col2', 'DESC NULLS LAST']).parse + assert_equal parsed, %(SELECT id, name FROM table_name ORDER BY col1 , col2 DESC NULLS LAST) end def test_select_having - parsed = Qx.select(:id, "name").from(:table_name).having("COUNT(col1) > $n", n: 1).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name HAVING (COUNT(col1) > 1)) + parsed = Qx.select(:id, 'name').from(:table_name).having('COUNT(col1) > $n', n: 1).parse + assert_equal parsed, %(SELECT id, name FROM table_name HAVING (COUNT(col1) > 1)) end + def test_select_and_having - parsed = Qx.select(:id, "name").from(:table_name).having("COUNT(col1) > $n", n: 1).and_having("SUM(col2) > $m", m: 2).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name HAVING (COUNT(col1) > 1) AND (SUM(col2) > 2)) + parsed = Qx.select(:id, 'name').from(:table_name).having('COUNT(col1) > $n', n: 1).and_having('SUM(col2) > $m', m: 2).parse + assert_equal parsed, %(SELECT id, name FROM table_name HAVING (COUNT(col1) > 1) AND (SUM(col2) > 2)) end def test_select_limit - parsed = Qx.select(:id, "name").from(:table_name).limit(10).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name LIMIT 10) + parsed = Qx.select(:id, 'name').from(:table_name).limit(10).parse + assert_equal parsed, %(SELECT id, name FROM table_name LIMIT 10) end + def test_select_offset - parsed = Qx.select(:id, "name").from(:table_name).offset(10).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name OFFSET 10) + parsed = Qx.select(:id, 'name').from(:table_name).offset(10).parse + assert_equal parsed, %(SELECT id, name FROM table_name OFFSET 10) end def test_select_join - parsed = Qx.select(:id, "name").from(:table_name).join(['assoc1', 'assoc1.table_name_id=table_name.id']).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name JOIN assoc1 ON assoc1.table_name_id=table_name.id) + parsed = Qx.select(:id, 'name').from(:table_name).join(['assoc1', 'assoc1.table_name_id=table_name.id']).parse + assert_equal parsed, %(SELECT id, name FROM table_name JOIN assoc1 ON assoc1.table_name_id=table_name.id) end + def test_select_add_join - parsed = Qx.select(:id, "name").from(:table_name).join('assoc1', 'assoc1.table_name_id=table_name.id') - .add_join(['assoc2', 'assoc2.table_name_id=table_name.id']).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name JOIN assoc1 ON assoc1.table_name_id=table_name.id JOIN assoc2 ON assoc2.table_name_id=table_name.id) + parsed = Qx.select(:id, 'name').from(:table_name).join('assoc1', 'assoc1.table_name_id=table_name.id') + .add_join(['assoc2', 'assoc2.table_name_id=table_name.id']).parse + assert_equal parsed, %(SELECT id, name FROM table_name JOIN assoc1 ON assoc1.table_name_id=table_name.id JOIN assoc2 ON assoc2.table_name_id=table_name.id) end + def test_select_left_join - parsed = Qx.select(:id, "name").from(:table_name).left_join(['assoc1', 'assoc1.table_name_id=table_name.id']).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name LEFT JOIN assoc1 ON assoc1.table_name_id=table_name.id) + parsed = Qx.select(:id, 'name').from(:table_name).left_join(['assoc1', 'assoc1.table_name_id=table_name.id']).parse + assert_equal parsed, %(SELECT id, name FROM table_name LEFT JOIN assoc1 ON assoc1.table_name_id=table_name.id) end + def test_select_add_left_join - parsed = Qx.select(:id, "name").from(:table_name).left_join('assoc1', 'assoc1.table_name_id=table_name.id') - .add_left_join(['assoc2', 'assoc2.table_name_id=table_name.id']).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name LEFT JOIN assoc1 ON assoc1.table_name_id=table_name.id LEFT JOIN assoc2 ON assoc2.table_name_id=table_name.id) + parsed = Qx.select(:id, 'name').from(:table_name).left_join('assoc1', 'assoc1.table_name_id=table_name.id') + .add_left_join(['assoc2', 'assoc2.table_name_id=table_name.id']).parse + assert_equal parsed, %(SELECT id, name FROM table_name LEFT JOIN assoc1 ON assoc1.table_name_id=table_name.id LEFT JOIN assoc2 ON assoc2.table_name_id=table_name.id) end def test_select_where_subquery - parsed = Qx.select(:id, "name").from(:table_name).where("id IN ($ids)", ids: Qx.select("id").from("assoc")).parse - assert_equal parsed, %Q(SELECT id, name FROM table_name WHERE (id IN (SELECT id FROM assoc))) + parsed = Qx.select(:id, 'name').from(:table_name).where('id IN ($ids)', ids: Qx.select('id').from('assoc')).parse + assert_equal parsed, %(SELECT id, name FROM table_name WHERE (id IN (SELECT id FROM assoc))) end def test_select_join_subquery - parsed = Qx.select(:id).from(:table).join([Qx.select(:id).from(:assoc).as(:assoc), "assoc.table_id=table.id"]).parse - assert_equal parsed, %Q(SELECT id FROM table JOIN (SELECT id FROM assoc) AS "assoc" ON assoc.table_id=table.id) + parsed = Qx.select(:id).from(:table).join([Qx.select(:id).from(:assoc).as(:assoc), 'assoc.table_id=table.id']).parse + assert_equal parsed, %(SELECT id FROM table JOIN (SELECT id FROM assoc) AS "assoc" ON assoc.table_id=table.id) end def test_select_from_subquery parsed = Qx.select(:id).from(Qx.select(:id).from(:table).as(:table)).parse - assert_equal parsed, %Q(SELECT id FROM (SELECT id FROM table) AS "table") + assert_equal parsed, %(SELECT id FROM (SELECT id FROM table) AS "table") end def test_select_integration parsed = Qx.select(:id) - .from(:table) - .join([Qx.select(:id).from(:assoc).as(:assoc), 'assoc.table_id=table.id']) - .left_join(['lefty', 'lefty.table_id=table.id']) - .where('x = $n', n: 1) - .and_where('y = $n', n: 1) - .group_by(:x) - .order_by(:y) - .having('COUNT(x) > $n', n: 1) - .and_having('COUNT(y) > $n', n: 1) - .limit(10) - .offset(10) - .parse - assert_equal parsed, %Q(SELECT id FROM table JOIN (SELECT id FROM assoc) AS "assoc" ON assoc.table_id=table.id LEFT JOIN lefty ON lefty.table_id=table.id WHERE (x = 1) AND (y = 1) GROUP BY x HAVING (COUNT(x) > 1) AND (COUNT(y) > 1) ORDER BY y LIMIT 10 OFFSET 10) + .from(:table) + .join([Qx.select(:id).from(:assoc).as(:assoc), 'assoc.table_id=table.id']) + .left_join(['lefty', 'lefty.table_id=table.id']) + .where('x = $n', n: 1) + .and_where('y = $n', n: 1) + .group_by(:x) + .order_by(:y) + .having('COUNT(x) > $n', n: 1) + .and_having('COUNT(y) > $n', n: 1) + .limit(10) + .offset(10) + .parse + assert_equal parsed, %(SELECT id FROM table JOIN (SELECT id FROM assoc) AS "assoc" ON assoc.table_id=table.id LEFT JOIN lefty ON lefty.table_id=table.id WHERE (x = 1) AND (y = 1) GROUP BY x HAVING (COUNT(x) > 1) AND (COUNT(y) > 1) ORDER BY y LIMIT 10 OFFSET 10) end def test_insert_into_values_hash parsed = Qx.insert_into(:table_name).values(x: 1).parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x") VALUES (1)) + assert_equal parsed, %(INSERT INTO "table_name" ("x") VALUES (1)) end + def test_insert_into_values_hash_array - parsed = Qx.insert_into(:table_name).values([{x: 1}, {x: 2}]).parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x") VALUES (1), (2)) + parsed = Qx.insert_into(:table_name).values([{ x: 1 }, { x: 2 }]).parse + assert_equal parsed, %(INSERT INTO "table_name" ("x") VALUES (1), (2)) end + def test_insert_into_values_csv_style parsed = Qx.insert_into(:table_name).values([['x'], [1], [2]]).parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x") VALUES (1), (2)) + assert_equal parsed, %(INSERT INTO "table_name" ("x") VALUES (1), (2)) end + def test_insert_into_values_common_values - parsed = Qx.insert_into(:table_name).values([{x: 'bye'}, {x: 'hi'}]).common_values(z: 1).parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x", "z") VALUES ($Q$bye$Q$, 1), ($Q$hi$Q$, 1)) + parsed = Qx.insert_into(:table_name).values([{ x: 'bye' }, { x: 'hi' }]).common_values(z: 1).parse + assert_equal parsed, %(INSERT INTO "table_name" ("x", "z") VALUES ($Q$bye$Q$, 1), ($Q$hi$Q$, 1)) end + def test_insert_into_values_timestamps parsed = Qx.insert_into(:table_name).values(x: 1).ts.parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x", created_at, updated_at) VALUES (1, '#{Time.now.utc}', '#{Time.now.utc}')) + assert_equal parsed, %(INSERT INTO "table_name" ("x", created_at, updated_at) VALUES (1, '#{Time.now.utc}', '#{Time.now.utc}')) end + def test_insert_into_values_returning parsed = Qx.insert_into(:table_name).values(x: 1).returning('*').parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x") VALUES (1) RETURNING *) + assert_equal parsed, %(INSERT INTO "table_name" ("x") VALUES (1) RETURNING *) end + def test_insert_into_select - parsed = Qx.insert_into(:table_name, ['hi']).select('hi').from(:table2).where("x=y").parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("hi") SELECT hi FROM table2 WHERE (x=y)) + parsed = Qx.insert_into(:table_name, ['hi']).select('hi').from(:table2).where('x=y').parse + assert_equal parsed, %(INSERT INTO "table_name" ("hi") SELECT hi FROM table2 WHERE (x=y)) end def test_update_set - parsed = Qx.update(:table_name).set(x: 1).where("y = 2").parse - assert_equal parsed, %Q(UPDATE "table_name" SET "x" = 1 WHERE (y = 2)) + parsed = Qx.update(:table_name).set(x: 1).where('y = 2').parse + assert_equal parsed, %(UPDATE "table_name" SET "x" = 1 WHERE (y = 2)) end + def test_update_timestamps now = Time.now.utc - parsed = Qx.update(:table_name).set(x: 1).where("y = 2").timestamps.parse - assert_equal parsed, %Q(UPDATE "table_name" SET "x" = 1, updated_at = '#{now}' WHERE (y = 2)) + parsed = Qx.update(:table_name).set(x: 1).where('y = 2').timestamps.parse + assert_equal parsed, %(UPDATE "table_name" SET "x" = 1, updated_at = '#{now}' WHERE (y = 2)) end def test_update_on_conflict - Qx.update(:table_name).set(x: 1).where("y = 2").on_conflict(:nothing).parse - assert_equal parsed, %Q(UPDATE "table_name" SET "x" = 1 WHERE (y = 2) ON CONFLICT DO NOTHING) + Qx.update(:table_name).set(x: 1).where('y = 2').on_conflict(:nothing).parse + assert_equal parsed, %(UPDATE "table_name" SET "x" = 1 WHERE (y = 2) ON CONFLICT DO NOTHING) end def test_insert_timestamps now = Time.now.utc - parsed = Qx.insert_into(:table_name).values({x: 1}).ts.parse - assert_equal parsed, %Q(INSERT INTO "table_name" ("x", created_at, updated_at) VALUES (1, '#{now}', '#{now}')) + parsed = Qx.insert_into(:table_name).values(x: 1).ts.parse + assert_equal parsed, %(INSERT INTO "table_name" ("x", created_at, updated_at) VALUES (1, '#{now}', '#{now}')) end def test_delete_from parsed = Qx.delete_from(:table_name).where(x: 1).parse - assert_equal parsed, %Q(DELETE FROM "table_name" WHERE ("x" IN (1))) + assert_equal parsed, %(DELETE FROM "table_name" WHERE ("x" IN (1))) end def test_pagination parsed = Qx.select(:x).from(:y).paginate(4, 30).parse - assert_equal parsed, %Q(SELECT x FROM y LIMIT 30 OFFSET 90) + assert_equal parsed, %(SELECT x FROM y LIMIT 30 OFFSET 90) end def test_execute_string - result = Qx.execute("SELECT * FROM (VALUES ($x)) AS t", x: 'x') - assert_equal result, [{'column1' => 'x'}] + result = Qx.execute('SELECT * FROM (VALUES ($x)) AS t', x: 'x') + assert_equal result, [{ 'column1' => 'x' }] end + def test_execute_format_csv - result = Qx.execute("SELECT * FROM (VALUES ($x)) AS t", {x: 'x'}, {format: 'csv'}) + result = Qx.execute('SELECT * FROM (VALUES ($x)) AS t', { x: 'x' }, format: 'csv') assert_equal result, [['column1'], ['x']] end + def test_execute_on_instances result = Qx.insert_into(:users).values(id: 1, email: 'uzr@example.com').execute - result = Qx.execute(Qx.select("*").from(:users).limit(1)) - assert_equal result, [{'id' => 1, 'email' => 'uzr@example.com'}] + result = Qx.execute(Qx.select('*').from(:users).limit(1)) + assert_equal result, [{ 'id' => 1, 'email' => 'uzr@example.com' }] Qx.delete_from(:users).where(id: 1).execute end def test_explain - parsed = Qx.select("*").from("table_name").explain.parse - assert_equal parsed, %Q(EXPLAIN SELECT * FROM table_name) + parsed = Qx.select('*').from('table_name').explain.parse + assert_equal parsed, %(EXPLAIN SELECT * FROM table_name) end # Manually test this one for now def test_pp_select - pp = Qx.select("id, name").from("table_name").where(status: 'active').and_where(id: Qx.select("id").from("roles").where(name: "admin")).pp + pp = Qx.select('id, name').from('table_name').where(status: 'active').and_where(id: Qx.select('id').from('roles').where(name: 'admin')).pp pp2 = Qx.insert_into(:table_name).values([x: 1, y: 2]).pp pp3 = Qx.update(:table_name).set(x: 1, y: 2).where(z: 1, a: 22).pp pp_delete = Qx.delete_from(:table_name).where(id: 123).pp - puts "" - puts "--- pretty print" + puts '' + puts '--- pretty print' puts pp puts pp2 puts pp3 puts pp_delete - puts "---" + puts '---' end def test_to_json parsed = Qx.select(:id).from(:users).to_json(:t).parse - assert_equal parsed, %Q(SELECT array_to_json(array_agg(row_to_json(t))) FROM (SELECT id FROM users) AS "t") + assert_equal parsed, %(SELECT array_to_json(array_agg(row_to_json(t))) FROM (SELECT id FROM users) AS "t") end def test_to_json_nested definitions = Qx.select(:part_of_speech, :body) - .from(:definitions) - .where("word_id=words.id") - .order_by("position ASC") - .to_json(:ds) - .as("definitions") + .from(:definitions) + .where('word_id=words.id') + .order_by('position ASC') + .to_json(:ds) + .as('definitions') parsed = Qx.select(:text, :pronunciation, definitions) - .from(:words) - .where("text='autumn'") - .to_json(:ws) - .parse + .from(:words) + .where("text='autumn'") + .to_json(:ws) + .parse assert_equal parsed, "SELECT array_to_json(array_agg(row_to_json(ws))) FROM (SELECT text, pronunciation, (SELECT array_to_json(array_agg(row_to_json(ds))) FROM (SELECT part_of_speech, body FROM definitions WHERE (word_id=words.id) ORDER BY position ASC ) AS \"ds\") AS \"definitions\" FROM words WHERE (text='autumn')) AS \"ws\"" end def test_copy_csv_execution - data = {'id' => '1', 'email' => 'uzr@example.com'} + data = { 'id' => '1', 'email' => 'uzr@example.com' } filename = '/tmp/qx-test.csv' Qx.insert_into(:users).values(data).ex - copy = Qx.select("*").from("users").execute(copy_csv: filename) + copy = Qx.select('*').from('users').execute(copy_csv: filename) contents = File.open(filename, 'r').read - csv_data = contents.split("\n").map{|l| l.split(",")} + csv_data = contents.split("\n").map { |l| l.split(',') } headers = csv_data.first row = csv_data.last assert_equal data.keys, headers @@ -265,11 +284,8 @@ class QxTest < Minitest::Test end def test_remove_clause - expr = Qx.select("*").from("table").limit(1) + expr = Qx.select('*').from('table').limit(1) expr = expr.remove_clause('limit') - assert_equal "SELECT * FROM table", expr.parse + assert_equal 'SELECT * FROM table', expr.parse end - - - end diff --git a/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap b/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap deleted file mode 100644 index 9ab548a2..00000000 --- a/javascripts/src/components/common/__snapshots__/Modal.spec.tsx.snap +++ /dev/null @@ -1,454 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Modal active modal displays matches snapshot 1`] = ` - - - -
- - -
-
- -
-
- } - > - - -
-
- -
- - - - -
- - -
-`; - -exports[`Modal nothing displayed if inactive 1`] = ` - -`; diff --git a/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap b/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap deleted file mode 100644 index 4cb66665..00000000 --- a/javascripts/src/components/common/__snapshots__/Spinner.spec.tsx.snap +++ /dev/null @@ -1,221 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Spinner has custom color 1`] = ` - - -
- - - Loading... - - -
-
-
-`; - -exports[`Spinner is large 1`] = ` - - -
- - - Loading... - - -
-
-
-`; - -exports[`Spinner is normal 1`] = ` - - -
- - - Loading... - - -
-
-
-`; - -exports[`Spinner is small 1`] = ` - - -
- - - Loading... - - -
-
-
-`; diff --git a/lib/audit.rb b/lib/audit.rb index a7ea242e..c2cd87a8 100644 --- a/lib/audit.rb +++ b/lib/audit.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Audit - # Given a list of pairs of nonprofit ids and stripe_account_ids (eg [[4341, 'acct_arst'], [3624, 'acct_arst']]) # Find all their available balances on both stripe and CC # Give all the ones that dont match up with the difference @@ -23,9 +24,9 @@ module Audit fees = p.payments.sum(:fee_total) net = p.payments.sum(:net_amount) puts [ - [p.gross_amount, p.fee_total, p.net_amount].join(", ") + " -- payout columns", - [gross, fees, net].join(", ") + " -- summed from payments", - [p.gross_amount - gross, p.fee_total - fees, p.net_amount - net].join(", ") + " -- differences" + [p.gross_amount, p.fee_total, p.net_amount].join(', ') + ' -- payout columns', + [gross, fees, net].join(', ') + ' -- summed from payments', + [p.gross_amount - gross, p.fee_total - fees, p.net_amount - net].join(', ') + ' -- differences' ].join("\n") end @@ -36,20 +37,21 @@ module Audit starting_after = nil transfers = [] loop do - new_transfers = Stripe::Transfer.all({limit: 100, starting_after: starting_after, destination: acct}).data + new_transfers = Stripe::Transfer.all(limit: 100, starting_after: starting_after, destination: acct).data break if new_transfers.empty? + transfers += new_transfers starting_after = new_transfers.last.id end ActiveRecord::Base.logger = logger - return transfers + transfers end # Given a list of Stripe transaction objects, see if any are missing on CommitChange def self.find_missing_charges(transfers) transfers - .map{|t| [t.source_transaction, t.amount]} - .select{|id, amount| Charge.where(stripe_charge_id: id, amount: amount).empty?} + .map { |t| [t.source_transaction, t.amount] } + .select { |id, amount| Charge.where(stripe_charge_id: id, amount: amount).empty? } end # Audit some basic balances for a nonprofit with those on Stripe @@ -58,7 +60,7 @@ module Audit puts "Stripe Dashboard: https://dashboard.stripe.com/#{np.stripe_account_id}" puts "CC Payments: https://commitchange.com/nonprofits/#{id}/payments" puts "CC Payouts: https://commitchange.com/nonprofits/#{id}/payouts" - + begin stripe_balances = Stripe::Balance.retrieve(stripe_account: np.stripe_account_id) available = stripe_balances['available'].first['amount'] @@ -74,36 +76,35 @@ module Audit cc_net: bal['net_amount'], diff: bal['net_amount'] - (available + pending) } - return data + data end - # Get the total gross, net + # Get the total gross, net # Pretty much duped from QueryPayments def self.np_balances(np_id) payment_ids_expr = Qx.select('DISTINCT payments.id') - .from(:payments) - .left_join( - [:charges, 'charges.payment_id=payments.id'], - [:refunds, 'refunds.payment_id=payments.id'], - [:disputes, 'disputes.payment_id=payments.id'] - ) - .where('payments.nonprofit_id=$id', id: np_id) - .and_where("refunds.payment_id IS NOT NULL OR charges.payment_id IS NOT NULL OR disputes.payment_id IS NOT NULL") - .and_where(%Q( + .from(:payments) + .left_join( + [:charges, 'charges.payment_id=payments.id'], + [:refunds, 'refunds.payment_id=payments.id'], + [:disputes, 'disputes.payment_id=payments.id'] + ) + .where('payments.nonprofit_id=$id', id: np_id) + .and_where('refunds.payment_id IS NOT NULL OR charges.payment_id IS NOT NULL OR disputes.payment_id IS NOT NULL') + .and_where(%( (refunds.payment_id IS NOT NULL AND (refunds.disbursed IS NULL OR refunds.disbursed='f')) OR (charges.status='available' OR charges.status='pending') OR (disputes.status='lost') )) - return Qx.select( - 'coalesce(SUM(payments.gross_amount), 0) AS gross_amount', - 'coalesce(SUM(payments.fee_total), 0) AS fee_total', - 'coalesce(SUM(payments.net_amount), 0) AS net_amount', - 'COUNT(payments.*) AS count' - ) + Qx.select( + 'coalesce(SUM(payments.gross_amount), 0) AS gross_amount', + 'coalesce(SUM(payments.fee_total), 0) AS fee_total', + 'coalesce(SUM(payments.net_amount), 0) AS net_amount', + 'COUNT(payments.*) AS count' + ) .from(:payments) - .where("payments.id IN ($ids)", ids: payment_ids_expr) + .where('payments.id IN ($ids)', ids: payment_ids_expr) .execute .first end - end diff --git a/lib/calculate/calculate_fees.rb b/lib/calculate/calculate_fees.rb index dc67ce49..846d2baf 100644 --- a/lib/calculate/calculate_fees.rb +++ b/lib/calculate/calculate_fees.rb @@ -1,16 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CalculateFees BaseFeeRate = 0.022 # 2.2% PerTransaction = 30 # 30 cents - def self.for_single_amount(amount, platform_fee=0.0) - ParamValidation.new({fee: platform_fee, amount: amount}, { - amount: {min: 0, is_integer: true}, - fee: {min: 0.0, is_float: true} - }) + def self.for_single_amount(amount, platform_fee = 0.0) + ParamValidation.new({ fee: platform_fee, amount: amount }, + amount: { min: 0, is_integer: true }, + fee: { min: 0.0, is_float: true }) fee = BaseFeeRate + platform_fee (amount * fee).ceil.to_i + PerTransaction end - end - diff --git a/lib/calculate/calculate_suggested_amounts.rb b/lib/calculate/calculate_suggested_amounts.rb index d4af6fab..4c8eaec8 100644 --- a/lib/calculate/calculate_suggested_amounts.rb +++ b/lib/calculate/calculate_suggested_amounts.rb @@ -1,50 +1,48 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'numeric' module CalculateSuggestedAmounts MIN = 25 - MAX = 100000000 - BRACKETS = [{range: MIN...1000, delta:100}, - {range: 1000...5000, delta: 500}, - {range: 5000...MAX, delta: 2500}] + MAX = 100_000_000 + BRACKETS = [{ range: MIN...1000, delta: 100 }, + { range: 1000...5000, delta: 500 }, + { range: 5000...MAX, delta: 2500 }].freeze # Calculates a set of suggested donation amounts based upon our internal special algorithm # This is most useful for suggesting amounts a recurring donor could change to # @return [Array] suggested amounts for your donation # @param [Number] amount the amount in cents to start from def self.calculate(amount) - ParamValidation.new({amount: amount}, amount: {required:true, is_a: Numeric, min: MIN, max:MAX }) + ParamValidation.new({ amount: amount }, amount: { required: true, is_a: Numeric, min: MIN, max: MAX }) result = [] step_down_val = step_down_value(amount) - unless step_down_val.nil? - result.push(step_down_val) - end + result.push(step_down_val) unless step_down_val.nil? higher_amounts = [] - while (higher_amounts.empty? || (higher_amounts.length < 3 && higher_amounts.last() != nil)) + while higher_amounts.empty? || (higher_amounts.length < 3 && !higher_amounts.last.nil?) if higher_amounts.empty? higher_amounts.push(step_up_value(amount)) else higher_amounts.push(step_up_value(higher_amounts.last)) end end - result.concat(higher_amounts.reject {|i| i.nil?}) + result.concat(higher_amounts.reject(&:nil?)) end def self.step_down_value(amount) - initial_bracket = get_bracket_by_amount(amount) - #check_floor_for_delta + # check_floor_for_delta delta_floor = amount.floor_for_delta(initial_bracket[:delta]) - #not on a delta, just send a floor - if (delta_floor != amount) + # not on a delta, just send a floor + if delta_floor != amount return delta_floor < MIN ? nil : delta_floor end - potential_lower_amount = amount - initial_bracket[:delta] # is potential_lower_amount < our MIN? if so, return nil @@ -52,30 +50,25 @@ module CalculateSuggestedAmounts new_bracket = get_bracket_by_amount(potential_lower_amount) - #if in same bracket, potential_lower_amount is our step_down_value + # if in same bracket, potential_lower_amount is our step_down_value - if initial_bracket == new_bracket - return potential_lower_amount - end + return potential_lower_amount if initial_bracket == new_bracket - #we're going to step down by our new bracket value then - return amount - new_bracket[:delta] + # we're going to step down by our new bracket value then + amount - new_bracket[:delta] end - def self.step_up_value(amount) - bracket = get_bracket_by_amount(amount) - #check_ceil_for_delta + # check_ceil_for_delta delta_ceil = amount.ceil_for_delta(bracket[:delta]) - #not on a delta, just send a ceil - if (delta_ceil != amount) + # not on a delta, just send a ceil + if delta_ceil != amount return delta_ceil >= MAX ? nil : delta_ceil end - potential_higher_amount = amount + bracket[:delta] # is potential_lower_amount < our MIN? if so, return nil @@ -84,10 +77,7 @@ module CalculateSuggestedAmounts potential_higher_amount end - - - def self.get_bracket_by_amount(amount) BRACKETS.select { |i| i[:range].cover?(amount) }.first end -end \ No newline at end of file +end diff --git a/lib/cancel_billing_subscription.rb b/lib/cancel_billing_subscription.rb index 75ea72c0..1590fbcc 100644 --- a/lib/cancel_billing_subscription.rb +++ b/lib/cancel_billing_subscription.rb @@ -1,19 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CancelBillingSubscription - # @param [Nonprofit] nonprofit # @return [BillingSubscription] new billing subscription for the nonprofit - def self.with_stripe(nonprofit) + def self.with_stripe(nonprofit) begin - ParamValidation.new({nonprofit: nonprofit}, { - nonprofit: {required: true, is_a: Nonprofit} - }) + ParamValidation.new({ nonprofit: nonprofit }, + nonprofit: { required: true, is_a: Nonprofit }) rescue ParamValidation::ValidationError => e - return {json: {error: "Validation error\n #{e.message}", errors: e.data}, status: :unprocessable_entity} + return { json: { error: "Validation error\n #{e.message}", errors: e.data }, status: :unprocessable_entity } end - np_card = nonprofit.active_card - billing_subscription = nonprofit.billing_subscription - return {json:{error: 'We don\'t have a subscription for your non-profit. Please contact support.'}, status: :unprocessable_entity} if np_card.nil? || billing_subscription.nil? # stripe_customer_id on Card object + np_card = nonprofit.active_card + billing_subscription = nonprofit.billing_subscription + return { json: { error: 'We don\'t have a subscription for your non-profit. Please contact support.' }, status: :unprocessable_entity } if np_card.nil? || billing_subscription.nil? # stripe_customer_id on Card object # Cancel and delete the subscription on Stripe begin @@ -21,15 +21,15 @@ module CancelBillingSubscription stripe_subscription = customer.subscriptions.retrieve(billing_subscription.stripe_subscription_id) s = stripe_subscription.delete(at_period_end: false) rescue Stripe::StripeError => e - return {json: {error: "Oops! There was an error processing your subscription cancellation. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error processing your subscription cancellation. Error: #{e}" }, status: :unprocessable_entity } end billing_plan_id = Settings.default_bp.id - billing_subscription.update_attributes({ + billing_subscription.update( billing_plan_id: billing_plan_id, status: 'active' - }) + ) - return {json:{}, status: :ok} - end + { json: {}, status: :ok } + end end diff --git a/lib/chunked_uploader.rb b/lib/chunked_uploader.rb new file mode 100644 index 00000000..ee657846 --- /dev/null +++ b/lib/chunked_uploader.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module ChunkedUploader + + # Copy a string using chunks instead of all as one string. This is useful reducing memory usage when you want to do a huge export + # + # This code copies each chunk to a tempfile and then opens the tempfile and passes the IO object to the block + # @param [Enumerable] chunk_enum an enumerable of strings. + # @block accepts an IO for passing to upload + def self.upload(chunk_enum, &block) + file_name = File.join(Dir.tmpdir, SecureRandom.uuid) + + File.open(file_name, 'w') do |file| + chunk_enum.each do |chunk| + file.write(chunk) + end + end + + File.open(file_name, 'r') do |file| + yield(file) + end + + File.delete(file_name) + end +end diff --git a/lib/chunked_uploader/s3.rb b/lib/chunked_uploader/s3.rb deleted file mode 100644 index 579c6bac..00000000 --- a/lib/chunked_uploader/s3.rb +++ /dev/null @@ -1,37 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module ChunkedUploader - class S3 - MINIMUMBUFFER_SIZE = 5.megabytes - - S3_BUCKET_NAME = Settings.aws.bucket_name - - # Upload a string to s3 using chunks instead of all as one string. This is useful reducing memory usage on huge files - # @param [Enumerable] chunk_enum an enumerable of strings. - # @param [String] path the path to the object on your S3 bucket - # @returns the url to your uploaded file - def self.upload(path,chunk_enum, metadata={}) - s3 = AWS::S3.new - bucket = s3.buckets[S3_BUCKET_NAME] - object = bucket.objects[path] - io = StringIO.new('', 'w') - content_type = metadata[:content_type] ? metadata[:content_type] : nil - content_disposition = metadata[:content_disposition] ? metadata[:content_disposition] : nil - begin - object.multipart_upload(:acl => :public_read, :content_type => content_type, content_disposition: content_disposition) do |upload| - chunk_enum.each do |chunk| - export_returned = io.write(chunk) - if (io.size >= MINIMUMBUFFER_SIZE) - upload.add_part(io.string) - io.reopen('') - end - end - upload.add_part(io.string) - end - object.public_url.to_s - rescue => e - io.close - raise e - end - end - end -end \ No newline at end of file diff --git a/lib/construct/construct_billing_subscription.rb b/lib/construct/construct_billing_subscription.rb index 51941ed4..846da585 100644 --- a/lib/construct/construct_billing_subscription.rb +++ b/lib/construct/construct_billing_subscription.rb @@ -1,22 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'stripe' require 'active_support/core_ext' module ConstructBillingSubscription + def self.with_stripe(np, billing_plan) + raise ArgumentError, 'Billing plan not found' if billing_plan.nil? - def self.with_stripe(np, billing_plan) - raise ArgumentError.new("Billing plan not found") if billing_plan.nil? - trial_end = QueryBillingSubscriptions.currently_in_trial?(np.id) ? (np.created_at + 15.days).to_i : nil - customer = Stripe::Customer.retrieve np.active_card.stripe_customer_id - stripe_subscription = customer.subscriptions.create({ - plan: billing_plan.stripe_plan_id, - trial_end: trial_end - }) - return { - billing_plan_id: billing_plan.id, - stripe_subscription_id: stripe_subscription.id, - status: stripe_subscription.status - } - end - + customer = Stripe::Customer.retrieve np.active_card.stripe_customer_id + stripe_subscription = customer.subscriptions.create( + plan: billing_plan.stripe_plan_id + ) + { + billing_plan_id: billing_plan.id, + stripe_subscription_id: stripe_subscription.id, + status: stripe_subscription.status + } + end end diff --git a/lib/construct/construct_nonprofit.rb b/lib/construct/construct_nonprofit.rb index 7fa5d263..b06bdfae 100644 --- a/lib/construct/construct_nonprofit.rb +++ b/lib/construct/construct_nonprofit.rb @@ -1,17 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'create/stripe/create_stripe_account' module ConstructNonprofit - - def self.construct(user, h) - h[:verification_status] = 'unverified' - h[:published] = true - h[:statement] = h[:name][0..16] - h.except!(:website) if h[:website].blank? - stripe_acct = CreateStripeAccount.for_nonprofit(user, h) - h[:stripe_account_id] = stripe_acct.id - return h - end - + def self.construct(user, h) + h[:verification_status] = 'unverified' + h[:published] = true + h[:statement] = h[:name][0..16] + h.except!(:website) if h[:website].blank? + stripe_acct = CreateStripeAccount.for_nonprofit(user, h) + h[:stripe_account_id] = stripe_acct.id + h + end end - diff --git a/lib/controllers/campaign_helper.rb b/lib/controllers/campaign_helper.rb deleted file mode 100644 index d7a568a0..00000000 --- a/lib/controllers/campaign_helper.rb +++ /dev/null @@ -1,23 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Controllers::CampaignHelper - include Controllers::NonprofitHelper - -private - - def current_campaign - @campaign ||= FetchCampaign.with_params params, current_nonprofit - raise ActionController::RoutingError.new "Campaign not found" if @campaign.nil? - return @campaign - end - - def current_campaign_editor? - !params[:preview] && (current_nonprofit_user? || current_role?(:campaign_editor, current_campaign.id) || current_role?(:super_admin)) - end - - def authenticate_campaign_editor! - unless current_campaign_editor? - block_with_sign_in 'You need to be a campaign editor to do that.' - end - end - -end diff --git a/lib/controllers/event_helper.rb b/lib/controllers/event_helper.rb deleted file mode 100644 index a58c778f..00000000 --- a/lib/controllers/event_helper.rb +++ /dev/null @@ -1,27 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Controllers::EventHelper - include Controllers::NonprofitHelper - -private - - def current_event_admin? - current_nonprofit_admin? - end - - def current_event_editor? - !params[:preview] && (current_nonprofit_user? || current_role?(:event_editor, current_event.id) || current_role?(:super_admin)) - end - - def authenticate_event_editor! - unless current_event_editor? - block_with_sign_in 'You need to be the event organizer or a nonprofit administrator before doing that.' - end - end - - def current_event - @event ||= FetchEvent.with_params params, current_nonprofit - raise ActionController::RoutingError.new "Event not found" if @event.nil? - return @event - end - -end diff --git a/lib/controllers/nonprofit_helper.rb b/lib/controllers/nonprofit_helper.rb deleted file mode 100644 index a62cf078..00000000 --- a/lib/controllers/nonprofit_helper.rb +++ /dev/null @@ -1,59 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Controllers::NonprofitHelper - -private - - def authenticate_nonprofit_user! - unless current_nonprofit_user? - block_with_sign_in 'Please sign in' - end - end - - def authenticate_nonprofit_admin! - unless current_nonprofit_admin? - block_with_sign_in 'Please sign in' - end - end - - def authenticate_min_nonprofit_plan plan_tier - unless current_nonprofit_user? && current_plan_tier >= plan_tier - block_with_sign_in 'Please sign in' - end - end - - def current_nonprofit_user? - return false if params[:preview] - return false unless current_nonprofit_without_exception - @current_user_role ||= current_role?([:nonprofit_admin, :nonprofit_associate], current_nonprofit_without_exception.id) || current_role?(:super_admin) - end - - def current_nonprofit_admin? - return false if !current_user || current_user.roles.empty? - @current_admin_role ||= current_role?(:nonprofit_admin, current_nonprofit.id) || current_role?(:super_admin) - end - - def current_nonprofit - @nonprofit = current_nonprofit_without_exception - raise ActionController::RoutingError.new "Nonprofit not found" if @nonprofit.nil? - return @nonprofit - end - - def current_nonprofit_without_exception - key = "current_nonprofit_#{current_user_id}_params_#{[params[:state_code], params[:city], params[:name], params[:nonprofit_id], params[:id]].join("_")}" - FetchNonprofit.with_params params, administered_nonprofit - end - - def donation_stub - return current_nonprofit_without_exception.donations.last unless current_nonprofit_without_exception.donations.empty? - OpenStruct.new( - amount: 2000, - created_at: Time.zone.now, - nonprofit: current_nonprofit_without_exception, - campaign: nil, - designation: "Donor's designation here", - dedication: "Donor's dedication here", - id: 1 - ) - end - -end diff --git a/lib/copy_naming_algorithm.rb b/lib/copy_naming_algorithm.rb index 729c7eba..ab8373d1 100644 --- a/lib/copy_naming_algorithm.rb +++ b/lib/copy_naming_algorithm.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CopyNamingAlgorithm DEFAULT_MAX_LENGTH = 255 DEFAULT_MAX_COPIES = 255 - DEFAULT_SEPARATOR_BEFORE_COPY_NUMBER = "_" + DEFAULT_SEPARATOR_BEFORE_COPY_NUMBER = '_' def copy_addition raise NotImplementedError @@ -20,7 +22,7 @@ class CopyNamingAlgorithm DEFAULT_MAX_COPIES end - def get_already_used_name_entities(base_name) + def get_already_used_name_entities(_base_name) raise NotImplementedError end @@ -30,36 +32,35 @@ class CopyNamingAlgorithm def create_copy_name(name_to_copy) # remove copy addition and number - base_name = name_to_copy.gsub(/#{Regexp.quote(self.copy_addition)}(#{Regexp.quote(separator_before_copy_number)}\d+)?\z/,'') - name_entities_to_check_against = get_already_used_name_entities(base_name).collect{|entity| self.get_name_for_entity(entity)}.to_a - (0..max_copies-1).each {|copy_num| + base_name = name_to_copy.gsub(/#{Regexp.quote(copy_addition)}(#{Regexp.quote(separator_before_copy_number)}\d+)?\z/, '') + name_entities_to_check_against = get_already_used_name_entities(base_name).collect { |entity| get_name_for_entity(entity) }.to_a + (0..max_copies - 1).each do |copy_num| name_to_test = generate_name(base_name, copy_num) - if (name_entities_to_check_against.none? {|entity_name| entity_name == name_to_test}) + if name_entities_to_check_against.none? { |entity_name| entity_name == name_to_test } return name_to_test end - } + end - raise UnableToCreateNameCopyError.new("It's not possible to generate a UNIQUE name using name_to_copy: #{name_to_copy} copy_addition: #{self.copy_addition} separator_before_copy_number: #{self.separator_before_copy_number} max_copy_num: #{self.max_copies} max_length: #{self.max_length}") + raise UnableToCreateNameCopyError, "It's not possible to generate a UNIQUE name using name_to_copy: #{name_to_copy} copy_addition: #{copy_addition} separator_before_copy_number: #{separator_before_copy_number} max_copy_num: #{max_copies} max_length: #{max_length}" end def generate_name(name_to_copy, copy_num) - what_to_add = self.copy_addition + self.separator_before_copy_number + generate_copy_number(copy_num) + what_to_add = copy_addition + separator_before_copy_number + generate_copy_number(copy_num) # is what_to_add longer than max length? If so, it's not possible to create a copy - if (what_to_add.length > self.max_length) - raise UnableToCreateNameCopyError.new("It's not possible to generate a name using name_to_copy: #{name_to_copy} copy_addition: #{self.copy_addition} separator_before_copy_number: #{self.separator_before_copy_number} copy_num: #{copy_num} max_length: #{self.max_length}") + if what_to_add.length > max_length + raise UnableToCreateNameCopyError, "It's not possible to generate a name using name_to_copy: #{name_to_copy} copy_addition: #{copy_addition} separator_before_copy_number: #{separator_before_copy_number} copy_num: #{copy_num} max_length: #{max_length}" end - max_length_for_name_to_copy = self.max_length - what_to_add.length - name_to_copy[0..max_length_for_name_to_copy-1] + what_to_add + + max_length_for_name_to_copy = max_length - what_to_add.length + name_to_copy[0..max_length_for_name_to_copy - 1] + what_to_add end def generate_copy_number(unprefixed_copy_number) - number_of_digits = Math.log10(self.max_copies).floor + 1 - "%0#{number_of_digits}d" % unprefixed_copy_number + number_of_digits = Math.log10(max_copies).floor + 1 + format("%0#{number_of_digits}d", unprefixed_copy_number) end - end class UnableToCreateNameCopyError < ArgumentError - -end \ No newline at end of file +end diff --git a/lib/create/create_campaign.rb b/lib/create/create_campaign.rb index b9b7dd03..45131ff8 100644 --- a/lib/create/create_campaign.rb +++ b/lib/create/create_campaign.rb @@ -1,5 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateCampaign CAMPAIGN_NAME_LENGTH_LIMIT = 60 -end \ No newline at end of file + # @return [Object] a json object for historical purposes + def self.create(params, nonprofit) + Time.use_zone(nonprofit.timezone || 'UTC') do + params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present? + end + + if !params[:campaign][:parent_campaign_id] + campaign = nonprofit.campaigns.create params[:campaign] + return campaign + else + profile_id = params[:campaign][:profile_id] + Profile.find(profile_id).update params[:profile] + return CreatePeerToPeerCampaign.create(params[:campaign], profile_id) + end + end +end diff --git a/lib/create/create_campaign_gift.rb b/lib/create/create_campaign_gift.rb index 17c15bbf..8f51d0a9 100644 --- a/lib/create/create_campaign_gift.rb +++ b/lib/create/create_campaign_gift.rb @@ -1,78 +1,77 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateCampaignGift - # @param [Hash] params - # * campaign_gift_option_id - # * donation_id - def self.create(params) - ParamValidation.new(params, { - :campaign_gift_option_id => { - :required => true, - :is_integer => true - }, - :donation_id => { - :required => true, - :is_integer => true - } - }) + # @param [Hash] params + # * campaign_gift_option_id + # * donation_id + def self.create(params) + ParamValidation.new(params, + campaign_gift_option_id: { + required: true, + is_integer: true + }, + donation_id: { + required: true, + is_integer: true + }) - donation = Donation.includes(:nonprofit).includes(:supporter).includes(:recurring_donation).includes(:campaign_gifts).where('id = ?', params[:donation_id]).first - unless donation - raise ParamValidation::ValidationError.new("#{params[:donation_id]} is not a valid donation id.", {:key => :donation_id}) - end + donation = Donation.includes(:nonprofit).includes(:supporter).includes(:recurring_donation).includes(:campaign_gifts).where('id = ?', params[:donation_id]).first + unless donation + raise ParamValidation::ValidationError.new("#{params[:donation_id]} is not a valid donation id.", key: :donation_id) + end - campaign_gift_option = CampaignGiftOption.includes(:campaign).where('id = ?', params[:campaign_gift_option_id]).first - unless campaign_gift_option - raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} is not a valid campaign gift option", {:key => :campaign_gift_option_id}) - end + campaign_gift_option = CampaignGiftOption.includes(:campaign).where('id = ?', params[:campaign_gift_option_id]).first + unless campaign_gift_option + raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} is not a valid campaign gift option", key: :campaign_gift_option_id) + end - #does donation already have a campaign_gift - if (donation.campaign_gifts.any?) - raise ParamValidation::ValidationError.new("#{params[:donation_id]} already has at least one associated campaign gift", :key => :donation_id) - end + # does donation already have a campaign_gift + if donation.campaign_gifts.any? + raise ParamValidation::ValidationError.new("#{params[:donation_id]} already has at least one associated campaign gift", key: :donation_id) + end - if (donation.campaign != campaign_gift_option.campaign) - raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} is not for the same campaign as donation #{params[:donation_id]}", {:key => :campaign_gift_option_id}) - end + if donation.campaign != campaign_gift_option.campaign + raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} is not for the same campaign as donation #{params[:donation_id]}", key: :campaign_gift_option_id) + end - if ((donation.recurring_donation != nil) && (campaign_gift_option.amount_recurring != nil && campaign_gift_option.amount_recurring > 0)) - # it's a recurring_donation. Is it enough? for the gift level? - unless donation.recurring_donation.amount == (campaign_gift_option.amount_recurring) - AdminMailer.delay.notify_failed_gift(donation, campaign_gift_option) - raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} gift options requires a recurring donation of #{campaign_gift_option.amount_recurring} for donation #{donation.id}", {:key => :campaign_gift_option_id}) - end - else - unless donation.amount == (campaign_gift_option.amount_one_time) - AdminMailer.delay.notify_failed_gift(donation, campaign_gift_option) - raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} gift options requires a donation of #{campaign_gift_option.amount_one_time} for donation #{donation.id}", {:key => :campaign_gift_option_id}) - end - end + if !donation.recurring_donation.nil? && (!campaign_gift_option.amount_recurring.nil? && campaign_gift_option.amount_recurring > 0) + # it's a recurring_donation. Is it enough? for the gift level? + unless donation.recurring_donation.amount == campaign_gift_option.amount_recurring + AdminFailedGiftJob.perform_later(donation, campaign_gift_option) + raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} gift options requires a recurring donation of #{campaign_gift_option.amount_recurring} for donation #{donation.id}", key: :campaign_gift_option_id) + end + else + unless donation.amount == campaign_gift_option.amount_one_time + AdminFailedGiftJob.perform_later(donation, campaign_gift_option) + raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} gift options requires a donation of #{campaign_gift_option.amount_one_time} for donation #{donation.id}", key: :campaign_gift_option_id) + end + end - Qx.transaction do - # are any gifts available? - if campaign_gift_option.quantity.nil? || campaign_gift_option.quantity.zero?|| campaign_gift_option.total_gifts < campaign_gift_option.quantity - gift = CampaignGift.new params - gift.save! - return gift - end - end - AdminMailer.delay.notify_failed_gift(donation, campaign_gift_option) - raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} has no more inventory", {:key => :campaign_gift_option_id}) - - end - - def self.validate_campaign_gift(cg) - donation = cg.donation - campaign_gift_option = cg.campaign_gift_option - if ((donation.recurring_donation != nil) && (campaign_gift_option.amount_recurring != nil && campaign_gift_option.amount_recurring > 0)) - # it's a recurring_donation. Is it enough? for the gift level? - unless donation.recurring_donation.amount == (campaign_gift_option.amount_recurring) - raise ParamValidation::ValidationError.new("#{campaign_gift_option.id} gift options requires a recurring donation of at least #{campaign_gift_option.amount_recurring}", {:key => :campaign_gift_option_id}) - end - else - unless donation.amount == (campaign_gift_option.amount_one_time) - raise ParamValidation::ValidationError.new("#{campaign_gift_option.id} gift options requires a donation of at least #{campaign_gift_option.amount_one_time}", {:key => :campaign_gift_option_id}) - end - end - end + Qx.transaction do + # are any gifts available? + if campaign_gift_option.quantity.nil? || campaign_gift_option.quantity.zero? || campaign_gift_option.total_gifts < campaign_gift_option.quantity + gift = CampaignGift.new params + gift.save! + return gift + end + end + AdminFailedGiftJob.perform_later(donation, campaign_gift_option) + raise ParamValidation::ValidationError.new("#{params[:campaign_gift_option_id]} has no more inventory", key: :campaign_gift_option_id) + end + def self.validate_campaign_gift(cg) + donation = cg.donation + campaign_gift_option = cg.campaign_gift_option + if !donation.recurring_donation.nil? && (!campaign_gift_option.amount_recurring.nil? && campaign_gift_option.amount_recurring > 0) + # it's a recurring_donation. Is it enough? for the gift level? + unless donation.recurring_donation.amount == campaign_gift_option.amount_recurring + raise ParamValidation::ValidationError.new("#{campaign_gift_option.id} gift options requires a recurring donation of at least #{campaign_gift_option.amount_recurring}", key: :campaign_gift_option_id) + end + else + unless donation.amount == campaign_gift_option.amount_one_time + raise ParamValidation::ValidationError.new("#{campaign_gift_option.id} gift options requires a donation of at least #{campaign_gift_option.amount_one_time}", key: :campaign_gift_option_id) + end + end + end end diff --git a/lib/create/create_campaign_gift_option.rb b/lib/create/create_campaign_gift_option.rb index 98ca146b..ae162e03 100644 --- a/lib/create/create_campaign_gift_option.rb +++ b/lib/create/create_campaign_gift_option.rb @@ -1,10 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateCampaignGiftOption - - def self.create campaign, params - gift_option = campaign.campaign_gift_options.build params - gift_option.save - return gift_option - end - + def self.create(campaign, params) + gift_option = campaign.campaign_gift_options.build params + gift_option.save + gift_option + end end diff --git a/lib/create/create_custom_field_join.rb b/lib/create/create_custom_field_join.rb index 55ebc38c..62818e6b 100644 --- a/lib/create/create_custom_field_join.rb +++ b/lib/create/create_custom_field_join.rb @@ -1,39 +1,38 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateCustomFieldJoin + def self.create(supporter, _profile_id, params) + custom_field = supporter.custom_field_joins.create(params) + custom_field + end - def self.create(supporter, profile_id, params) - custom_field = supporter.custom_field_joins.create(params) - return custom_field - end + # In the future, this should create an activity feed entry - # In the future, this should create an activity feed entry + # @param [Array] custom_fields Hash with following keys: + # * custom_field_master_id [Integer] for the key corresponding to custom_field_master_id + # * value [Object] the expected value of the field. If this key is an empty string, we remove the custom_field - # @param [Array] custom_fields Hash with following keys: - # * custom_field_master_id [Integer] for the key corresponding to custom_field_master_id - # * value [Object] the expected value of the field. If this key is an empty string, we remove the custom_field - - def self.modify(np, user, supporter_ids, custom_fields) - return if supporter_ids.nil? || supporter_ids.empty? - return if custom_fields.nil? || custom_fields.empty? - supporter_ids.each do |sid| - supporter = np.supporters.find(sid) - custom_fields.each do |custom_field| - existing = supporter.custom_field_joins.find_by_custom_field_master_id(custom_field[:custom_field_master_id]) - if existing - existing.update_attributes({ - custom_field_master_id: custom_field[:custom_field_master_id], - value: custom_field[:value] - }) - else - self.create(supporter, user.profile.id, { - custom_field_master_id: custom_field[:custom_field_master_id], - value: custom_field[:value] - }) - end - end - end - return np - end + def self.modify(np, user, supporter_ids, custom_fields) + return if supporter_ids.nil? || supporter_ids.empty? + return if custom_fields.nil? || custom_fields.empty? + supporter_ids.each do |sid| + supporter = np.supporters.find(sid) + custom_fields.each do |custom_field| + existing = supporter.custom_field_joins.find_by_custom_field_master_id(custom_field[:custom_field_master_id]) + if existing + existing.update( + custom_field_master_id: custom_field[:custom_field_master_id], + value: custom_field[:value] + ) + else + create(supporter, user.profile.id, + custom_field_master_id: custom_field[:custom_field_master_id], + value: custom_field[:value]) + end + end + end + np + end end - diff --git a/lib/create/create_custom_field_master.rb b/lib/create/create_custom_field_master.rb index decc7ce6..470f5aed 100644 --- a/lib/create/create_custom_field_master.rb +++ b/lib/create/create_custom_field_master.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateCustomFieldMaster - - def self.create(nonprofit, params) - custom_field_master = nonprofit.custom_field_masters.create(params) - return custom_field_master - end + def self.create(nonprofit, params) + custom_field_master = nonprofit.custom_field_masters.create(params) + custom_field_master + end end diff --git a/lib/create/create_peer_to_peer_campaign.rb b/lib/create/create_peer_to_peer_campaign.rb index 854ca4db..7fe01ee3 100644 --- a/lib/create/create_peer_to_peer_campaign.rb +++ b/lib/create/create_peer_to_peer_campaign.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreatePeerToPeerCampaign def self.create(campaign_params, profile_id) begin - parent_campaign = Campaign.find(campaign_params[:parent_campaign_id]) - + parent_campaign = Campaign.find(campaign_params[:parent_campaign_id]) rescue ActiveRecord::RecordNotFound return { errors: { parent_campaign_id: 'not found' } }.as_json end - p2p_params = campaign_params.except(:nonprofit_id, :summary,:goal_amount) + p2p_params = campaign_params.except(:nonprofit_id, :summary, :goal_amount) p2p_params.merge!(parent_campaign.child_params) profile = Profile.find(profile_id) @@ -22,12 +23,22 @@ module CreatePeerToPeerCampaign campaign.profile = profile campaign.save - campaign.update_attribute(:main_image, parent_campaign.main_image) unless !parent_campaign.main_image rescue AWS::S3::Errors::NoSuchKey - campaign.update_attribute(:background_image, parent_campaign.background_image) unless !parent_campaign.background_image rescue AWS::S3::Errors::NoSuchKey - campaign.update_attribute(:banner_image, parent_campaign.banner_image) unless !parent_campaign.banner_image rescue AWS::S3::Errors::NoSuchKey + begin + campaign.main_image.attach(parent_campaign.main_image.blob) if parent_campaign.main_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end + begin + campaign.background_image.attach(parent_campaign.background_image.blob) if parent_campaign.background_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end + begin + campaign.banner_image.attach(parent_campaign.banner_image.blob) if parent_campaign.banner_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end - return { errors: campaign.errors.messages }.as_json unless campaign.errors.empty? - - campaign.as_json + campaign end end diff --git a/lib/create/create_tag_master.rb b/lib/create/create_tag_master.rb index 7c8fc139..9eef5b94 100644 --- a/lib/create/create_tag_master.rb +++ b/lib/create/create_tag_master.rb @@ -1,9 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module CreateTagMaster - - def self.create(nonprofit, params) - tag_master = nonprofit.tag_masters.create(params) - return tag_master - end + def self.create(nonprofit, params) + tag_master = nonprofit.tag_masters.create(params) + tag_master + end end - diff --git a/lib/create/stripe/create_stripe_account.rb b/lib/create/stripe/create_stripe_account.rb index 1d1a9412..75681c7c 100644 --- a/lib/create/stripe/create_stripe_account.rb +++ b/lib/create/stripe/create_stripe_account.rb @@ -1,37 +1,38 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'format/name' require 'get_data' require 'stripe' module CreateStripeAccount - - def self.for_nonprofit(user, params) - fst_name, lst_name = Format::Name.split_full(GetData.chain(user, :profile, :name)) - return Stripe::Account.create({ - managed: true, - email: params[:email], - business_name: params[:name], - business_url: params[:website], - legal_entity: { - type: 'company', - address: { - line1: params[:address], - city: params[:city], - state: params[:state_code], - postal_code: params[:zip_code], - country: 'US' - }, - business_name: params[:name], - business_tax_id: params[:ein], - first_name: fst_name, - last_name: lst_name - }, - product_description: 'Nonprofit donations', - tos_acceptance: { - date: Time.current.to_i, - ip: user.current_sign_in_ip - }, - transfer_schedule: { interval: 'manual' } - }) - end + def self.for_nonprofit(user, params) + fst_name, lst_name = Format::Name.split_full(GetData.chain(user, :profile, :name)) + Stripe::Account.create( + managed: true, + email: params[:email], + business_name: params[:name], + business_url: params[:website], + legal_entity: { + type: 'company', + address: { + line1: params[:address], + city: params[:city], + state: params[:state_code], + postal_code: params[:zip_code], + country: 'US' + }, + business_name: params[:name], + business_tax_id: params[:ein], + first_name: fst_name, + last_name: lst_name + }, + product_description: 'Nonprofit donations', + tos_acceptance: { + date: Time.current.to_i, + ip: user.current_sign_in_ip + }, + transfer_schedule: { interval: 'manual' } + ) + end end diff --git a/lib/cypher.rb b/lib/cypher.rb index 3f95b517..0f2e971b 100644 --- a/lib/cypher.rb +++ b/lib/cypher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'openssl' @@ -8,31 +10,30 @@ require 'openssl' # .iv, .auth_tag both are stored with the encrypted data module Cypher - def self.encrypt(data) cipher = create_cipher cipher.encrypt cipher.key = Base64.decode64(ENV['CYPHER_KEY']) iv = cipher.random_iv encrypted = cipher.update(data) + cipher.final - return {iv: Base64.encode64(iv), key: Base64.encode64(encrypted)} + { iv: Base64.encode64(iv), key: Base64.encode64(encrypted) } end # hash must have properties for :iv and :key def self.decrypt(hash) - iv, encrypted = [Base64.decode64(hash['iv']), Base64.decode64(hash['key'])] + iv = Base64.decode64(hash['iv']) + encrypted = Base64.decode64(hash['key']) decipher = create_cipher decipher.decrypt decipher.key = Base64.decode64(ENV['CYPHER_KEY']) decipher.iv = iv - return decipher.update(encrypted) + decipher.final + decipher.update(encrypted) + decipher.final end -private + private def self.create_cipher OpenSSL::Cipher::AES256.new(:CBC) end - end diff --git a/lib/delayed_job_helper.rb b/lib/delayed_job_helper.rb deleted file mode 100644 index 7189a29e..00000000 --- a/lib/delayed_job_helper.rb +++ /dev/null @@ -1,27 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'qx' -require 'delayed_job' - -module DelayedJobHelper - - # Create a serialized delayed job handler for use in inserting new delayed jobs with raw sql - # Be sure to wrap the handler in double quotes when inserting, not single - def self.create_handler(obj, method_name, args) - Delayed::PerformableMethod.new(obj, method_name, args).to_yaml.to_s - end - - # Manually enqueue a job - def self.enqueue_job(obj, method_name, args, options={}) - handler = Delayed::PerformableMethod.new(obj, method_name, args).to_yaml.to_s - Qx.insert_into(:delayed_jobs) - .values({ - created_at: Time.current, - updated_at: Time.current, - priority: options[:priority] || 0, - attempts: 0, - handler: handler, - run_at: options[:run_at] || Time.current, - queue: options[:queue] - }).returning('*').execute - end -end diff --git a/lib/delete/delete_campaign_gift_option.rb b/lib/delete/delete_campaign_gift_option.rb index bcb0afe1..249c0ef4 100644 --- a/lib/delete/delete_campaign_gift_option.rb +++ b/lib/delete/delete_campaign_gift_option.rb @@ -1,27 +1,26 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module DeleteCampaignGiftOption def self.delete(campaign, campaign_gift_option_id) - ParamValidation.new({:campaign => campaign, - :campaign_gift_option_id => campaign_gift_option_id}, - { - :campaign => { - :required => true, - :is_a => Campaign - }, - :campaign_gift_option_id => { - :required => true, - :is_integer => true - } - } - ) + ParamValidation.new({ campaign: campaign, + campaign_gift_option_id: campaign_gift_option_id }, + campaign: { + required: true, + is_a: Campaign + }, + campaign_gift_option_id: { + required: true, + is_integer: true + }) Qx.transaction do cgo = campaign.campaign_gift_options.where('id = ? ', campaign_gift_option_id).first unless cgo - raise ParamValidation::ValidationError.new("#{campaign_gift_option_id} is not a valid gift option for campaign #{campaign.id}", {:key => :campaign_gift_option_id}) + raise ParamValidation::ValidationError.new("#{campaign_gift_option_id} is not a valid gift option for campaign #{campaign.id}", key: :campaign_gift_option_id) end - if (cgo.campaign_gifts.any?) - raise ParamValidation::ValidationError.new("#{campaign_gift_option_id} already has campaign gifts. It can't be deleted for safety reasons.", {:key => :campaign_gift_option_id}) + if cgo.campaign_gifts.any? + raise ParamValidation::ValidationError.new("#{campaign_gift_option_id} already has campaign gifts. It can't be deleted for safety reasons.", key: :campaign_gift_option_id) end cgo.destroy @@ -29,4 +28,4 @@ module DeleteCampaignGiftOption return cgo end end -end \ No newline at end of file +end diff --git a/lib/delete/delete_custom_field_joins.rb b/lib/delete/delete_custom_field_joins.rb index ac3e8080..9e392733 100644 --- a/lib/delete/delete_custom_field_joins.rb +++ b/lib/delete/delete_custom_field_joins.rb @@ -1,39 +1,37 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module DeleteCustomFieldJoins - @columns = ['id', 'custom_field_master_id', 'supporter_id', 'value', 'created_at', 'updated_at', 'metadata'] + @columns = %w[id custom_field_master_id supporter_id value created_at updated_at metadata] def self.find_multiple_custom_field_joins - bad_results = Qx.select("CONCAT(custom_field_joins.supporter_id, '_', custom_field_joins.custom_field_master_id) AS our_concat, COUNT(id) AS our_count"). - from(:custom_field_joins). - group_by("our_concat"). - having('COUNT(id) > 1').parse - - - custom_field_joins_from_qx = CustomFieldJoin. - where("CONCAT(custom_field_joins.supporter_id, '_', custom_field_joins.custom_field_master_id) IN (SELECT our_concat FROM (#{bad_results}) AS ignore)"). - select('id, custom_field_master_id, supporter_id, created_at, updated_at') - grouped_custom_field_joins = custom_field_joins_from_qx.group_by{|tj| "#{tj.supporter_id}_#{tj.custom_field_master_id}"} + bad_results = Qx.select("CONCAT(custom_field_joins.supporter_id, '_', custom_field_joins.custom_field_master_id) AS our_concat, COUNT(id) AS our_count") + .from(:custom_field_joins) + .group_by('our_concat') + .having('COUNT(id) > 1').parse + custom_field_joins_from_qx = CustomFieldJoin + .where("CONCAT(custom_field_joins.supporter_id, '_', custom_field_joins.custom_field_master_id) IN (SELECT our_concat FROM (#{bad_results}) AS ignore)") + .select('id, custom_field_master_id, supporter_id, created_at, updated_at') + grouped_custom_field_joins = custom_field_joins_from_qx.group_by { |tj| "#{tj.supporter_id}_#{tj.custom_field_master_id}" } ids_to_delete = [] - grouped_custom_field_joins.each { |_, v| - - sorted = v.sort_by {|a| a.updated_at }.to_a - ids_to_delete += sorted.map{|i| i.id}[0, sorted.count - 1] - } + grouped_custom_field_joins.each do |_, v| + sorted = v.sort_by(&:updated_at).to_a + ids_to_delete += sorted.map(&:id)[0, sorted.count - 1] + end ids_to_delete end def self.copy_and_delete(ids_to_delete) if ids_to_delete.any? - Qx.insert_into(:custom_field_joins_backup, @columns).select(@columns).from(:custom_field_joins).where('id IN ($ids)', ids:ids_to_delete).execute + Qx.insert_into(:custom_field_joins_backup, @columns).select(@columns).from(:custom_field_joins).where('id IN ($ids)', ids: ids_to_delete).execute CustomFieldJoin.where('id IN (?)', ids_to_delete).delete_all end - end def self.revert Qx.insert_into(:custom_field_joins, @columns).select(@columns).from(:custom_field_joins_backup).execute - Qx.execute_raw("DELETE FROM custom_field_joins_backup") + Qx.execute_raw('DELETE FROM custom_field_joins_backup') end -end \ No newline at end of file +end diff --git a/lib/delete/delete_tag_joins.rb b/lib/delete/delete_tag_joins.rb index 72e347d5..0e0c875d 100644 --- a/lib/delete/delete_tag_joins.rb +++ b/lib/delete/delete_tag_joins.rb @@ -1,25 +1,25 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module DeleteTagJoins - @columns = ['id', 'created_at', 'updated_at', 'metadata', 'tag_master_id', 'supporter_id'] + @columns = %w[id created_at updated_at metadata tag_master_id supporter_id] def self.find_multiple_tag_joins - qx_results = Qx.select("CONCAT(tag_joins.supporter_id, '_', tag_joins.tag_master_id) AS our_concat, COUNT(id) AS our_count"). - from(:tag_joins). - group_by("our_concat"). - having('COUNT(id) > 1'). - execute - - tag_joins_from_qx = TagJoin.where("CONCAT(supporter_id, '_', tag_master_id) IN (?)", qx_results.map{|i| i["our_concat"] }).select('id, tag_master_id, supporter_id, created_at') - grouped_tagged_joins = tag_joins_from_qx.group_by{|tj| "#{tj.supporter_id}_#{tj.tag_master_id}"} + qx_results = Qx.select("CONCAT(tag_joins.supporter_id, '_', tag_joins.tag_master_id) AS our_concat, COUNT(id) AS our_count") + .from(:tag_joins) + .group_by('our_concat') + .having('COUNT(id) > 1') + .execute + tag_joins_from_qx = TagJoin.where("CONCAT(supporter_id, '_', tag_master_id) IN (?)", qx_results.map { |i| i['our_concat'] }).select('id, tag_master_id, supporter_id, created_at') + grouped_tagged_joins = tag_joins_from_qx.group_by { |tj| "#{tj.supporter_id}_#{tj.tag_master_id}" } ids_to_delete = [] - grouped_tagged_joins.each { |_, v| - - sorted = v.sort_by {|a| a.created_at }.to_a - ids_to_delete += sorted.map{|i| i.id}[0, sorted.count - 1] - } + grouped_tagged_joins.each do |_, v| + sorted = v.sort_by(&:created_at).to_a + ids_to_delete += sorted.map(&:id)[0, sorted.count - 1] + end ids_to_delete end @@ -27,18 +27,17 @@ module DeleteTagJoins def self.copy_and_delete(ids_to_delete) if ids_to_delete.any? - #select_query = Qx.select(@columns).from(:tag_joins).where('id IN ($ids)', ids:ids_to_delete).parse + # select_query = Qx.select(@columns).from(:tag_joins).where('id IN ($ids)', ids:ids_to_delete).parse - Qx.insert_into(:tag_joins_backup, @columns).select(@columns).from(:tag_joins).where('id IN ($ids)', ids:ids_to_delete).execute - # Qx.execute_raw("INSERT INTO tag_joins_backup ('id', '' #{select_query}") + Qx.insert_into(:tag_joins_backup, @columns).select(@columns).from(:tag_joins).where('id IN ($ids)', ids: ids_to_delete).execute + # Qx.execute_raw("INSERT INTO tag_joins_backup ('id', '' #{select_query}") TagJoin.where('id IN (?)', ids_to_delete).delete_all end - end def self.revert Qx.insert_into(:tag_joins, @columns).select(@columns).from(:tag_joins_backup).execute - #Qx.execute_raw("INSERT INTO tag_joins SELECT * FROM tag_joins_backup") - Qx.execute_raw("DELETE FROM tag_joins_backup") + # Qx.execute_raw("INSERT INTO tag_joins SELECT * FROM tag_joins_backup") + Qx.execute_raw('DELETE FROM tag_joins_backup') end -end \ No newline at end of file +end diff --git a/lib/email.rb b/lib/email.rb index 4b34cb5d..255c722f 100644 --- a/lib/email.rb +++ b/lib/email.rb @@ -1,7 +1,7 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Email - - Regex ||= /^[^ ]+@[^ ]+\.[^ ]+/i - #PsqlRegex ||= '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+[.][A-Za-z]+$' - + Regex ||= /\A[^ ]+@[^ ]+\.[^ ]+/i.freeze + # PsqlRegex ||= '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+[.][A-Za-z]+$' end diff --git a/lib/email_job_queue.rb b/lib/email_job_queue.rb deleted file mode 100644 index 25482a03..00000000 --- a/lib/email_job_queue.rb +++ /dev/null @@ -1,6 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module EmailJobQueue - def self.queue(klass, *args) - Delayed::Job.enqueue klass.new(*args) - end -end \ No newline at end of file diff --git a/lib/errors/authentication_error.rb b/lib/errors/authentication_error.rb index 603bff36..01f51b35 100644 --- a/lib/errors/authentication_error.rb +++ b/lib/errors/authentication_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class AuthenticationError < RuntimeError -end \ No newline at end of file +end diff --git a/lib/errors/cc_org_error.rb b/lib/errors/cc_org_error.rb index 0d1dff0e..9dcadea5 100644 --- a/lib/errors/cc_org_error.rb +++ b/lib/errors/cc_org_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class CCOrgError < RuntimeError -end \ No newline at end of file +end diff --git a/lib/errors/charge_error.rb b/lib/errors/charge_error.rb index 33ddb2c1..7f9160e0 100644 --- a/lib/errors/charge_error.rb +++ b/lib/errors/charge_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ChargeError < RuntimeError -end \ No newline at end of file +end diff --git a/lib/errors/expired_token_error.rb b/lib/errors/expired_token_error.rb index 066c32d4..2fa14242 100644 --- a/lib/errors/expired_token_error.rb +++ b/lib/errors/expired_token_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class ExpiredTokenError < RuntimeError -end \ No newline at end of file +end diff --git a/lib/errors/not_enough_quantity_error.rb b/lib/errors/not_enough_quantity_error.rb index d9f74792..018d5ffe 100644 --- a/lib/errors/not_enough_quantity_error.rb +++ b/lib/errors/not_enough_quantity_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class NotEnoughQuantityError < CCOrgError attr_accessor :klass, :id, :requested @@ -7,4 +9,4 @@ class NotEnoughQuantityError < CCOrgError @requested = requested super(msg) end -end \ No newline at end of file +end diff --git a/lib/event_publisher.rb b/lib/event_publisher.rb new file mode 100644 index 00000000..a1c7c537 --- /dev/null +++ b/lib/event_publisher.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class EventPublisher + include Wisper::Publisher + + def announce(event, *args) + broadcast(event, *args) + end + + def subscribe_async(listener, options = {}) + subscribe(listener, options.merge(async: true)) + end +end \ No newline at end of file diff --git a/lib/export/export_payments.rb b/lib/export/export_payments.rb index a54d5dc1..07fc9902 100644 --- a/lib/export/export_payments.rb +++ b/lib/export/export_payments.rb @@ -1,9 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ExportPayments - def self.initiate_export(npo_id, params, user_id) - ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id }, npo_id: { required: true, is_integer: true }, params: { required: true, is_hash: true }, @@ -12,6 +12,7 @@ module ExportPayments unless npo raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) @@ -19,7 +20,7 @@ module ExportPayments e = Export.create(nonprofit: npo, user: user, status: :queued, export_type: 'ExportPayments', parameters: params.to_json) - DelayedJobHelper.enqueue_job(ExportPayments, :run_export, [npo_id, params.to_json, user_id, e.id]) + PaymentExportCreateJob.perform_later(npo_id, params.to_json, user_id, e.id) end def self.run_export(npo_id, params, user_id, export_id) @@ -30,7 +31,7 @@ module ExportPayments user_id: { required: true, is_integer: true }, export_id: { required: true, is_integer: true }) - params = JSON.parse(params, :object_class=> HashWithIndifferentAccess) + params = JSON.parse(params, object_class: HashWithIndifferentAccess) # verify that it's also a hash since we can't do that at once ParamValidation.new({ params: params }, params: { is_hash: true }) @@ -45,30 +46,32 @@ module ExportPayments unless Nonprofit.exists?(npo_id) raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) end - file_date = Time.now.getutc().strftime('%m-%d-%Y--%H-%M-%S') - filename = "tmp/csv-exports/payments-#{file_date}.csv" + file_date = Time.now.getutc.strftime('%m-%d-%Y--%H-%M-%S') + filename = "tmp/csv-exports/payments-#{file_date}-#{SecureRandom.uuid}.csv" - url = CHUNKED_UPLOADER.upload(filename, QueryPayments.for_export_enumerable(npo_id, params, 30000).map{|i| i.to_csv}, :content_type => 'text/csv', content_disposition: 'attachment') + ChunkedUploader.upload(QueryPayments.for_export_enumerable(npo_id, params, 30_000).map(&:to_csv)) do |io| + CHUNKED_UPLOAD_SERVICE.upload(filename, io, content_type: 'text/csv', content_disposition: 'attachment') + end + url = CHUNKED_UPLOAD_SERVICE.url(filename) export.url = url export.status = :completed export.ended = Time.now export.save! - ExportMailer.delay.export_payments_completed_notification(export) - rescue => e + ExportPaymentsCompletedJob.perform_later(export) + rescue StandardError => e if export export.status = :failed export.exception = e.to_s export.ended = Time.now export.save! - if user - ExportMailer.delay.export_payments_failed_notification(export) - end + ExportPaymentsFailedJob.perform_later(export) if user raise e end raise e diff --git a/lib/export/export_recurring_donations.rb b/lib/export/export_recurring_donations.rb index a8b146e5..b5e5b3be 100644 --- a/lib/export/export_recurring_donations.rb +++ b/lib/export/export_recurring_donations.rb @@ -1,9 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ExportRecurringDonations - def self.initiate_export(npo_id, params, user_id) - ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id }, npo_id: { required: true, is_integer: true }, params: { required: true, is_hash: true }, @@ -12,6 +12,7 @@ module ExportRecurringDonations unless npo raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) @@ -19,7 +20,7 @@ module ExportRecurringDonations e = Export.create(nonprofit: npo, user: user, status: :queued, export_type: 'ExportRecurringDonations', parameters: params.to_json) - DelayedJobHelper.enqueue_job(ExportRecurringDonations, :run_export, [npo_id, params.to_json, user_id, e.id]) + RecurringDonationsExportCreateJob.perform_later(npo_id, params.to_json, user_id, e.id) end def self.run_export(npo_id, params, user_id, export_id) @@ -30,7 +31,7 @@ module ExportRecurringDonations user_id: { required: true, is_integer: true }, export_id: { required: true, is_integer: true }) - params = JSON.parse(params, :object_class=> HashWithIndifferentAccess) + params = JSON.parse(params, object_class: HashWithIndifferentAccess) # verify that it's also a hash since we can't do that at once ParamValidation.new({ params: params }, params: { is_hash: true }) @@ -45,29 +46,33 @@ module ExportRecurringDonations unless Nonprofit.exists?(npo_id) raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) end - file_date = Time.now.getutc().strftime('%m-%d-%Y--%H-%M-%S') - filename = "tmp/csv-exports/recurring_donations-#{file_date}.csv" + file_date = Time.now.getutc.strftime('%m-%d-%Y--%H-%M-%S') + filename = "tmp/csv-exports/recurring_donations-#{file_date}-#{SecureRandom.uuid}.csv" - url = CHUNKED_UPLOADER.upload(filename, QueryRecurringDonations.for_export_enumerable(npo_id, params, 30000).map{|i| i.to_csv}, :content_type => 'text/csv', content_disposition: 'attachment') + ChunkedUploader.upload(QueryRecurringDonations.for_export_enumerable(npo_id, params, 30_000).map(&:to_csv)) do |io| + CHUNKED_UPLOAD_SERVICE.upload(filename, io, content_type: 'text/csv', content_disposition: 'attachment') + end + url = CHUNKED_UPLOAD_SERVICE.url(filename) export.url = url export.status = :completed export.ended = Time.now export.save! - ExportMailer.delay.export_recurring_donations_completed_notification(export) - rescue => e + ExportRecurringDonationsCompletedJob.perform_later(export) + rescue StandardError => e if export export.status = :failed export.exception = e.to_s export.ended = Time.now export.save! if user - ExportMailer.delay.export_recurring_donations_failed_notification(export) + ExportRecurringDonationsFailedJob.perform_later(export) end raise e end diff --git a/lib/export/export_supporter_notes.rb b/lib/export/export_supporter_notes.rb index 83b9316e..3ae4a641 100644 --- a/lib/export/export_supporter_notes.rb +++ b/lib/export/export_supporter_notes.rb @@ -1,74 +1,79 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ExportSupporterNotes - def self.initiate_export(npo_id, params, user_id) + def self.initiate_export(npo_id, params, user_id) + ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id }, + npo_id: { required: true, is_integer: true }, + params: { required: true, is_hash: true }, + user_id: { required: true, is_integer: true }) + npo = Nonprofit.where('id = ?', npo_id).first + unless npo + raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) + end - ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id }, - npo_id: { required: true, is_integer: true }, - params: { required: true, is_hash: true }, - user_id: { required: true, is_integer: true }) - npo = Nonprofit.where('id = ?', npo_id).first - unless npo - raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) - end - user = User.where('id = ?', user_id).first - unless user - raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) - end - - e = Export.create(nonprofit: npo, user: user, status: :queued, export_type: 'ExportSupporterNotes', parameters: params.to_json) - - DelayedJobHelper.enqueue_job(ExportSupporterNotes, :run_export, [npo_id, params.to_json, user_id, e.id]) + user = User.where('id = ?', user_id).first + unless user + raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) + end + + e = Export.create(nonprofit: npo, user: user, status: :queued, export_type: 'ExportSupporterNotes', parameters: params.to_json) + + SupporterNotesExportCreateJob.perform_later(npo_id, params.to_json, user_id, e.id) + end + + def self.run_export(npo_id, params, user_id, export_id) + # need to check that + ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id, export_id: export_id }, + npo_id: { required: true, is_integer: true }, + params: { required: true, is_json: true }, + user_id: { required: true, is_integer: true }, + export_id: { required: true, is_integer: true }) + + params = JSON.parse(params, object_class: HashWithIndifferentAccess) + # verify that it's also a hash since we can't do that at once + ParamValidation.new({ params: params }, + params: { is_hash: true }) + begin + export = Export.find(export_id) + rescue ActiveRecord::RecordNotFound + raise ParamValidation::ValidationError.new("Export #{export_id} doesn't exist!", key: :export_id) + end + export.status = :started + export.save! + + unless Nonprofit.exists?(npo_id) + raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) + end + + user = User.where('id = ?', user_id).first + unless user + raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) + end + + file_date = Time.now.getutc.strftime('%m-%d-%Y--%H-%M-%S') + filename = "tmp/csv-exports/supporters-notes-#{file_date}-#{SecureRandom.uuid}.csv" + + ChunkedUploader.upload(QuerySupporters.supporter_note_export_enumerable(npo_id, params, 30_000).map(&:to_csv)) do |io| + CHUNKED_UPLOAD_SERVICE.upload(filename, io, content_type: 'text/csv', content_disposition: 'attachment') + end + url = CHUNKED_UPLOAD_SERVICE.url(filename) + export.url = url + export.status = :completed + export.ended = Time.now + export.save! + ExportSupporterNotesCompletedJob.perform_later(export) + rescue StandardError => e + if export + export.status = :failed + export.exception = e.to_s + export.ended = Time.now + export.save! + if user + ExportSupporterNotesFailedJob.perform_later export end - - def self.run_export(npo_id, params, user_id, export_id) - # need to check that - ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id, export_id: export_id }, - npo_id: { required: true, is_integer: true }, - params: { required: true, is_json: true }, - user_id: { required: true, is_integer: true }, - export_id: { required: true, is_integer: true }) - - params = JSON.parse(params, :object_class=> HashWithIndifferentAccess) - # verify that it's also a hash since we can't do that at once - ParamValidation.new({ params: params }, - params: { is_hash: true }) - begin - export = Export.find(export_id) - rescue ActiveRecord::RecordNotFound - raise ParamValidation::ValidationError.new("Export #{export_id} doesn't exist!", key: :export_id) - end - export.status = :started - export.save! - - unless Nonprofit.exists?(npo_id) - raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) - end - user = User.where('id = ?', user_id).first - unless user - raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) - end - - file_date = Time.now.getutc().strftime('%m-%d-%Y--%H-%M-%S') - filename = "tmp/csv-exports/supporters-notes-#{file_date}.csv" - - url = CHUNKED_UPLOADER.upload(filename, QuerySupporters.supporter_note_export_enumerable(npo_id, params, 30000).map{|i| i.to_csv}, content_type: 'text/csv', content_disposition: 'attachment') - export.url = url - export.status = :completed - export.ended = Time.now - export.save! - - EmailJobQueue.queue(JobTypes::ExportSupporterNotesCompletedJob, export) - rescue => e - if export - export.status = :failed - export.exception = e.to_s - export.ended = Time.now - export.save! - if user - EmailJobQueue.queue(JobTypes::ExportSupporterNotesFailedJob, export) - end - raise e - end - raise e - end -end \ No newline at end of file + raise e + end + raise e + end +end diff --git a/lib/export/export_supporters.rb b/lib/export/export_supporters.rb index c657ffb0..866093bc 100644 --- a/lib/export/export_supporters.rb +++ b/lib/export/export_supporters.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true + module ExportSupporters def self.initiate_export(npo_id, params, user_id) - ParamValidation.new({ npo_id: npo_id, params: params, user_id: user_id }, npo_id: { required: true, is_integer: true }, params: { required: true, is_hash: true }, @@ -9,6 +10,7 @@ module ExportSupporters unless npo raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) @@ -16,7 +18,7 @@ module ExportSupporters e = Export.create(nonprofit: npo, user: user, status: :queued, export_type: 'ExportSupporters', parameters: params.to_json) - DelayedJobHelper.enqueue_job(ExportSupporters, :run_export, [npo_id, params.to_json, user_id, e.id]) + SupportersExportCreateJob.perform_later(npo_id, params.to_json, user_id, e.id) end def self.run_export(npo_id, params, user_id, export_id) @@ -27,10 +29,9 @@ module ExportSupporters user_id: { required: true, is_integer: true }, export_id: { required: true, is_integer: true }) - params = JSON.parse(params, :object_class=> HashWithIndifferentAccess) + params = JSON.parse(params, object_class: HashWithIndifferentAccess) # verify that it's also a hash since we can't do that at once - ParamValidation.new({ params: params }, - params: { is_hash: true }) + ParamValidation.new({ params: params }, params: { is_hash: true }) begin export = Export.find(export_id) rescue ActiveRecord::RecordNotFound @@ -42,32 +43,34 @@ module ExportSupporters unless Nonprofit.exists?(npo_id) raise ParamValidation::ValidationError.new("Nonprofit #{npo_id} doesn't exist!", key: :npo_id) end + user = User.where('id = ?', user_id).first unless user raise ParamValidation::ValidationError.new("User #{user_id} doesn't exist!", key: :user_id) end - file_date = Time.now.getutc().strftime('%m-%d-%Y--%H-%M-%S') - filename = "tmp/csv-exports/supporters-#{file_date}.csv" + file_date = Time.now.getutc.strftime('%m-%d-%Y--%H-%M-%S') + filename = "tmp/csv-exports/supporters-#{file_date}-#{SecureRandom.uuid}.csv" - url = CHUNKED_UPLOADER.upload(filename, QuerySupporters.for_export_enumerable(npo_id, params, 30000).map{|i| i.to_csv}, content_type: 'text/csv', content_disposition: 'attachment') + ChunkedUploader.upload(QuerySupporters.for_export_enumerable(npo_id, params, 30_000).map(&:to_csv)) do |io| + CHUNKED_UPLOAD_SERVICE.upload(filename, io, content_type: 'text/csv', content_disposition: 'attachment') + end + url = CHUNKED_UPLOAD_SERVICE.url(filename) export.url = url export.status = :completed export.ended = Time.now export.save! - EmailJobQueue.queue(JobTypes::ExportSupportersCompletedJob, export) - rescue => e + ExportSupportersCompletedJob.perform_later export + rescue StandardError => e if export export.status = :failed export.exception = e.to_s export.ended = Time.now export.save! - if user - EmailJobQueue.queue(JobTypes::ExportSupportersFailedJob, export) - end + ExportSupportersFailedJob.perform_later(export) if user raise e end raise e end -end \ No newline at end of file +end diff --git a/lib/fetch/fetch_background_image.rb b/lib/fetch/fetch_background_image.rb deleted file mode 100644 index 79492cbd..00000000 --- a/lib/fetch/fetch_background_image.rb +++ /dev/null @@ -1,7 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module FetchBackgroundImage - - def self.with_model(model) - return model.background_image_url(:normal) unless model.background_image.file.nil? - end -end diff --git a/lib/fetch/fetch_campaign.rb b/lib/fetch/fetch_campaign.rb index 52bd775e..508a9afc 100644 --- a/lib/fetch/fetch_campaign.rb +++ b/lib/fetch/fetch_campaign.rb @@ -1,14 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchCampaign - - def self.with_params(params, nonprofit=nil) - nonprofit ||= FetchNonprofit.with_params(params) - if params[:campaign_slug] - return nonprofit.campaigns.where(slug: params[:campaign_slug]).last - elsif params[:campaign_id] || params[:id] - return nonprofit.campaigns.find(params[:campaign_id] || params[:id]) - end - end - + def self.with_params(params, nonprofit = nil) + nonprofit ||= FetchNonprofit.with_params(params) + if params[:campaign_slug] + return nonprofit.campaigns.where(slug: params[:campaign_slug]).last + elsif params[:campaign_id] || params[:id] + return nonprofit.campaigns.find(params[:campaign_id] || params[:id]) + end + end end - diff --git a/lib/fetch/fetch_coupon.rb b/lib/fetch/fetch_coupon.rb index 402a4e16..c9cb9af9 100644 --- a/lib/fetch/fetch_coupon.rb +++ b/lib/fetch/fetch_coupon.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchCoupon - def self.page params - return params[:name].gsub('-','_') if params[:name] - end -end \ No newline at end of file + def self.page(params) + return params[:name].tr('-', '_') if params[:name] + end +end diff --git a/lib/fetch/fetch_event.rb b/lib/fetch/fetch_event.rb index cf8a1a9c..0b501881 100644 --- a/lib/fetch/fetch_event.rb +++ b/lib/fetch/fetch_event.rb @@ -1,14 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchEvent - - def self.with_params(params, nonprofit=nil) - nonprofit ||= FetchNonprofit.with_params(params) - if params[:event_slug] - return nonprofit.events.find_by_slug(params[:event_slug]) - elsif params[:event_id] || params[:id] - return nonprofit.events.find(params[:event_id] || params[:id]) - end - end - + def self.with_params(params, nonprofit = nil) + nonprofit ||= FetchNonprofit.with_params(params) + if params[:event_slug] + return nonprofit.events.find_by_slug(params[:event_slug]) + elsif params[:event_id] || params[:id] + return nonprofit.events.find(params[:event_id] || params[:id]) + end + end end - diff --git a/lib/fetch/fetch_miscellaneous_np_info.rb b/lib/fetch/fetch_miscellaneous_np_info.rb index 85510589..8bac4187 100644 --- a/lib/fetch/fetch_miscellaneous_np_info.rb +++ b/lib/fetch/fetch_miscellaneous_np_info.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchMiscellaneousNpInfo def self.fetch(np_id) - ParamValidation.new({np_id: np_id}, np_id: {:required => true, :is_integer => true}) - raise ParamValidation::ValidationError.new("Nonprofit #{np_id} does not exist", {key: :np_id}) unless Nonprofit.exists?(np_id) + ParamValidation.new({ np_id: np_id }, np_id: { required: true, is_integer: true }) + raise ParamValidation::ValidationError.new("Nonprofit #{np_id} does not exist", key: :np_id) unless Nonprofit.exists?(np_id) + MiscellaneousNpInfo.where('nonprofit_id = ?', np_id).first_or_initialize end -end \ No newline at end of file +end diff --git a/lib/fetch/fetch_nonprofit.rb b/lib/fetch/fetch_nonprofit.rb index a7043742..32db3546 100644 --- a/lib/fetch/fetch_nonprofit.rb +++ b/lib/fetch/fetch_nonprofit.rb @@ -1,15 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchNonprofit - - def self.with_params(params, administered_nonprofit=nil) - if params[:state_code] && params[:city] && params[:name] - return Nonprofit.where(:state_code_slug => params[:state_code], :city_slug => params[:city], :slug => params[:name]).last - elsif params[:nonprofit_id] || params[:id] - return Nonprofit.find_by_id(params[:nonprofit_id] || params[:id]) + def self.with_params(params, administered_nonprofit = nil) + if params[:state_code] && params[:city] && params[:name] + Nonprofit.where(state_code_slug: params[:state_code], city_slug: params[:city], slug: params[:name]).last + elsif params[:nonprofit_id] || params[:id] + Nonprofit.find_by_id(params[:nonprofit_id] || params[:id]) elsif administered_nonprofit administered_nonprofit - end - end - + end + end end - diff --git a/lib/fetch/fetch_nonprofit_email.rb b/lib/fetch/fetch_nonprofit_email.rb index dc11e78d..0dbd4e72 100644 --- a/lib/fetch/fetch_nonprofit_email.rb +++ b/lib/fetch/fetch_nonprofit_email.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchNonprofitEmail + def self.with_charge(charge) + nonprofit = charge.nonprofit + nonprofit.email.blank? ? Settings.mailer.email : nonprofit.email + end - def self.with_charge charge - nonprofit = charge.nonprofit - nonprofit.email.blank? ? Settings.mailer.email : nonprofit.email - end - - def self.with_donation donation - nonprofit = donation.nonprofit - nonprofit.email.blank? ? Settings.mailer.email : nonprofit.email - end + def self.with_donation(donation) + nonprofit = donation.nonprofit + nonprofit.email.blank? ? Settings.mailer.email : nonprofit.email + end end diff --git a/lib/fetch/fetch_todo_status.rb b/lib/fetch/fetch_todo_status.rb index ac601198..245bcf12 100644 --- a/lib/fetch/fetch_todo_status.rb +++ b/lib/fetch/fetch_todo_status.rb @@ -1,28 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module FetchTodoStatus + def self.for_profile(np) + { + has_logo: np.logo?, + has_background: np.background_image?, + has_summary: np.summary?, + has_image: np.main_image?, + has_highlight: !np.achievements.join.blank?, + has_services: np.full_description? + } + end - def self.for_profile(np) - { - has_logo: np.logo?, - has_background: np.background_image?, - has_summary: np.summary?, - has_image: np.main_image?, - has_highlight: !np.achievements.join.blank?, - has_services: np.full_description? - } - end - - def self.for_dashboard(np) - { - has_campaign: np.campaigns.any?, - has_event: np.events.any?, - has_donation: np.donations.any?, - has_branding: np.brand_color?, - has_bank: np.bank_account.present?, - is_paying: np.billing_plan.present?, - has_imported: np.supporters.pluck(:imported_at).any?, - is_verified: np.verification_status == 'verified' && np.bank_account.present?, - has_thank_you: np.thank_you_note.present? - } - end + def self.for_dashboard(np) + { + has_campaign: np.campaigns.any?, + has_event: np.events.any?, + has_donation: np.donations.any?, + has_branding: np.brand_color?, + has_bank: np.bank_account.present?, + is_paying: np.billing_plan.present?, + has_imported: np.supporters.pluck(:imported_at).any?, + is_verified: np.verification_status == 'verified' && np.bank_account.present?, + has_thank_you: np.thank_you_note.present? + } + end end diff --git a/lib/fetch/stripe/fetch_stripe_account.rb b/lib/fetch/stripe/fetch_stripe_account.rb index 0e7f8aa3..f7cffa4e 100644 --- a/lib/fetch/stripe/fetch_stripe_account.rb +++ b/lib/fetch/stripe/fetch_stripe_account.rb @@ -1,15 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Retrive a stripe account object, catching any errors module FetchStripeAccount - - def self.with_account_id(stripe_account_id) - begin - stripe_acct = Stripe::Account.retrieve(stripe_account_id) - rescue - stripe_acct = nil - end - return stripe_acct - end - + def self.with_account_id(stripe_account_id) + begin + stripe_acct = Stripe::Account.retrieve(stripe_account_id) + rescue StandardError + stripe_acct = nil + end + stripe_acct + end end diff --git a/lib/format/format/address.rb b/lib/format/format/address.rb index 62781dec..1cf070d6 100644 --- a/lib/format/format/address.rb +++ b/lib/format/format/address.rb @@ -1,23 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Format; module Address - - def self.full_address(street, city, state, zip=nil) - # Albuquerque | NM | Albuquerque NM | 1234 Street Ln, Albuquerque NM - [[street, city].compact.join(", "), state, zip].compact.join(' ') - end +module Format + module Address + def self.full_address(street, city, state, zip = nil) + # Albuquerque | NM | Albuquerque NM | 1234 Street Ln, Albuquerque NM + [[street, city].compact.join(', '), state, zip].compact.join(' ') + end - def self.city_and_state(city,state) - [city, state].join(', ') if !city.blank? && !state.blank? - end + def self.city_and_state(city, state) + [city, state].join(', ') if !city.blank? && !state.blank? + end - def self.city_or_state(city,state) - city_and_state(city,state) || city || state - end + def self.city_or_state(city, state) + city_and_state(city, state) || city || state + end - def self.with_supporter(s) - return '' if s.nil? - [[s.address, s.city, s.state_code].reject(&:blank?).join(", "), s.zip_code].reject(&:blank?).join(" ") - end + def self.with_supporter(s) + return '' if s.nil? + [[s.address, s.city, s.state_code].reject(&:blank?).join(', '), s.zip_code].reject(&:blank?).join(' ') + end end; end - diff --git a/lib/format/format/csv.rb b/lib/format/format/csv.rb index 1eda6751..c107cbd4 100644 --- a/lib/format/format/csv.rb +++ b/lib/format/format/csv.rb @@ -1,33 +1,33 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'csv' require 'format/currency' module Format module Csv - # Convert an array of hashes of data into a csv # @param [Array] an array of hashes. The hash keys of the first item in the array become the CSV titles # @return [String] def self.from_data(arr) - return CSV.generate do |csv| - csv << arr.first.keys.map{|k| k.to_s.titleize} - arr.each{|h| csv << h.values} + CSV.generate do |csv| + csv << arr.first.keys.map { |k| k.to_s.titleize } + arr.each { |h| csv << h.values } end end def self.from_vectors(vecs) - return CSV.generate do |csv| - csv << vecs.first.to_a.map{|k| k.to_s.titleize} - vecs.drop(1).each{|v| csv << v.to_a} + CSV.generate do |csv| + csv << vecs.first.to_a.map { |k| k.to_s.titleize } + vecs.drop(1).each { |v| csv << v.to_a } end end def self.from_array(arr) - return CSV.generate do |csv| - csv << arr.first.map{|h| h.to_s.titleize} - arr.drop(1).each{|row| csv << (row||[])} + CSV.generate do |csv| + csv << arr.first.map { |h| h.to_s.titleize } + arr.drop(1).each { |row| csv << (row || []) } end end - end end diff --git a/lib/format/format/currency.rb b/lib/format/format/currency.rb index e81e1c01..e8161e3b 100644 --- a/lib/format/format/currency.rb +++ b/lib/format/format/currency.rb @@ -1,21 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Format; module Currency - - - # Converts currency units into subunits. - # @param [String] units - # @return [Integer] - def self.dollars_to_cents(units) - return (units.gsub(',','').gsub(Settings.intntl.currencies[0],'').to_f * 100).to_i - end - - # Converts currency subunits into units. - # @param [Integer] subunits - # @return [String] - def self.cents_to_dollars(subunits) - return (subunits.to_f / 100.0).to_s - .gsub(/^(\d+)\.0$/, '\1') # remove trailing zero if no decimals (eg. "1.0" -> "1") - .gsub(/^(\d+)\.(\d)$/, '\1.\20') # add a second zero if single decimal (eg. "9.9" -> "9.90") - end +module Format + module Currency + # Converts currency units into subunits. + # @param [String] units + # @return [Integer] + def self.dollars_to_cents(units) + (units.delete(',').gsub(Settings.intntl.currencies[0], '').to_f * 100).to_i + end + # Converts currency subunits into units. + # @param [Integer] subunits + # @return [String] + def self.cents_to_dollars(subunits) + (subunits.to_f / 100.0).to_s + .gsub(/^(\d+)\.0$/, '\1') # remove trailing zero if no decimals (eg. "1.0" -> "1") + .gsub(/^(\d+)\.(\d)$/, '\1.\20') # add a second zero if single decimal (eg. "9.9" -> "9.90") + end end; end diff --git a/lib/format/format/date.rb b/lib/format/format/date.rb index 7d803e3c..77c57ee2 100644 --- a/lib/format/format/date.rb +++ b/lib/format/format/date.rb @@ -1,64 +1,69 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'chronic' -module Format; module Date +module Format + module Date + ISORegex = /\d\d\d\d-\d\d-\d\d/.freeze - ISORegex = /\d\d\d\d-\d\d-\d\d/ - - def self.parse(str) - Chronic.parse(str) - end - - def self.from(str) - return DateTime.strptime(str, "%m/%d/%Y") - end - - def self.to_readable(date) - date.strftime("%A, %B #{date.day.ordinalize}") - end - - def self.full(date, timezone=nil) - return '' if date.nil? - date = Chronic.parse(date) if date.is_a?(String) - date = date.in_time_zone(timezone) if timezone - date.strftime("%m/%-d/%Y %l:%M%P") - end - - def self.full_range(date1, date2, timezone=nil) - return full(date1, timezone) if date2.nil? - return full(date2, timezone) if date1.nil? - if simple(date1) == simple(date2) - return full(date1, timezone) + ' - ' + time(date2, timezone) - else - return full(date1, timezone) + ' - ' + full(date2, timezone) + def self.parse(str) + Chronic.parse(str) end - end - def self.simple(date, timezone=nil) - return '' if date.nil? - date = Chronic.parse(date) if date.is_a?(String) - date = date.in_time_zone(timezone) if timezone - date.strftime("%m/%d/%Y") - end + def self.from(str) + DateTime.strptime(str, '%m/%d/%Y') + end - def self.time(datetime, timezone=nil) - return '' if datetime.nil? - datetime = Chronic.parse(datetime) if datetime.is_a?(String) - datetime = datetime.in_time_zone(timezone) if timezone - datetime.strftime("%l:%M%P") - end + def self.to_readable(date) + date.strftime("%A, %B #{date.day.ordinalize}") + end - def self.us_timezones - #zones=ActiveSupport::TimeZone.us_zones - zones=ActiveSupport::TimeZone.all - names = zones.map(&:name) - vals = zones.map{|t| t.tzinfo.name} - return names.zip(vals).sort_by{|name, val| name} - end + def self.full(date, timezone = nil) + return '' if date.nil? - def self.parse_partial_str(str) - return nil if str.nil? - Time.new(*str.match(/(\d\d\d\d)-?(\d\d)?-?(\d\d)?/).to_a[1..-1].compact.map(&:to_i)) - end + date = Chronic.parse(date) if date.is_a?(String) + date = date.in_time_zone(timezone) if timezone + date.strftime('%m/%-d/%Y %l:%M%P') + end + def self.full_range(date1, date2, timezone = nil) + return full(date1, timezone) if date2.nil? + return full(date2, timezone) if date1.nil? + if simple(date1) == simple(date2) + return full(date1, timezone) + ' - ' + time(date2, timezone) + else + return full(date1, timezone) + ' - ' + full(date2, timezone) + end + end + + def self.simple(date, timezone = nil) + return '' if date.nil? + + date = Chronic.parse(date) if date.is_a?(String) + date = date.in_time_zone(timezone) if timezone + date.strftime('%m/%d/%Y') + end + + def self.time(datetime, timezone = nil) + return '' if datetime.nil? + + datetime = Chronic.parse(datetime) if datetime.is_a?(String) + datetime = datetime.in_time_zone(timezone) if timezone + datetime.strftime('%l:%M%P') + end + + def self.us_timezones + # zones=ActiveSupport::TimeZone.us_zones + zones = ActiveSupport::TimeZone.all + names = zones.map(&:name) + vals = zones.map { |t| t.tzinfo.name } + names.zip(vals).sort_by { |name, _val| name } + end + + def self.parse_partial_str(str) + return nil if str.nil? + + Time.new(*str.match(/(\d\d\d\d)-?(\d\d)?-?(\d\d)?/).to_a[1..-1].compact.map(&:to_i)) + end end; end diff --git a/lib/format/format/dedication.rb b/lib/format/format/dedication.rb index 29650c18..86d6c388 100644 --- a/lib/format/format/dedication.rb +++ b/lib/format/format/dedication.rb @@ -1,16 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'json' module Format module Dedication - def self.from_json(json_text) begin hash = JSON.parse(json_text) - rescue + rescue StandardError return json_text end - return "Donation made in #{hash['type'] || 'honor'} of #{hash['name']}. Note: #{hash['note']}" + "Donation made in #{hash['type'] || 'honor'} of #{hash['name']}. Note: #{hash['note']}" end end end diff --git a/lib/format/format/geography.rb b/lib/format/format/geography.rb index 6ac3394c..f53f1532 100644 --- a/lib/format/format/geography.rb +++ b/lib/format/format/geography.rb @@ -1,305 +1,307 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Format; module Geography +module Format + module Geography + StateCodes = %w[AL AK AZ AR CA CO CT DE DC FL GA HI ID IL IN IA KS KY LA ME MD MA MI MN MS MO MT NE NV NH NJ NM NY NC ND OH OK OR PA PR RI SC SD TN TX UT VT VA WA WV WI WY].freeze - StateCodes = [ 'AL', 'AK', 'AZ', 'AR', 'CA', 'CO', 'CT', 'DE', 'DC', 'FL', 'GA', 'HI', 'ID', 'IL', 'IN', 'IA', 'KS', 'KY', 'LA', 'ME', 'MD', 'MA', 'MI', 'MN', 'MS', 'MO', 'MT', 'NE', 'NV', 'NH', 'NJ', 'NM', 'NY', 'NC', 'ND', 'OH', 'OK', 'OR', 'PA', 'PR', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY' ] + StateMappings = { + 'alabama' => 'AL', + 'alaska' => 'AK', + 'arizona' => 'AZ', + 'arkansas' => 'AR', + 'california' => 'CA', + 'colorado' => 'CO', + 'connecticut' => 'CT', + 'delaware' => 'DE', + 'district of columbia' => 'DC', + 'florida' => 'FL', + 'georgia' => 'GA', + 'hawaii' => 'HI', + 'idaho' => 'ID', + 'illinois' => 'IL', + 'indiana' => 'IN', + 'iowa' => 'IA', + 'kansas' => 'KS', + 'kentucky' => 'KY', + 'louisiana' => 'LA', + 'maine' => 'ME', + 'maryland' => 'MD', + 'massachusetts' => 'MA', + 'michigan' => 'MI', + 'minnesota' => 'MN', + 'mississippi' => 'MS', + 'missouri' => 'MO', + 'montana' => 'MT', + 'nebraska' => 'NE', + 'nevada' => 'NV', + 'new hampshire' => 'NH', + 'new jersey' => 'NJ', + 'new mexico' => 'NM', + 'new york' => 'NY', + 'north carolina' => 'NC', + 'north dakota' => 'ND', + 'ohio' => 'OH', + 'oklahoma' => 'OK', + 'oregon' => 'OR', + 'pennsylvania' => 'PA', + 'puerto rico' => 'PR', + 'rhode island' => 'RI', + 'south carolina' => 'SC', + 'south dakota' => 'SD', + 'tennessee' => 'TN', + 'texas' => 'TX', + 'utah' => 'UT', + 'vermont' => 'VT', + 'virginia' => 'VA', + 'washington' => 'WA', + 'west virginia' => 'WV', + 'wisconsin' => 'WI', + 'wyoming' => 'WY' + }.freeze - StateMappings = { - 'alabama' => 'AL', - 'alaska' => 'AK', - 'arizona' => 'AZ', - 'arkansas' => 'AR', - 'california' => 'CA', - 'colorado' => 'CO', - 'connecticut' => 'CT', - 'delaware' => 'DE', - 'district of columbia' => 'DC', - 'florida' => 'FL', - 'georgia' => 'GA', - 'hawaii' => 'HI', - 'idaho' => 'ID', - 'illinois' => 'IL', - 'indiana' => 'IN', - 'iowa' => 'IA', - 'kansas' => 'KS', - 'kentucky' => 'KY', - 'louisiana' => 'LA', - 'maine' => 'ME', - 'maryland' => 'MD', - 'massachusetts' => 'MA', - 'michigan' => 'MI', - 'minnesota' => 'MN', - 'mississippi' => 'MS', - 'missouri' => 'MO', - 'montana' => 'MT', - 'nebraska' => 'NE', - 'nevada' => 'NV', - 'new hampshire' => 'NH', - 'new jersey' => 'NJ', - 'new mexico' => 'NM', - 'new york' => 'NY', - 'north carolina' => 'NC', - 'north dakota' => 'ND', - 'ohio' => 'OH', - 'oklahoma' => 'OK', - 'oregon' => 'OR', - 'pennsylvania' => 'PA', - 'puerto rico' => 'PR', - 'rhode island' => 'RI', - 'south carolina' => 'SC', - 'south dakota' => 'SD', - 'tennessee' => 'TN', - 'texas' => 'TX', - 'utah' => 'UT', - 'vermont' => 'VT', - 'virginia' => 'VA', - 'washington' => 'WA', - 'west virginia' => 'WV', - 'wisconsin' => 'WI', - 'wyoming' => 'WY' - } + Countries = [ + 'Afghanistan', + 'Albania', + 'Algeria', + 'American Samoa', + 'Andorra', + 'Angola', + 'Anguilla', + 'Antarctica', + 'Antigua and Barbuda', + 'Argentina', + 'Armenia', + 'Aruba', + 'Australia', + 'Austria', + 'Azerbaijan', + 'Bahamas', + 'Bahrain', + 'Bangladesh', + 'Barbados', + 'Belarus', + 'Belgium', + 'Belize', + 'Benin', + 'Bermuda', + 'Bhutan', + 'Bolivia', + 'Bosnia and Herzegovina', + 'Botswana', + 'Bouvet Island', + 'Brazil', + 'British Indian Ocean Territory', + 'Brunei Darussalam', + 'Bulgaria', + 'Burkina Faso', + 'Burundi', + 'Cambodia', + 'Cameroon', + 'Canada', + 'Cape Verde', + 'Cayman Islands', + 'Central African Republic', + 'Chad', + 'Chile', + 'China', + 'Christmas Island', + 'Cocos (Keeling) Islands', + 'Colombia', + 'Comoros', + 'Congo', + 'Cook Islands', + 'Costa Rica', + "Cote D'ivoire", + 'Croatia', + 'Cuba', + 'Cyprus', + 'Czech Republic', + 'Denmark', + 'Djibouti', + 'Dominica', + 'Dominican Republic', + 'Ecuador', + 'Egypt', + 'El Salvador', + 'Equatorial Guinea', + 'Eritrea', + 'Estonia', + 'Ethiopia', + 'Falkland Islands (Malvinas)', + 'Faroe Islands', + 'Fiji', + 'Finland', + 'France', + 'French Guiana', + 'French Polynesia', + 'French Southern Territories', + 'Gabon', + 'Gambia', + 'Georgia', + 'Germany', + 'Ghana', + 'Gibraltar', + 'Greece', + 'Greenland', + 'Grenada', + 'Guadeloupe', + 'Guam', + 'Guatemala', + 'Guinea', + 'Guinea-bissau', + 'Guyana', + 'Haiti', + 'Heard Island and Mcdonald Islands', + 'Honduras', + 'Hong Kong', + 'Hungary', + 'Iceland', + 'India', + 'Indonesia', + 'Iran', + 'Iraq', + 'Ireland', + 'Israel', + 'Italy', + 'Jamaica', + 'Japan', + 'Jordan', + 'Kazakhstan', + 'Kenya', + 'Kiribati', + 'Korea (South)', + 'Kuwait', + 'Kyrgyzstan', + "Lao People's Democratic Republic", + 'Latvia', + 'Lebanon', + 'Lesotho', + 'Liberia', + 'Libyan Arab Jamahiriya', + 'Liechtenstein', + 'Lithuania', + 'Luxembourg', + 'Macao', + 'Macedonia', + 'Madagascar', + 'Malawi', + 'Malaysia', + 'Maldives', + 'Mali', + 'Malta', + 'Marshall Islands', + 'Martinique', + 'Mauritania', + 'Mauritius', + 'Mayotte', + 'Mexico', + 'Micronesia', + 'Moldova', + 'Monaco', + 'Mongolia', + 'Montserrat', + 'Morocco', + 'Mozambique', + 'Myanmar', + 'Namibia', + 'Nauru', + 'Nepal', + 'Netherlands', + 'Netherlands Antilles', + 'New Caledonia', + 'New Zealand', + 'Nicaragua', + 'Niger', + 'Nigeria', + 'Niue', + 'Norfolk Island', + 'Northern Mariana Islands', + 'Norway', + 'Oman', + 'Pakistan', + 'Palau', + 'Palestinian Territory', + 'Panama', + 'Papua New Guinea', + 'Paraguay', + 'Peru', + 'Philippines', + 'Pitcairn', + 'Poland', + 'Portugal', + 'Puerto Rico', + 'Qatar', + 'Reunion', + 'Romania', + 'Russia', + 'Rwanda', + 'Saint Helena', + 'Saint Kitts and Nevis', + 'Saint Lucia', + 'Saint Pierre and Miquelon', + 'Saint Vincent and The Grenadines', + 'Samoa', + 'San Marino', + 'Sao Tome and Principe', + 'Saudi Arabia', + 'Senegal', + 'Serbia and Montenegro', + 'Seychelles', + 'Sierra Leone', + 'Singapore', + 'Slovakia', + 'Slovenia', + 'Solomon Islands', + 'Somalia', + 'South Africa', + 'South Georgia and The South Sandwich Islands', + 'Spain', + 'Sri Lanka', + 'Sudan', + 'Suriname', + 'Svalbard and Jan Mayen', + 'Swaziland', + 'Sweden', + 'Switzerland', + 'Syria ', + 'Taiwan', + 'Tajikistan', + 'Tanzania', + 'Thailand', + 'Timor-leste', + 'Togo', + 'Tokelau', + 'Tonga', + 'Trinidad and Tobago', + 'Tunisia', + 'Turkey', + 'Turkmenistan', + 'Tuvalu', + 'Uganda', + 'Ukraine', + 'United Arab Emirates', + 'United Kingdom', + 'United States', + 'Uruguay', + 'Uzbekistan', + 'Vanuatu', + 'Venezuela', + 'Viet Nam', + 'Virgin Islands', + 'Wallis and Futuna', + 'Western Sahara', + 'Yemen', + 'Zambia', + 'Zimbabwe' + ].freeze -Countries = [ - "Afghanistan", - "Albania", - "Algeria", - "American Samoa", - "Andorra", - "Angola", - "Anguilla", - "Antarctica", - "Antigua and Barbuda", - "Argentina", - "Armenia", - "Aruba", - "Australia", - "Austria", - "Azerbaijan", - "Bahamas", - "Bahrain", - "Bangladesh", - "Barbados", - "Belarus", - "Belgium", - "Belize", - "Benin", - "Bermuda", - "Bhutan", - "Bolivia", - "Bosnia and Herzegovina", - "Botswana", - "Bouvet Island", - "Brazil", - "British Indian Ocean Territory", - "Brunei Darussalam", - "Bulgaria", - "Burkina Faso", - "Burundi", - "Cambodia", - "Cameroon", - "Canada", - "Cape Verde", - "Cayman Islands", - "Central African Republic", - "Chad", - "Chile", - "China", - "Christmas Island", - "Cocos (Keeling) Islands", - "Colombia", - "Comoros", - "Congo", - "Cook Islands", - "Costa Rica", - "Cote D'ivoire", - "Croatia", - "Cuba", - "Cyprus", - "Czech Republic", - "Denmark", - "Djibouti", - "Dominica", - "Dominican Republic", - "Ecuador", - "Egypt", - "El Salvador", - "Equatorial Guinea", - "Eritrea", - "Estonia", - "Ethiopia", - "Falkland Islands (Malvinas)", - "Faroe Islands", - "Fiji", - "Finland", - "France", - "French Guiana", - "French Polynesia", - "French Southern Territories", - "Gabon", - "Gambia", - "Georgia", - "Germany", - "Ghana", - "Gibraltar", - "Greece", - "Greenland", - "Grenada", - "Guadeloupe", - "Guam", - "Guatemala", - "Guinea", - "Guinea-bissau", - "Guyana", - "Haiti", - "Heard Island and Mcdonald Islands", - "Honduras", - "Hong Kong", - "Hungary", - "Iceland", - "India", - "Indonesia", - "Iran", - "Iraq", - "Ireland", - "Israel", - "Italy", - "Jamaica", - "Japan", - "Jordan", - "Kazakhstan", - "Kenya", - "Kiribati", - "Korea (South)", - "Kuwait", - "Kyrgyzstan", - "Lao People's Democratic Republic", - "Latvia", - "Lebanon", - "Lesotho", - "Liberia", - "Libyan Arab Jamahiriya", - "Liechtenstein", - "Lithuania", - "Luxembourg", - "Macao", - "Macedonia", - "Madagascar", - "Malawi", - "Malaysia", - "Maldives", - "Mali", - "Malta", - "Marshall Islands", - "Martinique", - "Mauritania", - "Mauritius", - "Mayotte", - "Mexico", - "Micronesia", - "Moldova", - "Monaco", - "Mongolia", - "Montserrat", - "Morocco", - "Mozambique", - "Myanmar", - "Namibia", - "Nauru", - "Nepal", - "Netherlands", - "Netherlands Antilles", - "New Caledonia", - "New Zealand", - "Nicaragua", - "Niger", - "Nigeria", - "Niue", - "Norfolk Island", - "Northern Mariana Islands", - "Norway", - "Oman", - "Pakistan", - "Palau", - "Palestinian Territory", - "Panama", - "Papua New Guinea", - "Paraguay", - "Peru", - "Philippines", - "Pitcairn", - "Poland", - "Portugal", - "Puerto Rico", - "Qatar", - "Reunion", - "Romania", - "Russia", - "Rwanda", - "Saint Helena", - "Saint Kitts and Nevis", - "Saint Lucia", - "Saint Pierre and Miquelon", - "Saint Vincent and The Grenadines", - "Samoa", - "San Marino", - "Sao Tome and Principe", - "Saudi Arabia", - "Senegal", - "Serbia and Montenegro", - "Seychelles", - "Sierra Leone", - "Singapore", - "Slovakia", - "Slovenia", - "Solomon Islands", - "Somalia", - "South Africa", - "South Georgia and The South Sandwich Islands", - "Spain", - "Sri Lanka", - "Sudan", - "Suriname", - "Svalbard and Jan Mayen", - "Swaziland", - "Sweden", - "Switzerland", - "Syria ", - "Taiwan", - "Tajikistan", - "Tanzania", - "Thailand", - "Timor-leste", - "Togo", - "Tokelau", - "Tonga", - "Trinidad and Tobago", - "Tunisia", - "Turkey", - "Turkmenistan", - "Tuvalu", - "Uganda", - "Ukraine", - "United Arab Emirates", - "United Kingdom", - "United States", - "Uruguay", - "Uzbekistan", - "Vanuatu", - "Venezuela", - "Viet Nam", - "Virgin Islands", - "Wallis and Futuna", - "Western Sahara", - "Yemen", - "Zambia", - "Zimbabwe" -] - - # Convert a full state name like "New Mexico" into a code like "NM" - # Will leave strings that are already state codes alone - def self.full_state_to_code(str) - str = str.strip - return str if StateCodes.include?(str.upcase) - return StateMappings[str.downcase] - end + # Convert a full state name like "New Mexico" into a code like "NM" + # Will leave strings that are already state codes alone + def self.full_state_to_code(str) + str = str.strip + return str if StateCodes.include?(str.upcase) + StateMappings[str.downcase] + end end; end diff --git a/lib/format/format/html.rb b/lib/format/format/html.rb index 23f2a543..8d2e5906 100644 --- a/lib/format/format/html.rb +++ b/lib/format/format/html.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Format module HTML def self.has_only_empty_tags(html_str) - return true if html_str && html_str.gsub(/<[^>]*>/ui,'').gsub(" ", "").strip == "" + return true if html_str && html_str.gsub(/<[^>]*>/ui, '').gsub(' ', '').strip == '' end end end diff --git a/lib/format/format/indefinitize.rb b/lib/format/format/indefinitize.rb index 68683734..e84fd2d9 100644 --- a/lib/format/format/indefinitize.rb +++ b/lib/format/format/indefinitize.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Format - module Indefinitize - VOWELS = %w(a e i o u) + module Indefinitize + VOWELS = %w[a e i o u].freeze - def self.article word - VOWELS.include?(word[0].downcase) ? 'an' : 'a' - end + def self.article(word) + VOWELS.include?(word[0].downcase) ? 'an' : 'a' + end - def self.with_article word - article(word) + ' ' + word - end - end + def self.with_article(word) + article(word) + ' ' + word + end + end end diff --git a/lib/format/format/interpolate.rb b/lib/format/format/interpolate.rb index dc53a5a7..16e1d3b9 100644 --- a/lib/format/format/interpolate.rb +++ b/lib/format/format/interpolate.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Format module Interpolate def self.with_hash(str, hash) return '' if str.nil? - str.gsub(/{{.+}}/){|key| hash[key.gsub(/[{}]/,'')]} + + str.gsub(/{{.+}}/) { |key| hash[key.gsub(/[{}]/, '')] } end end end diff --git a/lib/format/format/name.rb b/lib/format/format/name.rb index 53d4dccb..fea72fb2 100644 --- a/lib/format/format/name.rb +++ b/lib/format/format/name.rb @@ -1,17 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'active_support/core_ext' module Format - module Name - - def self.split_full(name) - return '' if name.nil? - name.split(/\ (\w+\s*)$/) - end + module Name + def self.split_full(name) + return '' if name.nil? + + name.split(/\ (\w+\s*)$/) + end # Format a nonprofit name into an email header def self.email_from_np(np_name) - "\"#{np_name.gsub(',', '').gsub("\"", '')}\" <#{Settings.mailer.email}>" + "\"#{np_name.delete(',').delete('"')}\" <#{Settings.mailer.email}>" end - end + end end diff --git a/lib/format/format/phone.rb b/lib/format/format/phone.rb index 81e88201..f20e21b4 100644 --- a/lib/format/format/phone.rb +++ b/lib/format/format/phone.rb @@ -1,22 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Format; module Phone - - def self.readable(number) - # Convert to: - # (505) 263-6320 - # or: - # 263-6320 - return '' if number.blank? - - stripped = number.gsub(/[-\(\)\.\s]/, '') # remove extra chars and space - if stripped.length == 10 - return "(#{stripped[0..2]}) #{stripped[3..5]}-#{stripped[6..9]}" - elsif stripped.length == 7 - return "#{stripped[0..2]}-#{stripped[3..6]}" - else - return number - end - end +module Format + module Phone + def self.readable(number) + # Convert to: + # (505) 263-6320 + # or: + # 263-6320 + return '' if number.blank? + stripped = number.gsub(/[-\(\)\.\s]/, '') # remove extra chars and space + if stripped.length == 10 + return "(#{stripped[0..2]}) #{stripped[3..5]}-#{stripped[6..9]}" + elsif stripped.length == 7 + return "#{stripped[0..2]}-#{stripped[3..6]}" + else + return number + end + end end; end - diff --git a/lib/format/format/remove_diacritics.rb b/lib/format/format/remove_diacritics.rb index 52680e0c..67ba2c74 100644 --- a/lib/format/format/remove_diacritics.rb +++ b/lib/format/format/remove_diacritics.rb @@ -1,16 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require "i18n" +require 'i18n' module Format - module RemoveDiacritics - - def self.from_hash(hash, keys) - # returns a new hash with any diacritics replaced with a plain character + module RemoveDiacritics + def self.from_hash(hash, keys) + # returns a new hash with any diacritics replaced with a plain character # only from values corresponding to specified keys: - # {"city" => "São Paulo"} ["city"] will return {"city" => "Sao Paulo"} - Hash[hash.map{|k, v| [k, (keys.include? k) ? I18n.transliterate(v) : v]}] - end - - end + # {"city" => "São Paulo"} ["city"] will return {"city" => "Sao Paulo"} + Hash[hash.map { |k, v| [k, (keys.include? k) ? I18n.transliterate(v) : v] }] + end + end end - diff --git a/lib/format/format/timezone.rb b/lib/format/format/timezone.rb index 08cec6dc..2fac11f6 100644 --- a/lib/format/format/timezone.rb +++ b/lib/format/format/timezone.rb @@ -1,25 +1,26 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Format module Timezone def self.to_proxy(str) - dict = { - "Hawaii" => 'Pacific/Honolulu', - "Alaska" => 'America/Juneau', - "Pacific Time (US & Canada)" => 'America/Los_Angeles', - "Arizona" => 'America/Phoenix', - "Mountain Time (US & Canada)" => 'America/Denver', - "Central Time (US & Canada)" => 'America/Chicago', - "Eastern Time (US & Canada)" => 'America/New_York', - "Indiana (East)" => 'America/Indiana/Indianapolis' + dict = { + 'Hawaii' => 'Pacific/Honolulu', + 'Alaska' => 'America/Juneau', + 'Pacific Time (US & Canada)' => 'America/Los_Angeles', + 'Arizona' => 'America/Phoenix', + 'Mountain Time (US & Canada)' => 'America/Denver', + 'Central Time (US & Canada)' => 'America/Chicago', + 'Eastern Time (US & Canada)' => 'America/New_York', + 'Indiana (East)' => 'America/Indiana/Indianapolis' } - if dict.has_key?(str) + if dict.key?(str) return dict[str] - elsif dict.has_value?(str) + elsif dict.value?(str) return str else - return false + return false end end end end - diff --git a/lib/format/format/url.rb b/lib/format/format/url.rb index 59a1fe42..1ff81c6f 100644 --- a/lib/format/format/url.rb +++ b/lib/format/format/url.rb @@ -1,27 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module Format; module Url +module Format + module Url + def self.without_prefix(url) + url.gsub(%r{(http(s)?://)|(www\.)|(\?.*$)|(#.*$)}, '') + end - def self.without_prefix(url) - url.gsub(/(http(s)?:\/\/)|(www\.)|(\?.*$)|(#.*$)/, '') - end + # Given ["What hello", "hi! lol?"] + # Return ["what-hello", "hi-lol"] + def self.convert_to_slug(*words) + return '' if words.empty? || !words.all? # true if any are nil or empty - # Given ["What hello", "hi! lol?"] - # Return ["what-hello", "hi-lol"] - def self.convert_to_slug(*words) - return '' if words.empty? || !words.all? # true if any are nil or empty - words.map do |d| - d.strip.downcase - .gsub(/['`]/,'') # no apostrophes - .gsub(/\./,'') # no dots - .gsub(/\s*@\s*/, ' at ') # @ -> at - .gsub(/\s*&\s*/, ' and ') # & -> and - .gsub(/\s*[^A-Za-z0-9\.\-]\s*/, '-') # replace oddballs with hyphen - .gsub(/\A[-\.]+|[-\.]+\z/,'') # strip leading/trailing hyphens - end.join("/") - end - - def self.concat(*urls) - return urls.join('/').gsub(/([^:])\/\/+/,'\1/') - end + words.map do |d| + d.strip.downcase + .gsub(/['`]/, '') # no apostrophes + .delete('.') # no dots + .gsub(/\s*@\s*/, ' at ') # @ -> at + .gsub(/\s*&\s*/, ' and ') # & -> and + .gsub(/\s*[^A-Za-z0-9\.\-]\s*/, '-') # replace oddballs with hyphen + .gsub(/\A[-\.]+|[-\.]+\z/, '') # strip leading/trailing hyphens + end.join('/') + end + def self.concat(*urls) + urls.join('/').gsub(%r{([^:])//+}, '\1/') + end end; end diff --git a/lib/generators/api/entity/USAGE b/lib/generators/api/entity/USAGE deleted file mode 100644 index 562368c0..00000000 --- a/lib/generators/api/entity/USAGE +++ /dev/null @@ -1,9 +0,0 @@ -Description: - Generates a new entity to be returned by the API - -Example: - rails generate api:entity EntityName - - This will create: - A new subclass of Grape::Entity called Houdini::V1::Entities::EntityName - in app/api/houdini/v1/entities/entity_name.rb diff --git a/lib/generators/api/entity/entity_generator.rb b/lib/generators/api/entity/entity_generator.rb deleted file mode 100644 index 8e781872..00000000 --- a/lib/generators/api/entity/entity_generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails/generators' -class Api::EntityGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) - def copy_to_entity - template 'entity.rb.erb', File.join("app/api/houdini/v1/entities", "#{name.underscore}.rb") - end -end diff --git a/lib/generators/api/resource/USAGE b/lib/generators/api/resource/USAGE deleted file mode 100644 index 74598d4b..00000000 --- a/lib/generators/api/resource/USAGE +++ /dev/null @@ -1,10 +0,0 @@ -Description: - Creates a new resource for API usage - -Example: - rails generate api:resource ResourceName - - This will create: - * A new subclass of Grape::API called Houdini::V1::ResourceName - at app/api/houdini/v1/resource_name.rb - * Houdini::V1::ResourceName mounted to the api in app/api/houdini/v1/api.rb \ No newline at end of file diff --git a/lib/generators/api/resource/resource_generator.rb b/lib/generators/api/resource/resource_generator.rb deleted file mode 100644 index 99aef6b1..00000000 --- a/lib/generators/api/resource/resource_generator.rb +++ /dev/null @@ -1,16 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails/generators' -class Api::ResourceGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) - def copy_to_resource - template 'resource.rb.erb', File.join("app/api/houdini/v1", "#{name.underscore}.rb") - end - - def copy_to_spec - template 'spec.rb.erb', File.join("spec/api/houdini/", "#{name.underscore}_spec.rb") - end - - def add_to_root_api - inject_into_file "app/api/houdini/v1/api.rb", "mount Houdini::V1::#{ name.camelcase} => \"/#{name.underscore}\"\n ", before:"# Additional mounts are added via generators above this line" - end -end diff --git a/lib/generators/api/resource/templates/resource.rb.erb b/lib/generators/api/resource/templates/resource.rb.erb deleted file mode 100644 index ac7e9d67..00000000 --- a/lib/generators/api/resource/templates/resource.rb.erb +++ /dev/null @@ -1,3 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::<%= name.camelcase %> < Grape::API -end \ No newline at end of file diff --git a/lib/generators/api/resource/templates/spec.rb.erb b/lib/generators/api/resource/templates/spec.rb.erb deleted file mode 100644 index c47d73e6..00000000 --- a/lib/generators/api/resource/templates/spec.rb.erb +++ /dev/null @@ -1,8 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails_helper' - -describe Houdini::V1::<%= name.camelcase %>, :type => :request do - describe :get do - - end -end \ No newline at end of file diff --git a/lib/generators/api/validator/templates/validator.rb.erb b/lib/generators/api/validator/templates/validator.rb.erb deleted file mode 100644 index 3dcb5584..00000000 --- a/lib/generators/api/validator/templates/validator.rb.erb +++ /dev/null @@ -1,6 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Validators::<%= name.camelcase %> < Grape::Validations::Base - def validate_param!(attr_name, params) - fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'MESSAGE' - end -end \ No newline at end of file diff --git a/lib/generators/api/validator/validator_generator.rb b/lib/generators/api/validator/validator_generator.rb deleted file mode 100644 index 7c57d094..00000000 --- a/lib/generators/api/validator/validator_generator.rb +++ /dev/null @@ -1,16 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails/generators' -class Api::ValidatorGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) - - def copy_to_validators - post_api_part = File.join("houdini/v1/validators", "#{name.underscore}.rb") - output_file = File.join("app/api", post_api_part ) - template 'validator.rb.erb', output_file - end - - def add_to_root_validations - post_api_part = File.join("houdini/v1/validators", "#{name.underscore}") - append_to_file "app/api/houdini/v1/validations.rb", "\nrequire '#{post_api_part}'" - end -end diff --git a/lib/generators/email_job/USAGE b/lib/generators/email_job/USAGE deleted file mode 100644 index dd722287..00000000 --- a/lib/generators/email_job/USAGE +++ /dev/null @@ -1,8 +0,0 @@ -Description: - Create a new EmailJob subclass in lib/job_types - -Example: - rails generate email_job JobName arg1 arg2 - - This will create: - A file called lib/job_types/job_name_job.rb with a module named JobNameJob diff --git a/lib/generators/email_job/email_job_generator.rb b/lib/generators/email_job/email_job_generator.rb deleted file mode 100644 index 64de7f90..00000000 --- a/lib/generators/email_job/email_job_generator.rb +++ /dev/null @@ -1,9 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class EmailJobGenerator < Rails::Generators::NamedBase - argument :attribs, :type => :array - source_root File.expand_path('../templates', __FILE__) - def copy_file_to_lib - template 'email_job_template.erb', "lib/job_types/#{name.underscore}.rb" - template 'email_job_spec_template.erb', "spec/lib/job_types/#{name.underscore}_spec.rb" - end -end diff --git a/lib/generators/email_job/templates/email_job_spec_template.erb b/lib/generators/email_job/templates/email_job_spec_template.erb deleted file mode 100644 index c5993d68..00000000 --- a/lib/generators/email_job/templates/email_job_spec_template.erb +++ /dev/null @@ -1,13 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails_helper.rb' - -describe JobTypes::<%= name %> do - describe '.perform' do - it 'calls the correct active mailer' do - expect(fail).to receive(:fail).with(fail).and_wrap_original{|m, *args| mailer = double('object'); expect(mailer).to receive(:deliver).and_return(nil); mailer} - - job = JobTypes::<%= name %>.new(fail) - job.perform - end - end -end \ No newline at end of file diff --git a/lib/generators/email_job/templates/email_job_template.erb b/lib/generators/email_job/templates/email_job_template.erb deleted file mode 100644 index 5969657c..00000000 --- a/lib/generators/email_job/templates/email_job_template.erb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class <%= name %> < EmailJob - attr_reader <%= attribs.map{|i| ":#{i}"}.join(", ") %> - - def initialize(<%= attribs.join(", ") %>)<% attribs.each {|i| %> - @<%= i %> = <%= i %><% }%> - end - - def perform - fail - end - end -end \ No newline at end of file diff --git a/lib/generators/libmodule/libmodule_generator.rb b/lib/generators/libmodule/libmodule_generator.rb index 0232b220..d82497e9 100644 --- a/lib/generators/libmodule/libmodule_generator.rb +++ b/lib/generators/libmodule/libmodule_generator.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class LibmoduleGenerator < Rails::Generators::NamedBase - argument :mod_type, :type => :string - source_root File.expand_path('../templates', __FILE__) + argument :mod_type, type: :string + source_root File.expand_path('templates', __dir__) def copy_file_to_lib template 'libmodule_template.erb', "lib/#{mod_type.underscore}/#{mod_type.underscore}_#{name.underscore}.rb" template 'libmodule_spec_template.erb', "spec/lib/#{mod_type.underscore}/#{mod_type.underscore}_#{name.underscore}_spec.rb" diff --git a/app/api/houdini/api.rb b/lib/generators/overrides.rb similarity index 50% rename from app/api/houdini/api.rb rename to lib/generators/overrides.rb index b2c54ab2..f4570846 100644 --- a/app/api/houdini/api.rb +++ b/lib/generators/overrides.rb @@ -1,5 +1,4 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::API < Grape::API - format :json - mount Houdini::V1::API => '/v1' -end \ No newline at end of file +require_relative './overrides/rails/plugin/plugin_builder' \ No newline at end of file diff --git a/lib/generators/overrides/rails/plugin/USAGE b/lib/generators/overrides/rails/plugin/USAGE new file mode 100644 index 00000000..9a7bf9f3 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/USAGE @@ -0,0 +1,10 @@ +Description: + The 'rails plugin new' command creates a skeleton for developing any + kind of Rails extension with ability to run tests using dummy Rails + application. + +Example: + rails plugin new ~/Code/Ruby/blog + + This generates a skeletal Rails plugin in ~/Code/Ruby/blog. + See the README in the newly created plugin to get going. diff --git a/lib/generators/overrides/rails/plugin/plugin_builder.rb b/lib/generators/overrides/rails/plugin/plugin_builder.rb new file mode 100644 index 00000000..062edc08 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/plugin_builder.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails/generators/rails/plugin/plugin_generator' +class PluginBuilder < Rails::PluginBuilder + def license + template "LICENSE" + template "AGPL-3.0.txt" + template "LGPL-3.0.txt" + template "GPL-3.0.txt" + end +end + +module Rails + module Generators + class PluginGenerator + def source_paths + [File.expand_path("templates", __dir__)] + end + end + end +end + diff --git a/lib/generators/overrides/rails/plugin/templates/%name%.gemspec.tt b/lib/generators/overrides/rails/plugin/templates/%name%.gemspec.tt new file mode 100644 index 00000000..94205764 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/%name%.gemspec.tt @@ -0,0 +1,24 @@ +require_relative "lib/<%= namespaced_name %>/version" + +Gem::Specification.new do |spec| + spec.name = <%= name.inspect %> + spec.version = <%= camelized_modules %>::VERSION + spec.authors = [<%= author.inspect %>] + spec.email = [<%= email.inspect %>] + spec.homepage = "TODO" + spec.summary = "TODO: Summary of <%= camelized_modules %>." + spec.description = "TODO: Description of <%= camelized_modules %>." + spec.license = "AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later" + + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' + # to allow pushing to a single host or delete this section to allow pushing to any host. + spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." + spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." + + spec.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "AGPL-3.0.txt", "GPL-3.0.txt", "LGPL-3.0.txt", "Rakefile", "README.md"] + + <%= '# ' if options.dev? || options.edge? || options.master? -%>spec.add_dependency "rails", "<%= Array(rails_version_specifier).join('", "') %>" +end diff --git a/lib/generators/overrides/rails/plugin/templates/AGPL-3.0.txt.tt b/lib/generators/overrides/rails/plugin/templates/AGPL-3.0.txt.tt new file mode 100644 index 00000000..be3f7b28 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/AGPL-3.0.txt.tt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/lib/generators/overrides/rails/plugin/templates/GPL-3.0.txt.tt b/lib/generators/overrides/rails/plugin/templates/GPL-3.0.txt.tt new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/GPL-3.0.txt.tt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/lib/generators/overrides/rails/plugin/templates/Gemfile.tt b/lib/generators/overrides/rails/plugin/templates/Gemfile.tt new file mode 100644 index 00000000..65e028db --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/Gemfile.tt @@ -0,0 +1,41 @@ +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + +<% if options[:skip_gemspec] -%> +<%= '# ' if options.dev? || options.edge? || options.master? -%>gem 'rails', '<%= Array(rails_version_specifier).join("', '") %>' +<% else -%> +# Specify your gem's dependencies in <%= name %>.gemspec. +gemspec +<% end -%> +<% unless options[:skip_active_record] -%> + +group :development do + gem '<%= gem_for_database[0] %>' +end +<% end -%> + +<% if options.dev? || options.edge? -%> +# Your gem is dependent on dev or edge Rails. Once you can lock this +# dependency down to a specific version, move it to your gemspec. +<% max_width = gemfile_entries.map { |g| g.name.length }.max -%> +<% gemfile_entries.each do |gem| -%> +<% if gem.comment -%> + +# <%= gem.comment %> +<% end -%> +<%= gem.commented_out ? '# ' : '' %>gem '<%= gem.name %>'<%= %(, '#{gem.version}') if gem.version -%> +<% if gem.options.any? -%> +, <%= gem.options.map { |k,v| + "#{k}: #{v.inspect}" }.join(', ') %> +<% end -%> +<% end -%> + +<% end -%> +<% if RUBY_ENGINE == 'ruby' -%> +# To use a debugger +# gem 'byebug', group: [:development, :test] +<% end -%> +<% if RUBY_PLATFORM.match(/bccwin|cygwin|emx|mingw|mswin|wince|java/) -%> + +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] +<% end -%> diff --git a/lib/generators/overrides/rails/plugin/templates/LGPL-3.0.txt.tt b/lib/generators/overrides/rails/plugin/templates/LGPL-3.0.txt.tt new file mode 100644 index 00000000..0a041280 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/LGPL-3.0.txt.tt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/lib/generators/overrides/rails/plugin/templates/LICENSE.tt b/lib/generators/overrides/rails/plugin/templates/LICENSE.tt new file mode 100644 index 00000000..c538fbd2 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/LICENSE.tt @@ -0,0 +1,86 @@ +# Free Software Licensing Information for <%= name.inspect %>. + +The primary license of <%= name.inspect %> is: + + AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + +This software's license gives you freedom; you can copy, convey, +propagate, redistribute and/or modify this program under the terms of +the GNU Affero General Public License (AGPL) as published by the Free +Software Foundation (FSF), either version 3 of the License, or (at your +option) any later version of the AGPL published by the FSF. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program in a file in the toplevel directory called +"AGPL-3.0.txt". If not, see . + +In addition to the permission granted by the AGPLv3, you also receive +permissions as written in The Web Template Output Additional Permission, +Version 3.0, as published by the Software Freedom Conservancy +("Conservancy"), either version 3 of that Additional Permission, or (at your +option) any later version of the Additional Permission as published by +Conservancy. + +You should have received a copy of the Web Template Output Additional +Permission, along with this program in a file in the toplevel directory +called "Web-Template-Output-Additional-Permission.txt". If not, see +. + +## Different Licenses for some files in <%= name.inspect %> Repository. + +1. Any file in the <%= name.inspect %> repository that does not specific a license, or is + not discussed explicitly in this toplevel "LICENSE" file is licensed: + + AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + + as specified in detail above. + +2. Files that contain the information "License: LGPL-3.0-or-later" are + licensed as below: + + You can copy, convey, propagate, redistribute and/or modify this program + under the terms of the GNU General Public License (GPL) as published by + the Free Software Foundation (FSF), either version 3 of the License, or + (at your option) any later version of the GPL published by the FSF. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with this program in a file in the toplevel directory called + "GPL-3.0.txt". If not, see . + + In addition to the permission granted by the GPLv3, you also receive + permissions as written in GNU Lesser General Public License (LGPL), + Version 3.0, as published by the Free Software Foundation (FSF), either + version 3 of the License, or (at your option) any later version of the + LGPL published by the FSF. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program in a file in the toplevel directory called + "LGPL-3.0.txt". If not, see . + +3. Files that contain the information "License: CC0-1.0" are licensed under + the Creative Commons Public Domain Dedication 1.0 Universal or any later + version as published by Creative Commons, Inc. ("CC0"). + +4. All SVG, ICO and PNG files in this repository are licensed as CC-0. + + This applies to any files that are both (a) in the standard SVG, ICO, + and/or PNG format, and (b) where their names end in .svg or .ico. + + The project may in future place later versions of these files under a + copyleft license, but will discuss that with the contributor community + before doing so. diff --git a/lib/generators/overrides/rails/plugin/templates/README.md.tt b/lib/generators/overrides/rails/plugin/templates/README.md.tt new file mode 100644 index 00000000..775a2482 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/README.md.tt @@ -0,0 +1,25 @@ +# <%= camelized_modules %> +Short description and motivation. + +## Usage +How to use my plugin. + +## Installation +Add this line to your application's Gemfile: + +```ruby +gem '<%= name %>' +``` + +And then execute: +```bash +$ bundle +``` + +Or install it yourself as: +```bash +$ gem install <%= name %> +``` + +## Contributing +Contribution directions go here. diff --git a/lib/generators/overrides/rails/plugin/templates/Rakefile.tt b/lib/generators/overrides/rails/plugin/templates/Rakefile.tt new file mode 100644 index 00000000..d131901b --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/Rakefile.tt @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require "bundler/setup" +<% if engine? && !options[:skip_active_record] && with_dummy_app? -%> + +APP_RAKEFILE = File.expand_path("<%= dummy_path -%>/Rakefile", __dir__) +load "rails/tasks/engine.rake" +<% end -%> +<% if engine? -%> + +load "rails/tasks/statistics.rake" +<% end -%> +<% unless options[:skip_gemspec] -%> + +require "bundler/gem_tasks" +<% end -%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt b/lib/generators/overrides/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt new file mode 100644 index 00000000..781b39d5 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/controllers/%namespaced_name%/application_controller.rb.tt @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class ApplicationController < ActionController::#{api? ? "API" : "Base"} + #{ api? ? '# ' : '' }protect_from_forgery with: :exception + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt b/lib/generators/overrides/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt new file mode 100644 index 00000000..7ad833e8 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/helpers/%namespaced_name%/application_helper.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + module ApplicationHelper + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt b/lib/generators/overrides/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt new file mode 100644 index 00000000..dfe0327a --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/jobs/%namespaced_name%/application_job.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class ApplicationJob < ActiveJob::Base + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt b/lib/generators/overrides/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt new file mode 100644 index 00000000..ebc54fa6 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/mailers/%namespaced_name%/application_mailer.rb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt b/lib/generators/overrides/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt new file mode 100644 index 00000000..b5ced125 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/models/%namespaced_name%/application_record.rb.tt @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt b/lib/generators/overrides/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt new file mode 100644 index 00000000..6e54a1ce --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/app/views/layouts/%namespaced_name%/application.html.erb.tt @@ -0,0 +1,18 @@ + + + + <%= humanized %> + <%%= csrf_meta_tags %> + <%%= csp_meta_tag %> + + <%%= stylesheet_link_tag "<%= namespaced_name %>/application", media: "all" %> + <%- unless options[:skip_javascript] -%> + <%%= javascript_include_tag "<%= namespaced_name %>/application" %> + <%- end -%> + + + +<%%= yield %> + + + diff --git a/lib/generators/overrides/rails/plugin/templates/bin/rails.tt b/lib/generators/overrides/rails/plugin/templates/bin/rails.tt new file mode 100644 index 00000000..fd4e3548 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/bin/rails.tt @@ -0,0 +1,30 @@ +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path('..', __dir__) +ENGINE_PATH = File.expand_path('../lib/<%= namespaced_name -%>/engine', __dir__) +<% if with_dummy_app? -%> +APP_PATH = File.expand_path('../<%= dummy_path -%>/config/application', __dir__) +<% end -%> + +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) + +<% if include_all_railties? -%> +require "rails/all" +<% else -%> +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +<%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" +require "action_controller/railtie" +<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" +require "action_view/railtie" +<%= comment_if :skip_action_cable %>require "action_cable/engine" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test %>require "rails/test_unit/railtie" +<% end -%> +require "rails/engine/commands" diff --git a/lib/generators/overrides/rails/plugin/templates/bin/test.tt b/lib/generators/overrides/rails/plugin/templates/bin/test.tt new file mode 100644 index 00000000..8e7d3216 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/bin/test.tt @@ -0,0 +1,4 @@ +$: << File.expand_path("../test", __dir__) + +require "bundler/setup" +require "rails/plugin/test" diff --git a/lib/generators/overrides/rails/plugin/templates/config/routes.rb.tt b/lib/generators/overrides/rails/plugin/templates/config/routes.rb.tt new file mode 100644 index 00000000..154452bf --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/config/routes.rb.tt @@ -0,0 +1,6 @@ +<% if mountable? -%> +<%= camelized_modules %>::Engine.routes.draw do +<% else -%> +Rails.application.routes.draw do +<% end -%> +end diff --git a/lib/generators/overrides/rails/plugin/templates/gitignore.tt b/lib/generators/overrides/rails/plugin/templates/gitignore.tt new file mode 100644 index 00000000..0d5f6f5c --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/gitignore.tt @@ -0,0 +1,21 @@ +/.bundle/ +/doc/ +/log/*.log +/pkg/ +/tmp/ +<% if with_dummy_app? -%> +<% if sqlite3? -%> +/<%= dummy_path %>/db/*.sqlite3 +/<%= dummy_path %>/db/*.sqlite3-* +<% end -%> +/<%= dummy_path %>/log/*.log +<% unless options[:skip_javascript] -%> +/<%= dummy_path %>/node_modules/ +/<%= dummy_path %>/yarn-error.log +<% end -%> +<% unless skip_active_storage? -%> +/<%= dummy_path %>/storage/ +<% end -%> +/<%= dummy_path %>/tmp/ +<% end -%> +.byebug_history diff --git a/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%.rb.tt b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%.rb.tt new file mode 100644 index 00000000..af240933 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%.rb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% if engine? -%> +require "<%= namespaced_name %>/engine" +<% else -%> +require "<%= namespaced_name %>/railtie" +<% end -%> + +<%= wrap_in_modules "# Your code goes here..." %> diff --git a/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt new file mode 100644 index 00000000..da610519 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/engine.rb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class Engine < ::Rails::Engine + #{mountable? ? ' isolate_namespace ' + camelized_modules : ' '} + #{api? ? " config.generators.api_only = true" : ' '} + end +rb +%> diff --git a/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt new file mode 100644 index 00000000..fe7d69e1 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/railtie.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules <<~rb + class Railtie < ::Rails::Railtie + end +rb +%> diff --git a/lib/generators/api/entity/templates/entity.rb.erb b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt similarity index 55% rename from lib/generators/api/entity/templates/entity.rb.erb rename to lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt index 64f86fee..de987942 100644 --- a/lib/generators/api/entity/templates/entity.rb.erb +++ b/lib/generators/overrides/rails/plugin/templates/lib/%namespaced_name%/version.rb.tt @@ -1,4 +1,4 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -class Houdini::V1::Entities::<%= name.camelcase %> < Grape::Entity +# frozen_string_literal: true -end \ No newline at end of file +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<%= wrap_in_modules "VERSION = '0.1.0'" %> diff --git a/lib/generators/overrides/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt b/lib/generators/overrides/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt new file mode 100644 index 00000000..89f2b829 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/lib/tasks/%namespaced_name%_tasks.rake.tt @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# desc "Explaining what the task does" +# task :<%= underscored_name %> do +# # Task goes here +# end diff --git a/lib/generators/overrides/rails/plugin/templates/rails/application.rb.tt b/lib/generators/overrides/rails/plugin/templates/rails/application.rb.tt new file mode 100644 index 00000000..95eb2f6f --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/application.rb.tt @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require_relative "boot" + +<% if include_all_railties? -%> +require "rails/all" +<% else -%> +require "rails" +# Pick the frameworks you want: +require "active_model/railtie" +require "active_job/railtie" +<%= comment_if :skip_active_record %>require "active_record/railtie" +<%= comment_if :skip_active_storage %>require "active_storage/engine" +require "action_controller/railtie" +<%= comment_if :skip_action_mailer %>require "action_mailer/railtie" +require "action_view/railtie" +<%= comment_if :skip_action_cable %>require "action_cable/engine" +<%= comment_if :skip_sprockets %>require "sprockets/railtie" +<%= comment_if :skip_test %>require "rails/test_unit/railtie" +<% end -%> + +Bundler.require(*Rails.groups) +require "<%= namespaced_name %>" + +<%= application_definition %> diff --git a/lib/generators/overrides/rails/plugin/templates/rails/boot.rb.tt b/lib/generators/overrides/rails/plugin/templates/rails/boot.rb.tt new file mode 100644 index 00000000..35d05c6d --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/boot.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# Set up gems listed in the Gemfile. +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__) + +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) +$LOAD_PATH.unshift File.expand_path('../../../lib', __dir__) diff --git a/lib/generators/overrides/rails/plugin/templates/rails/dummy_manifest.js.tt b/lib/generators/overrides/rails/plugin/templates/rails/dummy_manifest.js.tt new file mode 100644 index 00000000..03937cf8 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/dummy_manifest.js.tt @@ -0,0 +1,10 @@ +<% unless api? -%> +//= link_tree ../images +<% end -%> +<% unless options.skip_javascript -%> +//= link_directory ../javascripts .js +<% end -%> +//= link_directory ../stylesheets .css +<% if mountable? && !api? -%> +//= link <%= underscored_name %>_manifest.js +<% end -%> diff --git a/lib/generators/overrides/rails/plugin/templates/rails/engine_manifest.js.tt b/lib/generators/overrides/rails/plugin/templates/rails/engine_manifest.js.tt new file mode 100644 index 00000000..2f23844f --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/engine_manifest.js.tt @@ -0,0 +1,6 @@ +<% if mountable? -%> +<% if !options.skip_javascript -%> +//= link_directory ../javascripts/<%= namespaced_name %> .js +<% end -%> +//= link_directory ../stylesheets/<%= namespaced_name %> .css +<% end -%> diff --git a/lib/generators/overrides/rails/plugin/templates/rails/javascripts.js.tt b/lib/generators/overrides/rails/plugin/templates/rails/javascripts.js.tt new file mode 100644 index 00000000..51049826 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/javascripts.js.tt @@ -0,0 +1,17 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require rails-ujs +<% unless skip_active_storage? -%> +//= require activestorage +<% end -%> +//= require_tree . diff --git a/lib/generators/overrides/rails/plugin/templates/rails/routes.rb.tt b/lib/generators/overrides/rails/plugin/templates/rails/routes.rb.tt new file mode 100644 index 00000000..694510ed --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/routes.rb.tt @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + mount <%= camelized_modules %>::Engine => "/<%= name %>" +end diff --git a/lib/generators/overrides/rails/plugin/templates/rails/stylesheets.css b/lib/generators/overrides/rails/plugin/templates/rails/stylesheets.css new file mode 100644 index 00000000..0ebd7fe8 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/rails/stylesheets.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/lib/generators/overrides/rails/plugin/templates/test/%namespaced_name%_test.rb.tt b/lib/generators/overrides/rails/plugin/templates/test/%namespaced_name%_test.rb.tt new file mode 100644 index 00000000..d2317af7 --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/test/%namespaced_name%_test.rb.tt @@ -0,0 +1,7 @@ +require "test_helper" + +class <%= camelized_modules %>::Test < ActiveSupport::TestCase + test "truth" do + assert_kind_of Module, <%= camelized_modules %> + end +end diff --git a/lib/generators/overrides/rails/plugin/templates/test/application_system_test_case.rb.tt b/lib/generators/overrides/rails/plugin/templates/test/application_system_test_case.rb.tt new file mode 100644 index 00000000..d19212ab --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/test/application_system_test_case.rb.tt @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/lib/generators/overrides/rails/plugin/templates/test/integration/navigation_test.rb.tt b/lib/generators/overrides/rails/plugin/templates/test/integration/navigation_test.rb.tt new file mode 100644 index 00000000..ebbc098a --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/test/integration/navigation_test.rb.tt @@ -0,0 +1,7 @@ +require "test_helper" + +class NavigationTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/lib/generators/overrides/rails/plugin/templates/test/test_helper.rb.tt b/lib/generators/overrides/rails/plugin/templates/test/test_helper.rb.tt new file mode 100644 index 00000000..4f7a8d3d --- /dev/null +++ b/lib/generators/overrides/rails/plugin/templates/test/test_helper.rb.tt @@ -0,0 +1,29 @@ +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require_relative "<%= File.join('..', options[:dummy_path], 'config/environment') -%>" +<% unless options[:skip_active_record] -%> +ActiveRecord::Migrator.migrations_paths = [File.expand_path("../<%= options[:dummy_path] -%>/db/migrate", __dir__)] +<% if options[:mountable] -%> +ActiveRecord::Migrator.migrations_paths << File.expand_path('../db/migrate', __dir__) +<% end -%> +<% end -%> +require "rails/test_help" + +# Filter out the backtrace from minitest while preserving the one from other libraries. +Minitest.backtrace_filter = Minitest::BacktraceFilter.new + +<% unless engine? -%> +require "rails/test_unit/reporter" +Rails::TestUnitReporter.executable = 'bin/test' +<% end -%> + +<% unless options[:skip_active_record] -%> +# Load fixtures from the engine +if ActiveSupport::TestCase.respond_to?(:fixture_path=) + ActiveSupport::TestCase.fixture_path = File.expand_path("fixtures", __dir__) + ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path + ActiveSupport::TestCase.file_fixture_path = ActiveSupport::TestCase.fixture_path + "/files" + ActiveSupport::TestCase.fixtures :all +end +<% end -%> diff --git a/lib/generators/react/component/component_generator.rb b/lib/generators/react/component/component_generator.rb index 96b6b92e..fa2ae5a4 100644 --- a/lib/generators/react/component/component_generator.rb +++ b/lib/generators/react/component/component_generator.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class React::ComponentGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) + source_root File.expand_path('templates', __dir__) def copy_file_to_component - template 'component.tsx.erb', File.join("javascripts/src/components", *(class_path + ["#{file_name.camelize}.tsx"])) - template 'component.spec.tsx.erb', File.join("javascripts/src/components", *(class_path + ["#{file_name.camelize}.spec.tsx"])) + template 'component.tsx.erb', File.join('javascripts/src/components', *(class_path + ["#{file_name.camelize}.tsx"])) + template 'component.spec.tsx.erb', File.join('javascripts/src/components', *(class_path + ["#{file_name.camelize}.spec.tsx"])) end end diff --git a/lib/generators/react/lib/lib_generator.rb b/lib/generators/react/lib/lib_generator.rb index 1f07f34e..f40bcb67 100644 --- a/lib/generators/react/lib/lib_generator.rb +++ b/lib/generators/react/lib/lib_generator.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + class React::LibGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) + source_root File.expand_path('templates', __dir__) def copy_file_to_lib - template 'module.ts.erb', File.join("javascripts/src/lib/", *(class_path + ["#{file_name.underscore}.ts"])) - template 'module.spec.ts.erb', File.join("javascripts/src/lib/", *(class_path + ["#{file_name.underscore}.spec.ts"])) + template 'module.ts.erb', File.join('javascripts/src/lib/', *(class_path + ["#{file_name.underscore}.ts"])) + template 'module.spec.ts.erb', File.join('javascripts/src/lib/', *(class_path + ["#{file_name.underscore}.spec.ts"])) end end diff --git a/lib/generators/react/packroot/packroot_generator.rb b/lib/generators/react/packroot/packroot_generator.rb index a684165a..06354227 100644 --- a/lib/generators/react/packroot/packroot_generator.rb +++ b/lib/generators/react/packroot/packroot_generator.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module React class PackrootGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) + source_root File.expand_path('templates', __dir__) def copy_file_to_app template 'page.tsx.erb', "javascripts/app/#{file_name.underscore}.tsx" generate 'react:component', "#{file_name.underscore}/#{file_name.camelize}" end end end - diff --git a/lib/generators/ts/declaration/declaration_generator.rb b/lib/generators/ts/declaration/declaration_generator.rb index 3c1a9e09..d7bbc609 100644 --- a/lib/generators/ts/declaration/declaration_generator.rb +++ b/lib/generators/ts/declaration/declaration_generator.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Ts::DeclarationGenerator < Rails::Generators::NamedBase - source_root File.expand_path('../templates', __FILE__) + source_root File.expand_path('templates', __dir__) def copy_template - template 'template.d.ts.erb', File.join("types", name, 'index.d.ts') + template 'template.d.ts.erb', File.join('types', name, 'index.d.ts') end end diff --git a/lib/geocode_model.rb b/lib/geocode_model.rb index cf802e54..2d4903c6 100644 --- a/lib/geocode_model.rb +++ b/lib/geocode_model.rb @@ -1,47 +1,46 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module GeocodeModel - def self.supporter(id) supp = Supporter.find_by_id(id) - if supp.address && supp.state_code && supp.city - with_reverse(supp) - end + with_reverse(supp) if supp.address && supp.state_code && supp.city end - # Just a wrapper around a model's geocode method for delaying with: - # GeocodeModel.delay.geocode(user) - def self.geocode(model) - begin - model.geocode - rescue Exception => e - puts e - end - model.save - model - end + # Just a wrapper around a model's geocode method for delaying with: + # GeocodeModel.delay.geocode(user) + def self.geocode(model) + begin + model.geocode + rescue Exception => e + puts e + end + model.save + model + end - def self.with_reverse(model) - begin - model.geocode - model.reverse_geocode - rescue Exception => e - puts e - end - model.save - model - end + def self.with_reverse(model) + begin + model.geocode + model.reverse_geocode + rescue Exception => e + puts e + end + model.save + model + end - # Geocode and get the timezone for a model - def self.with_timezone(model) - begin - geocode(model) - rescue Exception => e - puts e - end - return model unless model.latitude && model.longitude + # Geocode and get the timezone for a model + def self.with_timezone(model) + begin + geocode(model) + rescue Exception => e + puts e + end + return model unless model.latitude && model.longitude - model.timezone = NearestTimeZone.to(model.latitude, model.longitude) - model.save - model - end + model.timezone = NearestTimeZone.to(model.latitude, model.longitude) + model.save + model + end end diff --git a/lib/get_data.rb b/lib/get_data.rb index 295fc19f..71ab6957 100644 --- a/lib/get_data.rb +++ b/lib/get_data.rb @@ -1,33 +1,33 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module GetData + def self.chain(obj, *methods) + methods.each do |m| + if m.is_a?(Array) + params = m[1..-1] + m = m[0] + end - def self.chain(obj, *methods) - methods.each do |m| - if m.is_a?(Array) - params = m[1..-1] - m = m[0] - end - - if obj != nil && obj.respond_to?(m) - obj = obj.send(m, *params) - elsif obj.respond_to?(:has_key?) && obj.has_key?(m) - obj = obj[m] - else - return nil - end - end - return obj - end + if !obj.nil? && obj.respond_to?(m) + obj = obj.send(m, *params) + elsif obj.respond_to?(:has_key?) && obj.key?(m) + obj = obj[m] + else + return nil + end + end + obj + end def self.hash(h, *keys) - keys.each do |k| - if h.has_key?(k) - h = h[k] - else - return nil - end - end - return h + keys.each do |k| + if h.key?(k) + h = h[k] + else + return nil + end + end + h end end - diff --git a/lib/hash.rb b/lib/hash.rb index d12f3b70..8c00bc76 100644 --- a/lib/hash.rb +++ b/lib/hash.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Hash def keep_keys(*keys) - keys = keys.map{|k| k.to_s} - clone.delete_if{|k,v| !keys.include?(k.to_s)} + keys = keys.map(&:to_s) + clone.delete_if { |k, _v| !keys.include?(k.to_s) } end def keep_keys!(*keys) - keys = keys.map{|k| k.to_s} - delete_if{|k,v| !keys.include?(k.to_s)} + keys = keys.map(&:to_s) + delete_if { |k, _v| !keys.include?(k.to_s) } end end diff --git a/lib/health_report.rb b/lib/health_report.rb index 53e4130c..e93ca417 100644 --- a/lib/health_report.rb +++ b/lib/health_report.rb @@ -1,34 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' require 'format/csv' require 'format/currency' - module HealthReport # Send an email report about what has happend on the servers and database in the last 24hrs, and how things are running # Returns a hash of metrics data def self.query_data # Transaction metrics - charges = Qx.select("COUNT(charges.id), SUM(charges.amount), SUM(charges.fee) as fees") - .from("charges") - .where("created_at > $d", d: 24.hours.ago) - .and_where("charges.status != 'failed'") - .ex.last + charges = Qx.select('COUNT(charges.id), SUM(charges.amount), SUM(charges.fee) as fees') + .from('charges') + .where('created_at > $d', d: 24.hours.ago) + .and_where("charges.status != 'failed'") + .ex.last # Recurring donation metrics - rec_dons = Qx.select("COUNT(id), SUM(amount)") - .from("recurring_donations") - .where("active=TRUE") - .ex.last + rec_dons = Qx.select('COUNT(id), SUM(amount)') + .from('recurring_donations') + .where('active=TRUE') + .ex.last # Info about disabled nonprofit accounts due to ident verification - disabled_nps = Qx.select("nonprofits.id", "nonprofits.name", "nonprofits.stripe_account_id") - .from("nonprofits") - .where("verification_status != 'verified'") - .and_where("created_at > $d", d: 3.months.ago) - .ex(format: 'csv') + disabled_nps = Qx.select('nonprofits.id', 'nonprofits.name', 'nonprofits.stripe_account_id') + .from('nonprofits') + .where("verification_status != 'verified'") + .and_where('created_at > $d', d: 3.months.ago) + .ex(format: 'csv') - return { + { charges_count: charges['count'], charges_sum: charges['sum'], charges_fees: charges['fees'], @@ -39,10 +40,10 @@ module HealthReport end # Given a hash of data, formats it into a multi-line string - def self.format_data data + def self.format_data(data) disabled_nps = Format::Csv.from_array(data[:recently_disabled_nps]) - return %Q( + %( Transaction Metrics for the last 24hrs: Total count: #{data[:charges_count]} Total amount: $#{Format::Currency.cents_to_dollars(data[:charges_sum])} diff --git a/lib/htp.rb b/lib/htp.rb index 7385bbb0..ee28d9ee 100644 --- a/lib/htp.rb +++ b/lib/htp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Hamster Table Print diff --git a/lib/image.rb b/lib/image.rb index 9e7a8e65..c25bcc72 100644 --- a/lib/image.rb +++ b/lib/image.rb @@ -1,19 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module Image + AssetPath = 'https://dmnsmycmdpaix.cloudfront.net/uploads' - AssetPath = "https://dmnsmycmdpaix.cloudfront.net/uploads" + DefaultProfileUrl = Settings.default.image.profile + DefaultNonprofitUrl = Settings.default.image.nonprofit + DefaultCampaignUrl = Settings.default.image.campaign - DefaultProfileUrl = Settings.default.image.profile; - DefaultNonprofitUrl = Settings.default.image.nonprofit; - DefaultCampaignUrl = Settings.default.image.campaign; - - def self._url(resource_name, image_name, version='normal') - %Q( + def self._url(resource_name, image_name, version = 'normal') + %( concat(#{Qexpr.quote AssetPath} , '/', #{Qexpr.quote resource_name} , '/', #{Qexpr.quote image_name} , '/', #{resource_name + '.id'} , '/', #{Qexpr.quote version}, '_', #{resource_name + '.' + image_name}) - ) + ) end end diff --git a/lib/import/import_civicrm_payments.rb b/lib/import/import_civicrm_payments.rb index 71bac5ff..257c74e9 100644 --- a/lib/import/import_civicrm_payments.rb +++ b/lib/import/import_civicrm_payments.rb @@ -1,93 +1,81 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ImportCivicrmPayments - ## MINIMALLY TESTED!!! def self.import_from_csv(csv_body, nonprofit, field_of_supporter_id) - Qx.transaction do - CSV::Converters[:blank_to_nil] = lambda do |field| - field && field.empty? ? nil : field + Qx.transaction do + CSV::Converters[:blank_to_nil] = lambda do |field| + field && field.empty? ? nil : field + end + + csv = CSV.new(csv_body, headers: true, converters: [:blank_to_nil]) + contrib_records = csv.to_a.map(&:to_hash) + pay_imp = PaymentImport.create(nonprofit: nonprofit) + + supporter_id_custom_field = CustomFieldMaster.where('nonprofit_id = ? AND name = ?', nonprofit.id, field_of_supporter_id).first + + unless supporter_id_custom_field + raise ParamValidation::ValidationError.new("There is no custom field for nonprofit #{nonprofit.id} and field named #{field_of_supporter_id}", key: :field_of_supporter_id) + end + + supporters_with_fields = Supporter.includes(:custom_field_joins).where('supporters.nonprofit_id = ? AND custom_field_joins.custom_field_master_id = ?', nonprofit.id, supporter_id_custom_field.id) + questionable_records = [] + contrib_records.each do |r| + our_supporter = supporters_with_fields.where('custom_field_joins.value = ?', r[field_of_supporter_id].to_s).first + unless our_supporter + questionable_records.push(r) + next + end + + known_fields = ['Date Received', 'Total Amount'] + + notes = '' + r.except(known_fields).keys.each do |k| + notes += "#{k}: #{r[k]}\n" + end + + offsite = nil + if r['payment_instrument'] == 'Check' + offsite = { kind: 'check', check_number: r['Check Number'] } + end + + puts r['Date Received'] + date_received = nil + + Time.use_zone('Pacific Time (US & Canada)') do + date_received = Time.zone.parse(r['Date Received']) + puts date_received + end + + d = InsertDonation.offsite( + { + amount: Format::Currency.dollars_to_cents(r['Total Amount']), + nonprofit_id: nonprofit.id, + supporter_id: our_supporter.id, + comment: notes, + date: date_received.to_s, + offsite_payment: offsite + }.with_indifferent_access + ) + puts d + pay_imp.donations.push(Donation.find(d[:json]['donation']['id'])) + end + questionable_records end - - csv = CSV.new(csv_body, :headers => true, :converters => [ :blank_to_nil]) - contrib_records = csv.to_a.map {|row| row.to_hash } - pay_imp = PaymentImport.create(nonprofit: nonprofit) - - supporter_id_custom_field = CustomFieldMaster.where("nonprofit_id = ? AND name = ?", nonprofit.id, field_of_supporter_id).first - - unless supporter_id_custom_field - raise ParamValidation::ValidationError.new("There is no custom field for nonprofit #{nonprofit.id} and field named #{field_of_supporter_id}", {key: :field_of_supporter_id}) - end - - supporters_with_fields = Supporter.includes(:custom_field_joins).where("supporters.nonprofit_id = ? AND custom_field_joins.custom_field_master_id = ?", nonprofit.id, supporter_id_custom_field.id) - questionable_records = [] - contrib_records.each {|r| - - our_supporter = supporters_with_fields.where('custom_field_joins.value = ?', r[field_of_supporter_id].to_s).first - unless our_supporter - questionable_records.push(r) - next - end - - known_fields = ['Date Received', 'Total Amount', ] - - notes = "" - r.except(known_fields).keys.each{|k| - notes += "#{k}: #{r[k]}\n" - } - - - offsite = nil - if r['payment_instrument'] == 'Check' - offsite = {kind: 'check', check_number: r['Check Number']} - end - - puts r['Date Received'] - date_received = nil - - - Time.use_zone('Pacific Time (US & Canada)') do - date_received = Time.zone.parse(r['Date Received']) - puts date_received - end - - - - - d = InsertDonation.offsite( - { - amount: Format::Currency.dollars_to_cents(r['Total Amount']), - nonprofit_id: nonprofit.id, - supporter_id: our_supporter.id, - comment: notes, - date: date_received.to_s, - offsite_payment: offsite - }.with_indifferent_access - ) - puts d - pay_imp.donations.push(Donation.find(d[:json]['donation']['id'])) - } - questionable_records - end end def self.undo(import_id) Qx.transaction do - import = PaymentImport.find(import_id) - import.donations.each{|d| + import = PaymentImport.find(import_id) + import.donations.each do |d| + d.payments.each(&:destroy) + d.offsite_payment&.destroy - d.payments.each{|p| - p.destroy - } - if d.offsite_payment - d.offsite_payment.destroy + d.destroy end - - d.destroy - - } - - import.destroy + import.destroy end end -end \ No newline at end of file +end diff --git a/lib/include_asset.rb b/lib/include_asset.rb index cd10bdb3..924946c1 100644 --- a/lib/include_asset.rb +++ b/lib/include_asset.rb @@ -1,18 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module IncludeAsset - # These are custom asset include functions for use in views that cache-bust using the current git version def self.js(path) - %Q().html_safe + %().html_safe end def self.css(path) - %Q().html_safe + %().html_safe end -private - + private + def self.asset_version ENV['ASSET_VERSION'] end diff --git a/lib/insert/insert_activities.rb b/lib/insert/insert_activities.rb index 7fe6aa18..5bfc572d 100644 --- a/lib/insert/insert_activities.rb +++ b/lib/insert/insert_activities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' require 'active_support/core_ext' @@ -5,9 +7,8 @@ require 'format/currency' require 'format/date' module InsertActivities - def self.insert_cols - ["action_type", "public", "created_at", "updated_at", "supporter_id", "attachment_type", "attachment_id", "nonprofit_id", "date", "json_data", "kind"] + %w[action_type public created_at updated_at supporter_id attachment_type attachment_id nonprofit_id date json_data kind] end # These line up with the above columns @@ -26,188 +27,186 @@ module InsertActivities def self.for_recurring_donations(payment_ids) insert_recurring_donations_expr - .and_where("payments.id IN ($ids)", ids: payment_ids) + .and_where('payments.id IN ($ids)', ids: payment_ids) .execute end def self.insert_recurring_donations_expr Qx.insert_into(:activities, insert_cols) .select(defaults.concat([ - "payments.supporter_id", - "'Payment' AS attachment_type", - "payments.id AS attachment_id", - "payments.nonprofit_id", - "payments.date", - "json_build_object('gross_amount', payments.gross_amount, 'start_date', donations.created_at, 'designation', donations.designation, 'dedication', donations.dedication, 'interval', recurring_donations.interval, 'time_unit', recurring_donations.time_unit)", - "'RecurringDonation' AS kind" - ])) + 'payments.supporter_id', + "'Payment' AS attachment_type", + 'payments.id AS attachment_id', + 'payments.nonprofit_id', + 'payments.date', + "json_build_object('gross_amount', payments.gross_amount, 'start_date', donations.created_at, 'designation', donations.designation, 'dedication', donations.dedication, 'interval', recurring_donations.interval, 'time_unit', recurring_donations.time_unit)", + "'RecurringDonation' AS kind" + ])) .from(:payments) - .join(:donations, "donations.id=payments.donation_id") - .add_join(:recurring_donations, "recurring_donations.donation_id=donations.id") + .join(:donations, 'donations.id=payments.donation_id') + .add_join(:recurring_donations, 'recurring_donations.donation_id=donations.id') .where("payments.kind='RecurringDonation'") end def self.for_one_time_donations(payment_ids) insert_one_time_donations_expr - .and_where("payments.id IN ($ids)", ids: payment_ids) + .and_where('payments.id IN ($ids)', ids: payment_ids) .execute end def self.insert_one_time_donations_expr Qx.insert_into(:activities, insert_cols) .select(defaults.concat([ - "payments.supporter_id", - "'Payment' AS attachment_type", - "payments.id AS attachment_id", - "payments.nonprofit_id", - "payments.date", - "json_build_object('gross_amount', payments.gross_amount, 'designation', donations.designation, 'dedication', donations.dedication)", - "'Donation' AS kind" - ])) + 'payments.supporter_id', + "'Payment' AS attachment_type", + 'payments.id AS attachment_id', + 'payments.nonprofit_id', + 'payments.date', + "json_build_object('gross_amount', payments.gross_amount, 'designation', donations.designation, 'dedication', donations.dedication)", + "'Donation' AS kind" + ])) .from(:payments) - .join(:donations, "donations.id=payments.donation_id") + .join(:donations, 'donations.id=payments.donation_id') .where("payments.kind='Donation'") end - + def self.for_tickets(ticket_ids) insert_tickets_expr - .and_where("tickets.id IN ($ids)", ids: ticket_ids) + .and_where('tickets.id IN ($ids)', ids: ticket_ids) .execute end def self.insert_tickets_expr Qx.insert_into(:activities, insert_cols) - .select(defaults.concat([ - "tickets.supporter_id", - "'Ticket' AS attachment_type", - "tickets.id AS attachment_id", - "event.nonprofit_id", - "tickets.created_at AS date", - "json_build_object('gross_amount', coalesce(payment.gross_amount, 0), 'event_name', event.name, 'event_id', event.id, 'quantity', tickets.quantity)", - "'Ticket' AS kind" - ])) + .select(defaults.concat([ + 'tickets.supporter_id', + "'Ticket' AS attachment_type", + 'tickets.id AS attachment_id', + 'event.nonprofit_id', + 'tickets.created_at AS date', + "json_build_object('gross_amount', coalesce(payment.gross_amount, 0), 'event_name', event.name, 'event_id', event.id, 'quantity', tickets.quantity)", + "'Ticket' AS kind" + ])) .from(:tickets) - .join("payments AS payment", "payment.id=tickets.payment_id") - .add_join("events AS event", "event.id=tickets.event_id") + .join('payments AS payment', 'payment.id=tickets.payment_id') + .add_join('events AS event', 'event.id=tickets.event_id') end def self.for_refunds(payment_ids) insert_refunds_expr - .and_where("payments.id IN ($ids)", ids: payment_ids) + .and_where('payments.id IN ($ids)', ids: payment_ids) .execute end def self.insert_refunds_expr - Qx.insert_into(:activities, insert_cols.concat(["user_id"])) + Qx.insert_into(:activities, insert_cols.concat(['user_id'])) .select(defaults.concat([ - "payments.supporter_id", - "'Payment' AS attachment_type", - "payments.id AS attachment_id", - "payments.nonprofit_id", - "payments.date", - "json_build_object('gross_amount', payments.gross_amount, 'reason', refunds.reason, 'user_email', users.email)", - "'Refund' AS kind", - "users.id AS user_id" - ])) + 'payments.supporter_id', + "'Payment' AS attachment_type", + 'payments.id AS attachment_id', + 'payments.nonprofit_id', + 'payments.date', + "json_build_object('gross_amount', payments.gross_amount, 'reason', refunds.reason, 'user_email', users.email)", + "'Refund' AS kind", + 'users.id AS user_id' + ])) .from(:payments) - .join(:refunds, "refunds.payment_id=payments.id") - .left_join(:users, "refunds.user_id=users.id") + .join(:refunds, 'refunds.payment_id=payments.id') + .left_join(:users, 'refunds.user_id=users.id') .where("payments.kind='Refund'") end def self.for_disputes(payment_ids) insert_disputes_expr - .and_where("payments.id IN ($ids)", ids: payment_ids) + .and_where('payments.id IN ($ids)', ids: payment_ids) .execute end def self.insert_disputes_expr Qx.insert_into(:activities, insert_cols) .select(defaults.concat([ - "payments.supporter_id", - "'Payment' AS attachment_type", - "payments.id AS attachment_id", - "payments.nonprofit_id", - "payments.date", - "json_build_object('gross_amount', payments.gross_amount, 'reason', disputes.reason, 'original_kind', other_payment.kind, 'original_date', other_payment.date)", - "'Dispute' AS kind" - ])) + 'payments.supporter_id', + "'Payment' AS attachment_type", + 'payments.id AS attachment_id', + 'payments.nonprofit_id', + 'payments.date', + "json_build_object('gross_amount', payments.gross_amount, 'reason', disputes.reason, 'original_kind', other_payment.kind, 'original_date', other_payment.date)", + "'Dispute' AS kind" + ])) .from(:payments) - .join(:disputes, "disputes.payment_id=payments.id") - .add_join(:charges, "disputes.charge_id=charges.id") - .add_join("payments AS other_payment", "other_payment.id=charges.payment_id") + .join(:disputes, 'disputes.payment_id=payments.id') + .add_join(:charges, 'disputes.charge_id=charges.id') + .add_join('payments AS other_payment', 'other_payment.id=charges.payment_id') .where("payments.kind='Dispute'") end def self.for_supporter_emails(ids) insert_supporter_emails_expr - .and_where("supporter_emails.id IN ($ids)", ids: ids) + .and_where('supporter_emails.id IN ($ids)', ids: ids) .execute end def self.insert_supporter_emails_expr - Qx.insert_into(:activities, insert_cols.concat(["user_id"])) - .select(defaults.concat([ - "supporter_emails.supporter_id", - "'SupporterEmail' AS attachment_type", - "supporter_emails.id AS attachment_id", - "supporter_emails.nonprofit_id", - "supporter_emails.created_at AS date", - "json_build_object('gmail_thread_id', supporter_emails.gmail_thread_id, 'subject', supporter_emails.subject, 'from', supporter_emails.from)", - "'SupporterEmail' AS kind", - "users.id AS user_id" - ])) + Qx.insert_into(:activities, insert_cols.concat(['user_id'])) + .select(defaults.concat([ + 'supporter_emails.supporter_id', + "'SupporterEmail' AS attachment_type", + 'supporter_emails.id AS attachment_id', + 'supporter_emails.nonprofit_id', + 'supporter_emails.created_at AS date', + "json_build_object('gmail_thread_id', supporter_emails.gmail_thread_id, 'subject', supporter_emails.subject, 'from', supporter_emails.from)", + "'SupporterEmail' AS kind", + 'users.id AS user_id' + ])) .from(:supporter_emails) - .left_join(:users, "users.id=supporter_emails.user_id") + .left_join(:users, 'users.id=supporter_emails.user_id') end def self.for_supporter_notes(ids) insert_supporter_notes_expr - .and_where("supporter_notes.id IN ($ids)", ids: ids) + .and_where('supporter_notes.id IN ($ids)', ids: ids) .execute end def self.insert_supporter_notes_expr - Qx.insert_into(:activities, insert_cols.concat(["user_id"])) - .select(defaults.concat([ - "supporter_notes.supporter_id", - "'SupporterEmail' AS attachment_type", - "supporter_notes.id AS attachment_id", - "supporters.nonprofit_id", - "supporter_notes.created_at AS date", - "json_build_object('content', supporter_notes.content, 'user_email', users.email)", - "'SupporterNote' AS kind", - "users.id AS user_id" - ])) + Qx.insert_into(:activities, insert_cols.concat(['user_id'])) + .select(defaults.concat([ + 'supporter_notes.supporter_id', + "'SupporterEmail' AS attachment_type", + 'supporter_notes.id AS attachment_id', + 'supporters.nonprofit_id', + 'supporter_notes.created_at AS date', + "json_build_object('content', supporter_notes.content, 'user_email', users.email)", + "'SupporterNote' AS kind", + 'users.id AS user_id' + ])) .from(:supporter_notes) - .join("supporters", "supporters.id=supporter_notes.supporter_id") - .add_left_join(:users, "users.id=supporter_notes.user_id") + .join('supporters', 'supporters.id=supporter_notes.supporter_id') + .add_left_join(:users, 'users.id=supporter_notes.user_id') end def self.for_offsite_donations(payment_ids) insert_offsite_donations_expr - .and_where("payments.id IN ($ids)", ids: payment_ids) + .and_where('payments.id IN ($ids)', ids: payment_ids) .execute end def self.insert_offsite_donations_expr - Qx.insert_into(:activities, insert_cols.concat(["user_id"])) - .select(defaults.concat([ - "payments.supporter_id", - "'Payment' AS attachment_type", - "payments.id AS attachment_id", - "payments.nonprofit_id", - "payments.date", - "json_build_object('gross_amount', payments.gross_amount, 'designation', donations.designation, 'user_email', users.email)", - "'OffsitePayment' AS kind", - "users.id AS user_id" - ])) + Qx.insert_into(:activities, insert_cols.concat(['user_id'])) + .select(defaults.concat([ + 'payments.supporter_id', + "'Payment' AS attachment_type", + 'payments.id AS attachment_id', + 'payments.nonprofit_id', + 'payments.date', + "json_build_object('gross_amount', payments.gross_amount, 'designation', donations.designation, 'user_email', users.email)", + "'OffsitePayment' AS kind", + 'users.id AS user_id' + ])) .from(:payments) .where("payments.kind = 'OffsitePayment'") - .join(:offsite_payments, "offsite_payments.payment_id=payments.id") - .add_join(:donations, "payments.donation_id=donations.id") - .add_left_join(:users, "users.id=offsite_payments.user_id") + .join(:offsite_payments, 'offsite_payments.payment_id=payments.id') + .add_join(:donations, 'payments.donation_id=donations.id') + .add_left_join(:users, 'users.id=offsite_payments.user_id') end - - end diff --git a/lib/insert/insert_bank_account.rb b/lib/insert/insert_bank_account.rb index c96f1b70..44d5923a 100644 --- a/lib/insert/insert_bank_account.rb +++ b/lib/insert/insert_bank_account.rb @@ -1,67 +1,63 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertBankAccount - # @param [Nonprofit] nonprofit # # stripe_bank_account_token: data.stripe_resp.id, - #stripe_bank_account_id: data.stripe_resp.bank_account.id, - # name: data.stripe_resp.bank_account.bank_name + ' *' + data.stripe_resp.bank_account.last4, - # email: app.user.email + # stripe_bank_account_id: data.stripe_resp.bank_account.id, + # name: data.stripe_resp.bank_account.bank_name + ' *' + data.stripe_resp.bank_account.last4, + # email: app.user.email def self.with_stripe(nonprofit, user, params) - ParamValidation.new({nonprofit: nonprofit, user: user}, { - :nonprofit => { - :required => true, - :is_a => Nonprofit - }, - :user => { - :required => true, - :is_a => User - } - }) - ParamValidation.new(params|| {}, { - :stripe_bank_account_token => { - :required => true, - :not_blank => true - } - }) + ParamValidation.new({ nonprofit: nonprofit, user: user }, + nonprofit: { + required: true, + is_a: Nonprofit + }, + user: { + required: true, + is_a: User + }) + ParamValidation.new(params || {}, + stripe_bank_account_token: { + required: true, + not_blank: true + }) - unless (nonprofit.vetted) - raise ArgumentError.new "#{nonprofit.id} is not vetted." + unless nonprofit.vetted + raise ArgumentError, "#{nonprofit.id} is not vetted." end stripe_acct = Stripe::Account.retrieve(StripeAccount.find_or_create(nonprofit.id)) nonprofit.reload - #this shouldn't be possible but we'll check any who - if (nonprofit.stripe_account_id.blank?) - raise ArgumentError.new "#{nonprofit.id} does not have a valid stripe_account_id associated with it" + # this shouldn't be possible but we'll check any who + if nonprofit.stripe_account_id.blank? + raise ArgumentError, "#{nonprofit.id} does not have a valid stripe_account_id associated with it" end Qx.transaction do - begin - ba = stripe_acct.external_accounts.create(external_account: params[:stripe_bank_account_token]) - ba.default_for_currency = true - ba.save + ba = stripe_acct.external_accounts.create(external_account: params[:stripe_bank_account_token]) + ba.default_for_currency = true + ba.save - BankAccount.where('nonprofit_id = ?', nonprofit.id).update_all(deleted: true) + BankAccount.where('nonprofit_id = ?', nonprofit.id).update_all(deleted: true) - bank_account = BankAccount.create( - stripe_bank_account_id: ba.id, - stripe_bank_account_token: params[:stripe_bank_account_token], - confirmation_token: SecureRandom.uuid, - nonprofit: nonprofit, - name: params[:name] || "Bank #{SecureRandom.uuid}", - email: user.email, - pending_verification: true - ) + bank_account = BankAccount.create( + stripe_bank_account_id: ba.id, + stripe_bank_account_token: params[:stripe_bank_account_token], + confirmation_token: SecureRandom.uuid, + nonprofit: nonprofit, + name: params[:name] || "Bank #{SecureRandom.uuid}", + email: user.email, + pending_verification: true + ) - NonprofitMailer.delay.new_bank_account_notification(bank_account) - return bank_account - rescue Stripe::StripeError => error - params[:failure_message] = "Failed to connect the bank account: #{error.inspect}" - raise ArgumentError.new("Failed to connect the bank account: #{error.inspect}") - end + BankAccountCreateJob.perform_later(bank_account) + return bank_account + rescue Stripe::StripeError => error + params[:failure_message] = "Failed to connect the bank account: #{error.inspect}" + raise ArgumentError, "Failed to connect the bank account: #{error.inspect}" end - end -end \ No newline at end of file +end diff --git a/lib/insert/insert_billing_subscriptions.rb b/lib/insert/insert_billing_subscriptions.rb deleted file mode 100644 index 48bfecc0..00000000 --- a/lib/insert/insert_billing_subscriptions.rb +++ /dev/null @@ -1,34 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'qx' -require 'delayed_job_helper' -require 'active_support/core_ext' - - -module InsertBillingSubscriptions - - def self.trial(np_id, stripe_plan_id) - begin - nonprofit = Nonprofit.includes(:billing_subscription).find(np_id) - billing_plan = BillingPlan.where('stripe_plan_id = ?', stripe_plan_id).last - sub = nonprofit.create_billing_subscription(billing_plan: billing_plan, status: 'trialing') - n = 10 - DelayedJobHelper.enqueue_job(self, :check_trial, [sub['id']], {run_at: n.days.from_now}) - return {json: sub} - rescue ActiveRecord::RecordNotFound => e - return {json: { error: e }, status: :unprocessable_entity} - end - - end - - def self.check_trial(bs_id) - sub = Qx.fetch(:billing_subscriptions, bs_id).last - if sub['status'] == 'trialing' - Qx.update(:billing_subscriptions) - .set(status: 'inactive') - .timestamps - .where("id = $id", id: bs_id) - .execute - end - end - -end diff --git a/lib/insert/insert_card.rb b/lib/insert/insert_card.rb index dfb7709e..ffd6504b 100644 --- a/lib/insert/insert_card.rb +++ b/lib/insert/insert_card.rb @@ -1,8 +1,8 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'hash' module InsertCard - - # Create a new card # If a stripe_customer_id is present, then update that customer's primary source; otherwise create a new customer # @param [ActiveSupport::HashWithIndifferentAccess] card_data card data @@ -17,60 +17,55 @@ module InsertCard # @param [String] stripe_account_id not clear what this should do. # @param [Integer] event_id id for events with when you want it associated with an event - # @param [User] current_user the user making the request. Used for validating that the current_user can make a long term token request - def self.with_stripe(card_data, stripe_account_id=nil, event_id=nil, current_user = nil) - + # @param [User] current_user the user making the request. Used for validating that the current_user can make a long term token request + def self.with_stripe(card_data, _stripe_account_id = nil, event_id = nil, current_user = nil) begin - ParamValidation.new(card_data.merge({event_id: event_id}), { - holder_type: {required: true, included_in: ['Nonprofit', 'Supporter']}, - holder_id: {required: true}, - stripe_card_id: {not_blank: true, required: true}, - stripe_card_token: {not_blank: true, required: true}, - name: {not_blank: true, required: true}, - event_id: {is_reference: true} - }) + ParamValidation.new(card_data.merge(event_id: event_id), + holder_type: { required: true, included_in: %w[Nonprofit Supporter] }, + holder_id: { required: true }, + stripe_card_id: { not_blank: true, required: true }, + stripe_card_token: { not_blank: true, required: true }, + name: { not_blank: true, required: true }, + event_id: { is_reference: true }) rescue ParamValidation::ValidationError => e - return {json: {error: "Validation error\n #{e.message}", errors: e.data}, status: :unprocessable_entity} + return { json: { error: "Validation error\n #{e.message}", errors: e.data }, status: :unprocessable_entity } end - - # validate that the user is with the correct nonprofit - card_data = card_data.keep_keys(:holder_type, :holder_id, :stripe_card_id, :stripe_card_token, :name ) - holder_types = {'Nonprofit' => :nonprofit, 'Supporter' => :supporter} + card_data = card_data.keep_keys(:holder_type, :holder_id, :stripe_card_id, :stripe_card_token, :name) + holder_types = { 'Nonprofit' => :nonprofit, 'Supporter' => :supporter } holder_type = holder_types[card_data[:holder_type]] holder = nil begin if holder_type == :nonprofit - holder = Nonprofit.select("id, email").includes(:cards).find(card_data[:holder_id]) + holder = Nonprofit.select('id, email').includes(:cards).find(card_data[:holder_id]) elsif holder_type == :supporter - holder = Supporter.select("id, email, nonprofit_id").includes(:cards, :nonprofit).find(card_data[:holder_id]) + holder = Supporter.select('id, email, nonprofit_id').includes(:cards, :nonprofit).find(card_data[:holder_id]) end rescue ActiveRecord::RecordNotFound - return {json: {error: "Sorry, you need to provide a nonprofit or supporter"}, status: :unprocessable_entity} + return { json: { error: 'Sorry, you need to provide a nonprofit or supporter' }, status: :unprocessable_entity } end begin if holder_type == :supporter && event_id event = Event.where('id = ?', event_id).first unless event - raise ParamValidation::ValidationError.new("#{event_id} is not a valid event", {key: :event_id}) + raise ParamValidation::ValidationError.new("#{event_id} is not a valid event", key: :event_id) end - if (holder.nonprofit != event.nonprofit ) - raise ParamValidation::ValidationError.new("Event #{event_id} is not for the same nonprofit as supporter #{holder.id}", {key: :event_id}) + if holder.nonprofit != event.nonprofit + raise ParamValidation::ValidationError.new("Event #{event_id} is not for the same nonprofit as supporter #{holder.id}", key: :event_id) end unless QueryRoles.is_authorized_for_nonprofit?(current_user.id, holder.nonprofit.id) - raise AuthenticationError.new + raise AuthenticationError end end rescue AuthenticationError - return {json: {error: "You're not authorized to perform that action"}, status: :unauthorized} - rescue => e - return {json: {error: "Oops! There was an error: #{e.message}"}, status: :unprocessable_entity} - + return { json: { error: "You're not authorized to perform that action" }, status: :unauthorized } + rescue StandardError => e + return { json: { error: "Oops! There was an error: #{e.message}" }, status: :unprocessable_entity } end stripe_account_hash = {} # stripe_account_id ? {stripe_account: stripe_account_id} : {} begin @@ -84,43 +79,39 @@ module InsertCard card_data[:stripe_customer_id] = stripe_customer.id rescue Stripe::CardError => e - return {json: {error: "Oops! #{e.json_body[:error][:message]}"}, status: :unprocessable_entity} + return { json: { error: "Oops! #{e.json_body[:error][:message]}" }, status: :unprocessable_entity } rescue Stripe::StripeError => e - return {json: {error: "Oops! There was an error processing your payment, and it did not complete. Please try again in a moment. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error processing your payment, and it did not complete. Please try again in a moment. Error: #{e}" }, status: :unprocessable_entity } end card = nil source_token = nil begin - Card.transaction { - - if (holder_type == :nonprofit) + Card.transaction do + if holder_type == :nonprofit # @type [Nonprofit] holder card = holder.create_active_card(card_data) - elsif (holder_type == :supporter) + elsif holder_type == :supporter # @type [Supporter] holder card = holder.cards.create(card_data) params = {} - if event - params[:event] = event - end + params[:event] = event if event source_token = InsertSourceToken.create_record(card, params).token end card.save! - } + end rescue ActiveRecord::ActiveRecordError => e - return {json: {error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}" }, status: :unprocessable_entity } rescue e - return {json: {error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}" }, status: :unprocessable_entity } rescue e - return {json: {error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{e}" }, status: :unprocessable_entity } end - return { status: :ok, json: card.attributes.with_indifferent_access.merge(token: source_token) } - end + { status: :ok, json: card.attributes.with_indifferent_access.merge(token: source_token) } +end def self.customer_data(holder, card_data) { email: holder['email'], metadata: { cardholders_name: card_data[:cardholders_name], holder_id: card_data[:holder_id], holder_type: card_data[:holder_type] } } end - end diff --git a/lib/insert/insert_charge.rb b/lib/insert/insert_charge.rb index 15433c83..c8390ee4 100644 --- a/lib/insert/insert_charge.rb +++ b/lib/insert/insert_charge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' @@ -6,161 +8,155 @@ require 'stripe' require 'get_data' require 'active_support/core_ext' require 'query/billing_plans' -require 'stripe_account' unless !Settings.payment_provider.stripe_connect +require 'stripe_account' if Settings.payment_provider.stripe_connect module InsertCharge - # In data, pass in: amount, nonprofit_id, supporter_id, card_id, statement # Optionally pass in :metadata for stripe and donation_id to connect to donation? # @raise [ParamValidation::ValidationError] parameter validation occurred # @raise [Stripe::StripeError] the stripe account couldn't be accessed or created def self.with_stripe(data) + ParamValidation.new(data || {}, + amount: { + required: true, + is_integer: true, + min: 0 + }, + nonprofit_id: { + required: true, + is_integer: true + }, + supporter_id: { + required: true, + is_integer: true + }, + card_id: { + required: true, + is_integer: true + }, + statement: { + required: true, + not_blank: true + }) + + np = Nonprofit.where('id = ?', data[:nonprofit_id]).first + + unless np + raise ParamValidation::ValidationError.new("#{data[:nonprofit_id]} is not a valid Nonprofit", key: :nonprofit_id) + end + + supporter = Supporter.where('id = ?', data[:supporter_id]).first + + unless supporter + raise ParamValidation::ValidationError.new("#{data[:supporter_id]} is not a valid Supporter", key: :supporter_id) + end + + card = Card.where('id = ?', data[:card_id]).first + + unless card + raise ParamValidation::ValidationError.new("#{data[:card_id]} is not a valid card", key: :card_id) + end + + unless np == supporter.nonprofit + raise ParamValidation::ValidationError.new("#{data[:supporter_id]} does not belong to this nonprofit #{np.id}", key: :supporter_id) + end + + unless card.holder == supporter + if data[:old_donation] + # these are not new donations so we let them fly (for now) + else + raise ParamValidation::ValidationError.new("#{data[:card_id]} does not belong to this supporter #{supporter.id}", key: :card_id) + end + end + + result = {} + # Catch errors thrown by the stripe gem so we can respond with a 422 with an error message rather than 500 begin - ParamValidation.new(data || {}, { - :amount => { - :required => true, - :is_integer => true, - :min => 0 - }, - :nonprofit_id => { - :required => true, - :is_integer => true - }, - :supporter_id => { - :required => true, - :is_integer => true - }, - :card_id => { - :required => true, - :is_integer => true - }, - :statement => { - :required => true, - :not_blank => true - } - }) + stripe_customer_id = card.stripe_customer_id + stripe_account_id = StripeAccount.find_or_create(data[:nonprofit_id]) + rescue StandardError => e + raise e + end + nonprofit_currency = Qx.select(:currency).from(:nonprofits).where('id=$id', id: data[:nonprofit_id]).execute.first['currency'] - np = Nonprofit.where('id = ?', data[:nonprofit_id]).first + stripe_charge_data = { + customer: stripe_customer_id, + amount: data[:amount], + currency: nonprofit_currency, + description: data[:statement], + statement_descriptor: data[:statement][0..21].gsub(/[<>"']/, ''), + metadata: data[:metadata] + } - unless np - raise ParamValidation::ValidationError.new("#{data[:nonprofit_id]} is not a valid Nonprofit", {:key => :nonprofit_id}) - end - - supporter = Supporter.where('id = ?', data[:supporter_id]).first - - unless supporter - raise ParamValidation::ValidationError.new("#{data[:supporter_id]} is not a valid Supporter", {:key => :supporter_id}) - end - - card = Card.where('id = ?', data[:card_id]).first - - unless card - raise ParamValidation::ValidationError.new("#{data[:card_id]} is not a valid card", {:key => :card_id}) - end - - unless np == supporter.nonprofit - raise ParamValidation::ValidationError.new("#{data[:supporter_id]} does not belong to this nonprofit #{np.id}", {:key => :supporter_id}) - end - - unless card.holder == supporter - if (data[:old_donation]) - #these are not new donations so we let them fly (for now) - else - raise ParamValidation::ValidationError.new("#{data[:card_id]} does not belong to this supporter #{supporter.id}", {:key => :card_id}) - end - end - - result = {} - # Catch errors thrown by the stripe gem so we can respond with a 422 with an error message rather than 500 - begin - stripe_customer_id = card.stripe_customer_id - stripe_account_id = StripeAccount.find_or_create(data[:nonprofit_id]) - rescue => e - raise e - end - nonprofit_currency = Qx.select(:currency).from(:nonprofits).where("id=$id", id: data[:nonprofit_id]).execute.first['currency'] - - stripe_charge_data = { - customer: stripe_customer_id, - amount: data[:amount], - currency: nonprofit_currency, - description: data[:statement], - statement_descriptor: data[:statement][0..21].gsub(/[<>"']/,''), - metadata: data[:metadata] - } - - if Settings.payment_provider.stripe_connect - stripe_account_id = StripeAccount.find_or_create(data[:nonprofit_id]) - # Get the percentage fee on the nonprofit's billing plan - platform_fee = BillingPlans.get_percentage_fee(data[:nonprofit_id]) - fee = CalculateFees.for_single_amount(data[:amount], platform_fee) - stripe_charge_data[:application_fee]= fee + if Settings.payment_provider.stripe_connect + stripe_account_id = StripeAccount.find_or_create(data[:nonprofit_id]) + # Get the percentage fee on the nonprofit's billing plan + platform_fee = BillingPlans.get_percentage_fee(data[:nonprofit_id]) + fee = CalculateFees.for_single_amount(data[:amount], platform_fee) + stripe_charge_data[:application_fee] = fee # For backwards compatibility, see if the customer exists in the primary or the connected account # If it's a legacy customer, charge to the primary account and transfer with .destination # Otherwise, charge directly to the connected account - begin - stripe_cust = Stripe::Customer.retrieve(stripe_customer_id) - params = [stripe_charge_data.merge(destination: stripe_account_id), {}] - rescue - params = [stripe_charge_data, {stripe_account: stripe_account_id}] - end - else - fee=0 - stripe_charge_data[:source]=card['stripe_card_id'] - params = [stripe_charge_data, {}] - end - begin - stripe_charge = Stripe::Charge.create(*params) - rescue Stripe::CardError => e - failure_message = "There was an error with your card: #{e.json_body[:error][:message]}" - rescue Stripe::StripeError => e - failure_message = "We're sorry, but something went wrong. We've been notified about this issue." + stripe_cust = Stripe::Customer.retrieve(stripe_customer_id) + params = [stripe_charge_data.merge(destination: stripe_account_id), {}] + rescue StandardError + params = [stripe_charge_data, { stripe_account: stripe_account_id }] end + else + fee = 0 + stripe_charge_data[:source] = card['stripe_card_id'] + params = [stripe_charge_data, {}] + end + begin + stripe_charge = Stripe::Charge.create(*params) + rescue Stripe::CardError => e + failure_message = "There was an error with your card: #{e.json_body[:error][:message]}" + rescue Stripe::StripeError => e + failure_message = "We're sorry, but something went wrong. We've been notified about this issue." + end - charge = Charge.new + charge = Charge.new - charge.amount = data[:amount] - charge.fee = fee + charge.amount = data[:amount] + charge.fee = fee - charge.stripe_charge_id = GetData.chain(stripe_charge, :id) - charge.failure_message = failure_message - charge.status = GetData.chain(stripe_charge, :paid) ? 'pending' : 'failed' - charge.card = card - charge.donation = Donation.where('id = ?', data[:donation_id]).first - charge.supporter = supporter - charge.nonprofit = np + charge.stripe_charge_id = GetData.chain(stripe_charge, :id) + charge.failure_message = failure_message + charge.status = GetData.chain(stripe_charge, :paid) ? 'pending' : 'failed' + charge.card = card + charge.donation = Donation.where('id = ?', data[:donation_id]).first + charge.supporter = supporter + charge.nonprofit = np + charge.save! + result['charge'] = charge + + if stripe_charge && stripe_charge.status != 'failed' + payment = Payment.new + payment.gross_amount = data[:amount] + payment.fee_total = -fee + payment.net_amount = data[:amount] - fee + payment.towards = data[:towards] + payment.kind = data[:kind] + payment.donation = Donation.where('id = ?', data[:donation_id]).first + payment.nonprofit = np + payment.supporter = supporter + payment.refund_total = 0 + payment.date = data[:date] || result['charge'].created_at + payment.save! + + result['payment'] = payment + + charge.payment = payment charge.save! result['charge'] = charge - - if stripe_charge && stripe_charge.status != 'failed' - payment = Payment.new - payment.gross_amount = data[:amount] - payment.fee_total = -fee - payment.net_amount = data[:amount] - fee - payment.towards = data[:towards] - payment.kind = data[:kind] - payment.donation = Donation.where('id = ?', data[:donation_id]).first - payment.nonprofit = np - payment.supporter = supporter - payment.refund_total = 0 - payment.date = data[:date] || result['charge'].created_at - payment.save! - - - result['payment'] = payment - - charge.payment = payment - charge.save! - result['charge'] = charge - end - - return result - rescue => e - raise e end + + result + rescue StandardError => e + raise e end def self.with_sepa(data) @@ -171,7 +167,7 @@ module InsertCharge # TODO fee = 0 - #todo charge should be changed to SEPA charge + # TODO: charge should be changed to SEPA charge c = Charge.new c.direct_debit_detail = entities[:direct_debit_detail_id] @@ -186,9 +182,9 @@ module InsertCharge p = Payment.new - p.gross_amount= data[:amount] - p.fee_total= -fee - p.net_amount= data[:amount] - fee + p.gross_amount = data[:amount] + p.fee_total = -fee + p.net_amount = data[:amount] - fee p.towards = data[:towards] p.kind = data[:kind] p.nonprofit = entities[:nonprofit_id] diff --git a/lib/insert/insert_custom_field_joins.rb b/lib/insert/insert_custom_field_joins.rb index f25a38cd..12d40046 100644 --- a/lib/insert/insert_custom_field_joins.rb +++ b/lib/insert/insert_custom_field_joins.rb @@ -1,131 +1,123 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'delayed_job_helper' require 'qx' require 'update/update_custom_field_joins' module InsertCustomFieldJoins - # Bulk insert many field joins into many supporters # for every field name, find or create it for the nonprofit # field_data should be an array of arrays liks [['Company', 'Pixar'], # ['Shirt-size', 'Small']] def self.find_or_create(np_id, supporter_ids, field_data) - ParamValidation.new({np_id: np_id, supporter_ids: supporter_ids, field_data: field_data}, - { - :np_id => { - :required => true, - :is_integer=> true - }, - :supporter_ids => { - :required => true, - :is_array => true, - :min_length => 1 - }, - :field_data => { - :required => true, - :is_array => true, - :min_length => 1 - } - }) + ParamValidation.new({ np_id: np_id, supporter_ids: supporter_ids, field_data: field_data }, + np_id: { + required: true, + is_integer: true + }, + supporter_ids: { + required: true, + is_array: true, + min_length: 1 + }, + field_data: { + required: true, + is_array: true, + min_length: 1 + }) - #make sure the np exists - np = Nonprofit.where("id = ? ", np_id).first + # make sure the np exists + np = Nonprofit.where('id = ? ', np_id).first unless np - raise ParamValidation::ValidationError.new("#{np_id} is not a valid non-profit", {:key => :np_id}) + raise ParamValidation::ValidationError.new("#{np_id} is not a valid non-profit", key: :np_id) end - #make sure the supporters_ids exist - supporter_ids.each {|id| + # make sure the supporters_ids exist + supporter_ids.each do |id| unless np.supporters.where('id = ?', id).exists? - raise ParamValidation::ValidationError.new("#{id} is not a valid supporter for nonprofit #{np_id}", {:key => :supporter_ids}) + raise ParamValidation::ValidationError.new("#{id} is not a valid supporter for nonprofit #{np_id}", key: :supporter_ids) end - } + end Qx.transaction do # get the custom_field_master_id for each field_data name - cfm_id_to_value = field_data.map { |name, value| - cfm = CustomFieldMaster.where("nonprofit_id = ? and name = ?", np_id, name).first - unless cfm - cfm = CustomFieldMaster.create!(:nonprofit => np, :name => name) - end - {:custom_field_master_id => cfm.id, :value => value} - } + cfm_id_to_value = field_data.map do |name, value| + cfm = CustomFieldMaster.where('nonprofit_id = ? and name = ?', np_id, name).first + cfm ||= CustomFieldMaster.create!(nonprofit: np, name: name) + { custom_field_master_id: cfm.id, value: value } + end return in_bulk(np_id, supporter_ids, cfm_id_to_value) end end - # Validation: *np_id is valid, corresponds to real nonprofit # # # @param [Integer] np_id nonprofit_id whose custom_fields this applies to # @param [Array] supporter_ids the supporter ids in which the custom fields should be modified # @param [Array>] field_data the fields you'd like to modify. Each item is a hash with following keys: - # * custom_field_master_id [Integer] for the key corresponding to custom_field_master_id - # * value [Object] the expected value of the field. If this key is an empty string, we remove the custom_field + # * custom_field_master_id [Integer] for the key corresponding to custom_field_master_id + # * value [Object] the expected value of the field. If this key is an empty string, we remove the custom_field def self.in_bulk(np_id, supporter_ids, field_data) begin ParamValidation.new({ - np_id: np_id, - supporter_ids: supporter_ids, - field_data: field_data - }, { - np_id: {required: true, is_integer: true}, - supporter_ids: {required:true, is_array: true}, - field_data: { required: true, is_array: true} - # array_of_hashes: { - # selected: {required: true}, tag_master_id: {required: true, is_integer: true} - # } - - }) + np_id: np_id, + supporter_ids: supporter_ids, + field_data: field_data + }, + np_id: { required: true, is_integer: true }, + supporter_ids: { required: true, is_array: true }, + field_data: { required: true, is_array: true }) + # array_of_hashes: { + # selected: {required: true}, tag_master_id: {required: true, is_integer: true} + # } rescue ParamValidation::ValidationError => e - return {json: {error: "Validation error\n #{e.message}", errors: e.data}, status: :unprocessable_entity} + return { json: { error: "Validation error\n #{e.message}", errors: e.data }, status: :unprocessable_entity } end begin - return {json: {error: "Nonprofit #{np_id} is not valid"}, status: :unprocessable_entity} unless Nonprofit.exists?(np_id) + return { json: { error: "Nonprofit #{np_id} is not valid" }, status: :unprocessable_entity } unless Nonprofit.exists?(np_id) # verify that the supporters belong to the nonprofit supporter_ids = Supporter.where('nonprofit_id = ? and id IN (?)', np_id, supporter_ids).pluck(:id) unless supporter_ids.any? - return {json: {inserted_count: 0, removed_count: 0}, status: :ok} + return { json: { inserted_count: 0, removed_count: 0 }, status: :ok } end # filtering the tag_data to this nonprofit - valid_ids = CustomFieldMaster.where('nonprofit_id = ? and id IN (?)', np_id, field_data.map {|fd| fd[:custom_field_master_id] }).pluck(:id).to_a - filtered_field_data = field_data.select {|i| valid_ids.include? i[:custom_field_master_id ].to_i} + valid_ids = CustomFieldMaster.where('nonprofit_id = ? and id IN (?)', np_id, field_data.map { |fd| fd[:custom_field_master_id] }).pluck(:id).to_a + filtered_field_data = field_data.select { |i| valid_ids.include? i[:custom_field_master_id].to_i } # first, delete the items which should be removed - to_insert, to_remove = filtered_field_data.partition{|t| + to_insert, to_remove = filtered_field_data.partition do |t| !t[:value].blank? - } + end deleted = [] if to_remove.any? deleted = Qx.delete_from(:custom_field_joins) - .where("supporter_id IN ($ids)", ids: supporter_ids) - .and_where("custom_field_master_id in ($fields)", fields: to_remove.map{|t| t[:custom_field_master_id]}) - .returning('*') - .execute + .where('supporter_id IN ($ids)', ids: supporter_ids) + .and_where('custom_field_master_id in ($fields)', fields: to_remove.map { |t| t[:custom_field_master_id] }) + .returning('*') + .execute end - # next add only the selected tag_joins if to_insert.any? - insert_data = supporter_ids.map{|id| to_insert.map{|cfm| {supporter_id: id, custom_field_master_id: cfm[:custom_field_master_id], value: cfm[:value]}}}.flatten + insert_data = supporter_ids.map { |id| to_insert.map { |cfm| { supporter_id: id, custom_field_master_id: cfm[:custom_field_master_id], value: cfm[:value] } } }.flatten cfj = Qx.insert_into(:custom_field_joins) - .values(insert_data) - .timestamps - .on_conflict() - .conflict_columns(:supporter_id, :custom_field_master_id).upsert(:custom_field_join_supporter_unique_idx) - .returning('*') - .execute + .values(insert_data) + .timestamps + .on_conflict + .conflict_columns(:supporter_id, :custom_field_master_id).upsert(:custom_field_join_supporter_unique_idx) + .returning('*') + .execute else cfj = [] end rescue ActiveRecord::ActiveRecordError => e - return {json: {error: "A DB error occurred. Please contact support. \n #{e.message}"}, status: :unprocessable_entity} + return { json: { error: "A DB error occurred. Please contact support. \n #{e.message}" }, status: :unprocessable_entity } end # Create an activity for the modified tags for every supporter @@ -134,10 +126,8 @@ module InsertCustomFieldJoins # activities = Psql.execute( Qexpr.new.insert(:activities, activity_data) ) # Sync mailchimp lists, if present - #Mailchimp.delay.sync_supporters_to_list_from_tag_joins(np_id, supporter_ids, tag_data) - - return {json: {inserted_count: cfj.count, removed_count: deleted.count }, status: :ok} + # Mailchimp.delay.sync_supporters_to_list_from_tag_joins(np_id, supporter_ids, tag_data) + { json: { inserted_count: cfj.count, removed_count: deleted.count }, status: :ok } end - end diff --git a/lib/insert/insert_direct_debit_detail.rb b/lib/insert/insert_direct_debit_detail.rb index cf3219a4..d4cc73d6 100644 --- a/lib/insert/insert_direct_debit_detail.rb +++ b/lib/insert/insert_direct_debit_detail.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertDirectDebitDetail def self.execute(params) @@ -5,18 +7,18 @@ module InsertDirectDebitDetail direct_debit_detail = {} begin - DirectDebitDetail.transaction { + DirectDebitDetail.transaction do direct_debit_detail = DirectDebitDetail.create( bic: params[:sepa_params][:bic], iban: params[:sepa_params][:iban], account_holder_name: params[:sepa_params][:name], holder: supporter ) - } + end rescue ActiveRecord::ActiveRecordError => e - return {json: {error: "Oops! There was an error saving your direct debit details, and it did not complete. Please try again in a moment. Error: #{e}"}, status: :unprocessable_entity} + return { json: { error: "Oops! There was an error saving your direct debit details, and it did not complete. Please try again in a moment. Error: #{e}" }, status: :unprocessable_entity } end - return { status: :ok, json: direct_debit_detail } + { status: :ok, json: direct_debit_detail } end end diff --git a/lib/insert/insert_disputes.rb b/lib/insert/insert_disputes.rb index 41222f80..c6cd7e53 100644 --- a/lib/insert/insert_disputes.rb +++ b/lib/insert/insert_disputes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' @@ -5,42 +7,41 @@ require 'calculate/calculate_fees' require 'active_support/core_ext' module InsertDisputes - # A new dispute takes a charge id and dispute id and creates: # A dispute row with the charge gross amount and the dispute id # A payment row negative gross and net, just like a refund, but with kind "Dispute" def self.create_record(stripe_charge_id, stripe_dispute_id) # Find the existing charge - ch = Qx.select("*").from("charges").where("stripe_charge_id=$id", id: stripe_charge_id).ex.first - raise ArgumentError.new("Charge not found") if ch.nil? + ch = Qx.select('*').from('charges').where('stripe_charge_id=$id', id: stripe_charge_id).ex.first + raise ArgumentError, 'Charge not found' if ch.nil? result = {} now = Time.current result[:payment] = Psql.execute( Qexpr.new.insert(:payments, [{ - gross_amount: -ch['amount'], - fee_total: 0, - net_amount: -ch['amount'], - kind: 'Dispute', - refund_total: 0, - nonprofit_id: ch['nonprofit_id'], - supporter_id: ch['supporter_id'], - donation_id: ch['donation_id'], - date: now - }]).returning('*') + gross_amount: -ch['amount'], + fee_total: 0, + net_amount: -ch['amount'], + kind: 'Dispute', + refund_total: 0, + nonprofit_id: ch['nonprofit_id'], + supporter_id: ch['supporter_id'], + donation_id: ch['donation_id'], + date: now + }]).returning('*') ).first # Create a dispute record result[:dispute] = Psql.execute( Qexpr.new.insert(:disputes, [{ - gross_amount: ch['amount'], - status: :needs_response, - charge_id: ch['id'], - reason: :unrecognized, - payment_id: result[:payment]['id'], - stripe_dispute_id: stripe_dispute_id - }]).returning('*') + gross_amount: ch['amount'], + status: :needs_response, + charge_id: ch['id'], + reason: :unrecognized, + payment_id: result[:payment]['id'], + stripe_dispute_id: stripe_dispute_id + }]).returning('*') ).first # Prevent refunds from being able to happen on the payment @@ -49,8 +50,6 @@ module InsertDisputes # Insert an activity record InsertActivities.for_disputes([result[:payment]['id']]) - return result + result end - end - diff --git a/lib/insert/insert_donation.rb b/lib/insert/insert_donation.rb index 86875837..5c052f45 100644 --- a/lib/insert/insert_donation.rb +++ b/lib/insert/insert_donation.rb @@ -1,25 +1,25 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertDonation - - # Make a one-time donation (call InsertRecurringDonation.with_stripe to create a recurring donation) # In data, pass in: # amount, card_id, nonprofit_id, supporter_id # designation, dedication # recurring_donation if is recurring - def self.with_stripe(data, current_user=nil) + def self.with_stripe(data, current_user = nil) data = data.with_indifferent_access ParamValidation.new(data, common_param_validations - .merge(token: {required: true, format: UUID::Regex})) + .merge(token: { required: true, format: UUID::Regex })) source_token = QuerySourceToken.get_and_increment_source_token(data[:token], current_user) tokenizable = source_token.tokenizable QuerySourceToken.validate_source_token_type(source_token) - entities = RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id}) + entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id) - entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, {Campaign => :campaign_id, Event => :event_id, Profile => :profile_id}, true)) + entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, { Campaign => :campaign_id, Event => :event_id, Profile => :profile_id }, true)) validate_entities(entities) @@ -36,15 +36,14 @@ module InsertDonation data = data.except(:old_donation).except('old_donation') result = result.merge(insert_charge(data)) if result['charge']['status'] == 'failed' - raise ChargeError.new(result['charge']['failure_message']) + raise ChargeError, result['charge']['failure_message'] end + # Create the donation record - result['donation'] = self.insert_donation(data, entities) + result['donation'] = insert_donation(data, entities) update_donation_keys(result) result['activity'] = InsertActivities.for_one_time_donations([result['payment'].id]) - EmailJobQueue.queue(JobTypes::NonprofitPaymentNotificationJob, result['donation'].id) - EmailJobQueue.queue(JobTypes::DonorPaymentNotificationJob, result['donation'].id, entities[:supporter_id].locale) - QueueDonations.delay.execute_for_donation(result['donation'].id) + HoudiniEventPublisher.announce(:donation_create, result['donation'], result['donation'].supporter.locale) result end @@ -63,95 +62,93 @@ module InsertDonation # pass in amount, nonprofit_id, supporter_id, check_number # also pass in offsite_payment sub-hash (can be empty) def self.offsite(data) - ParamValidation.new(data, common_param_validations.merge(offsite_payment: {is_hash: true})) + ParamValidation.new(data, common_param_validations.merge(offsite_payment: { is_hash: true })) - entities = RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id}) - entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, {Campaign => :campaign_id, Event => :event_id, Profile => :profile_id}, true)) + entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id) + entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, { Campaign => :campaign_id, Event => :event_id, Profile => :profile_id }, true)) validate_entities(entities) data = date_from_data(data) - result = {'donation' => self.insert_donation(data.except('offsite_payment'), entities)} - result['payment'] = self.insert_payment('OffsitePayment', 0, result['donation']['id'], data) + result = { 'donation' => insert_donation(data.except('offsite_payment'), entities) } + result['payment'] = insert_payment('OffsitePayment', 0, result['donation']['id'], data) result['offsite_payment'] = Psql.execute( Qexpr.new.insert(:offsite_payments, [ - (data['offsite_payment'] || {}).merge({ - gross_amount: data['amount'], - nonprofit_id: data['nonprofit_id'], - supporter_id: data['supporter_id'], - donation_id: result['donation']['id'], - payment_id: result['payment']['id'], - date: data['date'] - }) - ]).returning('*') + (data['offsite_payment'] || {}).merge( + gross_amount: data['amount'], + nonprofit_id: data['nonprofit_id'], + supporter_id: data['supporter_id'], + donation_id: result['donation']['id'], + payment_id: result['payment']['id'], + date: data['date'] + ) + ]).returning('*') ).first result['activity'] = InsertActivities.for_offsite_donations([result['payment']['id']]) - QueueDonations.delay.execute_for_donation(result['donation'].id) - return {status: 200, json: result} + WeMoveExecuteForDonationsJob.perform_later(result['donation']) + { status: 200, json: result } end def self.with_sepa(data) data = data.with_indifferent_access ParamValidation.new(data, common_param_validations - .merge(direct_debit_detail_id: {required: true, is_reference: true})) + .merge(direct_debit_detail_id: { required: true, is_reference: true })) - entities = RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id}) + entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id) - entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, {Campaign => :campaign_id, Event => :event_id, Profile => :profile_id}, true)) + entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, { Campaign => :campaign_id, Event => :event_id, Profile => :profile_id }, true)) result = {} data[:date] = Time.now result = result.merge(insert_charge(data)) - result['donation'] = self.insert_donation(data, entities) + result['donation'] = insert_donation(data, entities) update_donation_keys(result) - EmailJobQueue.queue(JobTypes::NonprofitPaymentNotificationJob, result['donation'].id) - EmailJobQueue.queue(JobTypes::DonorDirectDebitNotificationJob, result['donation'].id, locale_for_supporter(result['donation'].supporter.id)) + HoudiniEventPublisher.announce(:donation_create, result['donation'], locale_for_supporter(result['donation'].supporter.id)) - QueueDonations.delay.execute_for_donation(result['donation'].id) # do this for making test consistent result['activity'] = {} result end -private + private def self.get_nonprofit_data(nonprofit_id) Psql.execute( Qexpr.new.select(:statement, :name).from(:nonprofits) - .where("id=$id", id: nonprofit_id) + .where('id=$id', id: nonprofit_id) ).first end def self.insert_charge(data) payment_provider = payment_provider(data) nonprofit_data = get_nonprofit_data(data['nonprofit_id']) - kind = data['recurring_donation'] ? "RecurringDonation" : "Donation" + kind = data['recurring_donation'] ? 'RecurringDonation' : 'Donation' if payment_provider == :credit_card - return InsertCharge.with_stripe({ + return InsertCharge.with_stripe( donation_id: data['donation_id'], kind: kind, towards: data['designation'], - metadata: {kind: kind, nonprofit_id: data['nonprofit_id']}, + metadata: { kind: kind, nonprofit_id: data['nonprofit_id'] }, statement: "Donation #{nonprofit_data['statement'] || nonprofit_data['name']}", amount: data['amount'], nonprofit_id: data['nonprofit_id'], supporter_id: data['supporter_id'], card_id: data['card_id'], old_donation: data['old_donation'] ? true : false - }) + ) elsif payment_provider == :sepa - return InsertCharge.with_sepa({ + return InsertCharge.with_sepa( donation_id: data['donation_id'], kind: kind, towards: data['designation'], - metadata: {kind: kind, nonprofit_id: data['nonprofit_id']}, + metadata: { kind: kind, nonprofit_id: data['nonprofit_id'] }, statement: "Donation #{nonprofit_data['statement'] || nonprofit_data['name']}", amount: data['amount'], nonprofit_id: data['nonprofit_id'], supporter_id: data['supporter_id'], direct_debit_detail_id: data['direct_debit_detail_id'] - }) + ) end end @@ -159,17 +156,17 @@ private def self.insert_payment(kind, fee_total, donation_id, data) Psql.execute( Qexpr.new.insert(:payments, [{ - donation_id: donation_id, - gross_amount: data['amount'], - nonprofit_id: data['nonprofit_id'], - supporter_id: data['supporter_id'], - refund_total: 0, - date: data['date'], - towards: data['designation'], - kind: kind, - fee_total: fee_total, - net_amount: data['amount'] - fee_total - }]).returning('*') + donation_id: donation_id, + gross_amount: data['amount'], + nonprofit_id: data['nonprofit_id'], + supporter_id: data['supporter_id'], + refund_total: 0, + date: data['date'], + towards: data['designation'], + kind: kind, + fee_total: fee_total, + net_amount: data['amount'] - fee_total + }]).returning('*') ).first end @@ -202,31 +199,31 @@ private def self.locale_for_supporter(supporter_id) Psql.execute( Qexpr.new.select(:locale).from(:supporters) - .where("id=$id", id: supporter_id) + .where('id=$id', id: supporter_id) ).first['locale'] end def self.payment_provider(data) - if data[:card_id] || data["card_id"] + if data[:card_id] || data['card_id'] :credit_card - elsif data[:direct_debit_detail_id] || data["direct_debit_detail_id"] + elsif data[:direct_debit_detail_id] || data['direct_debit_detail_id'] :sepa end end -def self.parse_date(date) + def self.parse_date(date) date.blank? ? Time.current : Chronic.parse(date) - end + end def self.common_param_validations { - amount: {required: true, is_integer: true}, - nonprofit_id: {required: true, is_reference: true}, - supporter_id: {required: true, is_reference: true}, - designation: {is_a: String}, - dedication: {is_a: String}, - campaign_id: {is_reference: true}, - event_id: {is_reference: true}, + amount: { required: true, is_integer: true }, + nonprofit_id: { required: true, is_reference: true }, + supporter_id: { required: true, is_reference: true }, + designation: { is_a: String }, + dedication: { is_a: String }, + campaign_id: { is_reference: true }, + event_id: { is_reference: true } } end @@ -236,11 +233,11 @@ def self.parse_date(date) raise ParamValidation::ValidationError.new("Supporter #{entities[:supporter_id].id} is deleted", key: :supporter_id) end - if entities[:event_id] && entities[:event_id].deleted + if entities[:event_id]&.deleted raise ParamValidation::ValidationError.new("Event #{entities[:event_id].id} is deleted", key: :event_id) end - if entities[:campaign_id] && entities[:campaign_id].deleted + if entities[:campaign_id]&.deleted raise ParamValidation::ValidationError.new("Campaign #{entities[:campaign_id].id} is deleted", key: :campaign_id) end diff --git a/lib/insert/insert_duplicate.rb b/lib/insert/insert_duplicate.rb index 60e6e6d3..3fa26af0 100644 --- a/lib/insert/insert_duplicate.rb +++ b/lib/insert/insert_duplicate.rb @@ -1,19 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertDuplicate def self.campaign(campaign_id, profile_id) - ParamValidation.new({:campaign_id => campaign_id, :profile_id => profile_id}, - { - :campaign_id => {:required => true, :is_integer => true}, - :profile_id => {:required => true, :is_integer => true} - }) - campaign = Campaign.where("id = ?", campaign_id).first + ParamValidation.new({ campaign_id: campaign_id, profile_id: profile_id }, + campaign_id: { required: true, is_integer: true }, + profile_id: { required: true, is_integer: true }) + campaign = Campaign.where('id = ?', campaign_id).first unless campaign - raise ParamValidation::ValidationError.new("#{campaign_id} is not a valid campaign", {:key => :campaign_id}) + raise ParamValidation::ValidationError.new("#{campaign_id} is not a valid campaign", key: :campaign_id) end - profile = Profile.where("id = ?", profile_id).first + profile = Profile.where('id = ?', profile_id).first unless profile - raise ParamValidation::ValidationError.new("#{profile_id} is not a valid profile", {:key =>:profile_id}) + raise ParamValidation::ValidationError.new("#{profile_id} is not a valid profile", key: :profile_id) end Qx.transaction do @@ -21,7 +21,7 @@ module InsertDuplicate dupe.slug = SlugCopyNamingAlgorithm.new(Campaign, dupe.nonprofit.id).create_copy_name(dupe.slug) dupe.name = NameCopyNamingAlgorithm.new(Campaign, dupe.nonprofit.id).create_copy_name(dupe.name) - if (dupe.end_datetime && dupe.end_datetime.ago(7.days) < DateTime.now) + if dupe.end_datetime && dupe.end_datetime.ago(7.days) < DateTime.now dupe.end_datetime = DateTime.now.since(7.days) end @@ -29,9 +29,17 @@ module InsertDuplicate dupe.save! - dupe.update_attribute(:main_image, campaign.main_image) unless !campaign.main_image rescue AWS::S3::Errors::NoSuchKey + begin + dupe.main_image.attach(campaign.main_image.blob) if campaign.main_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end - dupe.update_attribute(:background_image, campaign.background_image) unless !campaign.background_image rescue AWS::S3::Errors::NoSuchKey + begin + dupe.background_image.attach(campaign.background_image.blob) if campaign.background_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end InsertDuplicate.campaign_gift_options(campaign_id, dupe.id) @@ -40,19 +48,17 @@ module InsertDuplicate end def self.event(event_id, profile_id) - ParamValidation.new({:event_id => event_id, :profile_id => profile_id}, - { - :event_id => {:required => true, :is_integer => true}, - :profile_id => {:required => true, :is_integer => true} - }) - event = Event.where("id = ?", event_id).first + ParamValidation.new({ event_id: event_id, profile_id: profile_id }, + event_id: { required: true, is_integer: true }, + profile_id: { required: true, is_integer: true }) + event = Event.where('id = ?', event_id).first unless event - raise ParamValidation::ValidationError.new("#{event_id} is not a valid event", {:key => :event_id}) + raise ParamValidation::ValidationError.new("#{event_id} is not a valid event", key: :event_id) end - profile = Profile.where("id = ?", profile_id).first + profile = Profile.where('id = ?', profile_id).first unless profile - raise ParamValidation::ValidationError.new("#{profile_id} is not a valid profile", {:key =>:profile_id}) + raise ParamValidation::ValidationError.new("#{profile_id} is not a valid profile", key: :profile_id) end Qx.transaction do @@ -63,24 +69,31 @@ module InsertDuplicate we_changed_start_time = false - length_of_event = dupe.end_datetime - dupe.start_datetime - if (dupe.start_datetime.ago(7.days) < DateTime.now) + length_of_event = dupe.end_datetime - dupe.start_datetime + if dupe.start_datetime.ago(7.days) < DateTime.now dupe.start_datetime = DateTime.now.since(7.days) we_changed_start_time = true end - if (we_changed_start_time && dupe.end_datetime) + if we_changed_start_time && dupe.end_datetime dupe.end_datetime = dupe.start_datetime.since(length_of_event) end - dupe.published = false dupe.save! - dupe.update_attribute(:main_image, event.main_image) unless !event.main_image rescue AWS::S3::Errors::NoSuchKey + begin + dupe.main_image.attach(event.main_image.blob) if event.main_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end - dupe.update_attribute(:background_image, event.background_image) unless !event.background_image rescue AWS::S3::Errors::NoSuchKey + begin + dupe.background_image.attach( event.background_image.blob) if event.background_image.attached? + rescue StandardError + AWS::S3::Errors::NoSuchKey + end InsertDuplicate.ticket_levels(event_id, dupe.id) InsertDuplicate.event_discounts(event_id, dupe.id) @@ -92,58 +105,57 @@ module InsertDuplicate # selects all gift options associated with old campaign # and inserts them and creates associations with a new campaign def self.campaign_gift_options(old_campaign_id, new_campaign_id) - cgos = Qx.select("*") - .from("campaign_gift_options") - .where(campaign_id: old_campaign_id) - .execute - .map {|c| c.except("id", "created_at", "updated_at", "campaign_id") } + cgos = Qx.select('*') + .from('campaign_gift_options') + .where(campaign_id: old_campaign_id) + .execute + .map { |c| c.except('id', 'created_at', 'updated_at', 'campaign_id') } if cgos.any? - return Qx.insert_into("campaign_gift_options") - .values(cgos) - .common_values({campaign_id: new_campaign_id}) - .ts - .returning("*") - .execute + return Qx.insert_into('campaign_gift_options') + .values(cgos) + .common_values(campaign_id: new_campaign_id) + .ts + .returning('*') + .execute end end # selects all ticket levels associated with old event # and inserts them and creates associations with a new event def self.ticket_levels(old_event_id, new_event_id) - tls = Qx.select("*") - .from("ticket_levels") - .where(event_id: old_event_id) - .execute - .map {|t| t.except("id", "created_at", "updated_at", "event_id") } + tls = Qx.select('*') + .from('ticket_levels') + .where(event_id: old_event_id) + .execute + .map { |t| t.except('id', 'created_at', 'updated_at', 'event_id') } if tls.any? - return Qx.insert_into("ticket_levels") - .values(tls) - .common_values({event_id: new_event_id}) - .ts - .returning("*") - .execute + return Qx.insert_into('ticket_levels') + .values(tls) + .common_values(event_id: new_event_id) + .ts + .returning('*') + .execute end end # selects all discounts associated with old event # and inserts them and creates associations with a new event def self.event_discounts(old_event_id, new_event_id) - eds = Qx.select("*") - .from("event_discounts") - .where(event_id: old_event_id) - .execute - .map {|t| t.except("id", "created_at", "updated_at", "event_id") } + eds = Qx.select('*') + .from('event_discounts') + .where(event_id: old_event_id) + .execute + .map { |t| t.except('id', 'created_at', 'updated_at', 'event_id') } if eds.any? - return Qx.insert_into("event_discounts") - .values(eds) - .common_values({event_id: new_event_id}) - .ts - .returning("*") - .execute + return Qx.insert_into('event_discounts') + .values(eds) + .common_values(event_id: new_event_id) + .ts + .returning('*') + .execute end end end - diff --git a/lib/insert/insert_email_lists.rb b/lib/insert/insert_email_lists.rb index 30d04c35..d19c37e3 100644 --- a/lib/insert/insert_email_lists.rb +++ b/lib/insert/insert_email_lists.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module InsertEmailLists - def self.for_mailchimp(npo_id, tag_master_ids) # Partial SQL expression for deleting deselected tags delete_expr = Qx.delete_from(:email_lists).where(nonprofit_id: npo_id).returning('mailchimp_list_id') @@ -10,35 +11,33 @@ module InsertEmailLists if tag_master_ids.empty? # no tags were selected; remove all email lists deleted = delete_expr.execute else # Remove all email lists that exist in the db that are not included in tag_master_ids - deleted = delete_expr.where("tag_master_id NOT IN($ids)", ids: tag_master_ids).execute + deleted = delete_expr.where('tag_master_id NOT IN($ids)', ids: tag_master_ids).execute end - mailchimp_lists_to_delete = deleted.map{|h| h['mailchimp_list_id']} + mailchimp_lists_to_delete = deleted.map { |h| h['mailchimp_list_id'] } result = Mailchimp.delete_mailchimp_lists(npo_id, mailchimp_lists_to_delete) - return {deleted: deleted, deleted_result: result} if tag_master_ids.empty? + return { deleted: deleted, deleted_result: result } if tag_master_ids.empty? - existing = Qx.select("tag_master_id").from(:email_lists) - .where(nonprofit_id: npo_id) - .and_where("tag_master_id IN ($ids)", ids: tag_master_ids) - .execute + existing = Qx.select('tag_master_id').from(:email_lists) + .where(nonprofit_id: npo_id) + .and_where('tag_master_id IN ($ids)', ids: tag_master_ids) + .execute tag_master_ids -= existing lists = Mailchimp.create_mailchimp_lists(npo_id, tag_master_ids) - if !lists || !lists.any? || !lists.first[:name] - raise Exception.new("Unable to create mailchimp lists. Response was: #{lists}") + if !lists || lists.none? || !lists.first[:name] + raise Exception, "Unable to create mailchimp lists. Response was: #{lists}" end inserted_lists = Qx.insert_into(:email_lists) - .values( lists.map{|ls| {list_name: ls[:name], mailchimp_list_id: ls[:id], tag_master_id: ls[:tag_master_id]}}) - .common_values({ nonprofit_id: npo_id, }) - .ts - .returning('*') - .execute + .values(lists.map { |ls| { list_name: ls[:name], mailchimp_list_id: ls[:id], tag_master_id: ls[:tag_master_id] } }) + .common_values(nonprofit_id: npo_id) + .ts + .returning('*') + .execute - UpdateEmailLists.delay.populate_lists_on_mailchimp(npo_id) + EmailListCreateJob.perform_later(npo_id) - return {deleted: deleted, deleted_result: result, inserted_lists: inserted_lists, inserted_result: lists} + { deleted: deleted, deleted_result: result, inserted_lists: inserted_lists, inserted_result: lists } end - end - diff --git a/lib/insert/insert_full_contact_infos.rb b/lib/insert/insert_full_contact_infos.rb index 7b479390..f32ff447 100644 --- a/lib/insert/insert_full_contact_infos.rb +++ b/lib/insert/insert_full_contact_infos.rb @@ -1,25 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module InsertFullContactInfos - - # Work off of the full_contact_jobs queue def self.work_queue - ids = Qx.select('supporter_id').from('full_contact_jobs').ex.map{|h| h['supporter_id']} + ids = Qx.select('supporter_id').from('full_contact_jobs').ex.map { |h| h['supporter_id'] } Qx.delete_from('full_contact_jobs').where('TRUE').execute - self.bulk(ids) if ids.any? + bulk(ids) if ids.any? end - # Enqueue full contact jobs for a set of supporter ids def self.enqueue(supporter_ids) Qx.insert_into(:full_contact_jobs) - .values(supporter_ids.map{|id| {supporter_id: id}}) + .values(supporter_ids.map { |id| { supporter_id: id } }) .ex end - # We need to throttle our requests by 10ms since that is our rate limit on FullContact def self.bulk(supporter_ids) created_ids = [] @@ -30,9 +28,8 @@ module InsertFullContactInfos interval = 0.1 - (Time.current - now) # account for time taken in .single sleep interval if interval > 0 end - return created_ids + created_ids end - # Fetch and persist a single full contact record for a single supporter # return an exception if 404 or something else went poop @@ -63,54 +60,55 @@ module InsertFullContactInfos age: GetData.hash(result, 'demographics', 'age'), age_range: GetData.hash(result, 'demographics', 'age_range'), location_general: GetData.hash(result, 'demographics', 'location_general'), - websites: (GetData.hash(result, 'contact_info', 'websites') || []).map{|h| h.url}.join(','), + websites: (GetData.hash(result, 'contact_info', 'websites') || []).map(&:url).join(','), supporter_id: supporter_id } - if existing - full_contact_info = Qx.update(:full_contact_infos) - .set(info_data) - .timestamps - .where(id: existing['id']) - .returning('*') - .execute.first - else - full_contact_info = Qx.insert_into(:full_contact_infos) - .values(info_data) - .returning('*') - .timestamps - .execute.first - end + full_contact_info = if existing + Qx.update(:full_contact_infos) + .set(info_data) + .timestamps + .where(id: existing['id']) + .returning('*') + .execute.first + else + Qx.insert_into(:full_contact_infos) + .values(info_data) + .returning('*') + .timestamps + .execute.first + end if result['photos'].present? - photo_data = result['photos'].map{|h| {type_id: h.type_id, url: h.url, is_primary: h.is_primary}} - Qx.delete_from("full_contact_photos") + photo_data = result['photos'].map { |h| { type_id: h.type_id, url: h.url, is_primary: h.is_primary } } + Qx.delete_from('full_contact_photos') .where(full_contact_info_id: full_contact_info['id']) .execute full_contact_photos = Qx.insert_into(:full_contact_photos) - .values(photo_data) - .common_values(full_contact_info_id: full_contact_info['id']) - .timestamps - .returning("*") - .execute + .values(photo_data) + .common_values(full_contact_info_id: full_contact_info['id']) + .timestamps + .returning('*') + .execute end if result['social_profiles'].present? - profile_data = result['social_profiles'].map{|h| {type_id: h.type_id, username: h.username, uid: h.id, bio: h.bio, url: h.url, followers: h.followers, following: h.following} } - Qx.delete_from("full_contact_social_profiles") + profile_data = result['social_profiles'].map { |h| { type_id: h.type_id, username: h.username, uid: h.id, bio: h.bio, url: h.url, followers: h.followers, following: h.following } } + Qx.delete_from('full_contact_social_profiles') .where(full_contact_info_id: full_contact_info['id']) .execute full_contact_social_profiles = Qx.insert_into(:full_contact_social_profiles) - .values(profile_data) - .common_values(full_contact_info_id: full_contact_info['id']) - .timestamps - .returning("*") - .execute + .values(profile_data) + .common_values(full_contact_info_id: full_contact_info['id']) + .timestamps + .returning('*') + .execute end if result['digital_footprint'] && result['digital_footprint']['topics'].present? profile_data = result['social_profiles'] - .map{|h| { + .map do |h| + { type_id: h.type_id, username: h.username, uid: h.id, @@ -118,53 +116,55 @@ module InsertFullContactInfos url: h.url, followers: h.followers, following: h.following - } } - - vals = result['digital_footprint']['topics'].map{|h| h.value} + } + end + + vals = result['digital_footprint']['topics'].map(&:value) existing_vals = Qx.select('value').from('full_contact_topics') - .where("value IN ($vals)", vals: vals) - .and_where("full_contact_info_id=$id", id: full_contact_info['id']) - .execute.map{|h| h['value']} + .where('value IN ($vals)', vals: vals) + .and_where('full_contact_info_id=$id', id: full_contact_info['id']) + .execute.map { |h| h['value'] } topic_data = result['digital_footprint']['topics'] - .reject{|h| existing_vals.include?(h.value)} - .map{|h| {value: h.value, provider: h.provider}} + .reject { |h| existing_vals.include?(h.value) } + .map { |h| { value: h.value, provider: h.provider } } if topic_data.any? full_contact_topics = Qx.insert_into(:full_contact_topics) - .values(topic_data) - .common_values(full_contact_info_id: full_contact_info['id']) - .timestamps - .returning('*') - .execute + .values(topic_data) + .common_values(full_contact_info_id: full_contact_info['id']) + .timestamps + .returning('*') + .execute end end - if result['organizations'].present? Qx.delete_from('full_contact_orgs') .where(full_contact_info_id: full_contact_info['id']) .execute - org_data = result['organizations'].map{|h| { - is_primary: h.is_primary, - name: h.name, - start_date: h.start_date, - end_date: h.end_date, - title: h.title, - current: h.current - } } - .map{|h| h[:end_date] = Format::Date.parse_partial_str(h[:end_date]); h} - .map{|h| h[:start_date] = Format::Date.parse_partial_str(h[:start_date]); h} + org_data = result['organizations'].map do |h| + { + is_primary: h.is_primary, + name: h.name, + start_date: h.start_date, + end_date: h.end_date, + title: h.title, + current: h.current + } + end + .map { |h| h[:end_date] = Format::Date.parse_partial_str(h[:end_date]); h } + .map { |h| h[:start_date] = Format::Date.parse_partial_str(h[:start_date]); h } full_contact_orgs = Qx.insert_into(:full_contact_orgs) - .values(org_data) - .common_values(full_contact_info_id: full_contact_info['id']) - .timestamps - .returning('*') - .execute + .values(org_data) + .common_values(full_contact_info_id: full_contact_info['id']) + .timestamps + .returning('*') + .execute end - return { + { 'full_contact_info' => full_contact_info, 'full_contact_photos' => full_contact_photos, 'full_contact_social_profiles' => full_contact_social_profiles, @@ -176,35 +176,30 @@ module InsertFullContactInfos # Delete all orphaned full contact infos that do not have supporters # or full_contact photos, social_profiles, topics, orgs, etc that do not have a parent info def self.cleanup_orphans - Qx.delete_from("full_contact_infos") - .where("id IN ($ids)", ids: Qx.select("full_contact_infos.id") - .from("full_contact_infos") - .left_join("supporters", "full_contact_infos.supporter_id=supporters.id") - .where("supporters.id IS NULL") - ).ex - Qx.delete_from("full_contact_photos") - .where("id IN ($ids)", ids: Qx.select("full_contact_photos.id") - .from("full_contact_photos") - .left_join("full_contact_infos", "full_contact_infos.id=full_contact_photos.full_contact_info_id") - .where("full_contact_infos.id IS NULL") - ).ex - Qx.delete_from("full_contact_social_profiles") - .where("id IN ($ids)", ids: Qx.select("full_contact_social_profiles.id") - .from("full_contact_social_profiles") - .left_join("full_contact_infos", "full_contact_infos.id=full_contact_social_profiles.full_contact_info_id") - .where("full_contact_infos.id IS NULL") - ).ex - Qx.delete_from("full_contact_topics") - .where("id IN ($ids)", ids: Qx.select("full_contact_topics.id") - .from("full_contact_topics") - .left_join("full_contact_infos", "full_contact_infos.id=full_contact_topics.full_contact_info_id") - .where("full_contact_infos.id IS NULL") - ).ex - Qx.delete_from("full_contact_orgs") - .where("id IN ($ids)", ids: Qx.select("full_contact_orgs.id") - .from("full_contact_orgs") - .left_join("full_contact_infos", "full_contact_infos.id=full_contact_orgs.full_contact_info_id") - .where("full_contact_infos.id IS NULL") - ).ex + Qx.delete_from('full_contact_infos') + .where('id IN ($ids)', ids: Qx.select('full_contact_infos.id') + .from('full_contact_infos') + .left_join('supporters', 'full_contact_infos.supporter_id=supporters.id') + .where('supporters.id IS NULL')).ex + Qx.delete_from('full_contact_photos') + .where('id IN ($ids)', ids: Qx.select('full_contact_photos.id') + .from('full_contact_photos') + .left_join('full_contact_infos', 'full_contact_infos.id=full_contact_photos.full_contact_info_id') + .where('full_contact_infos.id IS NULL')).ex + Qx.delete_from('full_contact_social_profiles') + .where('id IN ($ids)', ids: Qx.select('full_contact_social_profiles.id') + .from('full_contact_social_profiles') + .left_join('full_contact_infos', 'full_contact_infos.id=full_contact_social_profiles.full_contact_info_id') + .where('full_contact_infos.id IS NULL')).ex + Qx.delete_from('full_contact_topics') + .where('id IN ($ids)', ids: Qx.select('full_contact_topics.id') + .from('full_contact_topics') + .left_join('full_contact_infos', 'full_contact_infos.id=full_contact_topics.full_contact_info_id') + .where('full_contact_infos.id IS NULL')).ex + Qx.delete_from('full_contact_orgs') + .where('id IN ($ids)', ids: Qx.select('full_contact_orgs.id') + .from('full_contact_orgs') + .left_join('full_contact_infos', 'full_contact_infos.id=full_contact_orgs.full_contact_info_id') + .where('full_contact_infos.id IS NULL')).ex end end diff --git a/lib/insert/insert_import.rb b/lib/insert/insert_import.rb index 5de38679..d1c2adc7 100644 --- a/lib/insert/insert_import.rb +++ b/lib/insert/insert_import.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' require 'required_keys' @@ -9,22 +11,19 @@ require 'insert/insert_custom_field_joins' require 'insert/insert_tag_joins' module InsertImport - # Wrap the import in a transaction and email any errors def self.from_csv_safe(data) - begin - Qx.transaction do - InsertImport.from_csv(data) - end - rescue Exception => e - body = "Import failed. Error: #{e}" - GenericMailer.generic_mail( - 'support@commitchange.com', 'Jay Bot', # FROM - body, - 'Import error', # SUBJECT - 'support@commitchange.com', 'Jay' # TO - ).deliver + Qx.transaction do + InsertImport.from_csv(data) end + rescue Exception => e + body = "Import failed. Error: #{e}" + GenericMailer.generic_mail( + 'support@commitchange.com', 'Jay Bot', # FROM + body, + 'Import error', # SUBJECT + 'support@commitchange.com', 'Jay' # TO + ).deliver end # Insert a bunch of Supporter and related data using a CSV and a bunch of header_matches @@ -33,22 +32,21 @@ module InsertImport # data: nonprofit_id, user_email, user_id, file, header_matches # Will send a notification email to user_email when the import is completed def self.from_csv(data) - ParamValidation.new(data, { - file_uri: {required: true}, - header_matches: {required: true}, - nonprofit_id: {required: true, is_integer: true}, - user_email: {required: true} - }) + ParamValidation.new(data, + file_uri: { required: true }, + header_matches: { required: true }, + nonprofit_id: { required: true, is_integer: true }, + user_email: { required: true }) import = Qx.insert_into(:imports) - .values({ - date: Time.current, - nonprofit_id: data[:nonprofit_id], - user_id: data[:user_id] - }) - .timestamps - .returning('*') - .execute.first + .values( + date: Time.current, + nonprofit_id: data[:nonprofit_id], + user_id: data[:user_id] + ) + .timestamps + .returning('*') + .execute.first row_count = 0 imported_count = 0 supporter_ids = [] @@ -59,9 +57,10 @@ module InsertImport CSV.new(open(data[:file_uri]), headers: :first_row).each do |row| row_count += 1 # triplet of [header_name, value, import_key] - matches = row.map{|key, val| [key, val, data[:header_matches][key]]} + matches = row.map { |key, val| [key, val, data[:header_matches][key]] } next if matches.empty? - table_data = matches.reduce({}) do |acc, triplet| + + table_data = matches.each_with_object({}) do |triplet, acc| key, val, match = triplet if match == 'custom_field' acc['custom_fields'] ||= [] @@ -76,7 +75,6 @@ module InsertImport acc[table][col] = val end end - acc end # Create supporter record @@ -96,11 +94,11 @@ module InsertImport if table_data['supporter']['id'] && table_data['custom_fields'] && table_data['custom_fields'].any? InsertCustomFieldJoins.find_or_create(data[:nonprofit_id], [table_data['supporter']['id']], table_data['custom_fields']) end - + # Create new tags if table_data['supporter']['id'] && table_data['tags'] && table_data['tags'].any? # Split tags by semicolons - tags = table_data['tags'].select{|t| t.present?}.map{|t| t.split(/[;,]/).map(&:strip)}.flatten + tags = table_data['tags'].select(&:present?).map { |t| t.split(/[;,]/).map(&:strip) }.flatten InsertTagJoins.find_or_create(data[:nonprofit_id], [table_data['supporter']['id']], tags) end @@ -119,7 +117,7 @@ module InsertImport # Create payment record if table_data['donation'] && table_data['donation']['id'] - table_data['payment'] = Qx.insert_into(:payments).values({ + table_data['payment'] = Qx.insert_into(:payments).values( gross_amount: table_data['donation']['amount'], fee_total: 0, net_amount: table_data['donation']['amount'], @@ -129,8 +127,8 @@ module InsertImport donation_id: table_data['donation']['id'], towards: table_data['donation']['designation'], date: table_data['donation']['date'] - }).ts.returning('*') - .execute.first + ).ts.returning('*') + .execute.first imported_count += 1 else table_data['payment'] = {} @@ -138,7 +136,7 @@ module InsertImport # Create offsite payment record if table_data['donation'] && table_data['donation']['id'] - table_data['offsite_payment'] = Qx.insert_into(:offsite_payments).values({ + table_data['offsite_payment'] = Qx.insert_into(:offsite_payments).values( gross_amount: table_data['donation']['amount'], check_number: GetData.chain(table_data['offsite_payment'], 'check_number'), kind: table_data['offsite_payment'] && table_data['offsite_payment']['check_number'] ? 'check' : '', @@ -147,8 +145,8 @@ module InsertImport donation_id: table_data['donation']['id'], payment_id: table_data['payment']['id'], date: table_data['donation']['date'] - }).ts.returning('*') - .execute.first + ).ts.returning('*') + .execute.first imported_count += 1 else table_data['offsite_payment'] = {} @@ -161,12 +159,12 @@ module InsertImport InsertActivities.for_offsite_donations(created_payment_ids) if created_payment_ids.count > 0 import = Qx.update(:imports) - .set(row_count: row_count, imported_count: imported_count) - .where(id: import['id']) - .returning('*') - .execute.first + .set(row_count: row_count, imported_count: imported_count) + .where(id: import['id']) + .returning('*') + .execute.first InsertFullContactInfos.enqueue(supporter_ids) if supporter_ids.any? - ImportMailer.delay.import_completed_notification(import['id']) - return import + ImportCompletedJob.perform_later(Import.find(import['id'])) + import end end diff --git a/lib/insert/insert_nonprofit_keys.rb b/lib/insert/insert_nonprofit_keys.rb index 10dc47db..c1b70160 100644 --- a/lib/insert/insert_nonprofit_keys.rb +++ b/lib/insert/insert_nonprofit_keys.rb @@ -1,35 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'httparty' require 'cypher' - module InsertNonprofitKeys include HTTParty - def self.insert_mailchimp_access_token(npo_id, code) - form_data = "grant_type=authorization_code&client_id=#{URI.escape ENV['MAILCHIMP_OAUTH_CLIENT_ID']}&client_secret=#{ENV['MAILCHIMP_OAUTH_CLIENT_SECRET']}&redirect_uri=#{ENV['MAILCHIMP_REDIRECT_URL']}%2Fmailchimp-landing&code=#{URI.escape code}" + def self.insert_mailchimp_access_token(npo_id, code) + form_data = "grant_type=authorization_code&client_id=#{URI.escape ENV['MAILCHIMP_OAUTH_CLIENT_ID']}&client_secret=#{ENV['MAILCHIMP_OAUTH_CLIENT_SECRET']}&redirect_uri=#{ENV['MAILCHIMP_REDIRECT_URL']}%2Fmailchimp-landing&code=#{URI.escape code}" - response = post('https://login.mailchimp.com/oauth2/token', { body: form_data }) - if response['error'] - raise Exception.new(response['error']) - end - response['access_token'] = Cypher.encrypt(response['access_token']) + response = post('https://login.mailchimp.com/oauth2/token', body: form_data) + raise Exception, response['error'] if response['error'] - key_row_id = Qx.select("*") - .from(:nonprofit_keys).where(nonprofit_id: npo_id) - .execute.map{|h| h['id']}.first + response['access_token'] = Cypher.encrypt(response['access_token']) - if key_row_id.nil? - Qx.insert_into(:nonprofit_keys) - .values({nonprofit_id: npo_id, mailchimp_token: response['access_token'].to_json}) - .ts.execute - else - Qx.update(:nonprofit_keys) - .set(mailchimp_token: response['access_token']) - .ts.where({'id' => key_row_id}) - .execute - end + key_row_id = Qx.select('*') + .from(:nonprofit_keys).where(nonprofit_id: npo_id) + .execute.map { |h| h['id'] }.first - return response['access_token'] + if key_row_id.nil? + Qx.insert_into(:nonprofit_keys) + .values(nonprofit_id: npo_id, mailchimp_token: response['access_token'].to_json) + .ts.execute + else + Qx.update(:nonprofit_keys) + .set(mailchimp_token: response['access_token']) + .ts.where('id' => key_row_id) + .execute end + + response['access_token'] + end end diff --git a/lib/insert/insert_payout.rb b/lib/insert/insert_payout.rb index bfc6eabe..e9acb808 100644 --- a/lib/insert/insert_payout.rb +++ b/lib/insert/insert_payout.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Create a new payout @@ -10,82 +12,83 @@ require 'update/update_disputes' require 'param_validation' module InsertPayout - # Pass in the following inside the data hash: # - stripe_account_id # - email # - user_ip # - bank_name # options hash can have a :date (before date) for only paying out funds before a certain date (useful for only disbursing the prev month) - def self.with_stripe(np_id, data, options={}) - bigger_data = (data ? data : {}).merge(np_id: np_id) - ParamValidation.new(bigger_data, { - np_id: {required: true, is_integer: true}, - stripe_account_id: {not_blank: true, required: true}, - email: {not_blank: true, required: true}, - user_ip: {not_blank: true, required: true}, - bank_name: {not_blank: true, required: true} - }) + def self.with_stripe(np_id, data, options = {}) + bigger_data = (data || {}).merge(np_id: np_id) + ParamValidation.new(bigger_data, + np_id: { required: true, is_integer: true }, + stripe_account_id: { not_blank: true, required: true }, + email: { not_blank: true, required: true }, + user_ip: { not_blank: true, required: true }, + bank_name: { not_blank: true, required: true }) options ||= {} entities = RetrieveActiveRecordItems.retrieve_from_keys(bigger_data, Nonprofit => :np_id) payment_ids = QueryPayments.ids_for_payout(np_id, options) if payment_ids.count < 1 - raise ArgumentError.new("No payments are available for disbursal on this account.") + raise ArgumentError, 'No payments are available for disbursal on this account.' end + totals = QueryPayments.get_payout_totals(payment_ids) nonprofit_currency = entities[:np_id].currency now = Time.current begin stripe_transfer = StripeUtils.create_transfer(totals['net_amount'], data[:stripe_account_id], nonprofit_currency) - Psql.transaction do - # Create the Transfer on Stripe + Psql.transaction do + # Create the Transfer on Stripe - # Retrieve all payments with available charges and undisbursed refunds - # Mark all the above payments as disbursed - UpdateCharges.disburse_all_with_payments(payment_ids) - # Mark all the above refunds as disbursed - UpdateRefunds.disburse_all_with_payments(payment_ids) - # Mark all disputes as lost_and_paid - UpdateDisputes.disburse_all_with_payments(payment_ids) - # Get gross total, total fees, net total, and total count - # Create the payout record (whether it succeeded on Stripe or not) - payout = Psql.execute( - Qexpr.new.insert(:payouts, [{ - net_amount: totals['net_amount'], - nonprofit_id: np_id, - failure_message: stripe_transfer['failure_message'], - status: stripe_transfer.status, - fee_total: totals['fee_total'], - gross_amount: totals['gross_amount'], - email: data[:email], - count: totals['count'], - stripe_transfer_id: stripe_transfer.id, - user_ip: data[:user_ip], - ach_fee: 0, - bank_name: data[:bank_name]}]) - .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name') - ).first - # Create PaymentPayout records linking all the payments to the payout - pps = Psql.execute(Qexpr.new.insert('payment_payouts', payment_ids.map{|id| {payment_id: id.to_i}}, {common_data: {payout_id: payout['id'].to_i}})) - NonprofitMailer.delay.pending_payout_notification(payout['id'].to_i) + # Retrieve all payments with available charges and undisbursed refunds + # Mark all the above payments as disbursed + UpdateCharges.disburse_all_with_payments(payment_ids) + # Mark all the above refunds as disbursed + UpdateRefunds.disburse_all_with_payments(payment_ids) + # Mark all disputes as lost_and_paid + UpdateDisputes.disburse_all_with_payments(payment_ids) + # Get gross total, total fees, net total, and total count + # Create the payout record (whether it succeeded on Stripe or not) + payout = Psql.execute( + Qexpr.new.insert(:payouts, [{ + net_amount: totals['net_amount'], + nonprofit_id: np_id, + failure_message: stripe_transfer['failure_message'], + status: stripe_transfer.status, + fee_total: totals['fee_total'], + gross_amount: totals['gross_amount'], + email: data[:email], + count: totals['count'], + stripe_transfer_id: stripe_transfer.id, + user_ip: data[:user_ip], + ach_fee: 0, + bank_name: data[:bank_name] + }]) + .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name') + ).first + # Create PaymentPayout records linking all the payments to the payout + pps = Psql.execute(Qexpr.new.insert('payment_payouts', payment_ids.map { |id| { payment_id: id.to_i } }, common_data: { payout_id: payout['id'].to_i })) + PayoutPendingJob.perform_later(Payout.find(payout['id'].to_i)) return payout end rescue Stripe::StripeError => e payout = Psql.execute( - Qexpr.new.insert(:payouts, [{ - net_amount: totals['net_amount'], - nonprofit_id: np_id, - failure_message: e.message, - status: 'failed', - fee_total: totals['fee_total'], - gross_amount: totals['gross_amount'], - email: data[:email], - count: totals['count'], - stripe_transfer_id: nil, - user_ip: data[:user_ip], - ach_fee: 0, - bank_name: data[:bank_name]}]) - .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name') + Qexpr.new.insert(:payouts, [{ + net_amount: totals['net_amount'], + nonprofit_id: np_id, + failure_message: e.message, + status: 'failed', + fee_total: totals['fee_total'], + gross_amount: totals['gross_amount'], + email: data[:email], + count: totals['count'], + stripe_transfer_id: nil, + user_ip: data[:user_ip], + ach_fee: 0, + bank_name: data[:bank_name] + }]) + .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name') ).first return payout end diff --git a/lib/insert/insert_recurring_donation.rb b/lib/insert/insert_recurring_donation.rb index fc2acba6..fbadf5a0 100644 --- a/lib/insert/insert_recurring_donation.rb +++ b/lib/insert/insert_recurring_donation.rb @@ -1,41 +1,40 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertRecurringDonation - # Create a recurring_donation, donation, payment, charge, and activity # See controllers/nonprofits/recurring_donations_controller#create for the data params to pass in def self.with_stripe(data) data = data.with_indifferent_access ParamValidation.new(data, InsertDonation.common_param_validations - .merge(token: {required: true, format: UUID::Regex})) + .merge(token: { required: true, format: UUID::Regex })) - unless data[:recurring_donation].nil? + if data[:recurring_donation].nil? + data[:recurring_donation] = {} + else - ParamValidation.new(data[:recurring_donation], { - interval: {is_integer: true}, - start_date: {can_be_date: true}, - time_unit: {included_in: %w(month day week year)}, - paydate: {is_integer:true} - }) - if (data[:recurring_donation][:paydate]) + ParamValidation.new(data[:recurring_donation], + interval: { is_integer: true }, + start_date: { can_be_date: true }, + time_unit: { included_in: %w[month day week year] }, + paydate: { is_integer: true }) + if data[:recurring_donation][:paydate] data[:recurring_donation][:paydate] = data[:recurring_donation][:paydate].to_i end - ParamValidation.new(data[:recurring_donation], { - paydate: {min:1, max:28} - }) + ParamValidation.new(data[:recurring_donation], + paydate: { min: 1, max: 28 }) - else - data[:recurring_donation] = {} end source_token = QuerySourceToken.get_and_increment_source_token(data[:token], nil) tokenizable = source_token.tokenizable QuerySourceToken.validate_source_token_type(source_token) - entities = RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id}) + entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id) - entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, {Campaign => :campaign_id, Event => :event_id, Profile => :profile_id}, true)) + entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, { Campaign => :campaign_id, Event => :event_id, Profile => :profile_id }, true)) InsertDonation.validate_entities(entities) @@ -52,10 +51,10 @@ module InsertRecurringDonation data = data.except(:old_donation).except('old_donation') # if start date is today, make initial charge first test_start_date = get_test_start_date(data) - if test_start_date == nil || Time.current >= test_start_date + if test_start_date.nil? || Time.current >= test_start_date result = result.merge(InsertDonation.insert_charge(data)) if result['charge']['status'] == 'failed' - raise ChargeError.new(result['charge']['failure_message']) + raise ChargeError, result['charge']['failure_message'] end end @@ -63,7 +62,7 @@ module InsertRecurringDonation result['donation'] = InsertDonation.insert_donation(data, entities) entities[:donation_id] = result['donation'] # Create the recurring_donation record - result['recurring_donation'] = insert_recurring_donation(data,entities) + result['recurring_donation'] = insert_recurring_donation(data, entities) # Update charge foreign keys if result['payment'] InsertDonation.update_donation_keys(result) @@ -71,13 +70,11 @@ module InsertRecurringDonation result['activity'] = InsertActivities.for_recurring_donations([result['payment'].id]) end # Send receipts - EmailJobQueue.queue(JobTypes::NonprofitPaymentNotificationJob, result['donation'].id) - EmailJobQueue.queue(JobTypes::DonorPaymentNotificationJob, result['donation'].id, entities[:supporter_id].locale) - QueueDonations.delay.execute_for_donation(result['donation']['id']) - return result + HoudiniEventPublisher.announce(:recurring_donation_create, result['donation'], entities[:supporter_id].locale) + result end -def self.with_sepa(data) + def self.with_sepa(data) data = set_defaults(data) data = data.merge(payment_provider: payment_provider(data)) result = {} @@ -87,38 +84,33 @@ def self.with_sepa(data) end result['donation'] = Psql.execute(Qexpr.new.insert(:donations, [ - data.except(:recurring_donation) - ]).returning('*')).first + data.except(:recurring_donation) + ]).returning('*')).first result['recurring_donation'] = Psql.execute(Qexpr.new.insert(:recurring_donations, [ - data[:recurring_donation].merge(donation_id: result['donation']['id']) - ]).returning('*')).first + data[:recurring_donation].merge(donation_id: result['donation']['id']) + ]).returning('*')).first - if result['payment'] - InsertDonation.update_donation_keys(result) - end + InsertDonation.update_donation_keys(result) if result['payment'] - DonationMailer.delay.nonprofit_payment_notification(result['donation']['id']) - DonationMailer.delay.donor_direct_debit_notification(result['donation']['id'], locale_for_supporter(result['donation']['supporter_id'])) - - QueueDonations.delay.execute_for_donation(result['donation']['id']) + HoudiniEventPublisher.announce(:recurring_donation_create, result['donation'], entities[:supporter_id].locale) { status: 200, json: result } - end - - - # the data model here is brutal. This needs to get cleaned up. - def self.convert_donation_to_recurring_donation(donation_id) - ParamValidation.new({donation_id: donation_id}, {donation_id: {:required => true, :is_integer => true}}) - don = Donation.where('id = ? ', donation_id).first - if !don - raise ParamValidation::ValidationError.new("#{donation_id} is not a valid donation", {:key => :donation_id, :val => donation_id}) end - rd = insert_recurring_donation({amount:don.amount, email: don.supporter.email, anonymous: don.anonymous, origin_url: don.origin_url, recurring_donation: { start_date: don.created_at, :paydate => convert_date_to_valid_paydate(don.created_at)}, date: don.created_at}, {supporter_id: don.supporter, nonprofit_id: don.nonprofit, donation_id: don}) + + # the data model here is brutal. This needs to get cleaned up. + def self.convert_donation_to_recurring_donation(donation_id) + ParamValidation.new({ donation_id: donation_id }, donation_id: { required: true, is_integer: true }) + don = Donation.where('id = ? ', donation_id).first + unless don + raise ParamValidation::ValidationError.new("#{donation_id} is not a valid donation", key: :donation_id, val: donation_id) + end + + rd = insert_recurring_donation({ amount: don.amount, email: don.supporter.email, anonymous: don.anonymous, origin_url: don.origin_url, recurring_donation: { start_date: don.created_at, paydate: convert_date_to_valid_paydate(don.created_at) }, date: don.created_at }, supporter_id: don.supporter, nonprofit_id: don.nonprofit, donation_id: don) don.recurring_donation = rd don.recurring = true - don.payment.kind = "RecurringDonation" + don.payment.kind = 'RecurringDonation' don.payment.save! rd.save! don.save! @@ -126,7 +118,7 @@ def self.with_sepa(data) rd end - def self.insert_recurring_donation(data, entities) + def self.insert_recurring_donation(data, entities) rd = RecurringDonation.new rd.amount = data[:amount] rd.nonprofit = entities[:nonprofit_id] @@ -134,17 +126,17 @@ def self.with_sepa(data) rd.supporter_id = entities[:supporter_id].id rd.active = true rd.edit_token = SecureRandom.uuid - rd.n_failures= 0 - rd.email= entities[:supporter_id].email - rd.interval = data[:recurring_donation][:interval].blank? ? 1 : data[:recurring_donation][:interval] - rd.time_unit= data[:recurring_donation][:time_unit].blank? ? 'month' : data[:recurring_donation][:time_unit] - if data[:recurring_donation][:start_date].blank? - rd.start_date= Time.current.beginning_of_day - elsif data[:recurring_donation][:start_date].is_a? Time - rd.start_date = data[:recurring_donation][:start_date] - else - rd.start_date = Chronic.parse(data[:recurring_donation][:start_date]) - end + rd.n_failures = 0 + rd.email = entities[:supporter_id].email + rd.interval = data[:recurring_donation][:interval].blank? ? 1 : data[:recurring_donation][:interval] + rd.time_unit = data[:recurring_donation][:time_unit].blank? ? 'month' : data[:recurring_donation][:time_unit] + rd.start_date = if data[:recurring_donation][:start_date].blank? + Time.current.beginning_of_day + elsif data[:recurring_donation][:start_date].is_a? Time + data[:recurring_donation][:start_date] + else + Chronic.parse(data[:recurring_donation][:start_date]) + end if rd.time_unit == 'month' && rd.interval == 1 && data[:recurring_donation][:paydate].nil? rd.paydate = convert_date_to_valid_paydate(rd.start_date) @@ -154,22 +146,20 @@ def self.with_sepa(data) rd.save! rd - end -def self.get_test_start_date(data) + end + + def self.get_test_start_date(data) unless data[:recurring_donation] && data[:recurring_donation][:start_date] return nil end - return Chronic.parse(data[:recurring_donation][:start_date]) - - - end - + Chronic.parse(data[:recurring_donation][:start_date]) + end def self.locale_for_supporter(supporter_id) Psql.execute( Qexpr.new.select(:locale).from(:supporters) - .where("id=$id", id: supporter_id) + .where('id=$id', id: supporter_id) ).first['locale'] end @@ -183,6 +173,6 @@ def self.get_test_start_date(data) def self.convert_date_to_valid_paydate(date) day = date.day - return day > 28 ? 28 : day + day > 28 ? 28 : day end end diff --git a/lib/insert/insert_refunds.rb b/lib/insert/insert_refunds.rb index c50a7b47..a6861454 100644 --- a/lib/insert/insert_refunds.rb +++ b/lib/insert/insert_refunds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'format/currency' require 'validation_error' @@ -10,30 +12,28 @@ require 'param_validation' require 'insert/insert_activities' module InsertRefunds - # Refund a given charge, up to its net amount # params: amount, donation obj def self.with_stripe(charge, h) - ParamValidation.new(charge, { - payment_id: {required: true, is_integer: true}, - stripe_charge_id: {required: true, format: /^(test_)?ch_.*$/}, - amount: {required: true, is_integer: true, min: 1}, - id: {required: true, is_integer: true}, - nonprofit_id: {required: true, is_integer: true}, - supporter_id: {required: true, is_integer: true} - }) - ParamValidation.new(h, { amount: {required: true, is_integer: true, min: 1} }) - - original_payment = Qx.select("*").from("payments").where(id: charge['payment_id']).execute.first - raise ActiveRecord::RecordNotFound.new("Cannot find original payment for refund on charge #{charge['id']}") if original_payment.nil? + ParamValidation.new(charge, + payment_id: { required: true, is_integer: true }, + stripe_charge_id: { required: true, format: /^(test_)?ch_.*$/ }, + amount: { required: true, is_integer: true, min: 1 }, + id: { required: true, is_integer: true }, + nonprofit_id: { required: true, is_integer: true }, + supporter_id: { required: true, is_integer: true }) + ParamValidation.new(h, amount: { required: true, is_integer: true, min: 1 }) + + original_payment = Qx.select('*').from('payments').where(id: charge['payment_id']).execute.first + raise ActiveRecord::RecordNotFound, "Cannot find original payment for refund on charge #{charge['id']}" if original_payment.nil? if original_payment['refund_total'].to_i + h['amount'].to_i > original_payment['gross_amount'].to_i - raise RuntimeError.new("Refund amount must be less than the net amount of the payment (for charge #{charge['id']})") + raise "Refund amount must be less than the net amount of the payment (for charge #{charge['id']})" end stripe_charge = Stripe::Charge.retrieve(charge['stripe_charge_id']) - refund_post_data = {'amount' => h['amount'], 'refund_application_fee' => true, 'reverse_transfer' => true} + refund_post_data = { 'amount' => h['amount'], 'refund_application_fee' => true, 'reverse_transfer' => true } refund_post_data['reason'] = h['reason'] unless h['reason'].blank? # Stripe will error on blank reason field stripe_refund = stripe_charge.refunds.create(refund_post_data) h['stripe_refund_id'] = stripe_refund.id @@ -45,19 +45,19 @@ module InsertRefunds fees = (h['amount'] * -original_payment['fee_total'] / original_payment['gross_amount']).ceil net = gross + fees # Create a corresponding negative payment record - payment_row = Qx.insert_into(:payments).values({ - gross_amount: gross, - fee_total: fees, - net_amount: net, - kind: 'Refund', - towards: original_payment['towards'], - date: refund_row['created_at'], - nonprofit_id: charge['nonprofit_id'], - supporter_id: charge['supporter_id'] - }) - .timestamps - .returning('*') - .execute.first + payment_row = Qx.insert_into(:payments).values( + gross_amount: gross, + fee_total: fees, + net_amount: net, + kind: 'Refund', + towards: original_payment['towards'], + date: refund_row['created_at'], + nonprofit_id: charge['nonprofit_id'], + supporter_id: charge['supporter_id'] + ) + .timestamps + .returning('*') + .execute.first InsertActivities.for_refunds([payment_row['id']]) @@ -66,10 +66,7 @@ module InsertRefunds # Update original payment to increment its refund_total for any future refund attempts Qx.update(:payments).set("refund_total=refund_total + #{h['amount'].to_i}").ts.where(id: original_payment['id']).execute # Send the refund receipts in a delayed job - Delayed::Job.enqueue JobTypes::DonorRefundNotificationJob.new(refund_row['id']) - Delayed::Job.enqueue JobTypes::NonprofitRefundNotificationJob.new(refund_row['id']) - return {'payment' => payment_row, 'refund' => refund_row} + HoudiniEventPublisher.announce(:create_refund, Refund.find(refund_row['id'])) + { 'payment' => payment_row, 'refund' => refund_row } end - end - diff --git a/lib/insert/insert_source_token.rb b/lib/insert/insert_source_token.rb index ece45da2..a9e92a67 100644 --- a/lib/insert/insert_source_token.rb +++ b/lib/insert/insert_source_token.rb @@ -1,14 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertSourceToken - - def self.create_record(tokenizable, params={}) - ParamValidation.new({tokenizable:tokenizable}.merge(params), { - tokenizable: {required:true}, - event: {is_a: Event}, - expiration_time: {is_integer: true, min: 1}, - max_uses: {is_integer: true, min: 1} - }) - if (params[:event] != nil) + def self.create_record(tokenizable, params = {}) + ParamValidation.new({ tokenizable: tokenizable }.merge(params), + tokenizable: { required: true }, + event: { is_a: Event }, + expiration_time: { is_integer: true, min: 1 }, + max_uses: { is_integer: true, min: 1 }) + if !params[:event].nil? max_uses = params[:max_uses] || Settings.source_tokens.event_donation_source.max_uses expiration_diff = params[:expiration_time] || Settings.source_tokens.event_donation_source.time_after_event expiration = params[:event].end_datetime + expiration_diff.to_i @@ -26,6 +26,4 @@ module InsertSourceToken c.save! c end - - -end \ No newline at end of file +end diff --git a/lib/insert/insert_supporter.rb b/lib/insert/insert_supporter.rb index 594b93a3..b37906bd 100644 --- a/lib/insert/insert_supporter.rb +++ b/lib/insert/insert_supporter.rb @@ -1,58 +1,57 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' require 'i18n' module InsertSupporter - - def self.create_or_update(np_id, data, update=false) - ParamValidation.new(data.merge(np_id: np_id), { - np_id: {required: true, is_integer: true} - }) - address_keys = ['name', 'address', 'city', 'country', 'state_code'] + def self.create_or_update(np_id, data, update = false) + ParamValidation.new(data.merge(np_id: np_id), + np_id: { required: true, is_integer: true }) + address_keys = %w[name address city country state_code] custom_fields = data['customFields'] data = HashWithIndifferentAccess.new(Format::RemoveDiacritics.from_hash(data, address_keys)) - .except(:customFields) + .except(:customFields) - supporter = Qx.select("*").from(:supporters) - .where("name = $n AND email = $e", n: data[:name], e: data[:email]) - .and_where("nonprofit_id=$id", id: np_id) - .and_where("coalesce(deleted, FALSE)=FALSE") - .execute.last - if supporter and update + supporter = Qx.select('*').from(:supporters) + .where('name = $n AND email = $e', n: data[:name], e: data[:email]) + .and_where('nonprofit_id=$id', id: np_id) + .and_where('coalesce(deleted, FALSE)=FALSE') + .execute.last + if supporter && update supporter = Qx.update(:supporters) - .set(defaults(data)) - .where("id=$id", id: supporter['id']) - .returning('*') - .timestamps - .execute.last - else + .set(defaults(data)) + .where('id=$id', id: supporter['id']) + .returning('*') + .timestamps + .execute.last + else supporter = Qx.insert_into(:supporters) - .values(defaults(data).merge(nonprofit_id: np_id)) - .returning('*') - .timestamps - .execute.last - end - - if custom_fields - InsertCustomFieldJoins.find_or_create(np_id, [supporter['id']], custom_fields) + .values(defaults(data).merge(nonprofit_id: np_id)) + .returning('*') + .timestamps + .execute.last end - #GeocodeModel.delay.supporter(supporter['id']) + if custom_fields + InsertCustomFieldJoins.find_or_create(np_id, [supporter['id']], custom_fields) + end + + # GeocodeModel.delay.supporter(supporter['id']) InsertFullContactInfos.enqueue([supporter['id']]) - return supporter + supporter end - - def self.defaults(h) + def self.defaults(h) h = h.except('profile_id') unless h['profile_id'].present? - if h['first_name'].present? || h['last_name'].present? - h['name'] = h['first_name'] || h['last_name'] - if h['first_name'] && h['last_name'] - h['name'] = "#{h['first_name'].strip} #{h['last_name'].strip}" - end - end + if h['first_name'].present? || h['last_name'].present? + h['name'] = h['first_name'] || h['last_name'] + if h['first_name'] && h['last_name'] + h['name'] = "#{h['first_name'].strip} #{h['last_name'].strip}" + end + end h['email_unsubscribe_uuid'] = SecureRandom.uuid @@ -62,8 +61,8 @@ module InsertSupporter h = h.except('address_line2') - return h - end + h + end # pass in a hash of supporter info, as well as # any property with tag_x will create a tag with name 'name' @@ -78,15 +77,14 @@ module InsertSupporter # The above will create a supporter with name/email, one tag with name 'xy', # and one field with name 'xy' and value 420 def self.with_tags_and_fields(np_id, data) - tags = data.select{|key, val| key.match(/^tag_/)}.map{|key, val| key.gsub('tag_', '')} - fields = data.select{|key, val| key.match(/^field_/)}.map{|key, val| [key.gsub('field_', ''), val]} - supp_cols = data.select{|key, val| !key.match(/^field_/) && !key.match(/^tag_/)} + tags = data.select { |key, _val| key.match(/^tag_/) }.map { |key, _val| key.gsub('tag_', '') } + fields = data.select { |key, _val| key.match(/^field_/) }.map { |key, val| [key.gsub('field_', ''), val] } + supp_cols = data.select { |key, _val| !key.match(/^field_/) && !key.match(/^tag_/) } supporter = create_or_update(np_id, supp_cols) - InsertTagJoins.delay.find_or_create(np_id, [supporter['id']], tags) if tags.any? - InsertCustomFieldJoins.delay.find_or_create(np_id, [supporter['id']], fields) if fields.any? + InsertTagJoins.find_or_create(np_id, [supporter['id']], tags) if tags.any? + InsertCustomFieldJoins.find_or_create(np_id, [supporter['id']], fields) if fields.any? - return supporter + supporter end - end diff --git a/lib/insert/insert_supporter_notes.rb b/lib/insert/insert_supporter_notes.rb index 003f2d06..f868c624 100644 --- a/lib/insert/insert_supporter_notes.rb +++ b/lib/insert/insert_supporter_notes.rb @@ -1,22 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'param_validation' require 'qx' module InsertSupporterNotes def self.create(notes) - ParamValidation.new(notes, { - root: { array_of_hashes: { - supporter_id: {required: true, is_integer: true}, - user_id: {required: true, is_integer: true}, - content: {required: true} - } } - }) + ParamValidation.new(notes, + root: { array_of_hashes: { + supporter_id: { required: true, is_integer: true }, + user_id: { required: true, is_integer: true }, + content: { required: true } + } }) inserted = Qx.insert_into(:supporter_notes) - .values(notes) - .timestamps - .returning('*') - .execute - InsertActivities.for_supporter_notes(inserted.map{|h| h['id']}) - return inserted + .values(notes) + .timestamps + .returning('*') + .execute + InsertActivities.for_supporter_notes(inserted.map { |h| h['id'] }) + inserted end end diff --git a/lib/insert/insert_tag_joins.rb b/lib/insert/insert_tag_joins.rb index 11fa284e..73423e49 100644 --- a/lib/insert/insert_tag_joins.rb +++ b/lib/insert/insert_tag_joins.rb @@ -1,10 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qx' module InsertTagJoins - - # @param [Integer] np_id id for a [Nonprofit] # @param [Integer] profile_id id for the [Profile] corresponding to the current user. Not used currently but needed # @param [Array] supporter_ids the ids of the all the supporters whose tags should be changed. @@ -15,69 +15,64 @@ module InsertTagJoins def self.in_bulk(np_id, profile_id, supporter_ids, tag_data) begin ParamValidation.new({ - np_id: np_id, - profile_id: profile_id, - supporter_ids: supporter_ids, - tag_data: tag_data - }, { - np_id: {required: true, is_integer: true}, - profile_id: {required: true, is_integer: true}, - supporter_ids: {is_array: true}, - tag_data: { required: true - # array_of_hashes: { - # selected: {required: true}, tag_master_id: {required: true, is_integer: true} - # } - } - }) + np_id: np_id, + profile_id: profile_id, + supporter_ids: supporter_ids, + tag_data: tag_data + }, + np_id: { required: true, is_integer: true }, + profile_id: { required: true, is_integer: true }, + supporter_ids: { is_array: true }, + tag_data: { required: true }) + # array_of_hashes: { + # selected: {required: true}, tag_master_id: {required: true, is_integer: true} + # } rescue ParamValidation::ValidationError => e - return {json: {error: "Validation error\n #{e.message}", errors: e.data}, status: :unprocessable_entity} + return { json: { error: "Validation error\n #{e.message}", errors: e.data }, status: :unprocessable_entity } end begin - return {json: {error: "Nonprofit #{np_id} is not valid"}, status: :unprocessable_entity} unless Nonprofit.exists?(np_id) - return {json: {error: "Profile #{profile_id} is not valid"}, status: :unprocessable_entity} unless Profile.exists?(profile_id) - + return { json: { error: "Nonprofit #{np_id} is not valid" }, status: :unprocessable_entity } unless Nonprofit.exists?(np_id) + return { json: { error: "Profile #{profile_id} is not valid" }, status: :unprocessable_entity } unless Profile.exists?(profile_id) # verify that the supporters belong to the nonprofit original_supporter_request = supporter_ids.count supporter_ids = Supporter.where('nonprofit_id = ? and id IN (?)', np_id, supporter_ids).pluck(:id) unless supporter_ids.any? - return {json: {inserted_count: 0, removed_count: 0}, status: :ok} + return { json: { inserted_count: 0, removed_count: 0 }, status: :ok } end # filtering the tag_data to this nonprofit - valid_ids = TagMaster.where('nonprofit_id = ? and id IN (?)', np_id, tag_data.map {|tg| tg[:tag_master_id] }).pluck(:id).to_a - filtered_tag_data = tag_data.select {|i| valid_ids.include? i[:tag_master_id].to_i} - - + valid_ids = TagMaster.where('nonprofit_id = ? and id IN (?)', np_id, tag_data.map { |tg| tg[:tag_master_id] }).pluck(:id).to_a + filtered_tag_data = tag_data.select { |i| valid_ids.include? i[:tag_master_id].to_i } # first, delete the items which should be removed - to_remove = filtered_tag_data.select{|t| !t[:selected]}.map{|t| t[:tag_master_id]} + to_remove = filtered_tag_data.reject { |t| t[:selected] }.map { |t| t[:tag_master_id] } deleted = [] if to_remove.any? deleted = Qx.delete_from(:tag_joins) - .where("supporter_id IN ($ids)", ids: supporter_ids) - .and_where("tag_master_id in ($tags)", tags: to_remove) - .returning('*') - .execute + .where('supporter_id IN ($ids)', ids: supporter_ids) + .and_where('tag_master_id in ($tags)', tags: to_remove) + .returning('*') + .execute end # next add only the selected tag_joins - to_insert = filtered_tag_data.select{|t| t[:selected]}.map{|t| t[:tag_master_id]} - insert_data = supporter_ids.map{|id| to_insert.map{|tag_master_id| {supporter_id: id, tag_master_id: tag_master_id}}}.flatten + to_insert = filtered_tag_data.select { |t| t[:selected] }.map { |t| t[:tag_master_id] } + insert_data = supporter_ids.map { |id| to_insert.map { |tag_master_id| { supporter_id: id, tag_master_id: tag_master_id } } }.flatten if insert_data.any? tags = Qx.insert_into(:tag_joins) - .values(insert_data) - .timestamps - .on_conflict() - .conflict_columns(:supporter_id, :tag_master_id).upsert(:tag_join_supporter_unique_idx) - .returning('*') - .execute + .values(insert_data) + .timestamps + .on_conflict + .conflict_columns(:supporter_id, :tag_master_id).upsert(:tag_join_supporter_unique_idx) + .returning('*') + .execute else tags = [] end rescue ActiveRecord::ActiveRecordError => e - return {json: {error: "A DB error occurred. Please contact support. \n #{e.message}"}, status: :unprocessable_entity} + return { json: { error: "A DB error occurred. Please contact support. \n #{e.message}" }, status: :unprocessable_entity } end # Create an activity for the modified tags for every supporter @@ -86,42 +81,37 @@ module InsertTagJoins # activities = Psql.execute( Qexpr.new.insert(:activities, activity_data) ) # Sync mailchimp lists, if present - Mailchimp.delay.sync_supporters_to_list_from_tag_joins(np_id, supporter_ids, tag_data) + MailchimpSupporterSyncJob.perform_later(np_id, supporter_ids, tag_data.as_json) - return {json: {inserted_count: tags.count, removed_count: deleted.count }, status: :ok} + { json: { inserted_count: tags.count, removed_count: deleted.count }, status: :ok } end - # Find or create many tag names for every supporter # Creates tag masters for tag names that are not present def self.find_or_create(np_id, supporter_ids, tag_names) # Pair each tag name with a tag master id tags = tag_names.map do |name| tm = Qx.select(:id).from(:tag_masters) - .where(name: name) - .and_where(nonprofit_id: np_id) - .execute.last - if !tm - tm = Qx.insert_into(:tag_masters).values({ - name: name, - nonprofit_id: np_id - }).ts.returning('id').execute.last - end + .where(name: name) + .and_where(nonprofit_id: np_id) + .execute.last + tm ||= Qx.insert_into(:tag_masters).values( + name: name, + nonprofit_id: np_id + ).ts.returning('id').execute.last [name, tm['id']] end tag_join_data = supporter_ids.map do |id| - tags.map{|name, tm_id| {supporter_id: id, tag_master_id: tm_id}} + tags.map { |_name, tm_id| { supporter_id: id, tag_master_id: tm_id } } end.flatten tag_joins = Qx.insert_into(:tag_joins) - .values(tag_join_data) - .ts.returning('id').execute + .values(tag_join_data) + .ts.returning('id').execute - return tag_joins + tag_joins end private - - end diff --git a/lib/insert/insert_tickets.rb b/lib/insert/insert_tickets.rb index e1ca13d6..d6c902de 100644 --- a/lib/insert/insert_tickets.rb +++ b/lib/insert/insert_tickets.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertTickets - # Will generate rows for payment, offsite_payment or charge, tickets, activities # pass in: # data: { @@ -16,32 +17,31 @@ module InsertTickets # } def self.create(data) data = data.with_indifferent_access - ParamValidation.new(data, { - tickets: {required: true, is_array: true}, - nonprofit_id: {required: true, is_reference: true}, - supporter_id: {required: true, is_reference: true}, - event_id: {required: true, is_reference: true}, - event_discount_id: {is_reference: true}, - kind: {included_in: ['free', 'charge', 'offsite']}, - token: {format: UUID::Regex}, - offsite_payment: {is_hash: true} - }) + ParamValidation.new(data, + tickets: { required: true, is_array: true }, + nonprofit_id: { required: true, is_reference: true }, + supporter_id: { required: true, is_reference: true }, + event_id: { required: true, is_reference: true }, + event_discount_id: { is_reference: true }, + kind: { included_in: %w[free charge offsite] }, + token: { format: UUID::Regex }, + offsite_payment: { is_hash: true }) - data[:tickets].each {|t| - ParamValidation.new(t, {quantity: {is_integer: true, required: true, min: 1}, ticket_level_id: {is_reference: true, required: true}}) - } + data[:tickets].each do |t| + ParamValidation.new(t, quantity: { is_integer: true, required: true, min: 1 }, ticket_level_id: { is_reference: true, required: true }) + end - ParamValidation.new(data[:offsite_payment], {kind: {included_in: %w(cash check)}}) if data[:offsite_payment] && !data[:offsite_payment][:kind].blank? + ParamValidation.new(data[:offsite_payment], kind: { included_in: %w[cash check] }) if data[:offsite_payment] && !data[:offsite_payment][:kind].blank? - entities = RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id, Event => :event_id}) + entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id, Event => :event_id) - entities.merge!(RetrieveActiveRecordItems.retrieve_from_keys(data, {EventDiscount => :event_discount_id}, true)) + entities.merge!(RetrieveActiveRecordItems.retrieve_from_keys(data, { EventDiscount => :event_discount_id }, true)) tl_entities = get_ticket_level_entities(data) validate_entities(entities, tl_entities) - #verify that enough tickets_available + # verify that enough tickets_available QueryTicketLevels.verify_tickets_available(data[:tickets]) gross_amount = QueryTicketLevels.gross_amount_from_tickets(data[:tickets], data[:event_discount_id]) @@ -63,28 +63,29 @@ module InsertTickets # Create charge for tickets elsif data['kind'] == 'charge' || !data['kind'] - source_token = QuerySourceToken.get_and_increment_source_token(data[:token],nil) + source_token = QuerySourceToken.get_and_increment_source_token(data[:token], nil) QuerySourceToken.validate_source_token_type(source_token) tokenizable = source_token.tokenizable ## does the card belong to the supporter? if tokenizable.holder != entities[:supporter_id] raise ParamValidation::ValidationError.new("Supporter #{entities[:supporter_id].id} does not own card #{tokenizable.id}", key: :token) end - result = result.merge(InsertCharge.with_stripe({ - kind: "Ticket", - towards: entities[:event_id].name, - metadata: {kind: "Ticket", event_id: entities[:event_id].id, nonprofit_id: entities[:nonprofit_id].id}, - statement: "Tickets #{entities[:event_id].name}", - amount: gross_amount, - nonprofit_id: entities[:nonprofit_id].id, - supporter_id: entities[:supporter_id].id, - card_id: tokenizable.id - })) + + result = result.merge(InsertCharge.with_stripe( + kind: 'Ticket', + towards: entities[:event_id].name, + metadata: { kind: 'Ticket', event_id: entities[:event_id].id, nonprofit_id: entities[:nonprofit_id].id }, + statement: "Tickets #{entities[:event_id].name}", + amount: gross_amount, + nonprofit_id: entities[:nonprofit_id].id, + supporter_id: entities[:supporter_id].id, + card_id: tokenizable.id + )) if result['charge']['status'] == 'failed' - raise ChargeError.new(result['charge']['failure_message']) + raise ChargeError, result['charge']['failure_message'] end else - raise ParamValidation::ValidationError.new("Ticket costs money but you didn't pay.", {key: :kind}) + raise ParamValidation::ValidationError.new("Ticket costs money but you didn't pay.", key: :kind) end end @@ -94,29 +95,28 @@ module InsertTickets result['tickets'] = generated_ticket_entities(data['tickets'], result, entities) # Create the activity rows for the tickets - InsertActivities.for_tickets(result['tickets'].map{|t| t.id}) + InsertActivities.for_tickets(result['tickets'].map(&:id)) - ticket_ids = result['tickets'].map{|t| t.id} + ticket_ids = result['tickets'].map(&:id) charge_id = result['charge'] ? result['charge'].id : nil - EmailJobQueue.queue(JobTypes::TicketMailerReceiptAdminJob, ticket_ids) - EmailJobQueue.queue(JobTypes::TicketMailerFollowupJob, ticket_ids, charge_id) - return result - end - + HoudiniEventPublisher.announce(:ticket_create, result['tickets'], result['charge']) + result + end # Generate a set of 'bid ids' (ids for each ticket scoped within the event) def self.generate_bid_ids(event_id, tickets) # Generate the bid ids last_bid_id = Psql.execute( - Qexpr.new.select("COUNT(*)").from(:tickets) - .where("event_id=$id", id: event_id)).first['count'].to_i - tickets.zip(last_bid_id + 1 .. last_bid_id + tickets.count).map{|h, id| h.merge('bid_id' => id)} + Qexpr.new.select('COUNT(*)').from(:tickets) + .where('event_id=$id', id: event_id) + ).first['count'].to_i + tickets.zip(last_bid_id + 1..last_bid_id + tickets.count).map { |h, id| h.merge('bid_id' => id) } end - #not really needed but used for breaking into the unit test and getting the IDs + # not really needed but used for breaking into the unit test and getting the IDs def self.generated_ticket_entities(ticket_data, result, entities) - ticket_data.map{|ticket_request| + ticket_data.map do |ticket_request| t = Ticket.new t.quantity = ticket_request['quantity'] t.ticket_level = ticket_request['ticket_level_id'] @@ -128,7 +128,7 @@ module InsertTickets t.event_discount = entities[:event_discount_id] t.save! t - }.to_a + end.to_a end def self.validate_entities(entities, tl_entities) @@ -141,8 +141,8 @@ module InsertTickets raise ParamValidation::ValidationError.new("Event #{entities[:event_id].id} is deleted", key: :event_id) end - #verify that enough tickets_available - tl_entities.each {|i| + # verify that enough tickets_available + tl_entities.each do |i| if i[:ticket_level_id].deleted raise ParamValidation::ValidationError.new("Ticket level #{i[:ticket_level_id].id} is deleted", key: :tickets) end @@ -150,7 +150,7 @@ module InsertTickets if i[:ticket_level_id].event != entities[:event_id] raise ParamValidation::ValidationError.new("Ticket level #{i[:ticket_level_id].id} does not belong to event #{entities[:event_id]}", key: :tickets) end - } + end # Does the supporter belong to the nonprofit? if entities[:supporter_id].nonprofit != entities[:nonprofit_id] @@ -168,35 +168,35 @@ module InsertTickets end def self.get_ticket_level_entities(data) - data[:tickets].map{|i| + data[:tickets].map do |i| { - quantity: i[:quantity], - ticket_level_id: RetrieveActiveRecordItems.retrieve_from_keys(i, TicketLevel => :ticket_level_id)[:ticket_level_id] + quantity: i[:quantity], + ticket_level_id: RetrieveActiveRecordItems.retrieve_from_keys(i, TicketLevel => :ticket_level_id)[:ticket_level_id] } - }.to_a + end.to_a end def self.create_payment(entities, gross_amount) p = Payment.new - p.gross_amount= gross_amount - p.nonprofit= entities[:nonprofit_id] - p.supporter= entities[:supporter_id] - p.refund_total= 0 + p.gross_amount = gross_amount + p.nonprofit = entities[:nonprofit_id] + p.supporter = entities[:supporter_id] + p.refund_total = 0 p.date = Time.current p.towards = entities[:event_id].name p.fee_total = 0 p.net_amount = gross_amount - p.kind= "OffsitePayment" + p.kind = 'OffsitePayment' p.save! p end def self.create_offsite_payment(entities, gross_amount, data, payment) p = OffsitePayment.new - p.gross_amount= gross_amount - p.nonprofit= entities[:nonprofit_id] - p.supporter= entities[:supporter_id] - p.date= Time.current + p.gross_amount = gross_amount + p.nonprofit = entities[:nonprofit_id] + p.supporter = entities[:supporter_id] + p.date = Time.current p.payment = payment p.kind = data['offsite_payment']['kind'] p.check_number = data['offsite_payment']['check_number'] diff --git a/lib/insert/insert_tracking.rb b/lib/insert/insert_tracking.rb index dc5dc3ff..45b8a69b 100644 --- a/lib/insert/insert_tracking.rb +++ b/lib/insert/insert_tracking.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module InsertTracking def self.create(params) result = {} result['tracking'] = Qx.insert_into(:trackings) - .values({ - utm_campaign: params[:utm_campaign], - utm_content: params[:utm_content], - utm_medium: params[:utm_medium], - utm_source: params[:utm_source], - donation_id: params[:donation_id] - }) - .timestamps - .returning('*') - .execute.first + .values( + utm_campaign: params[:utm_campaign], + utm_content: params[:utm_content], + utm_medium: params[:utm_medium], + utm_source: params[:utm_source], + donation_id: params[:donation_id] + ) + .timestamps + .returning('*') + .execute.first { status: 200, json: result } end diff --git a/lib/job_types/admin_failed_gift_job.rb b/lib/job_types/admin_failed_gift_job.rb deleted file mode 100644 index aa90f2ed..00000000 --- a/lib/job_types/admin_failed_gift_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class AdminFailedGiftJob < EmailJob - attr_reader :donation, :campaign_gift_option - - def initialize(donation, campaign_gift_option) - @donation = donation - @campaign_gift_option = campaign_gift_option - end - - def perform - AdminMailer.notify_failed_gift(@donation, @campaign_gift_option).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/admin_notice_job.rb b/lib/job_types/admin_notice_job.rb deleted file mode 100644 index a16720f1..00000000 --- a/lib/job_types/admin_notice_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class AdminNoticeJob < EmailJob - attr_reader :options - - def initialize(options) - @options = options - end - - def perform - GenericMailer.admin_notice(@options).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/campaign_creation_followup_job.rb b/lib/job_types/campaign_creation_followup_job.rb deleted file mode 100644 index cb627e0a..00000000 --- a/lib/job_types/campaign_creation_followup_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class CampaignCreationFollowupJob < EmailJob - attr_reader :campaign - - def initialize(campaign) - @campaign = campaign - end - - def perform - CampaignMailer.creation_followup(@campaign).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/donor_direct_debit_notification_job.rb b/lib/job_types/donor_direct_debit_notification_job.rb deleted file mode 100644 index dccf0fe0..00000000 --- a/lib/job_types/donor_direct_debit_notification_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class DonorDirectDebitNotificationJob < EmailJob - attr_reader :donation_id - - def initialize(donation_id, locale=I18n.locale) - @donation_id = donation_id - @locale = locale - end - - def perform - DonationMailer.donor_direct_debit_notification(@donation_id, @locale).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/donor_failed_recurring_donation_job.rb b/lib/job_types/donor_failed_recurring_donation_job.rb deleted file mode 100644 index 9b29bdd4..00000000 --- a/lib/job_types/donor_failed_recurring_donation_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class DonorFailedRecurringDonationJob < EmailJob - attr_reader :donation_id - - def initialize(donation_id) - @donation_id = donation_id - end - - def perform - DonationMailer.donor_failed_recurring_donation(@donation_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/donor_payment_notification_job.rb b/lib/job_types/donor_payment_notification_job.rb deleted file mode 100644 index 490bbaab..00000000 --- a/lib/job_types/donor_payment_notification_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class DonorPaymentNotificationJob < EmailJob - attr_reader :donation_id - def initialize(donation_id, locale=I18n.locale) - @donation_id = donation_id - @locale = locale - end - - def perform - DonationMailer.donor_payment_notification(@donation_id, @locale).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/donor_recurring_donation_change_amount_job.rb b/lib/job_types/donor_recurring_donation_change_amount_job.rb deleted file mode 100644 index 4cb920fb..00000000 --- a/lib/job_types/donor_recurring_donation_change_amount_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class DonorRecurringDonationChangeAmountJob < EmailJob - attr_reader :donation_id, :previous_amount - def initialize(donation_id, previous_amount=nil) - @donation_id = donation_id - @previous_amount = previous_amount - end - - def perform - DonationMailer.donor_recurring_donation_change_amount(@donation_id, @previous_amount).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/donor_refund_notification_job.rb b/lib/job_types/donor_refund_notification_job.rb deleted file mode 100644 index 8cc4357e..00000000 --- a/lib/job_types/donor_refund_notification_job.rb +++ /dev/null @@ -1,13 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class DonorRefundNotificationJob < EmailJob - attr_reader :refund_id - def initialize(refund_id) - @refund_id = refund_id - end - - def perform - UserMailer.refund_receipt(@refund_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/email_job.rb b/lib/job_types/email_job.rb deleted file mode 100644 index e5811637..00000000 --- a/lib/job_types/email_job.rb +++ /dev/null @@ -1,27 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class EmailJob - def perform - raise 'You need to override this' - end - - def max_attempts - MAX_EMAIL_JOB_ATTEMPTS || 1 - end - - def destroy_failed_jobs? - false - end - - def error(job, exception) - end - - def reschedule_at(current_time, attempts) - current_time + attempts**(2.195); - end - - def queue_name - 'email_queue' - end - end -end \ No newline at end of file diff --git a/lib/job_types/event_creation_followup_job.rb b/lib/job_types/event_creation_followup_job.rb deleted file mode 100644 index 9141f3c2..00000000 --- a/lib/job_types/event_creation_followup_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class EventCreationFollowupJob < EmailJob - attr_reader :event - - def initialize(event) - @event = event - end - - def perform - EventMailer.creation_followup(@event).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_payment_completed_job.rb b/lib/job_types/export_payment_completed_job.rb deleted file mode 100644 index ad60f392..00000000 --- a/lib/job_types/export_payment_completed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportPaymentCompletedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_payments_completed_notification(export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_payment_failed_job.rb b/lib/job_types/export_payment_failed_job.rb deleted file mode 100644 index bb1ffeaf..00000000 --- a/lib/job_types/export_payment_failed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportPaymentFailedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_payments_failed_notification(export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_recurring_donations_completed_job.rb b/lib/job_types/export_recurring_donations_completed_job.rb deleted file mode 100644 index 56f4ca1f..00000000 --- a/lib/job_types/export_recurring_donations_completed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportRecurringDonationsCompletedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_recurring_donations_completed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_recurring_donations_failed_job.rb b/lib/job_types/export_recurring_donations_failed_job.rb deleted file mode 100644 index dcfa69fb..00000000 --- a/lib/job_types/export_recurring_donations_failed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportRecurringDonationsFailedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_recurring_donations_failed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_supporter_notes_completed_job.rb b/lib/job_types/export_supporter_notes_completed_job.rb deleted file mode 100644 index 31ccd056..00000000 --- a/lib/job_types/export_supporter_notes_completed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportSupporterNotesCompletedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_supporter_notes_completed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_supporter_notes_failed_job.rb b/lib/job_types/export_supporter_notes_failed_job.rb deleted file mode 100644 index 40652dc1..00000000 --- a/lib/job_types/export_supporter_notes_failed_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ExportSupporterNotesFailedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_supporter_notes_failed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_supporters_completed_job.rb b/lib/job_types/export_supporters_completed_job.rb deleted file mode 100644 index 7c9b09da..00000000 --- a/lib/job_types/export_supporters_completed_job.rb +++ /dev/null @@ -1,13 +0,0 @@ -module JobTypes - class ExportSupportersCompletedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_supporters_completed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/export_supporters_failed_job.rb b/lib/job_types/export_supporters_failed_job.rb deleted file mode 100644 index e7555f9e..00000000 --- a/lib/job_types/export_supporters_failed_job.rb +++ /dev/null @@ -1,13 +0,0 @@ -module JobTypes - class ExportSupportersFailedJob < EmailJob - attr_reader :export - - def initialize(export) - @export = export - end - - def perform - ExportMailer.export_supporters_failed_notification(@export).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/generic_mail_job.rb b/lib/job_types/generic_mail_job.rb deleted file mode 100644 index 1a7738a5..00000000 --- a/lib/job_types/generic_mail_job.rb +++ /dev/null @@ -1,19 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class GenericMailJob < EmailJob - attr_reader :from_email, :from_name, :message, :subject, :to_email, :to_name - - def initialize(from_email, from_name, message, subject, to_email, to_name) - @from_email = from_email - @from_name = from_name - @message = message - @subject = subject - @to_email = to_email - @to_name = to_name - end - - def perform - GenericMailer.generic_mail(@from_email, @from_name, @message, @subject, @to_email, @to_name).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/import_complete_notification_job.rb b/lib/job_types/import_complete_notification_job.rb deleted file mode 100644 index fca866b7..00000000 --- a/lib/job_types/import_complete_notification_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class ImportCompleteNotificationJob < EmailJob - attr_reader :import_id - - def initialize(import_id) - @import_id = import_id - end - - def perform - ImportMailer.import_completed_notification(@import_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_admin_existing_invite_job.rb b/lib/job_types/nonprofit_admin_existing_invite_job.rb deleted file mode 100644 index 2f2a3322..00000000 --- a/lib/job_types/nonprofit_admin_existing_invite_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitAdminExistingInviteJob < EmailJob - attr_reader :role - - def initialize(role) - @role = role - end - - def perform - NonprofitAdminMailer.existing_invite(@role).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_admin_new_invite_job.rb b/lib/job_types/nonprofit_admin_new_invite_job.rb deleted file mode 100644 index c0d03dea..00000000 --- a/lib/job_types/nonprofit_admin_new_invite_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitAdminNewInviteJob < EmailJob - attr_reader :role, :raw_token - - def initialize(role, raw_token) - @role = role - @raw_token = raw_token - end - - def perform - NonprofitAdminMailer.new_invite(@role, @raw_token).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_admin_supporter_fundraiser_job.rb b/lib/job_types/nonprofit_admin_supporter_fundraiser_job.rb deleted file mode 100644 index 031fd930..00000000 --- a/lib/job_types/nonprofit_admin_supporter_fundraiser_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitAdminSupporterFundraiserJob < EmailJob - attr_reader :event_or_campaign - - def initialize(event_or_campaign) - @event_or_campaign = event_or_campaign - end - - def perform - NonprofitAdminMailer.supporter_fundraiser(@event_or_campaign).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_failed_recurring_donation_job.rb b/lib/job_types/nonprofit_failed_recurring_donation_job.rb deleted file mode 100644 index bb3f2f53..00000000 --- a/lib/job_types/nonprofit_failed_recurring_donation_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitFailedRecurringDonationJob < EmailJob - attr_reader :donation_id - - def initialize(donation_id) - @donation_id = donation_id - end - - def perform - DonationMailer.nonprofit_failed_recurring_donation(@donation_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_failed_verification_job.rb b/lib/job_types/nonprofit_failed_verification_job.rb deleted file mode 100644 index ba4e71b4..00000000 --- a/lib/job_types/nonprofit_failed_verification_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitFailedVerificationJob < EmailJob - attr_reader :np - - def initialize(np) - @np = np - end - - def perform - NonprofitMailer.failed_verification_notice(@np).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_new_bank_account_job.rb b/lib/job_types/nonprofit_new_bank_account_job.rb deleted file mode 100644 index b7688f7c..00000000 --- a/lib/job_types/nonprofit_new_bank_account_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitNewBankAccountJob < EmailJob - attr_reader :ba - - def initialize(ba) - @ba = ba - end - - def perform - NonprofitMailer.new_bank_account_notification(@ba).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_payment_notification_job.rb b/lib/job_types/nonprofit_payment_notification_job.rb deleted file mode 100644 index 3936115e..00000000 --- a/lib/job_types/nonprofit_payment_notification_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitPaymentNotificationJob < EmailJob - attr_reader :donation_id, :user_id - def initialize(donation_id, user_id=nil) - @donation_id = donation_id - @user_id = user_id - end - - def perform - DonationMailer.nonprofit_payment_notification(@donation_id, @user_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_pending_payout_job.rb b/lib/job_types/nonprofit_pending_payout_job.rb deleted file mode 100644 index 662e3f35..00000000 --- a/lib/job_types/nonprofit_pending_payout_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitPendingPayoutJob < EmailJob - attr_reader :payout_id - - def initialize(payout_id) - @payout_id = payout_id - end - - def perform - NonprofitMailer.pending_payout_notification(@payout_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_recurring_donation_cancellation_job.rb b/lib/job_types/nonprofit_recurring_donation_cancellation_job.rb deleted file mode 100644 index 0f0513d6..00000000 --- a/lib/job_types/nonprofit_recurring_donation_cancellation_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitRecurringDonationCancellationJob < EmailJob - attr_reader :donation_id - - def initialize(donation_id) - @donation_id = donation_id - end - - def perform - DonationMailer.nonprofit_recurring_donation_cancellation(@donation_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_recurring_donation_change_amount_job.rb b/lib/job_types/nonprofit_recurring_donation_change_amount_job.rb deleted file mode 100644 index c1209e40..00000000 --- a/lib/job_types/nonprofit_recurring_donation_change_amount_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitRecurringDonationChangeAmountJob < EmailJob - attr_reader :donation_id, :previous_amount - - def initialize(donation_id, previous_amount=nil) - @donation_id = donation_id - @previous_amount = previous_amount - end - - def perform - DonationMailer.nonprofit_recurring_donation_change_amount(@donation_id, @previous_amount).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_refund_notification_job.rb b/lib/job_types/nonprofit_refund_notification_job.rb deleted file mode 100644 index 9531c858..00000000 --- a/lib/job_types/nonprofit_refund_notification_job.rb +++ /dev/null @@ -1,13 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitRefundNotificationJob < EmailJob - attr_reader :refund_id - def initialize(refund_id) - @refund_id = refund_id - end - - def perform - NonprofitMailer.refund_notification(@refund_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_successful_verification_job.rb b/lib/job_types/nonprofit_successful_verification_job.rb deleted file mode 100644 index d11ae13c..00000000 --- a/lib/job_types/nonprofit_successful_verification_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitSuccessfulVerificationJob < EmailJob - attr_reader :np - - def initialize(np) - @np = np - end - - def perform - NonprofitMailer.successful_verification_notice(@np).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/nonprofit_welcome_job.rb b/lib/job_types/nonprofit_welcome_job.rb deleted file mode 100644 index dd0fb773..00000000 --- a/lib/job_types/nonprofit_welcome_job.rb +++ /dev/null @@ -1,14 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class NonprofitWelcomeJob < EmailJob - attr_reader :nonprofit_id - - def initialize(nonprofit_id) - @nonprofit_id = nonprofit_id - end - - def perform - NonprofitMailer.welcome(@nonprofit_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/ticket_mailer_followup_job.rb b/lib/job_types/ticket_mailer_followup_job.rb deleted file mode 100644 index e7749058..00000000 --- a/lib/job_types/ticket_mailer_followup_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class TicketMailerFollowupJob < EmailJob - attr_reader :ticket_ids, :charge_id - - def initialize(ticket_ids, charge_id) - @ticket_ids = ticket_ids - @charge_id = charge_id - end - - def perform - TicketMailer.followup(@ticket_ids, @charge_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/job_types/ticket_mailer_receipt_admin_job.rb b/lib/job_types/ticket_mailer_receipt_admin_job.rb deleted file mode 100644 index b65c2fe2..00000000 --- a/lib/job_types/ticket_mailer_receipt_admin_job.rb +++ /dev/null @@ -1,15 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -module JobTypes - class TicketMailerReceiptAdminJob < EmailJob - attr_reader :ticket_ids - - def initialize(ticket_ids, user_id=nil) - @ticket_ids = ticket_ids - @user_id = user_id - end - - def perform - TicketMailer.receipt_admin(@ticket_ids, @user_id).deliver - end - end -end \ No newline at end of file diff --git a/lib/json_resp.rb b/lib/json_resp.rb index d5dd39b0..b2f528f4 100644 --- a/lib/json_resp.rb +++ b/lib/json_resp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Provide a declarative json validation and error responses for the rails 'render' method in controllers # @@ -9,7 +11,6 @@ # * Respond with proper codes and error messages for everything class JsonResp - attr_accessor :errors def initialize(params, &block) @@ -17,36 +18,35 @@ class JsonResp validation = JsonResp::Validation.new(params) validation.instance_exec(params, &block) @errors = validation.errors - return self + self end - def when_valid(&block) - return {status: 422, json: {errors: @errors}} if @errors.any? + def when_valid + return { status: 422, json: { errors: @errors } } if @errors.any? + begin - @response = block.call(@params) + @response = yield(@params) rescue Exception => e - @response = {status: 500, json: {error: "We're sorry, but something went wrong. We've been notified about this issue."}} + @response = { status: 500, json: { error: "We're sorry, but something went wrong. We've been notified about this issue." } } puts e puts e.backtrace.first(10) end - return @response + @response end - # Validation of a set of request parameters class Validation - attr_accessor :errors, :params def initialize(params) @params = params @errors = [] - return self + self end def requires(*keys) - @errors.concat keys.select{|k| @params[k].blank? }.map{|k| "#{k} required"} - return Param.new(keys, @errors, @params) + @errors.concat keys.select { |k| @params[k].blank? }.map { |k| "#{k} required" } + Param.new(keys, @errors, @params) end def requires_either(key1, key2) @@ -54,16 +54,15 @@ class JsonResp if @params[key1].blank? && @params[key2].blank? @errors << error_message else - @errors.concat [key1, key2].select{|k| @params[k].blank? }.map{|k| "#{k} required"} + @errors.concat [key1, key2].select { |k| @params[k].blank? }.map { |k| "#{k} required" } end - return Param.new([key1, key2], @errors, @params) + Param.new([key1, key2], @errors, @params) end def optional(*keys) - keys_to_check = keys.select{|k| @params[k].present?} - return Param.new(keys_to_check, @errors, @params) + keys_to_check = keys.select { |k| @params[k].present? } + Param.new(keys_to_check, @errors, @params) end - end class Param @@ -74,67 +73,66 @@ class JsonResp attr_accessor :keys, :errors, :params def initialize(keys, errors, params) - @keys = keys.reject{|k| params[k].nil? } + @keys = keys.reject { |k| params[k].nil? } @errors = errors @params = params end def as_string - @errors.concat @keys.reject{|k| @params[k].is_a?(String)}.map{|k| "#{k} must be a string"} - return self + @errors.concat @keys.reject { |k| @params[k].is_a?(String) }.map { |k| "#{k} must be a string" } + self end def as_int @errors.concat @keys - .reject{|k| @params[k].is_a?(Integer) || @params[k].to_i.to_s == @params[k]} - .map{|k| "#{k} must be an integer"} - return self + .reject { |k| @params[k].is_a?(Integer) || @params[k].to_i.to_s == @params[k] } + .map { |k| "#{k} must be an integer" } + self end def with_format(regex) - @errors.concat @keys.reject{|k| @params[k] =~ regex}.map{|k| "#{k} must match: #{regex}"} - return self + @errors.concat @keys.reject { |k| @params[k] =~ regex }.map { |k| "#{k} must match: #{regex}" } + self end def one_of(*vals) - @errors.concat @keys.reject{|k| vals.include?(@params[k])}.map{|k| "#{k} must be one of: #{vals.join(", ")}"} - return self + @errors.concat @keys.reject { |k| vals.include?(@params[k]) }.map { |k| "#{k} must be one of: #{vals.join(', ')}" } + self end def nested(&block) - @errors.concat @keys.map{|k| Validation.new(@params[k]).instance_exec(@params, &block).errors}.flatten - return self + @errors.concat @keys.map { |k| Validation.new(@params[k]).instance_exec(@params, &block).errors }.flatten + self end def as_array - @errors.concat @keys.reject{|k| @params[k].is_a?(Array)}.map{|k| "#{k} must be an array"} + @errors.concat @keys.reject { |k| @params[k].is_a?(Array) }.map { |k| "#{k} must be an array" } end def array_of(&block) - @errors.concat @keys.reject{|k| @params[k].is_a?(Array)}.map{|k| "#{k} must be an array"} - @errors.concat @keys.map{|k| @params[k].map{|h| Validation.new(h).instance_exec(@params, &block).errors}}.flatten - return self + @errors.concat @keys.reject { |k| @params[k].is_a?(Array) }.map { |k| "#{k} must be an array" } + @errors.concat @keys.map { |k| @params[k].map { |h| Validation.new(h).instance_exec(@params, &block).errors } }.flatten + self end def as_date with_format /\d\d\d\d-\d\d-\d\d/ - @errors.concat @keys.map{|k| [k].concat @params[k].split('-').map(&:to_i)} - .reject{|key, year, month, day| year.present? && year > 1000 && year < 3000 && month.present? && month > 0 && month < 13 && day.present? && day > 0 && day < 32} - .map{|k, _,_,_| "#{k} must be a valid date"} + @errors.concat @keys.map { |k| [k].concat @params[k].split('-').map(&:to_i) } + .reject { |_key, year, month, day| year.present? && year > 1000 && year < 3000 && month.present? && month > 0 && month < 13 && day.present? && day > 0 && day < 32 } + .map { |k, _, _, _| "#{k} must be a valid date" } end def min(n) - @errors.concat @keys.reject{|k| @params[k] >= n}.map{|k| "#{k} must be at least #{n}"} - return self + @errors.concat @keys.reject { |k| @params[k] >= n }.map { |k| "#{k} must be at least #{n}" } + self end def max(n) - @errors.concat @keys.reject{|k| @params[k] <= n}.map{|k| "#{k} must be less than #{n + 1}"} - return self + @errors.concat @keys.reject { |k| @params[k] <= n }.map { |k| "#{k} must be less than #{n + 1}" } + self end - # TODO min_len, max_len, as_float, as_currency, as_time, as_datetime + # TODO: min_len, max_len, as_float, as_currency, as_time, as_datetime # TODO return err resp on unrecognized params - end end diff --git a/lib/list/list_activities.rb b/lib/list/list_activities.rb index da740931..d2ea977b 100644 --- a/lib/list/list_activities.rb +++ b/lib/list/list_activities.rb @@ -1,17 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module ListActivities - - def self.with_params(params, default_activities=nil) - acts = default_activities || Activity - acts = acts.includes(:supporter, :attachment, :host).order('created_at DESC') - acts = acts.where(host_id: params[:host_id]) unless params[:host_id].blank? - acts = acts.where(attachment_id: params[:attachment_id]) unless params[:attachment_id].blank? - acts = acts.where(nonprofit_id: params[:nonprofit_id]) unless params[:nonprofit_id].blank? - if params[:public] - acts = acts.is_public - end - acts = acts.limit(params[:limit]) unless params[:limit].blank? - return acts - end - + def self.with_params(params, default_activities = nil) + acts = default_activities || Activity + acts = acts.includes(:supporter, :attachment, :host).order('created_at DESC') + acts = acts.where(host_id: params[:host_id]) unless params[:host_id].blank? + acts = acts.where(attachment_id: params[:attachment_id]) unless params[:attachment_id].blank? + acts = acts.where(nonprofit_id: params[:nonprofit_id]) unless params[:nonprofit_id].blank? + acts = acts.is_public if params[:public] + acts = acts.limit(params[:limit]) unless params[:limit].blank? + acts + end end diff --git a/lib/mailchimp.rb b/lib/mailchimp.rb index 4215fbcc..e6ee5271 100644 --- a/lib/mailchimp.rb +++ b/lib/mailchimp.rb @@ -1,168 +1,164 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'httparty' require 'digest/md5' module Mailchimp - include HTTParty - format :json + include HTTParty + format :json def self.base_uri(key) dc = get_datacenter(key) - return "https://#{dc}.api.mailchimp.com/3.0" + "https://#{dc}.api.mailchimp.com/3.0" end - # Run the configuration from an initializer - # data: {:api_key => String} - def self.config(hash) - @options = { - :headers => { - 'Content-Type' => 'application/json', - 'Accept' => 'application/json' - } - } - @body = { - :apikey => hash[:api_key] - } - end + # Run the configuration from an initializer + # data: {:api_key => String} + def self.config(hash) + @options = { + headers: { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + } + } + @body = { + apikey: hash[:api_key] + } + end # Given a nonprofit mailchimp oauth2 key, return its current datacenter def self.get_datacenter(key) - metadata = HTTParty.get('https://login.mailchimp.com/oauth2/metadata', { - headers: { - 'User-Agent' => 'oauth2-draft-v10', - 'Host' => 'login.mailchimp.com', - 'Accept' => 'application/json', - 'Authorization' => "OAuth #{key}" - } - }) - return metadata['dc'] + metadata = HTTParty.get('https://login.mailchimp.com/oauth2/metadata', + headers: { + 'User-Agent' => 'oauth2-draft-v10', + 'Host' => 'login.mailchimp.com', + 'Accept' => 'application/json', + 'Authorization' => "OAuth #{key}" + }) + metadata['dc'] end - def self.signup email, mailchimp_list_id - body_hash = @body.merge({ - :id => mailchimp_list_id, - :email => {:email => email} - }) - post("/lists/subscribe", @options.merge(:body => body_hash.to_json)).parsed_response + def self.signup(email, mailchimp_list_id) + body_hash = @body.merge( + id: mailchimp_list_id, + email: { email: email } + ) + post('/lists/subscribe', @options.merge(body: body_hash.to_json)).parsed_response end def self.get_mailchimp_token(npo_id) mailchimp_token = QueryNonprofitKeys.get_key(npo_id, 'mailchimp_token') throw RuntimeError.new("No Mailchimp connection for this nonprofit: #{npo_id}") if mailchimp_token.nil? - return mailchimp_token + mailchimp_token end # Given a nonprofit id and a list of tag master ids that they make into email lists, # create those email lists on mailchimp and return an array of hashes of mailchimp list ids, names, and tag_master_id - def self.create_mailchimp_lists(npo_id, tag_master_ids) + def self.create_mailchimp_lists(npo_id, tag_master_ids) mailchimp_token = get_mailchimp_token(npo_id) uri = base_uri(mailchimp_token) puts "URI #{uri}" puts "KEY #{mailchimp_token}" npo = Qx.fetch(:nonprofits, npo_id).first - tags = Qx.select("DISTINCT(tag_masters.name) AS tag_name, tag_masters.id") - .from(:tag_masters) - .where({"tag_masters.nonprofit_id" => npo_id}) - .and_where("tag_masters.id IN ($ids)", ids: tag_master_ids) - .join(:nonprofits, "tag_masters.nonprofit_id = nonprofits.id") - .execute + tags = Qx.select('DISTINCT(tag_masters.name) AS tag_name, tag_masters.id') + .from(:tag_masters) + .where('tag_masters.nonprofit_id' => npo_id) + .and_where('tag_masters.id IN ($ids)', ids: tag_master_ids) + .join(:nonprofits, 'tag_masters.nonprofit_id = nonprofits.id') + .execute tags.map do |h| - list = post(uri+'/lists', { - basic_auth: {username: '', password: mailchimp_token}, - headers: {'Content-Type' => 'application/json'}, - body: { - name: 'CommitChange-'+h['tag_name'], - contact: { - company: npo['name'], - address1: npo['address'] || '', - city: npo['city'] || '', - state: npo['state_code'] || '', - zip: npo['zip_code'] || '', - country: npo['state_code'] || '', - phone: npo['phone'] || '' - }, - permission_reminder: 'You are a registered supporter of our nonprofit.', - campaign_defaults: { - from_name: npo['name'] || '', - from_email: npo['email'].blank? ? "support@commichange.com" : npo['email'], - subject: "Enter your subject here...", - language: 'en' - }, - email_type_option: false, - visibility: 'prv' - }.to_json - }) - if list.code != 200 - raise Exception.new("Failed to create list: #{list}") - end - {id: list['id'], name: list['name'], tag_master_id: h['id']} + list = post(uri + '/lists', + basic_auth: { username: '', password: mailchimp_token }, + headers: { 'Content-Type' => 'application/json' }, + body: { + name: 'CommitChange-' + h['tag_name'], + contact: { + company: npo['name'], + address1: npo['address'] || '', + city: npo['city'] || '', + state: npo['state_code'] || '', + zip: npo['zip_code'] || '', + country: npo['state_code'] || '', + phone: npo['phone'] || '' + }, + permission_reminder: 'You are a registered supporter of our nonprofit.', + campaign_defaults: { + from_name: npo['name'] || '', + from_email: npo['email'].blank? ? 'support@commichange.com' : npo['email'], + subject: 'Enter your subject here...', + language: 'en' + }, + email_type_option: false, + visibility: 'prv' + }.to_json) + raise Exception, "Failed to create list: #{list}" if list.code != 200 + + { id: list['id'], name: list['name'], tag_master_id: h['id'] } end end # Given a nonprofit id and post_data, which is an array of batch operation hashes # See here: http://developer.mailchimp.com/documentation/mailchimp/guides/how-to-use-batch-operations/ - # Perform all the batch operations and return a status report + # Perform all the batch operations and return a status report def self.perform_batch_operations(npo_id, post_data) return if post_data.empty? + mailchimp_token = get_mailchimp_token(npo_id) uri = base_uri(mailchimp_token) - batch_job_id = post(uri + '/batches', { - basic_auth: {username: "CommitChange", password: mailchimp_token}, - headers: {'Content-Type' => 'application/json'}, - body: {operations: post_data}.to_json - })['id'] + batch_job_id = post(uri + '/batches', + basic_auth: { username: 'CommitChange', password: mailchimp_token }, + headers: { 'Content-Type' => 'application/json' }, + body: { operations: post_data }.to_json)['id'] check_batch_status(npo_id, batch_job_id) end def self.check_batch_status(npo_id, batch_job_id) mailchimp_token = get_mailchimp_token(npo_id) uri = base_uri(mailchimp_token) - batch_status = get(uri+'/batches/'+batch_job_id, { - basic_auth: {username: "CommitChange", password: mailchimp_token}, - headers: {'Content-Type' => 'application/json'} - }) + batch_status = get(uri + '/batches/' + batch_job_id, + basic_auth: { username: 'CommitChange', password: mailchimp_token }, + headers: { 'Content-Type' => 'application/json' }) end def self.delete_mailchimp_lists(npo_id, mailchimp_list_ids) mailchimp_token = get_mailchimp_token(npo_id) uri = base_uri(mailchimp_token) mailchimp_list_ids.map do |id| - delete(uri + "/lists/#{id}", {basic_auth: {username: "CommitChange", password: mailchimp_token}}) + delete(uri + "/lists/#{id}", basic_auth: { username: 'CommitChange', password: mailchimp_token }) end end # `removed` and `added` are arrays of tag join ids that have been added or removed to a supporter def self.sync_supporters_to_list_from_tag_joins(npo_id, supporter_ids, tag_data) - emails = Qx.select(:email).from(:supporters).where("id IN ($ids)", ids: supporter_ids).execute.map{|h| h['email']} - to_add = get_mailchimp_list_ids(tag_data.select{|h| h['selected']}.map{|h| h['tag_master_id']}) - to_remove = get_mailchimp_list_ids(tag_data.reject{|h| h['selected']}.map{|h| h['tag_master_id']}) + emails = Qx.select(:email).from(:supporters).where('id IN ($ids)', ids: supporter_ids).execute.map { |h| h['email'] } + to_add = get_mailchimp_list_ids(tag_data.select { |h| h['selected'] }.map { |h| h['tag_master_id'] }) + to_remove = get_mailchimp_list_ids(tag_data.reject { |h| h['selected'] }.map { |h| h['tag_master_id'] }) return if to_add.empty? && to_remove.empty? - bulk_post = emails.map{|em| to_add.map{|ml_id| {method: 'POST', path: "lists/#{ml_id}/members", body: {email_address: em, status: 'subscribed'}.to_json}}}.flatten - bulk_delete = emails.map{|em| to_remove.map{|ml_id| {method: 'DELETE', path: "lists/#{ml_id}/members/#{Digest::MD5.hexdigest(em.downcase).to_s}"}}}.flatten + bulk_post = emails.map { |em| to_add.map { |ml_id| { method: 'POST', path: "lists/#{ml_id}/members", body: { email_address: em, status: 'subscribed' }.to_json } } }.flatten + bulk_delete = emails.map { |em| to_remove.map { |ml_id| { method: 'DELETE', path: "lists/#{ml_id}/members/#{Digest::MD5.hexdigest(em.downcase)}" } } }.flatten perform_batch_operations(npo_id, bulk_post.concat(bulk_delete)) end def self.get_mailchimp_list_ids(tag_master_ids) return [] if tag_master_ids.empty? - to_insert_data = Qx.select("email_lists.mailchimp_list_id") - .from(:tag_masters) - .where("tag_masters.id IN ($ids)", ids: tag_master_ids) - .join("email_lists", "email_lists.tag_master_id=tag_masters.id") - .execute.map{|h| h['mailchimp_list_id']} - end + to_insert_data = Qx.select('email_lists.mailchimp_list_id') + .from(:tag_masters) + .where('tag_masters.id IN ($ids)', ids: tag_master_ids) + .join('email_lists', 'email_lists.tag_master_id=tag_masters.id') + .execute.map { |h| h['mailchimp_list_id'] } + end # @param [Nonprofit] nonprofit def self.hard_sync_lists(nonprofit) - return if !nonprofit + return unless nonprofit nonprofit.tag_masters.not_deleted.each do |i| - if (i.email_list) - hard_sync_list(i.email_list) - end + hard_sync_list(i.email_list) if i.email_list end end @@ -172,54 +168,52 @@ module Mailchimp def self.hard_sync_list(email_list) ops = generate_batch_ops_for_hard_sync(email_list) perform_batch_operations(email_list.nonprofit.id, ops) - - end + end def self.generate_batch_ops_for_hard_sync(email_list) - #get the subscribers from mailchimp + # get the subscribers from mailchimp mailchimp_subscribers = get_list_mailchimp_subscribers(email_list) - #get our subscribers - our_supporters = email_list.tag_master.tag_joins.map{|i| i.supporter} + # get our subscribers + our_supporters = email_list.tag_master.tag_joins.map(&:supporter) - #split them as follows: + # split them as follows: # on both lists, on our list, on the mailchimp list in_both, in_mailchimp_only = mailchimp_subscribers.partition do |mc_sub| - our_supporters.any?{|s| s.email.downcase == mc_sub[:email_address].downcase} + our_supporters.any? { |s| s.email.casecmp(mc_sub[:email_address]).zero? } end - + _, in_our_side_only = our_supporters.partition do |s| - mailchimp_subscribers.any?{|mc_sub| s.email.downcase == mc_sub[:email_address].downcase} + mailchimp_subscribers.any? { |mc_sub| s.email.casecmp(mc_sub[:email_address]).zero? } end # if on our list, add to mailchimp - output = in_our_side_only.map{|i| - {method: 'POST', path: "lists/#{email_list.mailchimp_list_id}/members", body: {email_address: i.email, status: 'subscribed'}.to_json} - } + output = in_our_side_only.map do |i| + { method: 'POST', path: "lists/#{email_list.mailchimp_list_id}/members", body: { email_address: i.email, status: 'subscribed' }.to_json } + end # if on mailchimp list, delete from mailchimp - output = output.concat(in_mailchimp_only.map{|i| {method: 'DELETE', path: "lists/#{email_list.mailchimp_list_id}/members/#{i[:id]}"}}) + output = output.concat(in_mailchimp_only.map { |i| { method: 'DELETE', path: "lists/#{email_list.mailchimp_list_id}/members/#{i[:id]}" } }) - return output + output end def self.get_list_mailchimp_subscribers(email_list) mailchimp_token = get_mailchimp_token(email_list.tag_master.nonprofit.id) uri = base_uri(mailchimp_token) - result = get(uri + "/lists/#{email_list.mailchimp_list_id}/members?count=1000000000", { - basic_auth: {username: "CommitChange", password: mailchimp_token}, - headers: {'Content-Type' => 'application/json'}}) - members = result['members'].map do |i| - {id: i['id'], email_address: i['email_address']} - end.to_a + result = get(uri + "/lists/#{email_list.mailchimp_list_id}/members?count=1000000000", + basic_auth: { username: 'CommitChange', password: mailchimp_token }, + headers: { 'Content-Type' => 'application/json' }) + members = result['members'].map do |i| + { id: i['id'], email_address: i['email_address'] } + end.to_a end def self.get_email_lists(nonprofit) mailchimp_token = get_mailchimp_token(nonprofit.id) uri = base_uri(mailchimp_token) - result = get(uri + "/lists", { - basic_auth: {username: "CommitChange", password: mailchimp_token}, - headers: {'Content-Type' => 'application/json'}}) + result = get(uri + '/lists', + basic_auth: { username: 'CommitChange', password: mailchimp_token }, + headers: { 'Content-Type' => 'application/json' }) result['lists'] - - end + end end diff --git a/lib/maintain/maintain_dedications.rb b/lib/maintain/maintain_dedications.rb index d58ae687..d67f5099 100644 --- a/lib/maintain/maintain_dedications.rb +++ b/lib/maintain/maintain_dedications.rb @@ -1,44 +1,42 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module MaintainDedications def self.retrieve_json_dedications - return Qx.select('id', 'dedication').from(:donations) - .where("is_valid_json(dedication)").ex - + Qx.select('id', 'dedication').from(:donations) + .where('is_valid_json(dedication)').ex end - def self.retrieve_non_json_dedications(include_blank=false) + def self.retrieve_non_json_dedications(include_blank = false) temp = Qx.select('id', 'dedication').from(:donations) temp = temp.where("dedication IS NOT NULL AND dedication != ''") unless include_blank - temp = temp.and_where("NOT is_valid_json(dedication)") - return temp.ex + temp = temp.and_where('NOT is_valid_json(dedication)') + temp.ex end def self.create_json_dedications_from_plain_text(dedications) dedications.map do |i| - output = {id: i['id']} + output = { id: i['id'] } if i['dedication'] =~ /(((in (loving )?)?memory of|in memorium)\:? )(.+)/i - output[:dedication] = JSON.generate({type: 'memory', note: $+ }) + output[:dedication] = JSON.generate(type: 'memory', note: $+) elsif i['dedication'] =~ /((in honor of|honor of)\:? )(.+)/i - output[:dedication] = JSON.generate({type: 'honor', note: $+ }) + output[:dedication] = JSON.generate(type: 'honor', note: $+) else - output[:dedication] = JSON.generate({type: 'honor', note: i['dedication'] }) + output[:dedication] = JSON.generate(type: 'honor', note: i['dedication']) end output end.each do |i| - Qx.update(:donations).where('id = $id', {id: i[:id]}).set({dedication: i[:dedication]}).ex + Qx.update(:donations).where('id = $id', id: i[:id]).set(dedication: i[:dedication]).ex end end def self.add_honor_to_any_json_dedications_without_type(json_dedications) - json_dedications.map{|i| {'id' => i['id'], 'dedication' => JSON::parse(i['dedication']) }} - .select{|i| !%w(honor memory).include?(i['dedication']['type'])} - .map{|i| i['dedication']['type'] = 'honor'; i } - .each do |i| - Qx.update(:donations).where('id = $id', id: i['id']) - .set(dedication: JSON.generate(i['dedication'])).ex - end + json_dedications.map { |i| { 'id' => i['id'], 'dedication' => JSON.parse(i['dedication']) } } + .reject { |i| %w[honor memory].include?(i['dedication']['type']) } + .map { |i| i['dedication']['type'] = 'honor'; i } + .each do |i| + Qx.update(:donations).where('id = $id', id: i['id']) + .set(dedication: JSON.generate(i['dedication'])).ex + end end - - - -end \ No newline at end of file +end diff --git a/lib/maintain/maintain_payment_records.rb b/lib/maintain/maintain_payment_records.rb index b95edf92..a4705cc4 100644 --- a/lib/maintain/maintain_payment_records.rb +++ b/lib/maintain/maintain_payment_records.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MaintainPaymentRecords # For records which have no associated charge, refund, nonprofit, supporter, donation or a gross_amount # The record is basically useless @@ -6,7 +8,7 @@ module MaintainPaymentRecords end def self.set_payment_supporter_and_nonprofit_though_charge_refund(i) - p = Payment.includes(:refund => :charge).find(i) + p = Payment.includes(refund: :charge).find(i) p.supporter_id = p.refund.charge.supporter_id p.nonprofit_id = p.refund.charge.nonprofit_id p.refund.disbursed = true @@ -16,9 +18,7 @@ module MaintainPaymentRecords def self.delete_payment_and_offsite_payment_record(id) p = Payment.includes(:offsite_payment).find(id) - if (p.offsite_payment) - p.offsite_payment.destroy - end + p.offsite_payment&.destroy p.destroy end -end \ No newline at end of file +end diff --git a/lib/maintain/maintain_ticket_records.rb b/lib/maintain/maintain_ticket_records.rb index d4035197..abfdb27d 100644 --- a/lib/maintain/maintain_ticket_records.rb +++ b/lib/maintain/maintain_ticket_records.rb @@ -1,18 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module MaintainTicketRecords - # a function for taking every ticket record with a card and creating a token # if the event was in the last two weeks def self.tokenize_cards_already_on_tickets Qx.transaction do - event_ids = Event.where('end_datetime >= ?', Time.current-2.weeks).pluck(:id) + event_ids = Event.where('end_datetime >= ?', Time.current - 2.weeks).pluck(:id) t = Ticket.includes(:card).includes(:event).where('card_id IS NOT NULL and event_id IN (?)', event_ids) - t.each{|i| - token = InsertSourceToken.create_record(i.card, {event: i.event}) + t.each do |i| + token = InsertSourceToken.create_record(i.card, event: i.event) i.source_token = token i.save! - } + end end end -end \ No newline at end of file +end diff --git a/lib/merge_supporters.rb b/lib/merge_supporters.rb index a679b30b..04a79035 100644 --- a/lib/merge_supporters.rb +++ b/lib/merge_supporters.rb @@ -1,81 +1,80 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later - module MergeSupporters +# frozen_string_literal: true +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +module MergeSupporters # For supporters that have been merged, we want to update all their child tables to the new supporter_id def self.update_associations(old_supporter_ids, new_supporter_id, np_id, profile_id) # The new supporter needs to have the following tables from the merged supporters: - associations = [:activities, :donations, :recurring_donations, :offsite_payments, :payments, :tickets, :supporter_notes, :supporter_emails, :full_contact_infos] - + associations = %i[activities donations recurring_donations offsite_payments payments tickets supporter_notes supporter_emails full_contact_infos] + associations.each do |table_name| - Qx.update(table_name).set(supporter_id: new_supporter_id).where("supporter_id IN ($ids)", ids: old_supporter_ids).timestamps.execute + Qx.update(table_name).set(supporter_id: new_supporter_id).where('supporter_id IN ($ids)', ids: old_supporter_ids).timestamps.execute end old_supporters = Supporter.includes(:tag_joins).includes(:custom_field_joins).where('id in (?)', old_supporter_ids) - old_tags = old_supporters.map{|i| i.tag_joins.map{|j| j.tag_master}}.flatten.uniq + old_tags = old_supporters.map { |i| i.tag_joins.map(&:tag_master) }.flatten.uniq - #delete old tags + # delete old tags InsertTagJoins.in_bulk(np_id, profile_id, old_supporter_ids, - old_tags.map{|i| {tag_master_id: i.id, selected: false}}) + old_tags.map { |i| { tag_master_id: i.id, selected: false } }) + InsertTagJoins.in_bulk(np_id, profile_id, [new_supporter_id], old_tags.map { |i| { tag_master_id: i.id, selected: true } }) - InsertTagJoins.in_bulk(np_id, profile_id, [new_supporter_id], old_tags.map{|i| {tag_master_id: i.id, selected: true}}) + all_custom_field_joins = old_supporters.map(&:custom_field_joins).flatten + group_joins_by_custom_field_master = all_custom_field_joins.group_by { |i| i.custom_field_master.id } + one_custom_field_join_per_user = group_joins_by_custom_field_master.map do |_k, v| + v.sort_by(&:created_at).reverse.first + end - all_custom_field_joins = old_supporters.map{| i| i.custom_field_joins}.flatten - group_joins_by_custom_field_master = all_custom_field_joins.group_by{|i| i.custom_field_master.id} - one_custom_field_join_per_user = group_joins_by_custom_field_master.map{|k,v| - v.sort_by{|i| - i.created_at - }.reverse.first} + # delete old supporter custom_field + InsertCustomFieldJoins.in_bulk(np_id, old_supporter_ids, one_custom_field_join_per_user.map do |i| + { + custom_field_master_id: i.custom_field_master_id, + value: '' + } + end) - #delete old supporter custom_field - InsertCustomFieldJoins.in_bulk(np_id, old_supporter_ids, one_custom_field_join_per_user.map{|i| { - custom_field_master_id: i.custom_field_master_id, - value: "" - }}) - - #insert new supporter custom field - InsertCustomFieldJoins.in_bulk(np_id, [new_supporter_id], one_custom_field_join_per_user.map{|i| { - custom_field_master_id: i.custom_field_master_id, - value: i.value - }}) + # insert new supporter custom field + InsertCustomFieldJoins.in_bulk(np_id, [new_supporter_id], one_custom_field_join_per_user.map do |i| + { + custom_field_master_id: i.custom_field_master_id, + value: i.value + } + end) # Update all deleted/merged supporters to record when and where they got merged - Psql.execute(Qexpr.new.update(:supporters, {merged_at: Time.current, merged_into: new_supporter_id}).where("id IN ($ids)", ids: old_supporter_ids)) + Psql.execute(Qexpr.new.update(:supporters, merged_at: Time.current, merged_into: new_supporter_id).where('id IN ($ids)', ids: old_supporter_ids)) # Removing any duplicate custom fields UpdateCustomFieldJoins.delete_dupes([new_supporter_id]) end - def self.selected(merged_data, supporter_ids,np_id, profile_id) + def self.selected(merged_data, supporter_ids, np_id, profile_id) new_supporter = Supporter.new(merged_data) new_supporter.save! # Update merged supporters as deleted - Psql.execute(Qexpr.new.update(:supporters, {deleted: true}).where("id IN ($ids)", ids: supporter_ids)) + Psql.execute(Qexpr.new.update(:supporters, deleted: true).where('id IN ($ids)', ids: supporter_ids)) # Update all associated tables - self.update_associations(supporter_ids, new_supporter['id'],np_id, profile_id) - return {json: new_supporter, status: :ok} + update_associations(supporter_ids, new_supporter['id'], np_id, profile_id) + { json: new_supporter, status: :ok } end - # Merge supporters for a nonprofit based on an array of groups of ids, generated from QuerySupporters.dupes_on_email or dupes_on_names def self.merge_by_id_groups(np_id, arr_of_ids, profile_id) Qx.transaction do - arr_of_ids.select{|arr| arr.count > 1}.each do |ids| + arr_of_ids.select { |arr| arr.count > 1 }.each do |ids| # Get all column data from every supporter all_data = Psql.execute( Qexpr.new.from(:supporters) .select(:email, :name, :phone, :address, :city, :state_code, :zip_code, :organization, :country, :created_at) - .where("id IN ($ids)", ids: ids) - .order_by("created_at ASC") + .where('id IN ($ids)', ids: ids) + .order_by('created_at ASC') ) # Use the most recent non null/blank column data for the new supporter - data = all_data.reduce({}) do |acc, supp| - supp.except('created_at').each{|key, val| acc[key] = val unless val.blank?} - acc - end.merge({'nonprofit_id' => np_id}) + data = all_data.each_with_object({}) do |supp, acc| + supp.except('created_at').each { |key, val| acc[key] = val unless val.blank? } + end.merge('nonprofit_id' => np_id) MergeSupporters.selected(data, ids, np_id, profile_id) end end end - - end diff --git a/lib/metrics/nonprofit_metrics.rb b/lib/metrics/nonprofit_metrics.rb index 73aed549..ee651236 100644 --- a/lib/metrics/nonprofit_metrics.rb +++ b/lib/metrics/nonprofit_metrics.rb @@ -1,110 +1,110 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module NonprofitMetrics - def self.payments(np_id) Qx.select( - "(SUM(payments.gross_amount) / 100.0)::money::text AS total", - "(AVG(payments.gross_amount) / 100.0)::money::text AS average", - "(SUM(week.gross_amount) / 100.0)::money::text AS week", - "(SUM(month.gross_amount) / 100.0)::money::text AS month", - "(SUM(year.gross_amount) / 100.0)::money::text AS year", + '(SUM(payments.gross_amount) / 100.0)::money::text AS total', + '(AVG(payments.gross_amount) / 100.0)::money::text AS average', + '(SUM(week.gross_amount) / 100.0)::money::text AS week', + '(SUM(month.gross_amount) / 100.0)::money::text AS month', + '(SUM(year.gross_amount) / 100.0)::money::text AS year' ) - .from(:payments) - .left_join( - ['payments week', "week.id=payments.id AND week.date > date_trunc('week', NOW())"], - ['payments month', "month.id=payments.id AND month.date > date_trunc('month', NOW())"], - ['payments year', "year.id=payments.id AND year.date > date_trunc('year', NOW())"] - ) - .where("payments.nonprofit_id=$id", id: np_id) - .execute.last + .from(:payments) + .left_join( + ['payments week', "week.id=payments.id AND week.date > date_trunc('week', NOW())"], + ['payments month', "month.id=payments.id AND month.date > date_trunc('month', NOW())"], + ['payments year', "year.id=payments.id AND year.date > date_trunc('year', NOW())"] + ) + .where('payments.nonprofit_id=$id', id: np_id) + .execute.last end def self.recurring(np_id) # total, average, this month Qx.select( - "(SUM(recurring_donations.amount) / 100.0)::money::text AS total", - "(AVG(recurring_donations.amount) / 100.0)::money::text AS average", - "(SUM(month.amount) / 100.0)::money::text AS month", + '(SUM(recurring_donations.amount) / 100.0)::money::text AS total', + '(AVG(recurring_donations.amount) / 100.0)::money::text AS average', + '(SUM(month.amount) / 100.0)::money::text AS month' ) - .from(:recurring_donations) - .left_join("recurring_donations month", "month.id=recurring_donations.id AND month.created_at > date_trunc('month', NOW())") - .where("recurring_donations.active=TRUE") - .and_where("recurring_donations.n_failures < 3") - .and_where("recurring_donations.nonprofit_id=$id", id: np_id) - .execute.last + .from(:recurring_donations) + .left_join('recurring_donations month', "month.id=recurring_donations.id AND month.created_at > date_trunc('month', NOW())") + .where('recurring_donations.active=TRUE') + .and_where('recurring_donations.n_failures < 3') + .and_where('recurring_donations.nonprofit_id=$id', id: np_id) + .execute.last end def self.supporters(np_id) Qx.select( - "COUNT(supporters) AS total", - "COUNT(week) AS week", - "COUNT(month) AS month" + 'COUNT(supporters) AS total', + 'COUNT(week) AS week', + 'COUNT(month) AS month' ) - .from(:supporters) - .left_join("supporters week", "week.id=supporters.id AND week.created_at > date_trunc('week', NOW()) AND week.imported_at IS NULL") - .add_left_join("supporters month", "month.id=supporters.id AND month.created_at > date_trunc('month', NOW()) AND month.imported_at IS NULL") - .where("coalesce(supporters.deleted, FALSE) = FALSE") - .and_where("supporters.nonprofit_id=$id", id: np_id) - .execute.last + .from(:supporters) + .left_join('supporters week', "week.id=supporters.id AND week.created_at > date_trunc('week', NOW()) AND week.imported_at IS NULL") + .add_left_join('supporters month', "month.id=supporters.id AND month.created_at > date_trunc('month', NOW()) AND month.imported_at IS NULL") + .where('coalesce(supporters.deleted, FALSE) = FALSE') + .and_where('supporters.nonprofit_id=$id', id: np_id) + .execute.last end def self.recent_donations(np_id) Qx.select( - "(payments.gross_amount / 100)::money::text AS amount", - "payments.date", - "payments.id AS payment_id", - "supporters.name AS supporter_name", - "supporters.email AS supporter_email" + '(payments.gross_amount / 100)::money::text AS amount', + 'payments.date', + 'payments.id AS payment_id', + 'supporters.name AS supporter_name', + 'supporters.email AS supporter_email' ) - .from(:payments) - .join("supporters", "payments.supporter_id=supporters.id") - .where("payments.nonprofit_id=$id", id: np_id) - .and_where("payments.kind IN ('Donation', 'RecurringDonation', 'Ticket')") - .limit(10) - .order_by("payments.date DESC") - .execute + .from(:payments) + .join('supporters', 'payments.supporter_id=supporters.id') + .where('payments.nonprofit_id=$id', id: np_id) + .and_where("payments.kind IN ('Donation', 'RecurringDonation', 'Ticket')") + .limit(10) + .order_by('payments.date DESC') + .execute end def self.recent_supporters(np_id) - Qx.select("name", "email", "id", "created_at") - .from(:supporters) - .where("supporters.nonprofit_id=$id", id: np_id) - .and_where("coalesce(supporters.deleted, FALSE) = FALSE") - .and_where("supporters.import_id IS NULL") - .limit(10) - .order_by("supporters.created_at DESC") - .execute + Qx.select('name', 'email', 'id', 'created_at') + .from(:supporters) + .where('supporters.nonprofit_id=$id', id: np_id) + .and_where('coalesce(supporters.deleted, FALSE) = FALSE') + .and_where('supporters.import_id IS NULL') + .limit(10) + .order_by('supporters.created_at DESC') + .execute end def self.all_metrics(np_id) - keys = [:payments, :recurring, :supporters, :recent_donations, :recent_supporters, :published_campaigns] - keys.reduce({}) do |accum, elem| + keys = %i[payments recurring supporters recent_donations recent_supporters published_campaigns] + keys.each_with_object({}) do |elem, accum| accum[elem] = NonprofitMetrics.send(elem, np_id) - accum end end def self.published_campaigns(np_id) Qx.select( - "campaigns.name", - "campaigns.id", - "campaigns.created_at", - "campaigns.end_datetime", - "COUNT(supporters.id) AS supporter_count", - "(SUM(one_time.amount)/ 100)::money::text AS total_one_time", - "(SUM(recurring_donations.amount)/ 100)::money::text AS total_recurring" + 'campaigns.name', + 'campaigns.id', + 'campaigns.created_at', + 'campaigns.end_datetime', + 'COUNT(supporters.id) AS supporter_count', + '(SUM(one_time.amount)/ 100)::money::text AS total_one_time', + '(SUM(recurring_donations.amount)/ 100)::money::text AS total_recurring' ) - .from(:campaigns) - .left_join("donations", "donations.campaign_id=campaigns.id") - .add_left_join("donations AS one_time", "donations.id=one_time.id AND one_time.recurring_donation_id IS NULL") - .add_left_join("recurring_donations", "recurring_donations.donation_id=donations.id AND recurring_donations.active=TRUE") - .add_left_join("supporters", "supporters.id=donations.supporter_id") - .group_by("campaigns.id") - .where("campaigns.nonprofit_id=$id", id: np_id) - .and_where("campaigns.published = TRUE") - .order_by("campaigns.end_datetime DESC") - .execute + .from(:campaigns) + .left_join('donations', 'donations.campaign_id=campaigns.id') + .add_left_join('donations AS one_time', 'donations.id=one_time.id AND one_time.recurring_donation_id IS NULL') + .add_left_join('recurring_donations', 'recurring_donations.donation_id=donations.id AND recurring_donations.active=TRUE') + .add_left_join('supporters', 'supporters.id=donations.supporter_id') + .group_by('campaigns.id') + .where('campaigns.nonprofit_id=$id', id: np_id) + .and_where('campaigns.published = TRUE') + .order_by('campaigns.end_datetime DESC') + .execute end # Given a starting date, ending date, and time interval, @@ -113,44 +113,44 @@ module NonprofitMetrics # each hash is nested in an outer hash, set to a key that is also the date, lol # this is used in the payment_history query to fill in missing dates in the data. def self.payment_history_timespans(params) - raise ArgumentError.new("Invalid timespan") unless ['year', 'month', 'week', 'day'].include? params[:timeSpan] + raise ArgumentError, 'Invalid timespan' unless %w[year month week day].include? params[:timeSpan] + date_hash = {} beginning_of = 'beginning_of_' + params[:timeSpan] current_date = Chronic.parse(params[:startDate]).send(beginning_of) end_date = Chronic.parse(params[:endDate]).send(beginning_of) while current_date <= end_date - date = current_date.strftime("%F") - date_hash[date] = {'time_span' => date} + date = current_date.strftime('%F') + date_hash[date] = { 'time_span' => date } current_date += 1.send(params[:timeSpan]) end - return date_hash + date_hash end def self.payment_history(params) results = Qx.select( "to_char(date_trunc('#{params[:timeSpan]}', MAX(payments.date)), 'YYYY-MM-DD') AS time_span", - "coalesce(SUM(payments.gross_amount), 0) AS total_cents", - "coalesce(SUM(onetime.gross_amount ), 0) AS onetime_cents", - "coalesce(SUM(recurring.gross_amount ), 0) AS recurring_cents", - "coalesce(SUM(tickets.gross_amount ), 0) AS tickets_cents" + 'coalesce(SUM(payments.gross_amount), 0) AS total_cents', + 'coalesce(SUM(onetime.gross_amount ), 0) AS onetime_cents', + 'coalesce(SUM(recurring.gross_amount ), 0) AS recurring_cents', + 'coalesce(SUM(tickets.gross_amount ), 0) AS tickets_cents' ) - .from(:payments) - .left_join( - ["payments AS onetime", "onetime.id=payments.id AND onetime.kind='Donation'"], - ["payments AS recurring", "recurring.id=payments.id AND recurring.kind='RecurringDonation'"], - ["payments AS tickets", "tickets.id=payments.id AND tickets.kind='Ticket'"] - ) - .where("payments.nonprofit_id" => params[:id]) - .and_where("payments.date >= $d", d: params[:startDate]) - .and_where("payments.date <= $d", d: params[:endDate]) - .group_by("date_trunc('#{params[:timeSpan]}', payments.date)") - .order_by("MAX(payments.date)") - .execute + .from(:payments) + .left_join( + ['payments AS onetime', "onetime.id=payments.id AND onetime.kind='Donation'"], + ['payments AS recurring', "recurring.id=payments.id AND recurring.kind='RecurringDonation'"], + ['payments AS tickets', "tickets.id=payments.id AND tickets.kind='Ticket'"] + ) + .where('payments.nonprofit_id' => params[:id]) + .and_where('payments.date >= $d', d: params[:startDate]) + .and_where('payments.date <= $d', d: params[:endDate]) + .group_by("date_trunc('#{params[:timeSpan]}', payments.date)") + .order_by('MAX(payments.date)') + .execute date_hash = payment_history_timespans(params) - return results.reduce(date_hash){|acc, r| acc[r['time_span']] = r; acc}.values + results.each_with_object(date_hash) { |r, acc| acc[r['time_span']] = r; }.values end - end diff --git a/lib/name_copy_naming_algorithm.rb b/lib/name_copy_naming_algorithm.rb index 1fba6644..f70aae5f 100644 --- a/lib/name_copy_naming_algorithm.rb +++ b/lib/name_copy_naming_algorithm.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class NameCopyNamingAlgorithm < CopyNamingAlgorithm - attr_accessor :klass, :nonprofit_id # @param [Class] klass def initialize(klass, nonprofit_id) - @klass = klass - @nonprofit_id = nonprofit_id + @klass = klass + @nonprofit_id = nonprofit_id end def copy_addition @@ -13,7 +14,7 @@ class NameCopyNamingAlgorithm < CopyNamingAlgorithm end def separator_before_copy_number - " " + ' ' end def max_copies @@ -29,13 +30,10 @@ class NameCopyNamingAlgorithm < CopyNamingAlgorithm end def get_already_used_name_entities(base_name) - end_name = "#{copy_addition.gsub("(","\\(").gsub(")", "\\)")} \\d{2}" + end_name = "#{copy_addition.gsub('(', '\\(').gsub(')', '\\)')} \\d{2}" end_name_length = copy_addition.length + 3 amount_to_strip = end_name_length + base_name.length - max_length - if (amount_to_strip < 0) - amount_to_strip = 0 - end - @klass.method(:where).call('name SIMILAR TO ? AND nonprofit_id = ? AND (deleted IS NULL OR deleted = false)', "#{base_name[0..base_name.length-amount_to_strip-1]}_*" + end_name, nonprofit_id).select('name') + amount_to_strip = 0 if amount_to_strip < 0 + @klass.method(:where).call('name SIMILAR TO ? AND nonprofit_id = ? AND (deleted IS NULL OR deleted = false)', "#{base_name[0..base_name.length - amount_to_strip - 1]}_*" + end_name, nonprofit_id).select('name') end - -end \ No newline at end of file +end diff --git a/lib/notify/notify_user.rb b/lib/notify/notify_user.rb index 1a1073f1..ca0f85f7 100644 --- a/lib/notify/notify_user.rb +++ b/lib/notify/notify_user.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module NotifyUser - def self.send_confirmation_email(user_id) - ParamValidation.new({user_id: user_id}, user_id: {required:true, is_integer: true}) + ParamValidation.new({ user_id: user_id }, user_id: { required: true, is_integer: true }) user = User.where('id = ?', user_id).first - if !user - raise ParamValidation::ValidationError.new("#{user_id} is not a valid user id", {key: :user_id, val: user_id}) + unless user + raise ParamValidation::ValidationError.new("#{user_id} is not a valid user id", key: :user_id, val: user_id) end user.send_confirmation_instructions end -end \ No newline at end of file +end diff --git a/lib/numeric.rb b/lib/numeric.rb index af0b53b8..bf657fc0 100644 --- a/lib/numeric.rb +++ b/lib/numeric.rb @@ -1,18 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class Numeric # Works like Numeric#floor but uses an offset other than 1. Ex: 6.floor_for_delta(5) -> 5 # @param [Integer] delta the integer offsets from zero to round down to # @return [Integer] def floor_for_delta(delta) - raise ArgumentError.new('delta must be a positive integer') unless delta.is_a?(Integer) && delta > 0 - (self % delta).zero? ? self : ((self.to_i / delta)) * delta; + raise ArgumentError, 'delta must be a positive integer' unless delta.is_a?(Integer) && delta > 0 + + (self % delta).zero? ? self : ((to_i / delta)) * delta end # Works like Numeric#ceil but uses an offset other than 1. Ex: 6.floor_for_delta(5) -> 10 # @param [Integer] delta the integer offsets from zero to round up to # @return [Integer] def ceil_for_delta(delta) - raise ArgumentError.new('delta must be a positive integer') unless delta.is_a?(Integer) && delta > 0 - (self % delta).zero? ? self : ((self.floor.to_i / delta)+1) * delta; + raise ArgumentError, 'delta must be a positive integer' unless delta.is_a?(Integer) && delta > 0 + + (self % delta).zero? ? self : ((floor.to_i / delta) + 1) * delta end -end \ No newline at end of file +end diff --git a/lib/onboard_accounts.rb b/lib/onboard_accounts.rb index a04a605f..156b2ca7 100644 --- a/lib/onboard_accounts.rb +++ b/lib/onboard_accounts.rb @@ -1,51 +1,50 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'param_validation' require 'qx' module OnboardAccounts - def self.create_org(params) nonprofit_data = set_nonprofit_defaults(params['nonprofit']) - ParamValidation.new(nonprofit_data, { - name: {required: true}, - # email: {required: true}, - # phone: {required: true}, - city: {required: true}, - state_code: {required: true} - }) + ParamValidation.new(nonprofit_data, + name: { required: true }, + # email: {required: true}, + # phone: {required: true}, + city: { required: true }, + state_code: { required: true }) user_data = set_user_defaults(params['user']) - ParamValidation.new(user_data, { - name: {required: true}, - email: {required: true}, - password: {required: true}, - phone: {required: true} - }) + ParamValidation.new(user_data, + name: { required: true }, + email: { required: true }, + password: { required: true }, + phone: { required: true }) extra_info = params['extraInfo'] nonprofit = Qx.insert_into(:nonprofits) - .values(nonprofit_data).timestamps - .returning('*') - .execute.last + .values(nonprofit_data).timestamps + .returning('*') + .execute.last billing_plan_id = Settings.default_bp.id billing_subscription = Qx.insert_into(:billing_subscriptions) - .values({ - nonprofit_id: nonprofit['id'], - billing_plan_id: billing_plan_id, - status: 'active' - }) - .timestamps.execute.last + .values( + nonprofit_id: nonprofit['id'], + billing_plan_id: billing_plan_id, + status: 'active' + ) + .timestamps.execute.last # Create the user using the User and Role models (since we have to use Devise) user = User.create!(user_data) role = Qx.insert_into(:roles) - .values(user_id: user.id, name: 'nonprofit_admin', host_id: nonprofit['id'], host_type: 'Nonprofit') - .timestamps - .execute.last + .values(user_id: user.id, name: 'nonprofit_admin', host_id: nonprofit['id'], host_type: 'Nonprofit') + .timestamps + .execute.last - self.delay.send_onboard_email(nonprofit, nonprofit_data, user_data, extra_info) + delay.send_onboard_email(nonprofit, nonprofit_data, user_data, extra_info) - return { + { nonprofit: nonprofit, user: user, role: role, @@ -54,59 +53,57 @@ module OnboardAccounts end ### ethis is a one time method in order to add a user without testing for the method. Do not use this long term - def self.create_org_with_user(params, user=nil) + def self.create_org_with_user(params, user = nil) nonprofit_data = set_nonprofit_defaults(params['nonprofit']) - ParamValidation.new(nonprofit_data, { - name: {required: true}, - # email: {required: true}, - # phone: {required: true}, - city: {required: true}, - state_code: {required: true} - }) - if (!user) + ParamValidation.new(nonprofit_data, + name: { required: true }, + # email: {required: true}, + # phone: {required: true}, + city: { required: true }, + state_code: { required: true }) + unless user user_data = set_user_defaults(params['user']) - ParamValidation.new(user_data, { - name: {required: true}, - email: {required: true}, - password: {required: true}, - phone: {required: true} - }) + ParamValidation.new(user_data, + name: { required: true }, + email: { required: true }, + password: { required: true }, + phone: { required: true }) end extra_info = params['extraInfo'] nonprofit = Qx.insert_into(:nonprofits) - .values(nonprofit_data).timestamps - .returning('*') - .execute.last + .values(nonprofit_data).timestamps + .returning('*') + .execute.last # Create a billing subscription for the 6% fee tier billing_plan_id = Settings.default_bp.id billing_subscription = Qx.insert_into(:billing_subscriptions) - .values({ - nonprofit_id: nonprofit['id'], - billing_plan_id: billing_plan_id, - status: 'active' - }) - .timestamps.execute.last + .values( + nonprofit_id: nonprofit['id'], + billing_plan_id: billing_plan_id, + status: 'active' + ) + .timestamps.execute.last # Create the user using the User and Role models (since we have to use Devise) user = !user ? User.create!(user_data) : user role = Qx.insert_into(:roles) - .values(user_id: user.id, name: 'nonprofit_admin', host_id: nonprofit['id'], host_type: 'Nonprofit') - .timestamps - .execute.last + .values(user_id: user.id, name: 'nonprofit_admin', host_id: nonprofit['id'], host_type: 'Nonprofit') + .timestamps + .execute.last - self.delay.send_onboard_email(nonprofit, nonprofit_data, user_data, extra_info) + delay.send_onboard_email(nonprofit, nonprofit_data, user_data, extra_info) - return { - nonprofit: nonprofit, - user: user, - role: role, - billing_subscription: billing_subscription + { + nonprofit: nonprofit, + user: user, + role: role, + billing_subscription: billing_subscription } end def self.set_nonprofit_defaults(data) - data = data.merge({ + data = data.merge( verification_status: 'unverified', published: true, vetted: Settings.nonprofits_must_be_vetted ? false : true, @@ -114,7 +111,7 @@ module OnboardAccounts city_slug: Format::Url.convert_to_slug(data[:city]), state_code_slug: Format::Url.convert_to_slug(data[:state_code]), slug: Format::Url.convert_to_slug(data[:name]) - }) + ) data end @@ -127,10 +124,10 @@ module OnboardAccounts # user_data and extra_info are additional data hashes sent from the onboarding form def self.send_onboard_email(np, nonprofit_data, user_data, extra_info) # Send the welcome email to the nonprofit - NonprofitMailer.welcome(np['id']).deliver + NonprofitCreateJob.perform_later(Nonprofit.find(np['id'])) # Send an email notifying people internal folks of the new nonporfit, with the above info and extra_info to_emails = ['support@commitchange.com'] - message = %Q( + message = %( New signup on CommitChange for an organization with the name "#{np['name']}" Location: #{np['city']} #{np['state_code']}, #{np['zip_code']} Org Email: #{nonprofit_data['email']} @@ -140,7 +137,7 @@ module OnboardAccounts User Phone: #{user_data['phone']} Entity Type: #{extra_info['entity_type']} How they heard about us: #{extra_info['how_they_heard']} - What they want to use: #{['use_donations', 'use_crm', 'use_campaigns', 'use_events'].select{|x| extra_info[x] == 'on'}.join(", ")} + What they want to use: #{%w[use_donations use_crm use_campaigns use_events].select { |x| extra_info[x] == 'on' }.join(', ')} ) subject = "New Account Signup: #{np['name']}" GenericMailer.generic_mail('support@commitchange.com', 'CC Bot', message, subject, to_emails, '').deliver diff --git a/lib/parallel_ar.rb b/lib/parallel_ar.rb index 40d5629e..9537381f 100644 --- a/lib/parallel_ar.rb +++ b/lib/parallel_ar.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'parallel' module ParallelAr - def self.reduce(arr, accum, &block) Parallel.each(arr, in_threads: 8) do |elem| ActiveRecord::Base.connection_pool.with_connection do @@ -11,7 +12,6 @@ module ParallelAr end end end - return accum + accum end - end diff --git a/lib/path/nonprofit_path.rb b/lib/path/nonprofit_path.rb index 3b4cb912..f7a1de48 100644 --- a/lib/path/nonprofit_path.rb +++ b/lib/path/nonprofit_path.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module NonprofitPath + def self.show(np) + return '/' unless np - def self.show(np) - return "/" unless np - "/#{np.state_code_slug}/#{np.city_slug}/#{np.slug}" - end + "/#{np.state_code_slug}/#{np.city_slug}/#{np.slug}" + end - def self.dashboard(np) - "#{show(np)}/dashboard" - end + def self.dashboard(np) + "#{show(np)}/dashboard" + end end diff --git a/lib/pay_recurring_donation.rb b/lib/pay_recurring_donation.rb index 200158c9..c454a1e2 100644 --- a/lib/pay_recurring_donation.rb +++ b/lib/pay_recurring_donation.rb @@ -1,12 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'insert/insert_donation' require 'insert/insert_supporter_notes' require 'timespan' -require 'delayed_job_helper' module PayRecurringDonation - - # Pay ALL recurring donations that are currently due; each payment gets a queued delayed_job # Returns the number of queued jobs def self.pay_all_due_with_stripe @@ -14,24 +13,9 @@ module PayRecurringDonation ids = Psql.execute_vectors( QueryRecurringDonations._all_that_are_due )[1..-1].flatten - - jobs = ids.map do |id| - {handler: DelayedJobHelper.create_handler(PayRecurringDonation, :with_stripe, [id])} - end - Psql.execute(Qexpr.new.insert(:delayed_jobs, jobs, { - common_data: { - run_at: Time.current, - attempts: 0, - failed_at: nil, - last_error: nil, - locked_at: nil, - locked_by: nil, - priority: 0, - queue: "rec-don-payments" - } - })) - return ids + PayRecurringDonationsJob.perform_later(*id) + ids end # run the payrecurring_donation in development so I can make sure we have the expected failures @@ -60,20 +44,19 @@ module PayRecurringDonation # Charge an existing donation via stripe, only if it is due # Pass in an instance of an existing RecurringDonation def self.with_stripe(rd_id) - ParamValidation.new({:rd_id => rd_id}, { - :rd_id => { - :required => true, - :is_integer=> true - } - }) + ParamValidation.new({ rd_id: rd_id }, + rd_id: { + required: true, + is_integer: true + }) rd = RecurringDonation.where('id = ?', rd_id).first unless rd - raise ParamValidation::ValidationError.new("#{rd_id} is not a valid recurring donation", {:key => :rd_id}) + raise ParamValidation::ValidationError.new("#{rd_id} is not a valid recurring donation", key: :rd_id) end - return false if !QueryRecurringDonations.is_due?(rd_id) + return false unless QueryRecurringDonations.is_due?(rd_id) donation = Donation.where('id = ?', rd['donation_id']).first unless donation @@ -81,94 +64,86 @@ module PayRecurringDonation end result = {} - result = result.merge(InsertDonation.insert_charge({ - 'card_id' => donation['card_id'], - 'recurring_donation' => true, - 'designation' => donation['designation'], - 'amount' => donation['amount'], - 'nonprofit_id' => donation['nonprofit_id'], - 'donation_id' => donation['id'], - 'supporter_id' => donation['supporter_id'], - 'old_donation' => true - })) + result = result.merge(InsertDonation.insert_charge( + 'card_id' => donation['card_id'], + 'recurring_donation' => true, + 'designation' => donation['designation'], + 'amount' => donation['amount'], + 'nonprofit_id' => donation['nonprofit_id'], + 'donation_id' => donation['id'], + 'supporter_id' => donation['supporter_id'], + 'old_donation' => true + )) if result['charge']['status'] != 'failed' - result['recurring_donation'] = Psql.execute( - Qexpr.new.update(:recurring_donations, {n_failures: 0}) - .where("id=$id", id: rd_id).returning('*') - ).first - Delayed::Job.enqueue JobTypes::DonorPaymentNotificationJob.new(rd['donation_id']) - Delayed::Job.enqueue JobTypes::NonprofitPaymentNotificationJob.new(rd['donation_id']) + rd.update(n_failures: 0) + result['recurring_donation'] = rd + HoudiniEventPublisher.announce(:recurring_donation_payment_succeeded, donation, donation&.supporter&.locale || 'en') InsertActivities.for_recurring_donations([result['payment']['id']]) else - result['recurring_donation'] = Psql.execute( - Qexpr.new.update(:recurring_donations, {n_failures: rd['n_failures'] + 1}) - .where("id=$id", id: rd_id).returning('*') - ).first - DonationMailer.delay.donor_failed_recurring_donation(rd['donation_id']) - if rd['n_failures'] >= 3 - DonationMailer.delay.nonprofit_failed_recurring_donation(rd['donation_id']) - end - InsertSupporterNotes.create([{content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540}]) + + rd.n_failures += 1 + rd.save! + result['recurring_donation'] = rd + HoudiniEventPublisher.announce(:recurring_donation_payment_failed, donation) + InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540 }]) end - return result + result end - def self.fail_a_recurring_donation(rd, donation, notify_nonprofit=false) + def self.fail_a_recurring_donation(rd, donation, notify_nonprofit = false) recurring_donation = Psql.execute( - Qexpr.new.update(:recurring_donations, {n_failures: 3}) - .where("id=$id", id: rd['id']).returning('*') + Qexpr.new.update(:recurring_donations, n_failures: 3) + .where('id=$id', id: rd['id']).returning('*') ).first - DonationMailer.delay.donor_failed_recurring_donation(rd['donation_id']) + FailedRecurringDonationPaymentDonorEmailJob.perform_later Donation.find(rd['donation_id']) if notify_nonprofit - DonationMailer.delay.nonprofit_failed_recurring_donation(rd['donation_id']) + FailedRecurringDonationPaymentNonprofitEmailJob.perform_later Donation.find(rd['donation_id']) end - InsertSupporterNotes.create([{content: "This supporter had a payment failure for their recurring donation with ID #{rd['id']}", supporter_id: donation['supporter_id'], user_id: 540}]) - return recurring_donation + InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd['id']}", supporter_id: donation['supporter_id'], user_id: 540 }]) + recurring_donation end # Charge an existing donation via stripe, NO MATTER WHAT # Pass in an instance of an existing RecurringDonation - def self.with_stripe_BUT_NO_MATTER_WHAT(rd_id, enter_todays_date, run_this=false, set_this_true=false, this_one_needs_to_be_false=true, is_this_run_dangerously="no") - - if (PayRecurringDonation::ULTIMATE_VERIFICATION(enter_todays_date, run_this, set_this_true, this_one_needs_to_be_false, is_this_run_dangerously)) + def self.with_stripe_BUT_NO_MATTER_WHAT(rd_id, enter_todays_date, run_this = false, set_this_true = false, this_one_needs_to_be_false = true, is_this_run_dangerously = 'no') + if PayRecurringDonation::ULTIMATE_VERIFICATION(enter_todays_date, run_this, set_this_true, this_one_needs_to_be_false, is_this_run_dangerously) rd = Psql.execute("SELECT * FROM recurring_donations WHERE id=#{rd_id}").first donation = Psql.execute("SELECT * FROM donations WHERE id=#{rd['donation_id']}").first result = {} - result = result.merge(InsertDonation.insert_charge({ - 'card_id' => donation['card_id'], - 'recurring_donation' => true, - 'designation' => donation['designation'], - 'amount' => donation['amount'], - 'nonprofit_id' => donation['nonprofit_id'], - 'donation_id' => donation['id'], - 'supporter_id' => donation['supporter_id'] - })) + result = result.merge(InsertDonation.insert_charge( + 'card_id' => donation['card_id'], + 'recurring_donation' => true, + 'designation' => donation['designation'], + 'amount' => donation['amount'], + 'nonprofit_id' => donation['nonprofit_id'], + 'donation_id' => donation['id'], + 'supporter_id' => donation['supporter_id'] + )) if result['charge']['status'] != 'failed' result['recurring_donation'] = Psql.execute( - Qexpr.new.update(:recurring_donations, {n_failures: 0}) - .where("id=$id", id: rd_id).returning('*') + Qexpr.new.update(:recurring_donations, n_failures: 0) + .where('id=$id', id: rd_id).returning('*') ).first - Delayed::Job.enqueue JobTypes::DonorPaymentNotificationJob.new(rd['donation_id']) - Delayed::Job.enqueue JobTypes::NonprofitPaymentNotificationJob.new(rd['donation_id']) + ## add PaymentNotificationJobHere InsertActivities.for_recurring_donations([result['payment']['id']]) else result['recurring_donation'] = Psql.execute( - Qexpr.new.update(:recurring_donations, {n_failures: rd['n_failures'] + 1}) - .where("id=$id", id: rd_id).returning('*') + Qexpr.new.update(:recurring_donations, n_failures: rd['n_failures'] + 1) + .where('id=$id', id: rd_id).returning('*') ).first - DonationMailer.delay.donor_failed_recurring_donation(rd['donation_id']) + FailedRecurringDonationPaymentDonorEmailJob.perform_later Donation.find(rd['donation_id']) if rd['n_failures'] >= 3 - DonationMailer.delay.nonprofit_failed_recurring_donation(rd['donation_id']) + FailedRecurringDonationPaymentNonprofitEmailJob.perform_later Donation.find(rd['donation_id']) end - InsertSupporterNotes.create([{content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540}]) + InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540 }]) end return result end - return false + false end - def self.ULTIMATE_VERIFICATION(enter_todays_date, run_this=false, set_this_true=false, this_one_needs_to_be_false=true, is_this_run_dangerously="no") - return (Date.parse(enter_todays_date) == Date.today() && run_this && set_this_true && !this_one_needs_to_be_false && is_this_run_dangerously == "run dangerously") + def self.ULTIMATE_VERIFICATION(enter_todays_date, run_this = false, set_this_true = false, this_one_needs_to_be_false = true, is_this_run_dangerously = 'no') + (Date.parse(enter_todays_date) == Date.today && run_this && set_this_true && !this_one_needs_to_be_false && is_this_run_dangerously == 'run dangerously') end end diff --git a/lib/psql.rb b/lib/psql.rb index a9a18a1a..4226ce88 100644 --- a/lib/psql.rb +++ b/lib/psql.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Some convenience wrappers around the postgresql gem, allowing us to avoid activerecord dependency # combine usage of this library with Qexpr @@ -9,11 +11,10 @@ require 'qx' # Initialize the database connection module Psql - # Execute a sql statement (string) def self.execute(statement) puts statement if ENV['RAILS_ENV'] != 'production' && ENV['RAILS_LOG_LEVEL'] == 'debug' # log to STDOUT on dev/staging - return Qx.execute_raw(raw_expr_str(statement)) + Qx.execute_raw(raw_expr_str(statement)) end # A variation of execute that returns a vector of vectors rather than a vector of hashes @@ -21,7 +22,7 @@ module Psql def self.execute_vectors(statement) puts statement if ENV['RAILS_ENV'] != 'production' && ENV['RAILS_LOG_LEVEL'] == 'debug' # log to STDOUT on dev/staging raw_str = statement.to_s.uncolorize.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') - return Qx.execute_raw(raw_expr_str(statement), format: 'csv') + Qx.execute_raw(raw_expr_str(statement), format: 'csv') end def self.transaction(&block) @@ -30,11 +31,10 @@ module Psql end end -private + private # Raw expression string def self.raw_expr_str(statement) statement.to_s.uncolorize.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '') end - end diff --git a/lib/qexpr.rb b/lib/qexpr.rb index f66b14ee..fa3bee63 100644 --- a/lib/qexpr.rb +++ b/lib/qexpr.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # A module that allows you to construct complex SQL expressions by piecing # together methods in ruby. @@ -12,22 +14,19 @@ require 'hamster' require 'colorize' class Qexpr - attr_accessor :tree - - def initialize(h=nil) + def initialize(h = nil) @tree = Hamster::Hash[h] end - def to_s - self.parse + parse end # Parse an qexpr object into a sql string expression def parse - expr = "" + expr = '' if @tree[:insert] expr = "#{@tree[:insert]} #{@tree[:values].blue}" expr += "\nRETURNING ".bold.light_blue + (@tree[:returning] || ['id']).join(', ').blue @@ -37,8 +36,9 @@ class Qexpr # Query-based expessions expr = @tree[:update] || @tree[:delete_from] || @tree[:select] if expr.nil? || expr.empty? - raise ArgumentError.new("Must have a select, update, or delete clause") + raise ArgumentError, 'Must have a select, update, or delete clause' end + if @tree[:from] expr += "\nFROM".bold.light_blue + @tree[:from].map do |f| f.is_a?(String) ? f : " (#{f[:sub_expr].parse}\n) AS #{f[:as]}" @@ -52,44 +52,39 @@ class Qexpr expr += @tree[:limit] if @tree[:limit] expr += @tree[:offset] if @tree[:offset] - if @tree[:select] && @tree[:as] - expr = "(#{expr}) AS #{@tree[:as]}" - end + expr = "(#{expr}) AS #{@tree[:as]}" if @tree[:select] && @tree[:as] if @tree[:update] && @tree[:returning] expr += "\nRETURNING ".bold.light_blue + @tree[:returning].join(', ').blue end - return expr + expr end - # insert into table_name the values from every hash inside of arr # optionally pass in: # no_timestamps: don't set created_at and updated_at # common_data: a hash of data to set for all rows # returning: what columns to return - def insert(table_name, arr, options={}) + def insert(table_name, arr, options = {}) arr = [arr] if arr.is_a? Hash - arr = arr.map{|h| h.sort.to_h} # Make sure all key/vals are ordered the same way + arr = arr.map { |h| h.sort.to_h } # Make sure all key/vals are ordered the same way keys = arr.first.keys keys = keys.concat(options[:common_data].keys) if options[:common_data] - keys = keys.map{|k| "\"#{k}\""}.join(', ') - ts_columns = options[:no_timestamps] ? "" : "created_at, updated_at, " - ts_values = options[:no_timestamps] ? "" : "#{Qexpr.now}, #{Qexpr.now}, " - common_vals = options[:common_data] ? options[:common_data].values.map{|v| Qexpr.quote(v)} : [] - vals = arr.map{|h| '(' + ts_values + h.values.map{|v| Qexpr.quote(v)}.concat(common_vals).join(',') + ')'}.join(',') + keys = keys.map { |k| "\"#{k}\"" }.join(', ') + ts_columns = options[:no_timestamps] ? '' : 'created_at, updated_at, ' + ts_values = options[:no_timestamps] ? '' : "#{Qexpr.now}, #{Qexpr.now}, " + common_vals = options[:common_data] ? options[:common_data].values.map { |v| Qexpr.quote(v) } : [] + vals = arr.map { |h| '(' + ts_values + h.values.map { |v| Qexpr.quote(v) }.concat(common_vals).join(',') + ')' }.join(',') Qexpr.new @tree - .put(:insert, "INSERT INTO".bold.light_blue + " #{table_name} (#{ts_columns} #{keys})".blue) + .put(:insert, 'INSERT INTO'.bold.light_blue + " #{table_name} (#{ts_columns} #{keys})".blue) .put(:values, "\nVALUES".bold.light_blue + " #{vals}".blue) end - - def update(table_name, settings, os={}) - Qexpr.new @tree.put(:update, "UPDATE".bold.light_blue + " #{table_name}".blue + "\nSET".bold.light_blue + " #{settings.map{|key,val| "#{key.to_s}=#{Qexpr.quote(val)}"}.join(', ')}".blue) + def update(table_name, settings, _os = {}) + Qexpr.new @tree.put(:update, 'UPDATE'.bold.light_blue + " #{table_name}".blue + "\nSET".bold.light_blue + " #{settings.map { |key, val| "#{key}=#{Qexpr.quote(val)}" }.join(', ')}".blue) end - def delete_from(table_name) - Qexpr.new @tree.put(:delete_from, "DELETE FROM".bold.light_blue + " #{table_name}".blue) + Qexpr.new @tree.put(:delete_from, 'DELETE FROM'.bold.light_blue + " #{table_name}".blue) end # Create or append select columns @@ -97,12 +92,12 @@ class Qexpr if @tree[:select] Qexpr.new @tree.put(:select, @tree[:select] + ", #{cols.join(', ')}".blue) else - if cols.count < 4 - cols = " #{cols.join(", ")}" - else - cols = "\n #{cols.join("\n, ")}" - end - Qexpr.new @tree.put(:select, "\nSELECT".bold.light_blue + "#{cols}".blue) + cols = if cols.count < 4 + " #{cols.join(', ')}" + else + "\n #{cols.join("\n, ")}" + end + Qexpr.new @tree.put(:select, "\nSELECT".bold.light_blue + cols.to_s.blue) end end @@ -110,67 +105,59 @@ class Qexpr Qexpr.new @tree.put(:select, "\nSELECT DISTINCT".bold.light_blue + "\n #{cols.join("\n, ")}".blue) end - def select_distinct_on(cols_distinct, cols_select) - Qexpr.new @tree.put(:select, "SELECT DISTINCT ON".bold.light_blue + " (#{Array(cols_distinct).join(', ')})\n #{Array(cols_select).join("\n, ")}".blue) + Qexpr.new @tree.put(:select, 'SELECT DISTINCT ON'.bold.light_blue + " (#{Array(cols_distinct).join(', ')})\n #{Array(cols_select).join("\n, ")}".blue) end - - def from(expr, as=nil) + def from(expr, as = nil) Qexpr.new @tree.put(:from, (@tree[:from] || Hamster::Vector[]).add(Qexpr.from_expr(expr, as))) end - def group_by(*cols) Qexpr.new @tree.put(:group_by, "\nGROUP BY".bold.light_blue + " #{cols.join(', ')}".blue) end - def order_by(expr) - Qexpr.new @tree.put(:order_by, "\nORDER BY".bold.light_blue + " #{expr.to_s}".blue) + Qexpr.new @tree.put(:order_by, "\nORDER BY".bold.light_blue + " #{expr}".blue) end - def limit(i) Qexpr.new @tree.put(:limit, "\nLIMIT".bold.light_blue + " #{i.to_i}".blue) end - def offset(i) Qexpr.new @tree.put(:offset, "\nOFFSET".bold.light_blue + " #{i.to_i}".blue) end - - def join(table_name, on_expr, data={}) + def join(table_name, on_expr, data = {}) on_expr = Qexpr.interpolate_expr(on_expr, data) - return Qexpr.new @tree - .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nJOIN".bold.light_blue + " #{table_name}\n ".blue + "ON".bold.light_blue + " #{on_expr}".blue)) + Qexpr.new @tree + .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nJOIN".bold.light_blue + " #{table_name}\n ".blue + 'ON'.bold.light_blue + " #{on_expr}".blue)) end - def inner_join(table_name, on_expr, data={}) + def inner_join(table_name, on_expr, data = {}) on_expr = Qexpr.interpolate_expr(on_expr, data) - return Qexpr.new @tree - .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nINNER JOIN".bold.light_blue + " #{table_name}\n ".blue + "ON".bold.light_blue + " #{on_expr}".blue)) + Qexpr.new @tree + .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nINNER JOIN".bold.light_blue + " #{table_name}\n ".blue + 'ON'.bold.light_blue + " #{on_expr}".blue)) end - def left_outer_join(table_name, on_expr, data={}) + def left_outer_join(table_name, on_expr, data = {}) on_expr = Qexpr.interpolate_expr(on_expr, data) - return Qexpr.new @tree - .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nLEFT OUTER JOIN".bold.light_blue + " #{table_name}\n ".blue + "ON".bold.light_blue + " #{on_expr}".blue)) + Qexpr.new @tree + .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\nLEFT OUTER JOIN".bold.light_blue + " #{table_name}\n ".blue + 'ON'.bold.light_blue + " #{on_expr}".blue)) end - def join_lateral(join_name, select_statement, success_condition=true, data={}) + def join_lateral(join_name, select_statement, success_condition = true, data = {}) select_statement = Qexpr.interpolate_expr(select_statement, data) - return Qexpr.new @tree - .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\n JOIN LATERAL".bold.light_blue + " (#{select_statement})\n #{join_name} ".blue + "ON".bold.light_blue + " #{success_condition}".blue)) + Qexpr.new @tree + .put(:joins, (@tree[:joins] || Hamster::Vector[]).add("\n JOIN LATERAL".bold.light_blue + " (#{select_statement})\n #{join_name} ".blue + 'ON'.bold.light_blue + " #{success_condition}".blue)) end - def as(name) - return Qexpr.new @tree.put(:as, name) + Qexpr.new @tree.put(:as, name) end - def where(expr, data={}) + def where(expr, data = {}) expr = Qexpr.interpolate_expr(expr, data) if @tree[:where] Qexpr.new @tree.put(:where, @tree[:where] + "\nAND".bold.light_blue + " (#{expr})".blue) @@ -179,13 +166,11 @@ class Qexpr end end - def returning(*cols) Qexpr.new @tree.put(:returning, (@tree[:returning] || Hamster::Vector[]).concat(cols)) end - - def having(expr, data={}) + def having(expr, data = {}) if @tree[:having] Qexpr.new @tree.put(:having, @tree[:having] + "\nAND".bold.light_blue + " (#{Qexpr.interpolate_expr(expr, data)})".blue) else @@ -201,29 +186,29 @@ class Qexpr # Remove clauses from the expression # eg expr.remove(:from, :where) def remove(*keys) - return Qexpr.new keys.reduce(@tree){|tree, key| tree.delete(key)} + Qexpr.new keys.reduce(@tree) { |tree, key| tree.delete(key) } end # Quote a string for use in sql to prevent injection or weird errors # Always use this for all values! # Just uses double-dollar quoting universally. Should be generally safe and easy. - # Will return an unquoted value it it's a Fixnum + # Will return an unquoted value it it's a Integer def self.quote(val) - if val.is_a?(Fixnum) || (val.is_a?(String) && val =~ /^\$Q\$.+\$Q\$$/) # is a valid num or already quoted + if val.is_a?(Integer) || (val.is_a?(String) && val =~ /^\$Q\$.+\$Q\$$/) # is a valid num or already quoted val - elsif val == nil - "NULL" + elsif val.nil? + 'NULL' elsif !!val == val # is a boolean val ? "'t'" : "'f'" else - return "$Q$" + val.to_s + "$Q$" + '$Q$' + val.to_s + '$Q$' end end # An alias of PG.quote_ident, for convenience sake # Double-quotes sql identifiers def self.quote_ident(str) - str.split('.').map{|s| "\"#{s}\""}.join('.') + str.split('.').map { |s| "\"#{s}\"" }.join('.') end # sql-quoted datetime value useful for created_at and updated_at columns @@ -234,7 +219,7 @@ class Qexpr # Given a max page length and the current page, # return the offset value # (eg: page_length=30 and page=3, then return 60) - def self.page_offset(page_length, page=1) + def self.page_offset(page_length, page = 1) page = page.to_i page = 1 if page <= 0 Qexpr.quote((page.to_i - 1) * page_length.to_i) @@ -242,11 +227,12 @@ class Qexpr # Given the total row count, the max page length, and the current page, # return the total results left - def self.remaining_count(total_count, page_length, current_page=1) - return 0 unless current_page - rem = total_count.to_i - (current_page.to_i) * page_length.to_i - rem = 0 if rem < 0 - return rem + def self.remaining_count(total_count, page_length, current_page = 1) + return 0 unless current_page + + rem = total_count.to_i - current_page.to_i * page_length.to_i + rem = 0 if rem < 0 + rem end # Given a string sql expression with interpolations like "WHERE id > ${id}" @@ -256,14 +242,14 @@ class Qexpr expr.gsub(/\$\w+/) do |match| val = data[match.gsub(/[ \$]*/, '').to_sym] if val.is_a?(Array) || val.is_a?(Hamster::Vector) - val.to_a.map{|x| Qexpr.quote(x)}.join(', ') + val.to_a.map { |x| Qexpr.quote(x) }.join(', ') else Qexpr.quote val end end end -private + private # Given some kind of expr object (might be just a string or another whole Qexpr expr), and an 'as' value # then give back either a hash for the sub-Qexpr expression, or just a string. @@ -272,7 +258,7 @@ private if expr.is_a?(Qexpr) Hamster::Hash[sub_expr: expr, as: as] else - " #{expr} #{as ? "AS #{as.to_s}" : ""}" + " #{expr} #{as ? "AS #{as}" : ''}" end end end diff --git a/lib/qexpr_query_chunker.rb b/lib/qexpr_query_chunker.rb index c61cf28b..8853b25f 100644 --- a/lib/qexpr_query_chunker.rb +++ b/lib/qexpr_query_chunker.rb @@ -1,34 +1,27 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -#TODO combine these two items +# TODO combine these two items module QexprQueryChunker - # Used to get a chunk of a Qexpr query # @param [Integer] offset the offset for the beginning of the chunk # @param [Integer] limit the maximum number of rows to get in the chunk # @param [Boolean] skip_header whether you should skip the header row in the returned output. Defaults to false # @yieldreturn [Qexpr] a block which, when called, returns the main Qexpr query # @return [Enumerator] an Enumerator, with each item an array for a row - def self.get_chunk_of_query(offset=nil, limit=nil, skip_header=false, &block) - Enumerator.new {|y| + def self.get_chunk_of_query(offset = nil, limit = nil, skip_header = false, &block) + Enumerator.new do |y| + expr = block.call + expr = expr.offset(offset) if offset - expr = block.call() - if offset - expr = expr.offset(offset) - end - - if limit - expr = expr.limit(limit) - end + expr = expr.limit(limit) if limit vecs = Psql.execute_vectors(expr.parse) - if (!skip_header) - y << vecs.first.to_a.map{|k| k.to_s.titleize} + y << vecs.first.to_a.map { |k| k.to_s.titleize } unless skip_header - end - - vecs.drop(1).each{|v| y << v.to_a} - } + vecs.drop(1).each { |v| y << v.to_a } + end end # Get a lazy enumerable getting a query in chunks. block is a block used for creating a query for a new chunk @@ -39,23 +32,23 @@ module QexprQueryChunker # @yieldparam [Boolean] skip_header whether you should skip the header row in the returned output. # @yieldreturn [Enumerator] an Enumerator, with each item an array for a row # @return [Enumerator::Lazy] a lazy enumerator for getting every item in the query - def self.for_export_enumerable(chunk_limit=35000, &block) + def self.for_export_enumerable(chunk_limit = 35_000, &block) Enumerator.new do |y| last_export_length = 0 limit = chunk_limit page = 0 - while page == 0 || last_export_length == limit do + while page == 0 || last_export_length == limit # either we haven't started yet or the last export == limit (since if it didn't we're to the end) page += 1 offset = Qexpr.page_offset(limit, page) export_returned = block.call(offset, limit, page > 1).to_a - #we got the titles too if on_first, let's skip one row - last_export_length = page == 1 ? export_returned.length-1 : export_returned.length + # we got the titles too if on_first, let's skip one row + last_export_length = page == 1 ? export_returned.length - 1 : export_returned.length # efficient? no. Do we care? eh. - export_returned.each {|i| + export_returned.each do |i| y << i - } + end end end.lazy end -end \ No newline at end of file +end diff --git a/lib/query/billing_plans.rb b/lib/query/billing_plans.rb index c9973059..72ffa2ca 100644 --- a/lib/query/billing_plans.rb +++ b/lib/query/billing_plans.rb @@ -1,22 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module BillingPlans - def self.get_percentage_fee(nonprofit_id) - ParamValidation.new({nonprofit_id:nonprofit_id}, {nonprofit_id: {:required => true, :is_integer => true}}) + ParamValidation.new({ nonprofit_id: nonprofit_id }, nonprofit_id: { required: true, is_integer: true }) - unless (Nonprofit.exists?(nonprofit_id)) - raise ParamValidation::ValidationError.new("#{nonprofit_id} does not exist", {:key => :nonprofit_id} ) + unless Nonprofit.exists?(nonprofit_id) + raise ParamValidation::ValidationError.new("#{nonprofit_id} does not exist", key: :nonprofit_id) end - - result = Qx.select("billing_plans.percentage_fee") - .from("billing_plans") - .join("billing_subscriptions bs", "bs.billing_plan_id = billing_plans.id") - .where("bs.nonprofit_id=$id", id: nonprofit_id) - .execute - return result.empty? ? 0 : result.last['percentage_fee'] + result = Qx.select('billing_plans.percentage_fee') + .from('billing_plans') + .join('billing_subscriptions bs', 'bs.billing_plan_id = billing_plans.id') + .where('bs.nonprofit_id=$id', id: nonprofit_id) + .execute + result.empty? ? 0 : result.last['percentage_fee'] end - end diff --git a/lib/query/query_activities.rb b/lib/query/query_activities.rb index d1915b60..994edf3d 100644 --- a/lib/query/query_activities.rb +++ b/lib/query/query_activities.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module QueryActivities def self.for_timeline(nonprofit_id, supporter_id) - Qx.select( "activities.*") + Qx.select('activities.*') .from(:activities) .where("activities.supporter_id = #{supporter_id.to_i} AND activities.nonprofit_id = #{nonprofit_id.to_i}") .order_by('activities.date DESC') .execute end end - diff --git a/lib/query/query_billing_subscriptions.rb b/lib/query/query_billing_subscriptions.rb deleted file mode 100644 index 9962fc12..00000000 --- a/lib/query/query_billing_subscriptions.rb +++ /dev/null @@ -1,24 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'qx' -require 'active_support/core_ext' - -module QueryBillingSubscriptions - - def self.days_left_in_trial(np_id) - sub = Qx.fetch(:billing_subscriptions, {nonprofit_id: np_id}).last - return 0 if sub.nil? - return sub['status'] == 'trialing' ? (((sub['created_at'] + 10.days) - Time.current) / 86400).floor : 0 - end - - def self.plan_tier(np_id) - sub = Qx.fetch(:billing_subscriptions, {nonprofit_id: np_id}).last - return 2 if sub && sub['status'] != 'inactive' - return 0 - end - - def self.currently_in_trial?(np_id) - sub = Qx.fetch(:billing_subscriptions, {nonprofit_id: np_id}).last - return sub && sub['status'] == 'trialing' - end -end - diff --git a/lib/query/query_campaign_gifts.rb b/lib/query/query_campaign_gifts.rb index 6ebf2325..f4be0075 100644 --- a/lib/query/query_campaign_gifts.rb +++ b/lib/query/query_campaign_gifts.rb @@ -1,31 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Query code for both campaign_gift_options and campaign_gifts require 'psql' module QueryCampaignGifts - - - # Create a mapping of: { - # 'total_donations' => Integer, # total donations for gift options - # 'total_one_time' => Integer, # total one-time donations for gift option - # 'total_recurring' => Integer, - # 'name' => String, # name of the gift level - # - # Includes the overall sum as well as the donations without gift options - def self.report_metrics(campaign_id) - - - data = Psql.execute(%Q( + # Create a mapping of: { + # 'total_donations' => Integer, # total donations for gift options + # 'total_one_time' => Integer, # total one-time donations for gift option + # 'total_recurring' => Integer, + # 'name' => String, # name of the gift level + # + # Includes the overall sum as well as the donations without gift options + def self.report_metrics(campaign_id) + data = Psql.execute(%( SELECT campaign_gift_options.name , COUNT(*) AS total_donations , SUM(ds_one_time.amount) AS total_one_time , SUM(ds_recurring.amount) AS total_recurring FROM (#{donations_for_campaign(campaign_id).parse}) AS ds - LEFT OUTER JOIN (#{get_corresponding_payments(campaign_id, %Q(LEFT OUTER JOIN recurring_donations ON recurring_donations.donation_id = donations.id - ), %Q(WHERE recurring_donations.id IS NULL))}) ds_one_time + LEFT OUTER JOIN (#{get_corresponding_payments(campaign_id, %(LEFT OUTER JOIN recurring_donations ON recurring_donations.donation_id = donations.id + ), %(WHERE recurring_donations.id IS NULL))}) ds_one_time ON ds_one_time.id = ds.id - LEFT OUTER JOIN (#{get_corresponding_payments(campaign_id, %Q(INNER JOIN recurring_donations ON recurring_donations.donation_id = donations.id))}) ds_recurring + LEFT OUTER JOIN (#{get_corresponding_payments(campaign_id, %(INNER JOIN recurring_donations ON recurring_donations.donation_id = donations.id))}) ds_recurring ON ds_recurring.id = ds.id LEFT OUTER JOIN campaign_gifts ON campaign_gifts.donation_id=ds.id @@ -35,16 +33,15 @@ module QueryCampaignGifts ORDER BY total_donations DESC )) - return Hamster::Hash[data: data] - end + Hamster::Hash[data: data] + end - def self.donations_for_campaign(campaign_id) - Qx.select('donations.id, donations.amount').from(:donations).where("campaign_id IN ($ids)", {ids:QueryCampaigns.get_campaign_and_children(campaign_id) - }) - end + def self.donations_for_campaign(campaign_id) + Qx.select('donations.id, donations.amount').from(:donations).where('campaign_id IN ($ids)', ids: QueryCampaigns.get_campaign_and_children(campaign_id)) + end - def self.get_corresponding_payments(campaign_id, recurring_clauses, where_clauses="") - %Q(SELECT donations.id, payments.gross_amount AS amount + def self.get_corresponding_payments(campaign_id, recurring_clauses, where_clauses = '') + %(SELECT donations.id, payments.gross_amount AS amount FROM (#{donations_for_campaign(campaign_id).parse}) donations #{recurring_clauses} JOIN LATERAL ( @@ -55,6 +52,5 @@ module QueryCampaignGifts ) payments ON true #{where_clauses} ) - end + end end - diff --git a/lib/query/query_campaign_metrics.rb b/lib/query/query_campaign_metrics.rb index b2cc1529..30b03588 100644 --- a/lib/query/query_campaign_metrics.rb +++ b/lib/query/query_campaign_metrics.rb @@ -1,31 +1,29 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryCampaignMetrics - def self.on_donations(campaign_id) campaign = Campaign.find(campaign_id) result = Psql.execute( - Qexpr.new.select("COALESCE(COUNT(DISTINCT donations.id), 0) AS supporters_count", - "COALESCE(SUM(payments.gross_amount), 0) AS total_raised") - .from("campaigns") + Qexpr.new.select('COALESCE(COUNT(DISTINCT donations.id), 0) AS supporters_count', + 'COALESCE(SUM(payments.gross_amount), 0) AS total_raised') + .from('campaigns') .join( - "donations", "donations.campaign_id=campaigns.id" + 'donations', 'donations.campaign_id=campaigns.id' ) - .join_lateral("payments", QueryDonations.get_first_payment_for_donation.parse, true) + .join_lateral('payments', QueryDonations.get_first_payment_for_donation.parse, true) .where("campaigns.id IN (#{QueryCampaigns .get_campaign_and_children(campaign_id) - .parse - })") + .parse})") ).last - return { - 'supporters_count' => result['supporters_count'], - 'total_raised'=> result['total_raised'], - 'goal_amount'=> campaign.goal_amount, - 'show_total_count'=> campaign.show_total_count, - 'show_total_raised'=> campaign.show_total_raised + { + 'supporters_count' => result['supporters_count'], + 'total_raised' => result['total_raised'], + 'goal_amount' => campaign.goal_amount, + 'show_total_count' => campaign.show_total_count, + 'show_total_raised' => campaign.show_total_raised } end end - - diff --git a/lib/query/query_campaigns.rb b/lib/query/query_campaigns.rb index ec4eec89..b61775b6 100644 --- a/lib/query/query_campaigns.rb +++ b/lib/query/query_campaigns.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' module QueryCampaigns - def self.featured(limit, gross_more_than) expr = Qexpr.new.select( 'campaigns.name', @@ -11,77 +12,72 @@ module QueryCampaigns Image._url('campaigns', 'main_image', 'normal') + 'AS main_image_url', "concat('/nonprofits/', campaigns.nonprofit_id, '/campaigns/', campaigns.id) AS url" ).from( - Qexpr.new.select("campaigns.*", "SUM(donations.amount) AS gross") - .from("campaigns") - .join("donations", "donations.campaign_id=campaigns.id") - .group_by("campaigns.id"), - "campaigns" + Qexpr.new.select('campaigns.*', 'SUM(donations.amount) AS gross') + .from('campaigns') + .join('donations', 'donations.campaign_id=campaigns.id') + .group_by('campaigns.id'), + 'campaigns' ).where("campaigns.gross > $amount AND campaigns.published='t' AND campaigns.nonprofit_id!=$id", amount: gross_more_than, id: 4182) - .order_by("campaigns.updated_at ASC") - .limit(limit) + .order_by('campaigns.updated_at ASC') + .limit(limit) Psql.execute(expr.parse) end - def self.timeline(campaign_id) - ex = QueryCampaigns.payments_expression(campaign_id, true) - ex.group_by("DATE(payments.date)") - .order_by("DATE(payments.date)") + ex = QueryCampaigns.payments_expression(campaign_id, true) + ex.group_by('DATE(payments.date)') + .order_by('DATE(payments.date)') .execute end - def self.payments_expression(campaign_id, for_timeline) selects = [ - "coalesce(SUM(payments.gross_amount), 0) AS total_cents", - "coalesce(SUM(recurring.gross_amount), 0) AS recurring_cents", - "coalesce(SUM(offsite.gross_amount), 0) AS offsite_cents", - "coalesce(SUM(onetime.gross_amount), 0) AS onetime_cents"] + 'coalesce(SUM(payments.gross_amount), 0) AS total_cents', + 'coalesce(SUM(recurring.gross_amount), 0) AS recurring_cents', + 'coalesce(SUM(offsite.gross_amount), 0) AS offsite_cents', + 'coalesce(SUM(onetime.gross_amount), 0) AS onetime_cents' + ] - for_timeline ? - selects.push("MAX(DATE(payments.date)) AS date") : - selects.push("coalesce(count(supporters.id), 0) AS supporters_count") + for_timeline ? + selects.push('MAX(DATE(payments.date)) AS date') : + selects.push('coalesce(count(supporters.id), 0) AS supporters_count') - return Qx.select(*selects) - .from("payments") + Qx.select(*selects) + .from('payments') .left_join( - ["donations", "payments.donation_id=donations.id"], - ["payments AS onetime", "onetime.id=payments.id AND onetime.kind='Donation'"], - ["payments AS offsite", "offsite.id=payments.id AND offsite.kind='OffsitePayment'"], - ["payments AS recurring", "recurring.id=payments.id AND recurring.kind='RecurringDonation'"]) + ['donations', 'payments.donation_id=donations.id'], + ['payments AS onetime', "onetime.id=payments.id AND onetime.kind='Donation'"], + ['payments AS offsite', "offsite.id=payments.id AND offsite.kind='OffsitePayment'"], + ['payments AS recurring', "recurring.id=payments.id AND recurring.kind='RecurringDonation'"] + ) .where("donations.campaign_id IN (#{QueryCampaigns.get_campaign_and_children(campaign_id).parse})") end - def self.totals(campaign_id) - ex = QueryCampaigns.payments_expression(campaign_id, false) - ex.add_left_join(["supporters", "donations.supporter_id=supporters.id"]) + ex = QueryCampaigns.payments_expression(campaign_id, false) + ex.add_left_join(['supporters', 'donations.supporter_id=supporters.id']) .execute.first end - def self.name_and_id(npo_id) - np = Nonprofit.find(npo_id) campaigns = np.campaigns.not_deleted.includes(:profile).order('campaigns.name ASC') output = campaigns.map do |i| { - 'name' => i.name, - 'id' => i.id, - 'isChildCampaign' => i.child_campaign?, - 'creator' => i.profile&.name || "user ##{i.profile.id}" + 'name' => i.name, + 'id' => i.id, + 'isChildCampaign' => i.child_campaign?, + 'creator' => i.profile&.name || "user ##{i.profile.id}" } end output end def self.get_campaign_and_children(campaign_id) - Qx.select("id") - .from('campaigns') - .where("campaigns.id = $id OR campaigns.parent_campaign_id=$id", - id: campaign_id) + Qx.select('id') + .from('campaigns') + .where('campaigns.id = $id OR campaigns.parent_campaign_id=$id', + id: campaign_id) end - - end diff --git a/lib/query/query_charges.rb b/lib/query/query_charges.rb index 0f6c6019..832188db 100644 --- a/lib/query/query_charges.rb +++ b/lib/query/query_charges.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' module QueryCharges diff --git a/lib/query/query_custom_fields.rb b/lib/query/query_custom_fields.rb index 304c9a35..fec50508 100644 --- a/lib/query/query_custom_fields.rb +++ b/lib/query/query_custom_fields.rb @@ -1,20 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' module QueryCustomFields - - # Find all duplicate custom field joins on supporters # Returns an array of groups of duplicate custom_field_join_ids def self.find_dupes(np_id) - Qx.select("ARRAY_AGG(custom_field_joins.id)") + Qx.select('ARRAY_AGG(custom_field_joins.id)') .from(:custom_field_joins) - .join(:custom_field_masters, "custom_field_masters.id=custom_field_joins.custom_field_master_id") - .where("custom_field_masters.nonprofit_id=$id", id: np_id) - .group_by("custom_field_joins.custom_field_master_id", "custom_field_joins.value", "custom_field_joins.supporter_id") - .having("COUNT(custom_field_joins.id) > 1") + .join(:custom_field_masters, 'custom_field_masters.id=custom_field_joins.custom_field_master_id') + .where('custom_field_masters.nonprofit_id=$id', id: np_id) + .group_by('custom_field_joins.custom_field_master_id', 'custom_field_joins.value', 'custom_field_joins.supporter_id') + .having('COUNT(custom_field_joins.id) > 1') .execute(format: 'csv')[1..-1] end - end diff --git a/lib/query/query_donations.rb b/lib/query/query_donations.rb index 5c4a4317..9c7d106c 100644 --- a/lib/query/query_donations.rb +++ b/lib/query/query_donations.rb @@ -1,77 +1,77 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'query/query_supporters' module QueryDonations - # Export all donation data for a given campaign - def self.campaign_export(campaign_id) + def self.campaign_export(campaign_id) Psql.execute_vectors( Qexpr.new.select([ 'donations.created_at', - '(payments.gross_amount/100.00)::money::text AS amount', - "COUNT(recurring_donations.id) > 0 AS recurring", - "STRING_AGG(campaign_gift_options.name, ',') AS campaign_gift_names" + '(payments.gross_amount/100.00)::money::text AS amount', + 'COUNT(recurring_donations.id) > 0 AS recurring', + "STRING_AGG(campaign_gift_options.name, ',') AS campaign_gift_names" ].concat(QuerySupporters.supporter_export_selections) .concat([ - "supporters.id AS \"Supporter ID\"", - ]).concat([ - "coalesce(donations.designation, 'None') AS designation", - "#{QueryPayments.get_dedication_or_empty('type')}::text AS \"Dedication Type\"", - "#{QueryPayments.get_dedication_or_empty('name')}::text AS \"Dedicated To: Name\"", - "#{QueryPayments.get_dedication_or_empty('supporter_id')}::text AS \"Dedicated To: Supporter ID\"", - "#{QueryPayments.get_dedication_or_empty('contact', 'email')}::text AS \"Dedicated To: Email\"", - "#{QueryPayments.get_dedication_or_empty('contact', "phone")}::text AS \"Dedicated To: Phone\"", - "#{QueryPayments.get_dedication_or_empty( "contact", "address")}::text AS \"Dedicated To: Address\"", - "#{QueryPayments.get_dedication_or_empty( "note")}::text AS \"Dedicated To: Note\"", - "donations.campaign_id AS \"Campaign Id\"", - "users.email AS \"Campaign Creator Email\"" - ]) - ).from(:donations) - .join(:supporters, "supporters.id=donations.supporter_id") - .left_outer_join(:campaign_gifts, "campaign_gifts.donation_id=donations.id") - .left_outer_join(:campaign_gift_options, "campaign_gift_options.id=campaign_gifts.campaign_gift_option_id") - .left_outer_join(:recurring_donations, "recurring_donations.donation_id = donations.id") + 'supporters.id AS "Supporter ID"' + ]).concat([ + "coalesce(donations.designation, 'None') AS designation", + "#{QueryPayments.get_dedication_or_empty('type')}::text AS \"Dedication Type\"", + "#{QueryPayments.get_dedication_or_empty('name')}::text AS \"Dedicated To: Name\"", + "#{QueryPayments.get_dedication_or_empty('supporter_id')}::text AS \"Dedicated To: Supporter ID\"", + "#{QueryPayments.get_dedication_or_empty('contact', 'email')}::text AS \"Dedicated To: Email\"", + "#{QueryPayments.get_dedication_or_empty('contact', 'phone')}::text AS \"Dedicated To: Phone\"", + "#{QueryPayments.get_dedication_or_empty('contact', 'address')}::text AS \"Dedicated To: Address\"", + "#{QueryPayments.get_dedication_or_empty('note')}::text AS \"Dedicated To: Note\"", + 'donations.campaign_id AS "Campaign Id"', + 'users.email AS "Campaign Creator Email"' + ])).from(:donations) + .join(:supporters, 'supporters.id=donations.supporter_id') + .left_outer_join(:campaign_gifts, 'campaign_gifts.donation_id=donations.id') + .left_outer_join(:campaign_gift_options, 'campaign_gift_options.id=campaign_gifts.campaign_gift_option_id') + .left_outer_join(:recurring_donations, 'recurring_donations.donation_id = donations.id') .join_lateral(:payments, get_first_payment_for_donation.parse, true) .join(Qx.select('id, profile_id').from('campaigns') .where("id IN (#{QueryCampaigns .get_campaign_and_children(campaign_id) .parse})").as('campaigns').parse, - 'donations.campaign_id=campaigns.id') + 'donations.campaign_id=campaigns.id') .join(Qx.select('users.id, profiles.id AS profiles_id, users.email') .from('users') .add_join('profiles', 'profiles.user_id = users.id') - .as("users").parse, "users.profiles_id=campaigns.profile_id") - .group_by("donations.id", "supporters.id", "payments.id", "payments.gross_amount", "users.email") - .order_by("donations.date") + .as('users').parse, 'users.profiles_id=campaigns.profile_id') + .group_by('donations.id', 'supporters.id', 'payments.id', 'payments.gross_amount', 'users.email') + .order_by('donations.date') ) - end + end def self.for_campaign_activities(id) QueryDonations.activities_expression(['donations.recurring']) - .where("donations.campaign_id IN (#{QueryCampaigns + .where("donations.campaign_id IN (#{QueryCampaigns .get_campaign_and_children(id) .parse})") - .execute + .execute end def self.activities_expression(additional_selects) selects = [" - CASE + CASE WHEN donations.anonymous='t' - OR supporters.anonymous='t' - OR supporters.name='' - OR supporters.name IS NULL - THEN 'A supporter' - ELSE supporters.name - END AS supporter_name", - "(donations.amount / 100.0)::money::text as amount", - "donations.date"] + (additional_selects ? additional_selects : []) + OR supporters.anonymous='t' + OR supporters.name='' + OR supporters.name IS NULL + THEN 'A supporter' + ELSE supporters.name + END AS supporter_name", + '(donations.amount / 100.0)::money::text as amount', + 'donations.date'] + (additional_selects || []) - return Qx.select(selects.join(',')) + Qx.select(selects.join(',')) .from(:donations) .left_join(:supporters, 'donations.supporter_id=supporters.id') - .order_by("donations.date desc") + .order_by('donations.date desc') .limit(15) end @@ -79,21 +79,20 @@ module QueryDonations # !!! Note this returns the PAYMENT_IDS for each offsite donation def self.dupe_offsite_donations(np_id) payment_ids = Psql.execute_vectors( - Qexpr.new.select("ARRAY_AGG(payments.id) AS ids") - .from("donations") - .join(:offsite_payments, "offsite_payments.donation_id=donations.id") - .join(:payments, "payments.donation_id=donations.id") - .where("donations.nonprofit_id=$id", id: np_id) - .group_by("donations.supporter_id", "donations.amount", "donations.date") - .having("COUNT(donations.id) > 1") + Qexpr.new.select('ARRAY_AGG(payments.id) AS ids') + .from('donations') + .join(:offsite_payments, 'offsite_payments.donation_id=donations.id') + .join(:payments, 'payments.donation_id=donations.id') + .where('donations.nonprofit_id=$id', id: np_id) + .group_by('donations.supporter_id', 'donations.amount', 'donations.date') + .having('COUNT(donations.id) > 1') )[1..-1].map(&:flatten) end - def self.get_first_payment_for_donation(table_selector='donations') + def self.get_first_payment_for_donation(table_selector = 'donations') Qx.select('payments.id, payments.gross_amount').from(:payments) - .where("payments.donation_id = #{table_selector}.id") - .order_by('payments.created_at ASC') - .limit(1) + .where("payments.donation_id = #{table_selector}.id") + .order_by('payments.created_at ASC') + .limit(1) end - end diff --git a/lib/query/query_email_settings.rb b/lib/query/query_email_settings.rb index 2d1c4171..b2f6e639 100644 --- a/lib/query/query_email_settings.rb +++ b/lib/query/query_email_settings.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryEmailSettings - - Settings = ['notify_payments', 'notify_campaigns', 'notify_events', 'notify_payouts', 'notify_recurring_donations'] + Settings = %w[notify_payments notify_campaigns notify_events notify_payouts notify_recurring_donations].freeze def self.fetch(np_id, user_id) - es = Psql.execute(%Q( + es = Psql.execute(%( SELECT * FROM email_settings WHERE nonprofit_id=#{Qexpr.quote(np_id.to_i)} @@ -13,14 +14,14 @@ module QueryEmailSettings # If the user's event_settings table does not exist, return a hash with all settings true if es.nil? - es = Psql.execute(%Q( + es = Psql.execute(%( SELECT column_name FROM information_schema.columns WHERE table_name='email_settings' - )).map{|h| h['column_name']} - .reject{|name| ['id', 'nonprofit_id', 'user_id'].include?(name)} - .reduce({}){|h, name| h[name] = true; h} + )).map { |h| h['column_name'] } + .reject { |name| %w[id nonprofit_id user_id].include?(name) } + .each_with_object({}) { |name, h| h[name] = true; } end - return es + es end end diff --git a/lib/query/query_event_discounts.rb b/lib/query/query_event_discounts.rb index 1ab648a7..b0bb4d87 100644 --- a/lib/query/query_event_discounts.rb +++ b/lib/query/query_event_discounts.rb @@ -1,15 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryEventDiscounts - def self.with_event_ids(event_ids) return [] if event_ids.empty? - x = Psql.execute( - Qexpr.new.select("name", "id", "percent", "code", "created_at") - .from("event_discounts") - .where("event_discounts.event_id IN ($ids)", ids: event_ids) - .order_by("created_at DESC"), - ).map{|h| HashWithIndifferentAccess.new(h)} + x = Psql.execute( + Qexpr.new.select('name', 'id', 'percent', 'code', 'created_at') + .from('event_discounts') + .where('event_discounts.event_id IN ($ids)', ids: event_ids) + .order_by('created_at DESC') + ).map { |h| HashWithIndifferentAccess.new(h) } end - end diff --git a/lib/query/query_event_metrics.rb b/lib/query/query_event_metrics.rb index 5d555685..21fdb0be 100644 --- a/lib/query/query_event_metrics.rb +++ b/lib/query/query_event_metrics.rb @@ -1,47 +1,49 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryEventMetrics - - def self.expression(additional_selects=[]) + def self.expression(additional_selects = []) selects = [ - "coalesce(tickets.total, 0) AS total_attendees", - "coalesce(tickets.checked_in_count, 0) AS checked_in_count", - "coalesce(ticket_payments.total_paid, 0) AS tickets_total_paid", - "coalesce(donations.payment_total, 0) AS donations_total_paid", - "coalesce(ticket_payments.total_paid, 0) + coalesce(donations.payment_total, 0) AS total_paid" + 'coalesce(tickets.total, 0) AS total_attendees', + 'coalesce(tickets.checked_in_count, 0) AS checked_in_count', + 'coalesce(ticket_payments.total_paid, 0) AS tickets_total_paid', + 'coalesce(donations.payment_total, 0) AS donations_total_paid', + 'coalesce(ticket_payments.total_paid, 0) + coalesce(donations.payment_total, 0) AS total_paid' ] - tickets_sub = Qx.select("event_id", "SUM(quantity) AS total", "SUM(tickets.checked_in::int) AS checked_in_count") - .from("tickets") - .group_by("event_id") - .as("tickets") - - ticket_payments_subquery = Qx.select("payment_id", "MAX(event_id) AS event_id").from("tickets").group_by("payment_id").as("tickets") + tickets_sub = Qx.select('event_id', 'SUM(quantity) AS total', 'SUM(tickets.checked_in::int) AS checked_in_count') + .from('tickets') + .group_by('event_id') + .as('tickets') - ticket_payments_sub = Qx.select("SUM(payments.gross_amount) AS total_paid", "tickets.event_id") - .from(:payments) - .join(ticket_payments_subquery, "payments.id=tickets.payment_id") - .group_by("tickets.event_id") - .as("ticket_payments") + ticket_payments_subquery = Qx.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').group_by('payment_id').as('tickets') - donations_sub = Qx.select("event_id", "SUM(payments.gross_amount) as payment_total") - .from("donations") - .group_by("event_id") - .left_join("payments", "donations.id=payments.donation_id") - .as("donations") + ticket_payments_sub = Qx.select('SUM(payments.gross_amount) AS total_paid', 'tickets.event_id') + .from(:payments) + .join(ticket_payments_subquery, 'payments.id=tickets.payment_id') + .group_by('tickets.event_id') + .as('ticket_payments') + + donations_sub = Qx.select('event_id', 'SUM(payments.gross_amount) as payment_total') + .from('donations') + .group_by('event_id') + .left_join('payments', 'donations.id=payments.donation_id') + .as('donations') selects = selects.concat(additional_selects) - return Qx.select(*selects) - .from("events") - .left_join( - [tickets_sub, "tickets.event_id = events.id"], - [donations_sub, "donations.event_id = events.id"], - [ticket_payments_sub, "ticket_payments.event_id=events.id"] + Qx.select(*selects) + .from('events') + .left_join( + [tickets_sub, 'tickets.event_id = events.id'], + [donations_sub, 'donations.event_id = events.id'], + [ticket_payments_sub, 'ticket_payments.event_id=events.id'] ) end def self.with_event_ids(event_ids) return [] if event_ids.empty? - QueryEventMetrics.expression().where("events.id in ($ids)", ids: event_ids).execute + + QueryEventMetrics.expression.where('events.id in ($ids)', ids: event_ids).execute end def self.for_listings(id_type, id, params) @@ -61,29 +63,25 @@ module QueryEventMetrics exp = QueryEventMetrics.expression(selects) if id_type == 'profile' - exp = exp.and_where(["events.profile_id = $id", id: id]) + exp = exp.and_where(['events.profile_id = $id', id: id]) end if id_type == 'nonprofit' - exp = exp.and_where(["events.nonprofit_id = $id", id: id]) + exp = exp.and_where(['events.nonprofit_id = $id', id: id]) end if params['active'].present? exp = exp - .and_where(['events.end_datetime >= $date', date: Time.now]) - .and_where(["events.published = TRUE AND coalesce(events.deleted, FALSE) = FALSE"]) + .and_where(['events.end_datetime >= $date', date: Time.now]) + .and_where(['events.published = TRUE AND coalesce(events.deleted, FALSE) = FALSE']) end if params['past'].present? exp = exp - .and_where(['events.end_datetime < $date', date: Time.now]) - .and_where(["events.published = TRUE AND coalesce(events.deleted, FALSE) = FALSE"]) + .and_where(['events.end_datetime < $date', date: Time.now]) + .and_where(['events.published = TRUE AND coalesce(events.deleted, FALSE) = FALSE']) end if params['unpublished'].present? - exp = exp.and_where(["coalesce(events.published, FALSE) = FALSE AND coalesce(events.deleted, FALSE) = FALSE"]) + exp = exp.and_where(['coalesce(events.published, FALSE) = FALSE AND coalesce(events.deleted, FALSE) = FALSE']) end - if params['deleted'].present? - exp = exp.and_where(["events.deleted = TRUE"]) - end - return exp.execute + exp = exp.and_where(['events.deleted = TRUE']) if params['deleted'].present? + exp.execute end - end - diff --git a/lib/query/query_event_organizer.rb b/lib/query/query_event_organizer.rb index c70507b1..2bac4d1b 100644 --- a/lib/query/query_event_organizer.rb +++ b/lib/query/query_event_organizer.rb @@ -1,15 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryEventOrganizer - def self.with_event(event_id) + def self.with_event(event_id) Qx.select( - "coalesce(profiles.name, nonprofits.name) AS name", - "coalesce(users.email, nonprofits.email) AS email" - ) + 'coalesce(profiles.name, nonprofits.name) AS name', + 'coalesce(users.email, nonprofits.email) AS email' + ) .from(:events) - .left_join(:profiles, "profiles.id=events.profile_id") - .add_left_join(:users, "profiles.user_id=users.id") - .add_join(:nonprofits, "events.nonprofit_id=nonprofits.id") - .where("events.id=$id", id: event_id) + .left_join(:profiles, 'profiles.id=events.profile_id') + .add_left_join(:users, 'profiles.user_id=users.id') + .add_join(:nonprofits, 'events.nonprofit_id=nonprofits.id') + .where('events.id=$id', id: event_id) .execute.first - end + end end diff --git a/lib/query/query_events.rb b/lib/query/query_events.rb index 252236c7..37a3af5a 100644 --- a/lib/query/query_events.rb +++ b/lib/query/query_events.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' @@ -5,13 +7,13 @@ module QueryEvents def self.name_and_id(npo_id) Psql.execute( Qexpr.new.select( - "events.name", - "events.id") - .from("events") - .where("events.nonprofit_id=$id", id: npo_id) + 'events.name', + 'events.id' + ) + .from('events') + .where('events.nonprofit_id=$id', id: npo_id) .where("events.deleted='f' OR events.deleted IS NULL") - .order_by("events.name ASC") + .order_by('events.name ASC') ) end - end diff --git a/lib/query/query_full_contact_infos.rb b/lib/query/query_full_contact_infos.rb index dc288246..e8c3f134 100644 --- a/lib/query/query_full_contact_infos.rb +++ b/lib/query/query_full_contact_infos.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' module QueryFullContactInfos - def self.fetch_associated_tables(fc_info_id) - photo = Psql.execute( Qexpr.new.from(:full_contact_photos).select('url').where("full_contact_info_id = $id", id: fc_info_id).where("is_primary")) - orgs = Psql.execute( Qexpr.new.from(:full_contact_orgs).select('current', 'name', 'title', 'start_date', 'end_date').where("full_contact_info_id = $id", id: fc_info_id).order_by('start_date DESC NULLS LAST')) - topics = Psql.execute( Qexpr.new.from(:full_contact_topics).select('value').where("full_contact_info_id = $id", id: fc_info_id).order_by('value ASC')) - profiles = Psql.execute( Qexpr.new.from(:full_contact_social_profiles).select('type_id', 'followers', 'url').where("full_contact_info_id = $id", id: fc_info_id).order_by('type_id ASC')) - return { + photo = Psql.execute(Qexpr.new.from(:full_contact_photos).select('url').where('full_contact_info_id = $id', id: fc_info_id).where('is_primary')) + orgs = Psql.execute(Qexpr.new.from(:full_contact_orgs).select('current', 'name', 'title', 'start_date', 'end_date').where('full_contact_info_id = $id', id: fc_info_id).order_by('start_date DESC NULLS LAST')) + topics = Psql.execute(Qexpr.new.from(:full_contact_topics).select('value').where('full_contact_info_id = $id', id: fc_info_id).order_by('value ASC')) + profiles = Psql.execute(Qexpr.new.from(:full_contact_social_profiles).select('type_id', 'followers', 'url').where('full_contact_info_id = $id', id: fc_info_id).order_by('type_id ASC')) + { photo: photo, topics: topics, orgs: orgs, diff --git a/lib/query/query_nonprofit_keys.rb b/lib/query/query_nonprofit_keys.rb index a3abd041..3e0c9bef 100644 --- a/lib/query/query_nonprofit_keys.rb +++ b/lib/query/query_nonprofit_keys.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module QueryNonprofitKeys - def self.get_key(npo_id, key_name) query = Qx.select(key_name) - .from(:nonprofit_keys) - .where({'nonprofit_id' => npo_id}) - .execute - raise ActiveRecord::RecordNotFound.new("Nonprofit key does not exist: #{key_name}") if query.empty? - Cypher.decrypt(JSON.parse query.first[key_name]) - end + .from(:nonprofit_keys) + .where('nonprofit_id' => npo_id) + .execute + raise ActiveRecord::RecordNotFound, "Nonprofit key does not exist: #{key_name}" if query.empty? + Cypher.decrypt(JSON.parse(query.first[key_name])) + end end diff --git a/lib/query/query_nonprofits.rb b/lib/query/query_nonprofits.rb index bd8b069c..7e1e4a39 100644 --- a/lib/query/query_nonprofits.rb +++ b/lib/query/query_nonprofits.rb @@ -1,26 +1,27 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' require 'psql' module QueryNonprofits - def self.all_that_need_payouts Psql.execute_vectors( Qexpr.new.select( - "nonprofits.id", - "nonprofits.stripe_account_id", + 'nonprofits.id', + 'nonprofits.stripe_account_id', "'support@commitchange.com' AS email", "'192.168.0.1' AS user_ip", - "bank_accounts.name" + 'bank_accounts.name' ).from(:nonprofits) .where("nonprofits.verification_status='verified'") - .join(:bank_accounts, "bank_accounts.nonprofit_id=nonprofits.id") + .join(:bank_accounts, 'bank_accounts.nonprofit_id=nonprofits.id') .where("bank_accounts.pending_verification='f'") .join( - Qexpr.new.select("nonprofit_id") - .from(:charges).group_by("nonprofit_id") - .where("status='available'").as("charges"), - "charges.nonprofit_id=nonprofits.id" + Qexpr.new.select('nonprofit_id') + .from(:charges).group_by('nonprofit_id') + .where("status='available'").as('charges'), + 'charges.nonprofit_id=nonprofits.id' ) )[1..-1] end @@ -28,18 +29,16 @@ module QueryNonprofits def self.by_search_string(string) results = Psql.execute_vectors( Qexpr.new.select( - "nonprofits.id", - "nonprofits.name" + 'nonprofits.id', + 'nonprofits.name' ).from(:nonprofits) - .where("lower(nonprofits.name) LIKE lower($search)", search: "%#{string}%") + .where('lower(nonprofits.name) LIKE lower($search)', search: "%#{string}%") .where("nonprofits.published='t'") - .order_by("nonprofits.name ASC") + .order_by('nonprofits.name ASC') .limit(10) )[1..-1] - if results - results = results.map {|id, name| {id: id, name: name}} - end - return results + results = results.map { |id, name| { id: id, name: name } } if results + results end def self.for_admin(params) @@ -52,66 +51,67 @@ module QueryNonprofits 'nonprofits.verification_status', 'nonprofits.vetted', 'nonprofits.stripe_account_id', - 'coalesce(events.count, 0) AS events_count', - 'coalesce(campaigns.count, 0) AS campaigns_count', + 'coalesce(events.count, 0) AS events_count', + 'coalesce(campaigns.count, 0) AS campaigns_count', 'billing_plans.percentage_fee', 'cards.stripe_customer_id', 'charges.total_processed', 'charges.total_fees' ).from(:nonprofits) - .add_left_join(:cards, "cards.holder_id=nonprofits.id AND cards.holder_type='Nonprofit'") - .add_left_join(:billing_subscriptions, "billing_subscriptions.nonprofit_id=nonprofits.id") - .add_left_join(:billing_plans, "billing_subscriptions.billing_plan_id=billing_plans.id") - .add_left_join( - Qx.select( - "((SUM(coalesce(fee, 0)) * .978) / 100)::money::text AS total_fees", - "(SUM(coalesce(amount, 0)) / 100)::money::text AS total_processed", - "nonprofit_id") - .from(:charges) - .where("status != 'failed'") - .and_where("created_at::date >= '2017-03-15'") - .group_by("nonprofit_id") - .as("charges"), - "charges.nonprofit_id=nonprofits.id" - ) - .add_left_join( - Qx.select("COUNT(id)", "nonprofit_id") - .from(:events) - .group_by("nonprofit_id") - .as("events"), - "events.nonprofit_id=nonprofits.id" - ) - .add_left_join( - Qx.select("COUNT(id)", "nonprofit_id") - .from(:campaigns) - .group_by("nonprofit_id") - .as("campaigns"), - "campaigns.nonprofit_id=nonprofits.id" - ) - .paginate(params[:page].to_i, params[:page_length].to_i) - .order_by('nonprofits.created_at DESC') + .add_left_join(:cards, "cards.holder_id=nonprofits.id AND cards.holder_type='Nonprofit'") + .add_left_join(:billing_subscriptions, 'billing_subscriptions.nonprofit_id=nonprofits.id') + .add_left_join(:billing_plans, 'billing_subscriptions.billing_plan_id=billing_plans.id') + .add_left_join( + Qx.select( + '((SUM(coalesce(fee, 0)) * .978) / 100)::money::text AS total_fees', + '(SUM(coalesce(amount, 0)) / 100)::money::text AS total_processed', + 'nonprofit_id' + ) + .from(:charges) + .where("status != 'failed'") + .and_where("created_at::date >= '2017-03-15'") + .group_by('nonprofit_id') + .as('charges'), + 'charges.nonprofit_id=nonprofits.id' + ) + .add_left_join( + Qx.select('COUNT(id)', 'nonprofit_id') + .from(:events) + .group_by('nonprofit_id') + .as('events'), + 'events.nonprofit_id=nonprofits.id' + ) + .add_left_join( + Qx.select('COUNT(id)', 'nonprofit_id') + .from(:campaigns) + .group_by('nonprofit_id') + .as('campaigns'), + 'campaigns.nonprofit_id=nonprofits.id' + ) + .paginate(params[:page].to_i, params[:page_length].to_i) + .order_by('nonprofits.created_at DESC') - if params[:search].present? - expr = expr.where(%Q( + if params[:search].present? + expr = expr.where(%( nonprofits.name ILIKE $search OR nonprofits.email ILIKE $search OR nonprofits.city ILIKE $search ), search: '%' + params[:search] + '%') - end + end - return expr.execute + expr.execute end - def self.find_nonprofits_with_no_payments() + def self.find_nonprofits_with_no_payments Nonprofit.includes(:payments).where('payments.nonprofit_id IS NULL') end def self.find_nonprofits_with_payments_in_last_n_days(days) - Payment.where("date >= ?", Time.now - days.days).pluck('nonprofit_id').to_a.uniq + Payment.where('date >= ?', Time.now - days.days).pluck('nonprofit_id').to_a.uniq end def self.find_nonprofits_with_payments_but_not_in_last_n_days(days) recent_nonprofits = find_nonprofits_with_payments_in_last_n_days(days) - Payment.where("date < ?", Time.now - days.days).pluck('nonprofit_id').to_a.uniq.select{|i| !recent_nonprofits.include?(i)} + Payment.where('date < ?', Time.now - days.days).pluck('nonprofit_id').to_a.uniq.reject { |i| recent_nonprofits.include?(i) } end end diff --git a/lib/query/query_payments.rb b/lib/query/query_payments.rb index f535e791..203251d3 100644 --- a/lib/query/query_payments.rb +++ b/lib/query/query_payments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' require 'psql' @@ -6,8 +8,6 @@ require 'query/query_supporters' require 'active_support/core_ext' module QueryPayments - - # Fetch all payments connected to available charges, undisbursed refunds or lost disputes # Ids For Payouts collects all payments where: # *they have a connected charge, refund or dispute (CRD), i.e. the CRD's payment_id is not NULL and represents a record in payments @@ -20,38 +20,39 @@ module QueryPayments # * For refunds and disputes, the gross_amount should be negative since we're decreasing the nonprofit's balance # # In effect, we're getting the list of payments which haven't been paid out in a some fashion. This is not a great design but it works mostly. - def self.ids_for_payout(npo_id, options={}) + def self.ids_for_payout(npo_id, options = {}) end_of_day = (Time.current + 1.day).beginning_of_day Qx.select('DISTINCT payments.id') - .from(:payments) - .left_join(:charges, 'charges.payment_id=payments.id') - .add_left_join(:refunds, 'refunds.payment_id=payments.id') - .add_left_join(:disputes, 'disputes.payment_id=payments.id') - .where('payments.nonprofit_id=$id', id: npo_id) - .and_where("refunds.payment_id IS NOT NULL OR charges.payment_id IS NOT NULL OR disputes.payment_id IS NOT NULL") - .and_where(%Q( + .from(:payments) + .left_join(:charges, 'charges.payment_id=payments.id') + .add_left_join(:refunds, 'refunds.payment_id=payments.id') + .add_left_join(:disputes, 'disputes.payment_id=payments.id') + .where('payments.nonprofit_id=$id', id: npo_id) + .and_where('refunds.payment_id IS NOT NULL OR charges.payment_id IS NOT NULL OR disputes.payment_id IS NOT NULL') + .and_where(%( ((refunds.payment_id IS NOT NULL AND refunds.disbursed IS NULL) OR refunds.disbursed='f') OR (charges.status='available') OR (disputes.status='lost') )) - .and_where("payments.date <= $date", date: options[:date] || end_of_day) - .execute.map{|h| h['id']} + .and_where('payments.date <= $date', date: options[:date] || end_of_day) + .execute.map { |h| h['id'] } end # the amount to payout calculates the total payout based upon the payments it's provided, likely provided from ids_to_payout def self.get_payout_totals(payment_ids) - return {'gross_amount' => 0, 'fee_total' => 0, 'net_amount' => 0} if payment_ids.empty? + return { 'gross_amount' => 0, 'fee_total' => 0, 'net_amount' => 0 } if payment_ids.empty? + Qx.select( 'SUM(payments.gross_amount) AS gross_amount', 'SUM(payments.fee_total) AS fee_total', 'SUM(payments.net_amount) AS net_amount', - 'COUNT(payments.*) AS count') + 'COUNT(payments.*) AS count' + ) .from(:payments) - .where("payments.id IN ($ids)", ids: payment_ids) + .where('payments.id IN ($ids)', ids: payment_ids) .execute.first end - def self.nonprofit_balances(npo_id) Psql.execute( Qexpr.new.select( @@ -60,17 +61,17 @@ module QueryPayments 'COUNT(available) AS count_available', 'COUNT(pending) AS count_pending', 'COUNT(refunds) AS count_refunds', - 'COUNT(disputes) AS count_disputes') + 'COUNT(disputes) AS count_disputes' + ) .from(:payments) .left_outer_join('refunds', "refunds.payment_id=payments.id AND (refunds.disbursed='f' OR refunds.disbursed IS NULL)") - .left_outer_join("charges available", "available.status='available' AND available.payment_id=payments.id") - .left_outer_join("charges pending", "pending.status='pending' AND pending.payment_id=payments.id") - .left_outer_join("disputes", "disputes.status='lost' AND disputes.payment_id=payments.id") - .where("payments.nonprofit_id=$id", id: npo_id) + .left_outer_join('charges available', "available.status='available' AND available.payment_id=payments.id") + .left_outer_join('charges pending', "pending.status='pending' AND pending.payment_id=payments.id") + .left_outer_join('disputes', "disputes.status='lost' AND disputes.payment_id=payments.id") + .where('payments.nonprofit_id=$id', id: npo_id) ).first end - def self.full_search(npo_id, query) limit = 30 offset = Qexpr.page_offset(limit, query[:page]) @@ -87,84 +88,79 @@ module QueryPayments payments = Psql.execute(expr.limit(limit).offset(offset).parse) totals_query = expr - .remove(:select) - .remove(:order_by) - .select( - 'COALESCE(COUNT(payments.id), 0) AS count', - 'COALESCE((SUM(payments.gross_amount) / 100.0), 0)::money::text AS amount') + .remove(:select) + .remove(:order_by) + .select( + 'COALESCE(COUNT(payments.id), 0) AS count', + 'COALESCE((SUM(payments.gross_amount) / 100.0), 0)::money::text AS amount' + ) totals = Psql.execute(totals_query).first - return { + { data: payments, total_count: totals['count'], total_amount: totals['amount'], remaining: Qexpr.remaining_count(totals['count'], limit, query[:page]) } - end - # we must provide payments.*, supporters.*, donations.*, associated event_id, associated campaign_id def self.full_search_expr(npo_id, query) expr = Qexpr.new.from('payments') - .left_outer_join('supporters', "supporters.id=payments.supporter_id") - .left_outer_join('donations', 'donations.id=payments.donation_id' ) - .join("(#{select_to_filter_search(npo_id, query)}) AS \"filtered_payments\"", 'payments.id = filtered_payments.id') - .order_by('payments.date DESC') + .left_outer_join('supporters', 'supporters.id=payments.supporter_id') + .left_outer_join('donations', 'donations.id=payments.donation_id') + .join("(#{select_to_filter_search(npo_id, query)}) AS \"filtered_payments\"", 'payments.id = filtered_payments.id') + .order_by('payments.date DESC') - if ['asc', 'desc'].include? query[:sort_amount] + if %w[asc desc].include? query[:sort_amount] expr = expr.order_by("payments.gross_amount #{query[:sort_amount]}") end - if ['asc', 'desc'].include? query[:sort_date] + if %w[asc desc].include? query[:sort_date] expr = expr.order_by("payments.date #{query[:sort_date]}") end - if ['asc', 'desc'].include? query[:sort_name] + if %w[asc desc].include? query[:sort_name] expr = expr.order_by("coalesce(NULLIF(supporters.name, ''), NULLIF(supporters.email, '')) #{query[:sort_name]}") end - if ['asc', 'desc'].include? query[:sort_type] + if %w[asc desc].include? query[:sort_type] expr = expr.order_by("payments.kind #{query[:sort_type]}") end - if ['asc', 'desc'].include? query[:sort_towards] + if %w[asc desc].include? query[:sort_towards] expr = expr.order_by("NULLIF(payments.towards, '') #{query[:sort_towards]}") end - return expr + expr end # perform the search but only get the relevant payment_ids def self.select_to_filter_search(npo_id, query) - inner_donation_search = Qexpr.new.select('donations.*').from('donations') - if (query[:event_id].present?) + if query[:event_id].present? inner_donation_search = inner_donation_search.where('donations.event_id=$id', id: query[:event_id]) end - if (query[:campaign_id].present?) + if query[:campaign_id].present? campaign_search = campaign_and_child_query_as_raw_string inner_donation_search = inner_donation_search.where("donations.campaign_id IN (#{campaign_search})", id: query[:campaign_id]) end expr = Qexpr.new.select('payments.id').from('payments') - .left_outer_join('supporters', "supporters.id=payments.supporter_id") - .left_outer_join(inner_donation_search.as('donations'), 'donations.id=payments.donation_id' ) - .where('payments.nonprofit_id=$id', id: npo_id.to_i) + .left_outer_join('supporters', 'supporters.id=payments.supporter_id') + .left_outer_join(inner_donation_search.as('donations'), 'donations.id=payments.donation_id') + .where('payments.nonprofit_id=$id', id: npo_id.to_i) - - if query[:search].present? - expr = SearchVector.query(query[:search], expr) - end - if ['asc', 'desc'].include? query[:sort_amount] + expr = SearchVector.query(query[:search], expr) if query[:search].present? + if %w[asc desc].include? query[:sort_amount] expr = expr.order_by("payments.gross_amount #{query[:sort_amount]}") end - if ['asc', 'desc'].include? query[:sort_date] + if %w[asc desc].include? query[:sort_date] expr = expr.order_by("payments.date #{query[:sort_date]}") end - if ['asc', 'desc'].include? query[:sort_name] + if %w[asc desc].include? query[:sort_name] expr = expr.order_by("coalesce(NULLIF(supporters.name, ''), NULLIF(supporters.email, '')) #{query[:sort_name]}") end - if ['asc', 'desc'].include? query[:sort_type] + if %w[asc desc].include? query[:sort_type] expr = expr.order_by("payments.kind #{query[:sort_type]}") end - if ['asc', 'desc'].include? query[:sort_towards] + if %w[asc desc].include? query[:sort_towards] expr = expr.order_by("NULLIF(payments.towards, '') #{query[:sort_towards]}") end if query[:after_date].present? @@ -183,10 +179,10 @@ module QueryPayments expr = expr.where("to_char(payments.date, 'YYYY')=$year", year: query[:year]) end if query[:designation].present? - expr = expr.where("donations.designation @@ $s", s: "#{query[:designation]}") + expr = expr.where('donations.designation @@ $s', s: (query[:designation]).to_s) end if query[:dedication].present? - expr = expr.where("donations.dedication @@ $s", s: "#{query[:dedication]}") + expr = expr.where('donations.dedication @@ $s', s: (query[:dedication]).to_s) end if query[:donation_type].present? expr = expr.where('payments.kind IN ($kinds)', kinds: query[:donation_type].split(',')) @@ -194,66 +190,61 @@ module QueryPayments if query[:campaign_id].present? campaign_search = campaign_and_child_query_as_raw_string expr = expr - .left_outer_join("campaigns", "campaigns.id=donations.campaign_id" ) - .where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id]) + .left_outer_join('campaigns', 'campaigns.id=donations.campaign_id') + .where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id]) end if query[:event_id].present? - tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by("payment_id").as("tix") + tickets_subquery = Qexpr.new.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by('payment_id').as('tix') expr = expr - .left_outer_join(tickets_subquery, "tix.payment_id=payments.id") - .where("tix.event_id=$id OR donations.event_id=$id", id: query[:event_id]) + .left_outer_join(tickets_subquery, 'tix.payment_id=payments.id') + .where('tix.event_id=$id OR donations.event_id=$id', id: query[:event_id]) end expr = expr - #we have the first part of the search. We need to create the second in certain situations + # we have the first part of the search. We need to create the second in certain situations filtered_payment_id_search = expr.parse if query[:event_id].present? || query[:campaign_id].present? - filtered_payment_id_search = filtered_payment_id_search + " UNION DISTINCT " + create_reverse_select(npo_id, query).parse + filtered_payment_id_search = filtered_payment_id_search + ' UNION DISTINCT ' + create_reverse_select(npo_id, query).parse end - - filtered_payment_id_search end # we use this when we need to get the refund info - def self.create_reverse_select(npo_id, query) + def self.create_reverse_select(npo_id, query) inner_donation_search = Qexpr.new.select('donations.*').from('donations') - if (query[:event_id].present?) + if query[:event_id].present? inner_donation_search = inner_donation_search.where('donations.event_id=$id', id: query[:event_id]) end - if (query[:campaign_id].present?) + if query[:campaign_id].present? campaign_search = campaign_and_child_query_as_raw_string inner_donation_search = inner_donation_search.where("donations.campaign_id IN (#{campaign_search})", id: query[:campaign_id]) end expr = Qexpr.new.select('payments.id').from('payments') - .left_outer_join('supporters', "supporters.id=payments.supporter_id") + .left_outer_join('supporters', 'supporters.id=payments.supporter_id') .left_outer_join('refunds', 'payments.id=refunds.payment_id') - .left_outer_join('charges', 'refunds.charge_id=charges.id') - .left_outer_join('payments AS payments_orig', 'payments_orig.id=charges.payment_id') - .left_outer_join(inner_donation_search.as('donations'), 'donations.id=payments_orig.donation_id' ) - .where('payments.nonprofit_id=$id', id: npo_id.to_i) + .left_outer_join('charges', 'refunds.charge_id=charges.id') + .left_outer_join('payments AS payments_orig', 'payments_orig.id=charges.payment_id') + .left_outer_join(inner_donation_search.as('donations'), 'donations.id=payments_orig.donation_id') + .where('payments.nonprofit_id=$id', id: npo_id.to_i) - - if query[:search].present? - expr = SearchVector.query(query[:search], expr) - end - if ['asc', 'desc'].include? query[:sort_amount] + expr = SearchVector.query(query[:search], expr) if query[:search].present? + if %w[asc desc].include? query[:sort_amount] expr = expr.order_by("payments.gross_amount #{query[:sort_amount]}") end - if ['asc', 'desc'].include? query[:sort_date] + if %w[asc desc].include? query[:sort_date] expr = expr.order_by("payments.date #{query[:sort_date]}") end - if ['asc', 'desc'].include? query[:sort_name] + if %w[asc desc].include? query[:sort_name] expr = expr.order_by("coalesce(NULLIF(supporters.name, ''), NULLIF(supporters.email, '')) #{query[:sort_name]}") end - if ['asc', 'desc'].include? query[:sort_type] + if %w[asc desc].include? query[:sort_type] expr = expr.order_by("payments.kind #{query[:sort_type]}") end - if ['asc', 'desc'].include? query[:sort_towards] + if %w[asc desc].include? query[:sort_towards] expr = expr.order_by("NULLIF(payments.towards, '') #{query[:sort_towards]}") end if query[:after_date].present? @@ -272,10 +263,10 @@ module QueryPayments expr = expr.where("to_char(payments.date, 'YYYY')=$year", year: query[:year]) end if query[:designation].present? - expr = expr.where("donations.designation @@ $s", s: "#{query[:designation]}") + expr = expr.where('donations.designation @@ $s', s: (query[:designation]).to_s) end if query[:dedication].present? - expr = expr.where("donations.dedication @@ $s", s: "#{query[:dedication]}") + expr = expr.where('donations.dedication @@ $s', s: (query[:dedication]).to_s) end if query[:donation_type].present? expr = expr.where('payments.kind IN ($kinds)', kinds: query[:donation_type].split(',')) @@ -283,65 +274,60 @@ module QueryPayments if query[:campaign_id].present? campaign_search = campaign_and_child_query_as_raw_string expr = expr - .left_outer_join("campaigns", "campaigns.id=donations.campaign_id" ) - .where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id]) + .left_outer_join('campaigns', 'campaigns.id=donations.campaign_id') + .where("campaigns.id IN (#{campaign_search})", id: query[:campaign_id]) end if query[:event_id].present? - tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by("payment_id").as("tix") + tickets_subquery = Qexpr.new.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').where('tickets.event_id=$event_id', event_id: query[:event_id]).group_by('payment_id').as('tix') expr = expr - .left_outer_join(tickets_subquery, "tix.payment_id=payments_orig.id") - .where("tix.event_id=$id OR donations.event_id=$id", id: query[:event_id]) + .left_outer_join(tickets_subquery, 'tix.payment_id=payments_orig.id') + .where('tix.event_id=$id OR donations.event_id=$id', id: query[:event_id]) end expr end - def self.for_export_enumerable(npo_id, query, chunk_limit=35000) - ParamValidation.new({npo_id: npo_id, query:query}, {npo_id: {required: true, is_int: true}, - query: {required:true, is_hash: true}}) + def self.for_export_enumerable(npo_id, query, chunk_limit = 35_000) + ParamValidation.new({ npo_id: npo_id, query: query }, npo_id: { required: true, is_int: true }, + query: { required: true, is_hash: true }) - return QexprQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| + QexprQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| get_chunk_of_export(npo_id, query, offset, limit, skip_header) end - end def self.for_export(npo_id, query) - tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").group_by("payment_id").as("tickets") + tickets_subquery = Qexpr.new.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').group_by('payment_id').as('tickets') expr = full_search_expr(npo_id, query) - .select(*export_selects) - .left_outer_join('campaign_gifts', 'campaign_gifts.donation_id=donations.id') - .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') - .left_outer_join("(#{campaigns_with_creator_email}) AS campaigns_for_export", 'donations.campaign_id=campaigns_for_export.id') - .left_outer_join(tickets_subquery, 'tickets.payment_id=payments.id') - .left_outer_join('events events_for_export', 'events_for_export.id=tickets.event_id OR donations.event_id=events_for_export.id') - .left_outer_join('offsite_payments', 'offsite_payments.payment_id=payments.id') - .parse + .select(*export_selects) + .left_outer_join('campaign_gifts', 'campaign_gifts.donation_id=donations.id') + .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') + .left_outer_join("(#{campaigns_with_creator_email}) AS campaigns_for_export", 'donations.campaign_id=campaigns_for_export.id') + .left_outer_join(tickets_subquery, 'tickets.payment_id=payments.id') + .left_outer_join('events events_for_export', 'events_for_export.id=tickets.event_id OR donations.event_id=events_for_export.id') + .left_outer_join('offsite_payments', 'offsite_payments.payment_id=payments.id') + .parse Psql.execute_vectors(expr) end - def self.get_chunk_of_export(npo_id, query, offset=nil, limit=nil, skip_header=false ) - - return QexprQueryChunker.get_chunk_of_query(offset, limit, skip_header) { - - - tickets_subquery = Qexpr.new.select("payment_id", "MAX(event_id) AS event_id").from("tickets").group_by("payment_id").as("tickets") + def self.get_chunk_of_export(npo_id, query, offset = nil, limit = nil, skip_header = false) + QexprQueryChunker.get_chunk_of_query(offset, limit, skip_header) do + tickets_subquery = Qexpr.new.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').group_by('payment_id').as('tickets') expr = full_search_expr(npo_id, query) - .select(*export_selects) - .left_outer_join('campaign_gifts', 'campaign_gifts.donation_id=donations.id') - .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') - .left_outer_join("(#{campaigns_with_creator_email}) AS campaigns_for_export", 'donations.campaign_id=campaigns_for_export.id') - .left_outer_join(tickets_subquery, 'tickets.payment_id=payments.id') - .left_outer_join('events events_for_export', 'events_for_export.id=tickets.event_id OR donations.event_id=events_for_export.id') - .left_outer_join('offsite_payments', 'offsite_payments.payment_id=payments.id') - } + .select(*export_selects) + .left_outer_join('campaign_gifts', 'campaign_gifts.donation_id=donations.id') + .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') + .left_outer_join("(#{campaigns_with_creator_email}) AS campaigns_for_export", 'donations.campaign_id=campaigns_for_export.id') + .left_outer_join(tickets_subquery, 'tickets.payment_id=payments.id') + .left_outer_join('events events_for_export', 'events_for_export.id=tickets.event_id OR donations.event_id=events_for_export.id') + .left_outer_join('offsite_payments', 'offsite_payments.payment_id=payments.id') + end end - def self.get_dedication_or_empty(*path) - "json_extract_path_text(coalesce(nullif(trim(both from donations.dedication), ''), '{}')::json, #{path.map{|i| "'#{i}'"}.join(',')})" + "json_extract_path_text(coalesce(nullif(trim(both from donations.dedication), ''), '{}')::json, #{path.map { |i| "'#{i}'" }.join(',')})" end def self.export_selects @@ -350,114 +336,109 @@ module QueryPayments '(payments.fee_total / 100.0)::money::text AS fee_total', '(payments.net_amount / 100.0)::money::text AS net_amount', 'payments.kind AS type'] - .concat(QuerySupporters.supporter_export_selections) - .concat([ - "coalesce(donations.designation, 'None') AS designation", - "#{get_dedication_or_empty('type')}::text AS \"Dedication Type\"", - "#{get_dedication_or_empty('name')}::text AS \"Dedicated To: Name\"", - "#{get_dedication_or_empty('supporter_id')}::text AS \"Dedicated To: Supporter ID\"", - "#{get_dedication_or_empty('contact', 'email')}::text AS \"Dedicated To: Email\"", - "#{get_dedication_or_empty('contact', "phone")}::text AS \"Dedicated To: Phone\"", - "#{get_dedication_or_empty( "contact", "address")}::text AS \"Dedicated To: Address\"", - "#{get_dedication_or_empty( "note")}::text AS \"Dedicated To: Note\"", - 'donations.anonymous', - 'donations.comment', - "coalesce(nullif(campaigns_for_export.name, ''), 'None') AS campaign", - "campaigns_for_export.id AS \"Campaign Id\"", - "coalesce(nullif(campaigns_for_export.creator_email, ''), '') AS campaign_creator_email", - "coalesce(nullif(campaign_gift_options.name, ''), 'None') AS campaign_gift_level", - 'events_for_export.name AS event_name', - 'payments.id AS payment_id', - 'offsite_payments.check_number AS check_number', - 'donations.comment AS donation_note' - ]) + .concat(QuerySupporters.supporter_export_selections) + .concat([ + "coalesce(donations.designation, 'None') AS designation", + "#{get_dedication_or_empty('type')}::text AS \"Dedication Type\"", + "#{get_dedication_or_empty('name')}::text AS \"Dedicated To: Name\"", + "#{get_dedication_or_empty('supporter_id')}::text AS \"Dedicated To: Supporter ID\"", + "#{get_dedication_or_empty('contact', 'email')}::text AS \"Dedicated To: Email\"", + "#{get_dedication_or_empty('contact', 'phone')}::text AS \"Dedicated To: Phone\"", + "#{get_dedication_or_empty('contact', 'address')}::text AS \"Dedicated To: Address\"", + "#{get_dedication_or_empty('note')}::text AS \"Dedicated To: Note\"", + 'donations.anonymous', + 'donations.comment', + "coalesce(nullif(campaigns_for_export.name, ''), 'None') AS campaign", + 'campaigns_for_export.id AS "Campaign Id"', + "coalesce(nullif(campaigns_for_export.creator_email, ''), '') AS campaign_creator_email", + "coalesce(nullif(campaign_gift_options.name, ''), 'None') AS campaign_gift_level", + 'events_for_export.name AS event_name', + 'payments.id AS payment_id', + 'offsite_payments.check_number AS check_number', + 'donations.comment AS donation_note' + ]) end - # Create the data structure for the payout export CSVs # Has two sections: two rows for info about the payout, then all the rows after that are for the payments # TODO reuse the standard payment export query for the payment rows for this query def self.for_payout(npo_id, payout_id) - tickets_subquery = Qx.select("payment_id", "MAX(event_id) AS event_id").from("tickets").group_by("payment_id").as("tickets") + tickets_subquery = Qx.select('payment_id', 'MAX(event_id) AS event_id').from('tickets').group_by('payment_id').as('tickets') supporters_subq = Qx.select(QuerySupporters.supporter_export_selections) Qx.select( - "to_char(payouts.created_at, 'MM/DD/YYYY HH24:MIam') AS date", - "(payouts.gross_amount / 100.0)::money::text AS gross_total", - "(payouts.fee_total / 100.0)::money::text AS fee_total", - "(payouts.net_amount / 100.0)::money::text AS net_total", - "bank_accounts.name AS bank_name", - "payouts.status" - ) + "to_char(payouts.created_at, 'MM/DD/YYYY HH24:MIam') AS date", + '(payouts.gross_amount / 100.0)::money::text AS gross_total', + '(payouts.fee_total / 100.0)::money::text AS fee_total', + '(payouts.net_amount / 100.0)::money::text AS net_total', + 'bank_accounts.name AS bank_name', + 'payouts.status' + ) .from(:payouts) - .join(:bank_accounts, "bank_accounts.nonprofit_id=payouts.nonprofit_id") - .where("payouts.nonprofit_id=$id", id: npo_id) - .and_where("payouts.id=$id", id: payout_id) + .join(:bank_accounts, 'bank_accounts.nonprofit_id=payouts.nonprofit_id') + .where('payouts.nonprofit_id=$id', id: npo_id) + .and_where('payouts.id=$id', id: payout_id) .execute(format: 'csv') .concat([[]]) .concat( Qx.select([ "to_char(payments.date, 'MM/DD/YYYY HH24:MIam') AS \"Date\"", - "(payments.gross_amount/100.0)::money::text AS \"Gross Amount\"", - "(payments.fee_total / 100.0)::money::text AS \"Fee Total\"", - "(payments.net_amount / 100.0)::money::text AS \"Net Amount\"", - "payments.kind AS \"Type\"", - "payments.id AS \"Payment ID\"" - ].concat(QuerySupporters.supporter_export_selections) + '(payments.gross_amount/100.0)::money::text AS "Gross Amount"', + '(payments.fee_total / 100.0)::money::text AS "Fee Total"', + '(payments.net_amount / 100.0)::money::text AS "Net Amount"', + 'payments.kind AS "Type"', + 'payments.id AS "Payment ID"' + ].concat(QuerySupporters.supporter_export_selections) .concat([ - "coalesce(donations.designation, 'None') AS \"Designation\"", - "donations.dedication AS \"Honorarium/Memorium\"", - "donations.anonymous AS \"Anonymous?\"", - "donations.comment AS \"Comment\"", - "coalesce(nullif(campaigns.name, ''), 'None') AS \"Campaign\"", - "coalesce(nullif(campaign_gift_options.name, ''), 'None') AS \"Campaign Gift Level\"", - "coalesce(events.name, 'None') AS \"Event\"" - ]) - ) + "coalesce(donations.designation, 'None') AS \"Designation\"", + 'donations.dedication AS "Honorarium/Memorium"', + 'donations.anonymous AS "Anonymous?"', + 'donations.comment AS "Comment"', + "coalesce(nullif(campaigns.name, ''), 'None') AS \"Campaign\"", + "coalesce(nullif(campaign_gift_options.name, ''), 'None') AS \"Campaign Gift Level\"", + "coalesce(events.name, 'None') AS \"Event\"" + ])) .distinct_on('payments.date, payments.id') .from(:payments) - .join(:payment_payouts, "payment_payouts.payment_id=payments.id") - .add_join(:payouts, "payouts.id=payment_payouts.payout_id") - .left_join(:supporters, "payments.supporter_id=supporters.id") - .add_left_join(:donations, "donations.id=payments.donation_id") - .add_left_join(:campaigns, "donations.campaign_id=campaigns.id") - .add_left_join(:campaign_gifts, "donations.id=campaign_gifts.donation_id") - .add_left_join(:campaign_gift_options, "campaign_gift_options.id=campaign_gifts.campaign_gift_option_id") - .add_left_join(tickets_subquery, "tickets.payment_id=payments.id") - .add_left_join(:events, "events.id=tickets.event_id OR (events.id = donations.event_id)") - .where("payouts.id=$id", id: payout_id) - .and_where("payments.nonprofit_id=$id", id: npo_id) - .order_by("payments.date DESC, payments.id") + .join(:payment_payouts, 'payment_payouts.payment_id=payments.id') + .add_join(:payouts, 'payouts.id=payment_payouts.payout_id') + .left_join(:supporters, 'payments.supporter_id=supporters.id') + .add_left_join(:donations, 'donations.id=payments.donation_id') + .add_left_join(:campaigns, 'donations.campaign_id=campaigns.id') + .add_left_join(:campaign_gifts, 'donations.id=campaign_gifts.donation_id') + .add_left_join(:campaign_gift_options, 'campaign_gift_options.id=campaign_gifts.campaign_gift_option_id') + .add_left_join(tickets_subquery, 'tickets.payment_id=payments.id') + .add_left_join(:events, 'events.id=tickets.event_id OR (events.id = donations.event_id)') + .where('payouts.id=$id', id: payout_id) + .and_where('payments.nonprofit_id=$id', id: npo_id) + .order_by('payments.date DESC, payments.id') .execute(format: 'csv') ) end - def self.find_payments_where_too_far_from_charge_date(id=nil) + def self.find_payments_where_too_far_from_charge_date(id = nil) pay = Payment.includes(:donation).includes(:offsite_payment) - if (id) - pay = pay.where('id = ?', id) - end + pay = pay.where('id = ?', id) if id pay = pay.where('date IS NOT NULL').order('id ASC') - pay.all.each{|p| - next if p.offsite_payment != nil + pay.all.each do |p| + next unless p.offsite_payment.nil? + lowest_charge_for_payment = Charge.where('payment_id = ?', p.id).order('created_at ASC').limit(1).first - - if (lowest_charge_for_payment) + if lowest_charge_for_payment diff = p.date - lowest_charge_for_payment.created_at diff_too_big = diff > 10.minutes || diff < -10.minutes end - if (lowest_charge_for_payment && diff_too_big) + if lowest_charge_for_payment && diff_too_big yield(p.donation.id, p.donation.date, p.id, p.date, lowest_charge_for_payment.id, lowest_charge_for_payment.created_at, diff) end - - } + end end def self.campaign_and_child_query_as_raw_string - "SELECT c_temp.id from campaigns c_temp where c_temp.id=$id OR c_temp.parent_campaign_id=$id" + 'SELECT c_temp.id from campaigns c_temp where c_temp.id=$id OR c_temp.parent_campaign_id=$id' end def self.campaigns_with_creator_email - Qexpr.new.select('campaigns.*, users.email AS creator_email').from(:campaigns).left_outer_join(:profiles, "profiles.id = campaigns.profile_id").left_outer_join(:users, 'users.id = profiles.user_id') + Qexpr.new.select('campaigns.*, users.email AS creator_email').from(:campaigns).left_outer_join(:profiles, 'profiles.id = campaigns.profile_id').left_outer_join(:users, 'users.id = profiles.user_id') end end diff --git a/lib/query/query_profiles.rb b/lib/query/query_profiles.rb index cf3b7e56..d1938a9b 100644 --- a/lib/query/query_profiles.rb +++ b/lib/query/query_profiles.rb @@ -1,28 +1,30 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' module QueryProfiles - def self.for_admin(params) - expr = Qx.select( - 'profiles.name', - 'profiles.id', - 'profiles.created_at::date::text AS created_at', - 'users.confirmed_at AS is_confirmed', - 'users.email') - .from(:profiles) - .add_left_join("users", "profiles.user_id=users.id") - .order_by("profiles.created_at DESC") - .paginate(params[:page].to_i, params[:page_length].to_i) + expr = Qx.select( + 'profiles.name', + 'profiles.id', + 'profiles.created_at::date::text AS created_at', + 'users.confirmed_at AS is_confirmed', + 'users.email' + ) + .from(:profiles) + .add_left_join('users', 'profiles.user_id=users.id') + .order_by('profiles.created_at DESC') + .paginate(params[:page].to_i, params[:page_length].to_i) - if params[:search].present? - expr = expr.where(%Q( - profiles.name LIKE $search - OR users.email LIKE $search - OR users.name LIKE $search - ), search: '%' + params[:search].downcase + '%') - end + if params[:search].present? + expr = expr.where(%( + profiles.name LIKE $search + OR users.email LIKE $search + OR users.name LIKE $search + ), search: '%' + params[:search].downcase + '%') + end - return expr.execute + expr.execute end end diff --git a/lib/query/query_recurring_donations.rb b/lib/query/query_recurring_donations.rb index 8edd9108..8172a0a6 100644 --- a/lib/query/query_recurring_donations.rb +++ b/lib/query/query_recurring_donations.rb @@ -1,33 +1,33 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' require 'psql' module QueryRecurringDonations - # Calculate a nonprofit's total recurring donations def self.monthly_total(np_id) - Qx.select("coalesce(sum(amount), 0) AS sum") - .from("recurring_donations") + Qx.select('coalesce(sum(amount), 0) AS sum') + .from('recurring_donations') .where(nonprofit_id: np_id) .and_where(is_external_active_clause('recurring_donations')) .execute.first['sum'] end - # Fetch a single recurring donation for its edit page def self.fetch_for_edit(id) recurring_donation = Psql.execute( - Qexpr.new.select( - "recurring_donations.*", - "nonprofits.id AS nonprofit_id", - "nonprofits.name AS nonprofit_name", - "cards.name AS card_name" - ).from('recurring_donations') - .left_outer_join('donations', 'donations.id=recurring_donations.donation_id') - .left_outer_join('cards', 'donations.card_id=cards.id') - .left_outer_join('nonprofits', 'nonprofits.id=recurring_donations.nonprofit_id') - .where('recurring_donations.id=$id', id: id) - ).first + Qexpr.new.select( + 'recurring_donations.*', + 'nonprofits.id AS nonprofit_id', + 'nonprofits.name AS nonprofit_name', + 'cards.name AS card_name' + ).from('recurring_donations') + .left_outer_join('donations', 'donations.id=recurring_donations.donation_id') + .left_outer_join('cards', 'donations.card_id=cards.id') + .left_outer_join('nonprofits', 'nonprofits.id=recurring_donations.nonprofit_id') + .where('recurring_donations.id=$id', id: id) + ).first return recurring_donation if !recurring_donation || !recurring_donation['id'] @@ -39,22 +39,21 @@ module QueryRecurringDonations nonprofit = Nonprofit.find(recurring_donation['nonprofit_id']) - return { + { 'recurring_donation' => recurring_donation, 'supporter' => supporter, 'nonprofit' => nonprofit } end - # Construct a full query for the dashboard/export listings def self.full_search_expr(np_id, query) expr = Qexpr.new - .from('recurring_donations') - .left_outer_join('supporters', 'supporters.id=recurring_donations.supporter_id') - .join('donations', 'donations.id=recurring_donations.donation_id') - .left_outer_join('charges paid_charges', 'paid_charges.donation_id=donations.id') - .where('recurring_donations.nonprofit_id=$id', id: np_id.to_i) + .from('recurring_donations') + .left_outer_join('supporters', 'supporters.id=recurring_donations.supporter_id') + .join('donations', 'donations.id=recurring_donations.donation_id') + .left_outer_join('charges paid_charges', 'paid_charges.donation_id=donations.id') + .where('recurring_donations.nonprofit_id=$id', id: np_id.to_i) failed_or_active_clauses = [] @@ -73,29 +72,27 @@ module QueryRecurringDonations failed_or_active_clauses.push("(#{clause})") end - if (failed_or_active_clauses.any?) - expr = expr.where("#{failed_or_active_clauses.join(' OR ')}") + if failed_or_active_clauses.any? + expr = expr.where(failed_or_active_clauses.join(' OR ').to_s) end - - expr = expr.where("paid_charges.id IS NULL OR paid_charges.status != 'failed'") - .group_by('recurring_donations.id') - .order_by('recurring_donations.created_at') + expr = expr.where("paid_charges.id IS NULL OR paid_charges.status != 'failed'") + .group_by('recurring_donations.id') + .order_by('recurring_donations.created_at') if query[:search].present? matcher = "%#{query[:search].downcase.split(' ').join('%')}%" - expr = expr.where(%Q(( + expr = expr.where(%(( lower(supporters.name) LIKE $name OR lower(supporters.email) LIKE $email OR recurring_donations.amount=$amount OR recurring_donations.id=$id - )), {name: matcher, email: matcher, amount: query[:search].to_i, id: query[:search].to_i}) + )), name: matcher, email: matcher, amount: query[:search].to_i, id: query[:search].to_i) end - return expr + expr end - # Fetch the full table of results for the dashboard - def self.full_list(np_id, query={}) + def self.full_list(np_id, query = {}) limit = 30 offset = Qexpr.page_offset(limit, query[:page]) expr = full_search_expr(np_id, query).select( @@ -108,8 +105,9 @@ module QueryRecurringDonations 'MAX(supporters.email) AS email', 'MAX(supporters.name) AS name', 'MAX(supporters.id) AS supporter_id', - 'SUM(paid_charges.amount) AS total_given') - .limit(limit).offset(offset) + 'SUM(paid_charges.amount) AS total_given' + ) + .limit(limit).offset(offset) data = Psql.execute(expr) total_count = Psql.execute( @@ -118,75 +116,74 @@ module QueryRecurringDonations ).first['count'] total_amount = monthly_total(np_id) - return { + { data: data, total_amount: total_amount, total_count: total_count, - remaining: Qexpr.remaining_count(total_count, limit, query[:page]), + remaining: Qexpr.remaining_count(total_count, limit, query[:page]) } end - def self.for_export_enumerable(npo_id, query, chunk_limit=35000) - ParamValidation.new({npo_id: npo_id, query:query}, {npo_id: {required: true, is_int: true}, - query: {required:true, is_hash: true}}) + def self.for_export_enumerable(npo_id, query, chunk_limit = 35_000) + ParamValidation.new({ npo_id: npo_id, query: query }, npo_id: { required: true, is_int: true }, + query: { required: true, is_hash: true }) - return QexprQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| + QexprQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| get_chunk_of_export(npo_id, query, offset, limit, skip_header) end - end - def self.get_chunk_of_export(npo_id, query, offset=nil, limit=nil, skip_header=false ) + def self.get_chunk_of_export(npo_id, query, offset = nil, limit = nil, skip_header = false) root_url = query[:root_url] - return QexprQueryChunker.get_chunk_of_query(offset, limit, skip_header) { - full_search_expr(npo_id, query).select( - 'recurring_donations.created_at', - '(recurring_donations.amount / 100.0)::money::text AS amount', - "concat('Every ', recurring_donations.interval, ' ', recurring_donations.time_unit, '(s)') AS interval", - '(SUM(paid_charges.amount) / 100.0)::money::text AS total_contributed', - 'MAX(campaigns.name) AS campaign_name', - 'MAX(supporters.name) AS supporter_name', - 'MAX(supporters.email) AS supporter_email', - 'MAX(supporters.phone) AS phone', - 'MAX(supporters.address) AS address', - 'MAX(supporters.city) AS city', - 'MAX(supporters.state_code) AS state', - 'MAX(supporters.zip_code) AS zip_code', - 'MAX(cards.name) AS card_name', - 'recurring_donations.id AS "Recurring Donation ID"', - 'MAX(donations.id) AS "Donation ID"', - "CASE WHEN #{is_cancelled_clause('recurring_donations')} THEN 'true' ELSE 'false' END AS Cancelled", - "CASE WHEN #{is_failed_clause('recurring_donations')} THEN 'true' ELSE 'false' END AS Failed", - 'recurring_donations.cancelled_at AS "Cancelled At"', - "CASE WHEN #{is_active_clause('recurring_donations')} THEN concat('#{root_url}recurring_donations/', recurring_donations.id, '/edit?t=', recurring_donations.edit_token) ELSE '' END AS \"Donation Management Url\"") - .left_outer_join('campaigns' , 'campaigns.id=donations.campaign_id') - .left_outer_join('cards', 'cards.id=donations.card_id') - } + QexprQueryChunker.get_chunk_of_query(offset, limit, skip_header) do + full_search_expr(npo_id, query).select( + 'recurring_donations.created_at', + '(recurring_donations.amount / 100.0)::money::text AS amount', + "concat('Every ', recurring_donations.interval, ' ', recurring_donations.time_unit, '(s)') AS interval", + '(SUM(paid_charges.amount) / 100.0)::money::text AS total_contributed', + 'MAX(campaigns.name) AS campaign_name', + 'MAX(supporters.name) AS supporter_name', + 'MAX(supporters.email) AS supporter_email', + 'MAX(supporters.phone) AS phone', + 'MAX(supporters.address) AS address', + 'MAX(supporters.city) AS city', + 'MAX(supporters.state_code) AS state', + 'MAX(supporters.zip_code) AS zip_code', + 'MAX(cards.name) AS card_name', + 'recurring_donations.id AS "Recurring Donation ID"', + 'MAX(donations.id) AS "Donation ID"', + "CASE WHEN #{is_cancelled_clause('recurring_donations')} THEN 'true' ELSE 'false' END AS Cancelled", + "CASE WHEN #{is_failed_clause('recurring_donations')} THEN 'true' ELSE 'false' END AS Failed", + 'recurring_donations.cancelled_at AS "Cancelled At"', + "CASE WHEN #{is_active_clause('recurring_donations')} THEN concat('#{root_url}recurring_donations/', recurring_donations.id, '/edit?t=', recurring_donations.edit_token) ELSE '' END AS \"Donation Management Url\"" + ) + .left_outer_join('campaigns', 'campaigns.id=donations.campaign_id') + .left_outer_join('cards', 'cards.id=donations.card_id') + end end - def self.recurring_donations_without_cards - RecurringDonation.active.includes(:card).includes(:charges).includes(:donation).includes(:nonprofit).includes(:supporter).where("cards.id IS NULL").order("recurring_donations.created_at DESC") + RecurringDonation.active.includes(:card).includes(:charges).includes(:donation).includes(:nonprofit).includes(:supporter).where('cards.id IS NULL').order('recurring_donations.created_at DESC') end - - def self._splitting_rd_supporters_without_cards() + + def self._splitting_rd_supporters_without_cards supporters_with_valid_rds = [] supporters_without_valid_rds = [] send_to_wendy = [] - supporters_with_cardless_rds = recurring_donations_without_cards.map {|rd| rd.supporter}.uniq{|s| s.id} + supporters_with_cardless_rds = recurring_donations_without_cards.map(&:supporter).uniq(&:id) - #does the supporter have even one rd with a valid card - supporters_with_cardless_rds.each {|s| + # does the supporter have even one rd with a valid card + supporters_with_cardless_rds.each do |s| valid_rd = find_recurring_donation_with_a_card(s) # they have a recurring donation with a card for the same org - if (valid_rd) - if (s.recurring_donations.length > 2) - #they have too many recurring_donations. Send to wendy + if valid_rd + if s.recurring_donations.length > 2 + # they have too many recurring_donations. Send to wendy send_to_wendy.push(s) else # are the recurring_donations the same amount? - if (s.recurring_donations[0].amount == s.recurring_donations[1].amount) + if s.recurring_donations[0].amount == s.recurring_donations[1].amount supporters_with_valid_rds.push(s) else # they're not the same amount. We got no clue. Send to Wendy @@ -197,60 +194,60 @@ module QueryRecurringDonations # they have no other recurring donations supporters_without_valid_rds.push(s) end - } + end - return supporters_with_valid_rds, send_to_wendy, supporters_without_valid_rds + [supporters_with_valid_rds, send_to_wendy, supporters_without_valid_rds] end # @param [Array] wendy_list_of_supporters # @param [String] path - # def self.create_wendy_csv(path, wendy_list_of_supporters) - # CSV.open(path, 'wb') {|csv| - # csv << ['supporter id', 'nonprofit id', 'supporter name', 'supporter address', 'supporter city', 'supporter state', 'supporter ZIP', 'supporter country', 'supporter phone', 'supporter email', 'supporter rd amounts'] - # wendy_list_of_supporters.each { |s| - # amounts = '$'+ s.recurring_donations.active.collect {|rd| Format::Currency.cents_to_dollars(rd.amount)}.join(", $") - # csv << [s.id, s.nonprofit.id, s.name, s.address, s.city, s.state_code, s.zip_code, s.country, s.phone, s.email, amounts] - # } - # } - # end + # def self.create_wendy_csv(path, wendy_list_of_supporters) + # CSV.open(path, 'wb') {|csv| + # csv << ['supporter id', 'nonprofit id', 'supporter name', 'supporter address', 'supporter city', 'supporter state', 'supporter ZIP', 'supporter country', 'supporter phone', 'supporter email', 'supporter rd amounts'] + # wendy_list_of_supporters.each { |s| + # amounts = '$'+ s.recurring_donations.active.collect {|rd| Format::Currency.cents_to_dollars(rd.amount)}.join(", $") + # csv << [s.id, s.nonprofit.id, s.name, s.address, s.city, s.state_code, s.zip_code, s.country, s.phone, s.email, amounts] + # } + # } + # end # @param [Supporter] supporter def self.find_recurring_donation_with_a_card(supporter) - supporter.recurring_donations.select{|rd| - rd.donation != nil && rd.donation.card != nil}.first() + supporter.recurring_donations.select do |rd| + !rd.donation.nil? && !rd.donation.card.nil? + end .first end # Check if a single recdon is due -- used in PayRecurringDonation.with_stripe def self.is_due?(rd_id) Psql.execute( _all_that_are_due - .where("recurring_donations.id=$id", id: rd_id) + .where('recurring_donations.id=$id', id: rd_id) ).any? end - # Sql partial expression # Select all due recurring donations # Can use this for all donations in the db, or extend the query for only those with a nonprofit_id, supporter_id, etc (see is_due?) # XXX horrendous conditional --what is wrong with me? def self._all_that_are_due now = Time.current - Qexpr.new.select("recurring_donations.id") - .from(:recurring_donations) - .where("recurring_donations.active='t'") - .where("coalesce(recurring_donations.n_failures, 0) < 3") - .where("recurring_donations.start_date IS NULL OR recurring_donations.start_date <= $now", now: now) - .where("recurring_donations.end_date IS NULL OR recurring_donations.end_date > $now", now: now) - .join('donations','recurring_donations.donation_id=donations.id and (donations.payment_provider IS NULL OR donations.payment_provider!=\'sepa\')') - .left_outer_join( # Join the most recent paid charge - Qexpr.new.select(:donation_id, "MAX(created_at) AS created_at") - .from(:charges) - .where("status != 'failed'") - .group_by("donation_id") - .as("last_charge"), - "last_charge.donation_id=recurring_donations.donation_id" - ) - .where(%Q( + Qexpr.new.select('recurring_donations.id') + .from(:recurring_donations) + .where("recurring_donations.active='t'") + .where('coalesce(recurring_donations.n_failures, 0) < 3') + .where('recurring_donations.start_date IS NULL OR recurring_donations.start_date <= $now', now: now) + .where('recurring_donations.end_date IS NULL OR recurring_donations.end_date > $now', now: now) + .join('donations', 'recurring_donations.donation_id=donations.id and (donations.payment_provider IS NULL OR donations.payment_provider!=\'sepa\')') + .left_outer_join( # Join the most recent paid charge + Qexpr.new.select(:donation_id, 'MAX(created_at) AS created_at') + .from(:charges) + .where("status != 'failed'") + .group_by('donation_id') + .as('last_charge'), + 'last_charge.donation_id=recurring_donations.donation_id' + ) + .where(%( last_charge.donation_id IS NULL OR ( (recurring_donations.time_unit != 'month' OR recurring_donations.interval != 1) @@ -271,34 +268,34 @@ module QueryRecurringDonations ) ) ) - ), { - now: now, - beginning_of_month: now.beginning_of_month, - beginning_of_last_month: (now - 1.month).beginning_of_month, - today: now.day - }) - .order_by('recurring_donations.created_at') + ), + now: now, + beginning_of_month: now.beginning_of_month, + beginning_of_last_month: (now - 1.month).beginning_of_month, + today: now.day) + .order_by('recurring_donations.created_at') end + # Some general statistics for a nonprofit def self.overall_stats(np_id) - return Psql.execute( + Psql.execute( Qexpr.new.from(:recurring_donations) .select( - "money(avg(recurring_donations.amount) / 100.0) AS average", - "money(coalesce(sum(rds_active.amount), 0) / 100.0) AS active_sum", - "coalesce(count(rds_active), 0) AS active_count", - "money(coalesce(sum(rds_inactive.amount), 0) / 100.0) AS inactive_sum", - "coalesce(count(rds_inactive), 0) AS inactive_count", - "money(coalesce(sum(rds_failed.amount), 0) / 100.0) AS failed_sum", - "coalesce(count(rds_failed), 0) AS failed_count", - "money(coalesce(sum(rds_cancelled.amount), 0) / 100.0) AS cancelled_sum", - "coalesce(count(rds_cancelled), 0) AS cancelled_count" + 'money(avg(recurring_donations.amount) / 100.0) AS average', + 'money(coalesce(sum(rds_active.amount), 0) / 100.0) AS active_sum', + 'coalesce(count(rds_active), 0) AS active_count', + 'money(coalesce(sum(rds_inactive.amount), 0) / 100.0) AS inactive_sum', + 'coalesce(count(rds_inactive), 0) AS inactive_count', + 'money(coalesce(sum(rds_failed.amount), 0) / 100.0) AS failed_sum', + 'coalesce(count(rds_failed), 0) AS failed_count', + 'money(coalesce(sum(rds_cancelled.amount), 0) / 100.0) AS cancelled_sum', + 'coalesce(count(rds_cancelled), 0) AS cancelled_count' ) .left_outer_join('recurring_donations rds_active', "rds_active.id=recurring_donations.id AND #{is_external_active_clause('rds_active')}") .left_outer_join('recurring_donations rds_inactive', "rds_inactive.id=recurring_donations.id AND #{is_external_cancelled_clause('rds_inactive')}") .left_outer_join('recurring_donations rds_failed', "rds_failed.id=recurring_donations.id AND #{is_failed_clause('rds_failed')}") .left_outer_join('recurring_donations rds_cancelled', "rds_cancelled.id=recurring_donations.id AND #{is_cancelled_clause('rds_cancelled')}") - .where("recurring_donations.nonprofit_id=$id", id: np_id) + .where('recurring_donations.nonprofit_id=$id', id: np_id) ).first end @@ -316,7 +313,6 @@ module QueryRecurringDonations "#{field_for_rd}.active='t'" end - def self.is_cancelled_clause(field_for_rd) "NOT (#{is_active_clause(field_for_rd)})" end @@ -330,21 +326,23 @@ module QueryRecurringDonations end def self.last_charge - Qexpr.new.select(:donation_id, "MAX(created_at) AS created_at") - .from(:charges) - .where("status != 'failed'") - .group_by("donation_id") - .as("last_charge") + Qexpr.new.select(:donation_id, 'MAX(created_at) AS created_at') + .from(:charges) + .where("status != 'failed'") + .group_by('donation_id') + .as('last_charge') end def self.export_for_transfer(nonprofit_id) - items = RecurringDonation.where("nonprofit_id = ?", nonprofit_id).active.includes('supporter').includes('card').to_a - output = items.map{|i| {supporter: i.supporter.id, - supporter_name: i.supporter.name, - supporter_email: i.supporter.email, - amount: i.amount, - paydate: i.paydate, - card: i.card.stripe_card_id}} - return output.to_a + items = RecurringDonation.where('nonprofit_id = ?', nonprofit_id).active.includes('supporter').includes('card').to_a + output = items.map do |i| + { supporter: i.supporter.id, + supporter_name: i.supporter.name, + supporter_email: i.supporter.email, + amount: i.amount, + paydate: i.paydate, + card: i.card.stripe_card_id } + end + output.to_a end end diff --git a/lib/query/query_roles.rb b/lib/query/query_roles.rb index 01db6251..b7807784 100644 --- a/lib/query/query_roles.rb +++ b/lib/query/query_roles.rb @@ -1,27 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryRoles + def self.user_has_role?(user_id, role_names, host_id = nil) + expr = Qx.select('COUNT(roles)').from(:roles) + .where('name IN ($names)', names: Array(role_names)) + .and_where(user_id: user_id) + expr = expr.and_where(host_id: host_id) if host_id + expr.execute.first['count'] > 0 + end - def self.user_has_role?(user_id, role_names, host_id=nil) - expr = Qx.select("COUNT(roles)").from(:roles) - .where("name IN ($names)", names: Array(role_names)) - .and_where(user_id: user_id) - expr = expr.and_where(host_id: host_id) if host_id - return expr.execute.first['count'] > 0 - end + # Get host tables -- host can be nonprofit, campaign, event + def self.host_ids(user_id, role_names) + Qx.select('host_id').from(:roles) + .where(user_id: user_id) + .and_where('roles.name IN ($names)', names: role_names) + .execute.map { |h| h['host_id'] } + end - # Get host tables -- host can be nonprofit, campaign, event - def self.host_ids(user_id, role_names) - Qx.select("host_id").from(:roles) - .where(user_id: user_id) - .and_where("roles.name IN ($names)", names: role_names) - .execute.map{|h| h['host_id']} - end + def self.is_nonprofit_user?(user_id, np_id) + user_has_role?(user_id, %i[nonprofit_admin nonprofit_associate], np_id) + end - def self.is_nonprofit_user?(user_id, np_id) - user_has_role?(user_id, [:nonprofit_admin, :nonprofit_associate], np_id) - end - - def self.is_authorized_for_nonprofit?(user_id, np_id) - user_has_role?(user_id, [:super_admin]) || is_nonprofit_user?(user_id, np_id) - end -end \ No newline at end of file + def self.is_authorized_for_nonprofit?(user_id, np_id) + user_has_role?(user_id, [:super_admin]) || is_nonprofit_user?(user_id, np_id) + end +end diff --git a/lib/query/query_source_token.rb b/lib/query/query_source_token.rb index 25a7abb6..0c9233ec 100644 --- a/lib/query/query_source_token.rb +++ b/lib/query/query_source_token.rb @@ -1,6 +1,8 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QuerySourceToken - EXPIRED_TOKEN_MESSAGE = "There was an error processing your card and it was not charged. Please try again." + EXPIRED_TOKEN_MESSAGE = 'There was an error processing your card and it was not charged. Please try again.' AUTH_ERROR_MESSAGE = "You're not authorized to make this charge" # @param [String] source_token @@ -11,43 +13,36 @@ module QuerySourceToken # @raise [ExpiredTokenError] when the source token has already been used too many times # or we're past the expiration date def self.get_and_increment_source_token(token, user = nil) - ParamValidation.new({token: token}, { - token: {required: true, format: UUID::Regex} - }) + ParamValidation.new({ token: token }, + token: { required: true, format: UUID::Regex }) source_token = SourceToken.where('token = ?', token).first if source_token - source_token.with_lock { + source_token.with_lock do unless source_token_unexpired?(source_token) - raise ExpiredTokenError.new(EXPIRED_TOKEN_MESSAGE) + raise ExpiredTokenError, EXPIRED_TOKEN_MESSAGE end if source_token.event - unless user - raise AuthenticationError.new AUTH_ERROR_MESSAGE - end + raise AuthenticationError, AUTH_ERROR_MESSAGE unless user unless QueryRoles.is_authorized_for_nonprofit?(user.id, source_token.event.nonprofit.id) - raise AuthenticationError.new AUTH_ERROR_MESSAGE + raise AuthenticationError, AUTH_ERROR_MESSAGE end end source_token.total_uses = source_token.total_uses + 1 source_token.save! - } + end else - raise ParamValidation::ValidationError.new "#{token} doesn't represent a valid source", {:key => :token} + raise ParamValidation::ValidationError.new "#{token} doesn't represent a valid source", key: :token end source_token end - def self.source_token_unexpired?(source_token) - if source_token.max_uses <= source_token.total_uses - return false - end - if source_token.expiration < Time.now - return false - end + return false if source_token.max_uses <= source_token.total_uses + return false if source_token.expiration < Time.now + true end @@ -57,4 +52,4 @@ module QuerySourceToken raise ParamValidation::ValidationError.new("The item for token #{data[:token]} is not a Card", key: :token) end end -end \ No newline at end of file +end diff --git a/lib/query/query_supporters.rb b/lib/query/query_supporters.rb index 017a9948..eab913da 100644 --- a/lib/query/query_supporters.rb +++ b/lib/query/query_supporters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qexpr' require 'psql' @@ -6,44 +8,42 @@ require 'format/currency' require 'format/csv' module QuerySupporters - # Query supporters and their donations and gift levels for a campaign def self.campaign_list_expr(np_id, campaign_id, query) expr = Qexpr.new.from('supporters') - .left_outer_join('donations', 'donations.supporter_id=supporters.id') - .left_outer_join('campaign_gifts', 'donations.id=campaign_gifts.donation_id') - .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') - .join_lateral(:payments, Qx + .left_outer_join('donations', 'donations.supporter_id=supporters.id') + .left_outer_join('campaign_gifts', 'donations.id=campaign_gifts.donation_id') + .left_outer_join('campaign_gift_options', 'campaign_gifts.campaign_gift_option_id=campaign_gift_options.id') + .join_lateral(:payments, Qx .select('payments.id, payments.gross_amount').from(:payments) .where('payments.donation_id = donations.id') .order_by('payments.created_at ASC') .limit(1).parse, true) - .join(Qx.select('id, profile_id').from('campaigns') + .join(Qx.select('id, profile_id').from('campaigns') .where("id IN (#{QueryCampaigns .get_campaign_and_children(campaign_id) .parse})").as('campaigns').parse, - 'donations.campaign_id=campaigns.id') - .join(Qx.select('users.id, profiles.id AS profiles_id, users.email') + 'donations.campaign_id=campaigns.id') + .join(Qx.select('users.id, profiles.id AS profiles_id, users.email') .from('users') .add_join('profiles', 'profiles.user_id = users.id') - .as("users").parse, "users.profiles_id=campaigns.profile_id") - .where("supporters.nonprofit_id=$id", id: np_id) - .group_by('supporters.id') - .order_by('MAX(donations.date) DESC') + .as('users').parse, 'users.profiles_id=campaigns.profile_id') + .where('supporters.nonprofit_id=$id', id: np_id) + .group_by('supporters.id') + .order_by('MAX(donations.date) DESC') - if query[:search].present? - expr = expr.where(%Q( + if query[:search].present? + expr = expr.where(%( supporters.name ILIKE $search OR supporters.email ILIKE $search OR campaign_gift_options.name ILIKE $search ), search: '%' + query[:search] + '%') end - return expr + expr end - - # Used in the campaign donor listing - def self.campaign_list(np_id, campaign_id, query) + # Used in the campaign donor listing + def self.campaign_list(np_id, campaign_id, query) limit = 50 offset = Qexpr.page_offset(limit, query[:page]) @@ -60,24 +60,24 @@ module QuerySupporters ) total_count = Psql.execute( - Qexpr.new.select("COUNT(s)") + Qexpr.new.select('COUNT(s)') .from(campaign_list_expr(np_id, campaign_id, query).remove(:order_by).select('supporters.id').as('s').parse) ).first['count'] - return { - data: data, - total_count: total_count, - remaining: Qexpr.remaining_count(total_count, limit, query[:page]) + { + data: data, + total_count: total_count, + remaining: Qexpr.remaining_count(total_count, limit, query[:page]) } - end + end def self.full_search_metrics(np_id, query) total_count = full_filter_expr(np_id, query) - .select("COUNT(supporters)") - .remove_clause(:order_by) - .execute.first['count'] + .select('COUNT(supporters)') + .remove_clause(:order_by) + .execute.first['count'] - return { + { total_count: total_count, remaining_count: Qexpr.remaining_count(total_count, 30, query[:page]) } @@ -94,40 +94,34 @@ module QuerySupporters "to_char(payments.max_date, 'MM/DD/YY') AS last_contribution", 'payments.sum AS total_raised' ] - if query[:select] - select += query[:select].split(',') - end + select += query[:select].split(',') if query[:select] supps = full_filter_expr(np_id, query) - .select(*select) - .paginate(query[:page].to_i, 30) - .execute + .select(*select) + .paginate(query[:page].to_i, 30) + .execute - return { data: supps } + { data: supps } end - - def self._full_search(np_id, query) select = [ - 'supporters.name', - 'supporters.email', - 'supporters.is_unsubscribed_from_emails', - 'supporters.id AS id', - 'tags.names AS tags', - "to_char(payments.max_date, 'MM/DD/YY') AS last_contribution", - 'payments.sum AS total_raised' + 'supporters.name', + 'supporters.email', + 'supporters.is_unsubscribed_from_emails', + 'supporters.id AS id', + 'tags.names AS tags', + "to_char(payments.max_date, 'MM/DD/YY') AS last_contribution", + 'payments.sum AS total_raised' ] - if query[:select] - select += query[:select].split(',') - end + select += query[:select].split(',') if query[:select] supps = full_filter_expr(np_id, query) - .select(*select) - .paginate(query[:page].to_i, query[:page_length].to_i) - .execute + .select(*select) + .paginate(query[:page].to_i, query[:page_length].to_i) + .execute - return { data: supps } + { data: supps } end # # Given a list of supporters, you may want to remove duplicates from those supporters. @@ -154,74 +148,73 @@ module QuerySupporters # return new_supporters # end - # Perform all filters and search for /nonprofits/id/supporters dashboard and export def self.full_filter_expr(np_id, query) payments_subquery = - Qx.select("supporter_id", "SUM(gross_amount)", "MAX(date) AS max_date", "MIN(date) AS min_date", "COUNT(*) AS count") - .from( - Qx.select("supporter_id", "date", "gross_amount") - .from(:payments) - .join(Qx.select('id') - .from(:supporters) - .where("supporters.nonprofit_id = $id and deleted != 'true'", id: np_id ) - .as("payments_to_supporters"), "payments_to_supporters.id = payments.supporter_id" - ) - .as("outer_from_payment_to_supporter") - .parse) - .group_by(:supporter_id) - .as(:payments) + Qx.select('supporter_id', 'SUM(gross_amount)', 'MAX(date) AS max_date', 'MIN(date) AS min_date', 'COUNT(*) AS count') + .from( + Qx.select('supporter_id', 'date', 'gross_amount') + .from(:payments) + .join(Qx.select('id') + .from(:supporters) + .where("supporters.nonprofit_id = $id and deleted != 'true'", id: np_id) + .as('payments_to_supporters'), 'payments_to_supporters.id = payments.supporter_id') + .as('outer_from_payment_to_supporter') + .parse + ) + .group_by(:supporter_id) + .as(:payments) - tags_subquery = Qx.select("tag_joins.supporter_id", "ARRAY_AGG(tag_masters.id) AS ids", "ARRAY_AGG(tag_masters.name::text) AS names") - .from(:tag_joins) - .join(:tag_masters, "tag_masters.id=tag_joins.tag_master_id") - .where("tag_masters.deleted IS NULL") - .group_by("tag_joins.supporter_id") - .as(:tags) + tags_subquery = Qx.select('tag_joins.supporter_id', 'ARRAY_AGG(tag_masters.id) AS ids', 'ARRAY_AGG(tag_masters.name::text) AS names') + .from(:tag_joins) + .join(:tag_masters, 'tag_masters.id=tag_joins.tag_master_id') + .where('tag_masters.deleted IS NULL') + .group_by('tag_joins.supporter_id') + .as(:tags) expr = Qx.select('supporters.id').from(:supporters) - .where( - ["supporters.nonprofit_id=$id", id: np_id.to_i], - ["supporters.deleted != true"] - ) - .left_join( - [tags_subquery, "tags.supporter_id=supporters.id"], - [payments_subquery, "payments.supporter_id=supporters.id"] - ) - .order_by('payments.max_date DESC NULLS LAST') + .where( + ['supporters.nonprofit_id=$id', id: np_id.to_i], + ['supporters.deleted != true'] + ) + .left_join( + [tags_subquery, 'tags.supporter_id=supporters.id'], + [payments_subquery, 'payments.supporter_id=supporters.id'] + ) + .order_by('payments.max_date DESC NULLS LAST') if query[:last_payment_after].present? - expr = expr.and_where("payments.max_date > $d", d: Chronic.parse(query[:last_payment_after])) + expr = expr.and_where('payments.max_date > $d', d: Chronic.parse(query[:last_payment_after])) end if query[:last_payment_before].present? - expr = expr.and_where("payments.max_date < $d", d: Chronic.parse(query[:last_payment_before])) + expr = expr.and_where('payments.max_date < $d', d: Chronic.parse(query[:last_payment_before])) end if query[:first_payment_after].present? - expr = expr.and_where("payments.min_date > $d", d: Chronic.parse(query[:first_payment_after])) + expr = expr.and_where('payments.min_date > $d', d: Chronic.parse(query[:first_payment_after])) end if query[:first_payment_before].present? - expr = expr.and_where("payments.min_date < $d", d: Chronic.parse(query[:first_payment_before])) + expr = expr.and_where('payments.min_date < $d', d: Chronic.parse(query[:first_payment_before])) end if query[:total_raised_greater_than].present? - expr = expr.and_where("payments.sum > $amount", amount: query[:total_raised_greater_than].to_i * 100) + expr = expr.and_where('payments.sum > $amount', amount: query[:total_raised_greater_than].to_i * 100) end if query[:total_raised_less_than].present? - expr = expr.and_where("payments.sum < $amount OR payments.supporter_id IS NULL", amount: query[:total_raised_less_than].to_i * 100) + expr = expr.and_where('payments.sum < $amount OR payments.supporter_id IS NULL', amount: query[:total_raised_less_than].to_i * 100) end - if ['week', 'month', 'quarter', 'year'].include? query[:has_contributed_during] + if %w[week month quarter year].include? query[:has_contributed_during] d = Time.current.send('beginning_of_' + query[:has_contributed_during]) - expr = expr.and_where("payments.max_date >= $d", d: d) + expr = expr.and_where('payments.max_date >= $d', d: d) end - if ['week', 'month', 'quarter', 'year'].include? query[:has_not_contributed_during] + if %w[week month quarter year].include? query[:has_not_contributed_during] d = Time.current.send('beginning_of_' + query[:has_not_contributed_during]) - expr = expr.and_where("payments.count = 0 OR payments.max_date <= $d", d: d) + expr = expr.and_where('payments.count = 0 OR payments.max_date <= $d', d: d) end if query[:MAX_payment_before].present? date_ago = Timespan::TimeUnits[query[:MAX_payment_before]].utc - expr = expr.and_where("payments.max_date < $date OR payments.count = 0", date: date_ago) + expr = expr.and_where('payments.max_date < $date OR payments.count = 0', date: date_ago) end if query[:search].present? - expr = expr.and_where(%Q( + expr = expr.and_where(%( supporters.name ILIKE $search OR supporters.email ILIKE $search OR supporters.organization ILIKE $search @@ -229,65 +222,66 @@ module QuerySupporters end if query[:notes].present? notes_subquery = Qx.select("STRING_AGG(content, ' ') as content, supporter_id") - .from(:supporter_notes) - .group_by(:supporter_id) - .as(:notes) - expr = expr.add_left_join(notes_subquery, "notes.supporter_id=supporters.id") - .and_where("to_tsvector('english', notes.content) @@ plainto_tsquery('english', $notes)", notes: query[:notes]) + .from(:supporter_notes) + .group_by(:supporter_id) + .as(:notes) + expr = expr.add_left_join(notes_subquery, 'notes.supporter_id=supporters.id') + .and_where("to_tsvector('english', notes.content) @@ plainto_tsquery('english', $notes)", notes: query[:notes]) end if query[:custom_fields].present? - c_f_subquery = Qx.select("STRING_AGG(value, ' ') as value", "supporter_id") - .from(:custom_field_joins) - .group_by("custom_field_joins.supporter_id") - .as(:custom_fields) - expr = expr.add_left_join(c_f_subquery, "custom_fields.supporter_id=supporters.id") - .and_where("to_tsvector('english', custom_fields.value) @@ plainto_tsquery('english', $custom_fields)", custom_fields: query[:custom_fields]) + c_f_subquery = Qx.select("STRING_AGG(value, ' ') as value", 'supporter_id') + .from(:custom_field_joins) + .group_by('custom_field_joins.supporter_id') + .as(:custom_fields) + expr = expr.add_left_join(c_f_subquery, 'custom_fields.supporter_id=supporters.id') + .and_where("to_tsvector('english', custom_fields.value) @@ plainto_tsquery('english', $custom_fields)", custom_fields: query[:custom_fields]) end if query[:location].present? - expr = expr.and_where("lower(supporters.city) LIKE $city OR lower(supporters.zip_code) LIKE $zip", city: query[:location].downcase, zip: query[:location].downcase) + expr = expr.and_where('lower(supporters.city) LIKE $city OR lower(supporters.zip_code) LIKE $zip', city: query[:location].downcase, zip: query[:location].downcase) end if query[:recurring].present? - rec_ps_subquery = Qx.select("payments.count", "payments.supporter_id") - .from(:payments) - .where("kind='RecurringDonation'") - .group_by("payments.supporter_id") - .as(:rec_ps) - expr = expr.add_left_join(rec_ps_subquery, "rec_ps.supporter_id=supporters.id") - .and_where('rec_ps.count > 0') + rec_ps_subquery = Qx.select('payments.count', 'payments.supporter_id') + .from(:payments) + .where("kind='RecurringDonation'") + .group_by('payments.supporter_id') + .as(:rec_ps) + expr = expr.add_left_join(rec_ps_subquery, 'rec_ps.supporter_id=supporters.id') + .and_where('rec_ps.count > 0') end if query[:ids].present? - expr = expr.and_where("supporters.id IN ($ids)", ids: query[:ids].split(",").map(&:to_i)) + expr = expr.and_where('supporters.id IN ($ids)', ids: query[:ids].split(',').map(&:to_i)) end if query[:select].present? - expr = expr.select(*query[:select].split(",").map{|x| Qx.quote_ident(x)}) + expr = expr.select(*query[:select].split(',').map { |x| Qx.quote_ident(x) }) end # Sort by supporters who have all of the list of tag names if query[:tags].present? tag_ids = (query[:tags].is_a?(String) ? query[:tags].split(',') : query[:tags]).map(&:to_i) - expr = expr.and_where("tags.ids @> ARRAY[$tag_ids]", tag_ids: tag_ids) + expr = expr.and_where('tags.ids @> ARRAY[$tag_ids]', tag_ids: tag_ids) end if query[:campaign_id].present? - expr = expr.add_join("donations", "donations.supporter_id=supporters.id AND donations.campaign_id IN (#{QueryCampaigns + expr = expr.add_join('donations', "donations.supporter_id=supporters.id AND donations.campaign_id IN (#{QueryCampaigns .get_campaign_and_children(query[:campaign_id].to_i) .parse})") end if query[:event_id].present? - select_tickets_supporters = Qx.select("event_ticket_supporters.supporter_id") - .from( - "#{Qx.select("MAX(tickets.event_id) AS event_id", "tickets.supporter_id") - .from(:tickets) - .where("event_id = $event_id", event_id: query[:event_id]) - .group_by(:supporter_id).as('event_ticket_supporters').parse}" - ) + select_tickets_supporters = Qx.select('event_ticket_supporters.supporter_id') + .from( + Qx.select('MAX(tickets.event_id) AS event_id', 'tickets.supporter_id') + .from(:tickets) + .where('event_id = $event_id', event_id: query[:event_id]) + .group_by(:supporter_id).as('event_ticket_supporters').parse.to_s + ) select_donation_supporters = - Qx.select("event_donation_supporters.supporter_id") - .from( - "#{Qx.select("MAX(donations.event_id) AS event_id", "donations.supporter_id") - .from(:donations) - .where("event_id = $event_id", event_id: query[:event_id] ) - .group_by(:supporter_id).as('event_donation_supporters').parse}") + Qx.select('event_donation_supporters.supporter_id') + .from( + Qx.select('MAX(donations.event_id) AS event_id', 'donations.supporter_id') + .from(:donations) + .where('event_id = $event_id', event_id: query[:event_id]) + .group_by(:supporter_id).as('event_donation_supporters').parse.to_s + ) union_expr = "( #{select_tickets_supporters.parse} @@ -296,39 +290,38 @@ UNION DISTINCT ) AS event_supporters" expr = expr - .add_join( - union_expr, - "event_supporters.supporter_id=supporters.id" - ) + .add_join( + union_expr, + 'event_supporters.supporter_id=supporters.id' + ) end - if ['asc', 'desc'].include? query[:sort_name] - expr = expr.order_by(["supporters.name", query[:sort_name]]) + if %w[asc desc].include? query[:sort_name] + expr = expr.order_by(['supporters.name', query[:sort_name]]) end - if ['asc', 'desc'].include? query[:sort_contributed] - expr = expr.and_where("payments.sum > 0").order_by(["payments.sum", query[:sort_contributed]]) + if %w[asc desc].include? query[:sort_contributed] + expr = expr.and_where('payments.sum > 0').order_by(['payments.sum', query[:sort_contributed]]) end - if ['asc', 'desc'].include? query[:sort_last_payment] - expr = expr.order_by(["payments.max_date", "#{query[:sort_last_payment].upcase} NULLS LAST"]) + if %w[asc desc].include? query[:sort_last_payment] + expr = expr.order_by(['payments.max_date', "#{query[:sort_last_payment].upcase} NULLS LAST"]) end - return expr + expr end - def self.for_export_enumerable(npo_id, query, chunk_limit=35000) - ParamValidation.new({npo_id: npo_id, query:query}, {npo_id: {required: true, is_int: true}, - query: {required:true, is_hash: true}}) + def self.for_export_enumerable(npo_id, query, chunk_limit = 35_000) + ParamValidation.new({ npo_id: npo_id, query: query }, npo_id: { required: true, is_int: true }, + query: { required: true, is_hash: true }) - return QxQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| + QxQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| get_chunk_of_export(npo_id, query, offset, limit, skip_header) end - end - def self.get_chunk_of_export(np_id, query, offset=nil, limit=nil, skip_header=false) - return QxQueryChunker.get_chunk_of_query(offset, limit, skip_header) do + def self.get_chunk_of_export(np_id, query, offset = nil, limit = nil, skip_header = false) + QxQueryChunker.get_chunk_of_query(offset, limit, skip_header) do expr = full_filter_expr(np_id, query) selects = supporter_export_selections.concat([ - '(payments.sum / 100)::money::text AS total_contributed', - 'supporters.id AS id' + '(payments.sum / 100)::money::text AS total_contributed', + 'supporters.id AS id' ]) if query[:export_custom_fields] # Add a select/csv-column for every custom field master for this nonprofit @@ -340,57 +333,53 @@ UNION DISTINCT # ... ids = query[:export_custom_fields].split(',').map(&:to_i) if ids.any? - cfms = Qx.select("name", "id").from(:custom_field_masters).where(nonprofit_id: np_id).and_where("id IN ($ids)", ids: ids).ex + cfms = Qx.select('name', 'id').from(:custom_field_masters).where(nonprofit_id: np_id).and_where('id IN ($ids)', ids: ids).ex cfms.compact.map do |cfm| - table_alias = "cfjs_#{cfm['name'].gsub(/\$/, "")}" + table_alias = "cfjs_#{cfm['name'].delete('$')}" table_alias_quot = "\"#{table_alias}\"" - field_join_subq = Qx.select("STRING_AGG(value, ',') as value", "supporter_id") - .from("custom_field_joins") - .join("custom_field_masters" , "custom_field_masters.id=custom_field_joins.custom_field_master_id") - .where("custom_field_masters.id=$id", id: cfm['id']) - .group_by(:supporter_id) - .as(table_alias) + field_join_subq = Qx.select("STRING_AGG(value, ',') as value", 'supporter_id') + .from('custom_field_joins') + .join('custom_field_masters', 'custom_field_masters.id=custom_field_joins.custom_field_master_id') + .where('custom_field_masters.id=$id', id: cfm['id']) + .group_by(:supporter_id) + .as(table_alias) expr.add_left_join(field_join_subq, "#{table_alias_quot}.supporter_id=supporters.id") selects = selects.concat(["#{table_alias_quot}.value AS \"#{cfm['name']}\""]) end end end - - get_last_payment_query = Qx.select('supporter_id', "MAX(date) AS date") - .from(:payments) - .group_by("supporter_id") - .as("last_payment") + get_last_payment_query = Qx.select('supporter_id', 'MAX(date) AS date') + .from(:payments) + .group_by('supporter_id') + .as('last_payment') expr.add_left_join(get_last_payment_query, 'last_payment.supporter_id = supporters.id') selects = selects.concat(['last_payment.date as "Last Payment Received"']) - - supporter_note_query = Qx.select("STRING_AGG(supporter_notes.created_at || ': ' || supporter_notes.content, '\r\n' ORDER BY supporter_notes.created_at DESC) as notes", "supporter_notes.supporter_id") - .from(:supporter_notes) - .group_by('supporter_notes.supporter_id') - .as("supporter_note_query") + supporter_note_query = Qx.select("STRING_AGG(supporter_notes.created_at || ': ' || supporter_notes.content, '\r\n' ORDER BY supporter_notes.created_at DESC) as notes", 'supporter_notes.supporter_id') + .from(:supporter_notes) + .group_by('supporter_notes.supporter_id') + .as('supporter_note_query') expr.add_left_join(supporter_note_query, 'supporter_note_query.supporter_id=supporters.id') - selects = selects.concat(["supporter_note_query.notes AS notes"]).concat(["ARRAY_TO_STRING(tags.names, ',') as tags"]) - + selects = selects.concat(['supporter_note_query.notes AS notes']).concat(["ARRAY_TO_STRING(tags.names, ',') as tags"]) expr.select(selects) end end - def self.supporter_note_export_enumerable(npo_id, query, chunk_limit=35000) - ParamValidation.new({npo_id: npo_id, query:query}, {npo_id: {required: true, is_int: true}, - query: {required:true, is_hash: true}}) + def self.supporter_note_export_enumerable(npo_id, query, chunk_limit = 35_000) + ParamValidation.new({ npo_id: npo_id, query: query }, npo_id: { required: true, is_int: true }, + query: { required: true, is_hash: true }) - return QxQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| + QxQueryChunker.for_export_enumerable(chunk_limit) do |offset, limit, skip_header| get_chunk_of_supporter_note_export(npo_id, query, offset, limit, skip_header) end - end - def self.get_chunk_of_supporter_note_export(np_id, query, offset=nil, limit=nil, skip_header=false) - return QxQueryChunker.get_chunk_of_query(offset, limit, skip_header) do + def self.get_chunk_of_supporter_note_export(np_id, query, offset = nil, limit = nil, skip_header = false) + QxQueryChunker.get_chunk_of_query(offset, limit, skip_header) do expr = full_filter_expr(np_id, query) supporter_note_select = [ 'supporters.id', @@ -408,41 +397,41 @@ UNION DISTINCT def self.for_export(np_id, query) expr = full_filter_expr(np_id, query) selects = supporter_export_selections.concat([ - '(payments.sum / 100)::money::text AS total_contributed', - 'supporters.id AS id' - ]) + '(payments.sum / 100)::money::text AS total_contributed', + 'supporters.id AS id' + ]) if query[:export_custom_fields] # Add a select/csv-column for every custom field master for this nonprofit # and add a left join for every custom field master - # eg if the npo has a custom field like Employer with id 99, then the query will be - # SELECT export_cfj_Employer.value AS Employer, ... - # FROM supporters + # eg if the npo has a custom field like Employer with id 99, then the query will be + # SELECT export_cfj_Employer.value AS Employer, ... + # FROM supporters # LEFT JOIN custom_field_joins AS export_cfj_Employer ON export_cfj_Employer.supporter_id=supporters.id AND export_cfj_Employer.custom_field_master_id=99 # ... ids = query[:export_custom_fields].split(',').map(&:to_i) if ids.any? - cfms = Qx.select("name", "id").from(:custom_field_masters).where(nonprofit_id: np_id).and_where("id IN ($ids)", ids: ids).ex + cfms = Qx.select('name', 'id').from(:custom_field_masters).where(nonprofit_id: np_id).and_where('id IN ($ids)', ids: ids).ex cfms.compact.map do |cfm| - table_alias = "cfjs_#{cfm['name'].gsub(/\$/, "")}" + table_alias = "cfjs_#{cfm['name'].delete('$')}" table_alias_quot = "\"#{table_alias}\"" - field_join_subq = Qx.select("STRING_AGG(value, ',') as value", "supporter_id") - .from("custom_field_joins") - .join("custom_field_masters" , "custom_field_masters.id=custom_field_joins.custom_field_master_id") - .where("custom_field_masters.id=$id", id: cfm['id']) - .group_by(:supporter_id) - .as(table_alias) + field_join_subq = Qx.select("STRING_AGG(value, ',') as value", 'supporter_id') + .from('custom_field_joins') + .join('custom_field_masters', 'custom_field_masters.id=custom_field_joins.custom_field_master_id') + .where('custom_field_masters.id=$id', id: cfm['id']) + .group_by(:supporter_id) + .as(table_alias) expr.add_left_join(field_join_subq, "#{table_alias_quot}.supporter_id=supporters.id") selects = selects.concat(["#{table_alias_quot}.value AS \"#{cfm['name']}\""]) end end end - supporter_note_query = Qx.select("STRING_AGG(supporter_notes.created_at || ': ' || supporter_notes.content, '\r\n' ORDER BY supporter_notes.created_at DESC) as notes", "supporter_notes.supporter_id") - .from(:supporter_notes) - .group_by('supporter_notes.supporter_id') - .as("supporter_note_query") + supporter_note_query = Qx.select("STRING_AGG(supporter_notes.created_at || ': ' || supporter_notes.content, '\r\n' ORDER BY supporter_notes.created_at DESC) as notes", 'supporter_notes.supporter_id') + .from(:supporter_notes) + .group_by('supporter_notes.supporter_id') + .as('supporter_note_query') expr.add_left_join(supporter_note_query, 'supporter_note_query.supporter_id=supporters.id') - selects = selects.concat(["supporter_note_query.notes AS notes"]) + selects = selects.concat(['supporter_note_query.notes AS notes']) expr.select(selects).execute(format: 'csv') end @@ -451,17 +440,17 @@ UNION DISTINCT [ "substring(trim(both from supporters.name) from '^.+ ([^\s]+)$') AS \"Last Name\"", "substring(trim(both from supporters.name) from '^(.+) [^\s]+$') AS \"First Name\"", - "trim(both from supporters.name) AS \"Full Name\"", - "supporters.organization AS \"Organization\"", - "supporters.email \"Email\"", - "supporters.phone \"Phone\"", - "supporters.address \"Address\"", - "supporters.city \"City\"", - "supporters.state_code \"State\"", - "supporters.zip_code \"Postal Code\"", - "supporters.country \"Country\"", - "supporters.anonymous \"Anonymous?\"", - "supporters.id \"Supporter ID\"" + 'trim(both from supporters.name) AS "Full Name"', + 'supporters.organization AS "Organization"', + 'supporters.email "Email"', + 'supporters.phone "Phone"', + 'supporters.address "Address"', + 'supporters.city "City"', + 'supporters.state_code "State"', + 'supporters.zip_code "Postal Code"', + 'supporters.country "Country"', + 'supporters.anonymous "Anonymous?"', + 'supporters.id "Supporter ID"' ] end @@ -469,9 +458,9 @@ UNION DISTINCT # Partial sql expression def self.dupes_expr(np_id) - Qx.select("ARRAY_AGG(id) AS ids") + Qx.select('ARRAY_AGG(id) AS ids') .from(:supporters) - .where("nonprofit_id=$id", id: np_id) + .where('nonprofit_id=$id', id: np_id) .and_where("deleted='f' OR deleted IS NULL") .having('COUNT(id) > 1') end @@ -483,7 +472,7 @@ UNION DISTINCT # (each sub-array is a group of duplicates) def self.dupes_on_email(np_id) dupes_expr(np_id) - .and_where("email IS NOT NULL") + .and_where('email IS NOT NULL') .and_where("email != ''") .group_by(:email) .execute(format: 'csv')[1..-1] @@ -493,7 +482,7 @@ UNION DISTINCT # Find all duplicate supporters by the name column def self.dupes_on_name(np_id) dupes_expr(np_id) - .and_where("name IS NOT NULL") + .and_where('name IS NOT NULL') .group_by(:name) .execute(format: 'csv')[1..-1] .map(&:flatten) @@ -504,7 +493,7 @@ UNION DISTINCT def self.dupes_on_name_and_email(np_id) dupes_expr(np_id) .and_where("name IS NOT NULL AND email IS NOT NULL AND email != ''") - .group_by("name, email") + .group_by('name, email') .execute(format: 'csv')[1..-1] .map(&:flatten) end @@ -514,22 +503,22 @@ UNION DISTINCT # Only including payments for the given year def self.end_of_year_donor_report(np_id, year) supporter_expr = Qexpr.new - .select( supporter_export_selections.concat(["(payments.sum / 100.0)::money::text AS \"Total Contributions #{year}\"", "supporters.id"]) ) - .from(:supporters) - .join(Qexpr.new - .select("SUM(gross_amount)", "supporter_id") + .select(supporter_export_selections.concat(["(payments.sum / 100.0)::money::text AS \"Total Contributions #{year}\"", 'supporters.id'])) + .from(:supporters) + .join(Qexpr.new + .select('SUM(gross_amount)', 'supporter_id') .from(:payments) .group_by(:supporter_id) - .where("date >= $date", date: "#{year}-01-01 00:00:00 UTC") - .where("date < $date", date: "#{year+1}-01-01 00:00:00 UTC") - .as(:payments), "payments.supporter_id=supporters.id") - .where('payments.sum > 25000') - .as('supporters') + .where('date >= $date', date: "#{year}-01-01 00:00:00 UTC") + .where('date < $date', date: "#{year + 1}-01-01 00:00:00 UTC") + .as(:payments), 'payments.supporter_id=supporters.id') + .where('payments.sum > 25000') + .as('supporters') Psql.execute_vectors( Qexpr.new .select( - "supporters.*", + 'supporters.*', '(payments.gross_amount / 100.0)::money::text AS "Donation Amount"', 'payments.date AS "Donation Date"', 'payments.towards AS "Designation"' @@ -538,86 +527,84 @@ UNION DISTINCT .join(supporter_expr, 'supporters.id = payments.supporter_id') .where('payments.nonprofit_id = $id', id: np_id) .where('payments.date >= $date', date: "#{year}-01-01 00:00:00 UTC") - .where('payments.date < $date', date: "#{year+1}-01-01 00:00:00 UTC") - .order_by("supporters.\"MAX Name\", payments.date DESC") + .where('payments.date < $date', date: "#{year + 1}-01-01 00:00:00 UTC") + .order_by('supporters."MAX Name", payments.date DESC') ) end - # returns an array of common selects for supporters # which gets concated with an optional array of additional selects # used for merging supporters, crm profile and info card def self.profile_selects(arr = []) - ["supporters.id", - "supporters.name", - "supporters.email", - "supporters.address", - "supporters.state_code", - "supporters.city", - "supporters.zip_code", - "supporters.country", - "supporters.organization", - "supporters.phone"] + arr + ['supporters.id', + 'supporters.name', + 'supporters.email', + 'supporters.address', + 'supporters.state_code', + 'supporters.city', + 'supporters.zip_code', + 'supporters.country', + 'supporters.organization', + 'supporters.phone'] + arr end - # used on crm profile and info card - def self.profile_payments_subquery - Qx.select("supporter_id", "SUM(gross_amount)", "COUNT(id) AS count") - .from("payments") - .group_by("supporter_id") - .as("payments") + def self.profile_payments_subquery + Qx.select('supporter_id', 'SUM(gross_amount)', 'COUNT(id) AS count') + .from('payments') + .group_by('supporter_id') + .as('payments') end - # Get a large set of detailed info for a single supporter, to be displayed in # the side panel details of the supporter listing after clicking a row. def self.for_crm_profile(npo_id, ids) selects = [ - "supporters.created_at", - "supporters.imported_at", - "supporters.anonymous AS anon", - "supporters.is_unsubscribed_from_emails", - "COALESCE(MAX(payments.sum), 0) AS raised", - "COALESCE(MAX(payments.count), 0) AS payments_count", - "COALESCE(COUNT(recurring_donations.active), 0) AS recurring_donations_count", - "MAX(full_contact_infos.full_name) AS fc_full_name", - "MAX(full_contact_infos.age) AS fc_age", - "MAX(full_contact_infos.location_general) AS fc_location_general", - "MAX(full_contact_infos.websites) AS fc_websites"] + 'supporters.created_at', + 'supporters.imported_at', + 'supporters.anonymous AS anon', + 'supporters.is_unsubscribed_from_emails', + 'COALESCE(MAX(payments.sum), 0) AS raised', + 'COALESCE(MAX(payments.count), 0) AS payments_count', + 'COALESCE(COUNT(recurring_donations.active), 0) AS recurring_donations_count', + 'MAX(full_contact_infos.full_name) AS fc_full_name', + 'MAX(full_contact_infos.age) AS fc_age', + 'MAX(full_contact_infos.location_general) AS fc_location_general', + 'MAX(full_contact_infos.websites) AS fc_websites' + ] Qx.select(*QuerySupporters.profile_selects(selects)) - .from("supporters") + .from('supporters') .left_join( - ["donations", "donations.supporter_id=supporters.id"], - ["full_contact_infos", "full_contact_infos.supporter_id=supporters.id"], - ["recurring_donations", "recurring_donations.donation_id=donations.id"], - [QuerySupporters.profile_payments_subquery, "payments.supporter_id=supporters.id"]) - .group_by("supporters.id") - .where("supporters.id IN ($ids)", ids: ids) - .and_where("supporters.nonprofit_id = $id", id: npo_id) + ['donations', 'donations.supporter_id=supporters.id'], + ['full_contact_infos', 'full_contact_infos.supporter_id=supporters.id'], + ['recurring_donations', 'recurring_donations.donation_id=donations.id'], + [QuerySupporters.profile_payments_subquery, 'payments.supporter_id=supporters.id'] + ) + .group_by('supporters.id') + .where('supporters.id IN ($ids)', ids: ids) + .and_where('supporters.nonprofit_id = $id', id: npo_id) .execute end def self.for_info_card(id) - selects = ["COALESCE(MAX(payments.sum), 0) AS raised"] + selects = ['COALESCE(MAX(payments.sum), 0) AS raised'] Qx.select(*QuerySupporters.profile_selects(selects)) - .from("supporters") - .left_join([QuerySupporters.profile_payments_subquery, "payments.supporter_id=supporters.id"]) - .group_by("supporters.id") - .where("supporters.id=$id", id: id) + .from('supporters') + .left_join([QuerySupporters.profile_payments_subquery, 'payments.supporter_id=supporters.id']) + .group_by('supporters.id') + .where('supporters.id=$id', id: id) .execute.first end def self.merge_data(ids) Qx.select(*QuerySupporters.profile_selects) - .from("supporters") - .group_by("supporters.id") - .where("supporters.id IN ($ids)", ids: ids.split(',')) + .from('supporters') + .group_by('supporters.id') + .where('supporters.id IN ($ids)', ids: ids.split(',')) .execute end - def self.year_aggregate_report(npo_id, time_range_params) npo_id = npo_id.to_i @@ -626,10 +613,9 @@ UNION DISTINCT rescue ArgumentError => e raise ParamValidation::ValidationError.new(e.message, {}) end - ParamValidation.new({npo_id: npo_id}, { - npo_id: {required: true, is_integer: true} - }) - aggregate_dons = %Q( + ParamValidation.new({ npo_id: npo_id }, + npo_id: { required: true, is_integer: true }) + aggregate_dons = %( array_to_string( array_agg( payments.date::date || ' ' || @@ -642,51 +628,46 @@ UNION DISTINCT ) AS "Payment History" ) selects = supporter_export_selections.concat([ - "SUM(payments.gross_amount / 100)::text::money AS \"Total Payments\"", - "MAX(payments.date)::date AS \"Last Payment Date\"", - "AVG(payments.gross_amount / 100)::text::money AS \"Average Payment\"", - aggregate_dons - ]) - return Qx.select(selects) + 'SUM(payments.gross_amount / 100)::text::money AS "Total Payments"', + 'MAX(payments.date)::date AS "Last Payment Date"', + 'AVG(payments.gross_amount / 100)::text::money AS "Average Payment"', + aggregate_dons + ]) + Qx.select(selects) .from(:supporters) - .join("payments", "payments.supporter_id=supporters.id AND payments.date::date >= $min_date AND payments.date::date < $max_date",:min_date => min_date.to_date, :max_date => max_date.to_date ) + .join('payments', 'payments.supporter_id=supporters.id AND payments.date::date >= $min_date AND payments.date::date < $max_date', min_date: min_date.to_date, max_date: max_date.to_date) .where('supporters.nonprofit_id=$id', id: npo_id) - .group_by("supporters.id") + .group_by('supporters.id') .order_by("substring(trim(supporters.name) from '^.+ ([^\s]+)$')") .execute(format: 'csv') end - def self.get_min_or_max_dates_for_range(time_range_params) - begin - if (time_range_params[:year]) - if (time_range_params[:year].is_a?(Integer)) - return DateTime.new(time_range_params[:year], 1, 1), DateTime.new(time_range_params[:year]+1, 1, 1) - end - if (time_range_params[:year].is_a?(String)) - wip = time_range_params[:year].to_i - return DateTime.new(wip, 1, 1), DateTime.new(wip+1, 1, 1) - end + if time_range_params[:year] + if time_range_params[:year].is_a?(Integer) + return DateTime.new(time_range_params[:year], 1, 1), DateTime.new(time_range_params[:year] + 1, 1, 1) end - if (time_range_params[:start]) - start = parse_convert_datetime(time_range_params[:start]) - if (time_range_params[:end]) - end_datetime = parse_convert_datetime(time_range_params[:end]) - end - unless start.nil? - return start, end_datetime ? end_datetime : start + 1.year - end + if time_range_params[:year].is_a?(String) + wip = time_range_params[:year].to_i + return DateTime.new(wip, 1, 1), DateTime.new(wip + 1, 1, 1) end - raise ArgumentError.new("no valid time range provided") - rescue - raise ArgumentError.new("no valid time range provided") end + if time_range_params[:start] + start = parse_convert_datetime(time_range_params[:start]) + if time_range_params[:end] + end_datetime = parse_convert_datetime(time_range_params[:end]) + end + return start, end_datetime || start + 1.year unless start.nil? + end + raise ArgumentError, 'no valid time range provided' + rescue StandardError + raise ArgumentError, 'no valid time range provided' end def self.tag_joins(nonprofit_id, supporter_id) - Qx.select('tag_masters.id', 'tag_masters.name') + Qx.select('tag_masters.id', 'tag_masters.name') .from('tag_joins') .left_join('tag_masters', 'tag_masters.id = tag_joins.tag_master_id') .where( @@ -700,25 +681,18 @@ UNION DISTINCT # this is inefficient, don't use in live code def self.find_supporters_with_multiple_recurring_donations_evil_way(npo_id) supporters = Supporter.where('supporters.nonprofit_id = ?', npo_id).includes(:recurring_donations) - supporters.select{|s| s.recurring_donations.length > 1} + supporters.select { |s| s.recurring_donations.length > 1 } end # this is inefficient, don't use in live code def self.find_supporters_with_multiple_active_recurring_donations_evil_way(npo_id) supporters = Supporter.where('supporters.nonprofit_id = ?', npo_id).includes(:recurring_donations) - supporters.select{|s| s.recurring_donations.select{|rd| rd.active }.length > 1} + supporters.select { |s| s.recurring_donations.select(&:active).length > 1 } end def self.parse_convert_datetime(date) - if (date.is_a?(DateTime)) - return date - end - if (date.is_a?(Date)) - return date.to_datetime - end - if(date.is_a?(String)) - return DateTime.parse(date) - end + return date if date.is_a?(DateTime) + return date.to_datetime if date.is_a?(Date) + return DateTime.parse(date) if date.is_a?(String) end end - diff --git a/lib/query/query_ticket_levels.rb b/lib/query/query_ticket_levels.rb index 6e2fea0e..6e60d0a0 100644 --- a/lib/query/query_ticket_levels.rb +++ b/lib/query/query_ticket_levels.rb @@ -1,52 +1,52 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'hashie' module QueryTicketLevels - # Given an array of ticket hashes, where each hash has a ticket_level_id and a quantity, # calculate the gross amount for all the tickets # # This could probably be more efficient. I didn't think of a way to calculate it within the query itself. # Although I think it's O(n), and n will always be quite small (the number of tickets someone buys) def self.gross_amount_from_tickets(tickets, discount_id) - amounts = TicketLevel.where('id IN (?)', tickets.map{|h| h['ticket_level_id']}).map{|i| [i.id, i.amount]}.to_h - total = tickets.map{|t| amounts[t['ticket_level_id'].to_i].to_i * t['quantity'].to_i}.sum + amounts = TicketLevel.where('id IN (?)', tickets.map { |h| h['ticket_level_id'] }).map { |i| [i.id, i.amount] }.to_h + total = tickets.map { |t| amounts[t['ticket_level_id'].to_i].to_i * t['quantity'].to_i }.sum if discount_id perc = EventDiscount.find(discount_id).percent - total = total - (total * (perc / 100.0)).round + total -= (total * (perc / 100.0)).round end - return total + total end def self.with_event_id(event_id, is_admin) - expr = Qx.select("ticket_levels.*", "SUM(tickets.quantity) AS quantity") - .from(:ticket_levels) - .left_join([:tickets, "ticket_levels.id=tickets.ticket_level_id"]) - .group_by("ticket_levels.id") - .where("ticket_levels.event_id = $id", id: event_id) - .order_by("ticket_levels.order ASC, coalesce(ticket_levels.amount, 'Infinity'::float) ASC, LOWER(ticket_levels.name) ASC") # This puts free ticket levels at the bottom + expr = Qx.select('ticket_levels.*', 'SUM(tickets.quantity) AS quantity') + .from(:ticket_levels) + .left_join([:tickets, 'ticket_levels.id=tickets.ticket_level_id']) + .group_by('ticket_levels.id') + .where('ticket_levels.event_id = $id', id: event_id) + .order_by("ticket_levels.order ASC, coalesce(ticket_levels.amount, 'Infinity'::float) ASC, LOWER(ticket_levels.name) ASC") # This puts free ticket levels at the bottom - if !is_admin - expr = expr.and_where("coalesce(ticket_levels.admin_only, FALSE) = FALSE") + unless is_admin + expr = expr.and_where('coalesce(ticket_levels.admin_only, FALSE) = FALSE') end - return expr.execute + expr.execute end def self.verify_tickets_available(tickets) - tickets.each{|data| - if (data[:quantity] != 0) - tl = TicketLevel.find(data[:ticket_level_id]) - if tl.limit && tl.limit > 0 - already_sold = Ticket.where('ticket_level_id = ?', data[:ticket_level_id]).sum('tickets.quantity') - unless (already_sold + data[:quantity]) <= tl.limit - raise NotEnoughQuantityError.new(TicketLevel, data[:ticket_level_id], data[:quantity], "Oops! We sold out some of the tickets you wanted before ordering. Please refresh to see what tickets are still available.") - end - end - end - } - end + tickets.each do |data| + next unless data[:quantity] != 0 + tl = TicketLevel.find(data[:ticket_level_id]) + next unless tl.limit && tl.limit > 0 + + already_sold = Ticket.where('ticket_level_id = ?', data[:ticket_level_id]).sum('tickets.quantity') + unless (already_sold + data[:quantity]) <= tl.limit + raise NotEnoughQuantityError.new(TicketLevel, data[:ticket_level_id], data[:quantity], 'Oops! We sold out some of the tickets you wanted before ordering. Please refresh to see what tickets are still available.') + end + end + end end diff --git a/lib/query/query_tickets.rb b/lib/query/query_tickets.rb index f8582893..fc7fb696 100644 --- a/lib/query/query_tickets.rb +++ b/lib/query/query_tickets.rb @@ -1,72 +1,72 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueryTickets - def self.attendees_expr(event_id, query) expr = Qexpr.new - .from('tickets') - .where("coalesce(tickets.deleted, FALSE) = FALSE") - .left_outer_join("event_discounts", "event_discounts.id=tickets.event_discount_id") - .left_outer_join( - Qexpr.new.select("*") - .from(:supporters).group_by("id").as("supporters"), - 'tickets.supporter_id=supporters.id' - ) - .left_outer_join("charges", "charges.id=tickets.charge_id") - .left_outer_join( - Qexpr.new.select("charge_id", "SUM(coalesce(amount, 0)) AS amount") - .from(:refunds) - .group_by(:charge_id) - .as(:refunds), - "refunds.charge_id=charges.id" - ) - .left_outer_join( - Qexpr.new.select("id", "name", "amount") - .from(:ticket_levels).group_by("id").as("ticket_levels"), - 'tickets.ticket_level_id=ticket_levels.id' - ) - .left_outer_join( - Qexpr.new.select('token', 'tokenizable_id').from(:source_tokens).group_by( 'token', 'tokenizable_id').as('source_tokens'), - 'tickets.source_token_id=source_tokens.token' - ) - .left_outer_join( - # TODO this does not support anything other than cards! - Qexpr.new.select('id', 'name').from(:cards).group_by('id', 'name').as('cards'), - 'source_tokens.tokenizable_id = cards.id' - ) - .left_outer_join( - Qexpr.new.select('supporter_id', 'MAX(event_id) AS event_id', 'SUM(amount) AS total_amount') - .from(:donations).where("event_id=$id", id: event_id).group_by("supporter_id").as(:donations), - "donations.supporter_id=supporters.id AND donations.event_id=$id", id: event_id - ) - .where('tickets.event_id=$id', id: event_id) - .order_by('tickets.bid_id DESC') + .from('tickets') + .where('coalesce(tickets.deleted, FALSE) = FALSE') + .left_outer_join('event_discounts', 'event_discounts.id=tickets.event_discount_id') + .left_outer_join( + Qexpr.new.select('*') + .from(:supporters).group_by('id').as('supporters'), + 'tickets.supporter_id=supporters.id' + ) + .left_outer_join('charges', 'charges.id=tickets.charge_id') + .left_outer_join( + Qexpr.new.select('charge_id', 'SUM(coalesce(amount, 0)) AS amount') + .from(:refunds) + .group_by(:charge_id) + .as(:refunds), + 'refunds.charge_id=charges.id' + ) + .left_outer_join( + Qexpr.new.select('id', 'name', 'amount') + .from(:ticket_levels).group_by('id').as('ticket_levels'), + 'tickets.ticket_level_id=ticket_levels.id' + ) + .left_outer_join( + Qexpr.new.select('token', 'tokenizable_id').from(:source_tokens).group_by('token', 'tokenizable_id').as('source_tokens'), + 'tickets.source_token_id=source_tokens.token' + ) + .left_outer_join( + # TODO: this does not support anything other than cards! + Qexpr.new.select('id', 'name').from(:cards).group_by('id', 'name').as('cards'), + 'source_tokens.tokenizable_id = cards.id' + ) + .left_outer_join( + Qexpr.new.select('supporter_id', 'MAX(event_id) AS event_id', 'SUM(amount) AS total_amount') + .from(:donations).where('event_id=$id', id: event_id).group_by('supporter_id').as(:donations), + 'donations.supporter_id=supporters.id AND donations.event_id=$id', id: event_id + ) + .where('tickets.event_id=$id', id: event_id) + .order_by('tickets.bid_id DESC') if query[:search].present? query[:search] = "%#{query[:search].downcase.split(' ').join('%')}%" - expr = expr.where(%Q( + expr = expr.where(%( lower(supporters.name) LIKE $search OR lower(supporters.email) LIKE $search OR lower(ticket_levels.name) LIKE $search ), search: '%' + query[:search] + '%') end - if ['asc', 'desc'].include? query[:sort_attendee] + if %w[asc desc].include? query[:sort_attendee] expr = expr.order_by("lower(supporters.name) #{query[:sort_attendee]} NULLS LAST") end - if ['asc', 'desc'].include? query[:sort_id] + if %w[asc desc].include? query[:sort_id] expr = expr.order_by("tickets.bid_id #{query[:sort_id]}") end - if ['asc', 'desc'].include? query[:sort_note] + if %w[asc desc].include? query[:sort_note] expr = expr.order_by("lower(tickets.note) #{query[:sort_note]} NULLS LAST") end - if ['asc', 'desc'].include? query[:sort_ticket_level] + if %w[asc desc].include? query[:sort_ticket_level] expr = expr.order_by("lower(ticket_levels.name) #{query[:sort_ticket_level]} NULLS LAST") end - if ['asc', 'desc'].include? query[:sort_donation] + if %w[asc desc].include? query[:sort_donation] expr = expr.order_by("total_donations #{query[:sort_donation]} NULLS LAST") end - return expr + expr end - def self.attendees_list(event_id, query) limit = 30 offset = Qexpr.page_offset(limit, query[:page]) @@ -78,44 +78,42 @@ module QueryTickets ) total_count = Psql.execute( - Qexpr.new.select("COUNT(ts)") + Qexpr.new.select('COUNT(ts)') .from(attendees_expr(event_id, query) .remove(:order_by).select('tickets.id'), 'ts') ).first['count'] - #TODO this worries me. Seems like a recipe for slow returns... perhaps some caching of the tokens every so often? - data.each{|i| - unless (i['source_token_id'] && QuerySourceToken.source_token_unexpired?(SourceToken.find(i['source_token_id']))) + # TODO: this worries me. Seems like a recipe for slow returns... perhaps some caching of the tokens every so often? + data.each do |i| + unless i['source_token_id'] && QuerySourceToken.source_token_unexpired?(SourceToken.find(i['source_token_id'])) i['source_token_id'] = nil end - } + end - return { + { data: data, total_count: total_count, remaining: Qexpr.remaining_count(total_count, limit, query[:page]) } end - def self.for_export(event_id, query) Psql.execute_vectors( attendees_expr(event_id, query) .select([ - "tickets.bid_id AS id", - "ticket_levels.name AS ticket_level", - "MONEY((coalesce(charges.amount, 0) - coalesce(refunds.amount, 0)) / 100.0) AS ticket_cost", - "MONEY(coalesce(donations.total_amount, 0) / 100.0) AS total_donations", - "tickets.quantity", - "tickets.checked_in AS \"Checked In?\"", - "tickets.note", + 'tickets.bid_id AS id', + 'ticket_levels.name AS ticket_level', + 'MONEY((coalesce(charges.amount, 0) - coalesce(refunds.amount, 0)) / 100.0) AS ticket_cost', + 'MONEY(coalesce(donations.total_amount, 0) / 100.0) AS total_donations', + 'tickets.quantity', + 'tickets.checked_in AS "Checked In?"', + 'tickets.note', "CASE WHEN event_discounts.id IS NULL THEN 'None' ELSE concat(event_discounts.name, ' (', event_discounts.percent, '%)') END AS \"Discount\"", "CASE WHEN tickets.card_id IS NULL OR tickets.card_id = 0 THEN '' ELSE 'YES' END AS \"Card Saved?\"" ].concat(QuerySupporters.supporter_export_selections)) ) end - def self.attendees_list_selection ['tickets.id', 'tickets.bid_id', @@ -133,27 +131,25 @@ module QueryTickets 'supporters.email AS email', 'coalesce(donations.total_amount, 0) AS total_donations', 'source_tokens.token AS token', - 'cards.name AS card_name' - ] + 'cards.name AS card_name'] end def self.for_event_activities(event_id) selects = [" - CASE - WHEN supporters.anonymous='t' - OR supporters.name='' - OR supporters.name IS NULL - THEN 'A supporter' - ELSE supporters.name - END AS supporter_name", - 'tickets.quantity', 'tickets.created_at'] - return Qx.select(selects.join(',')) + CASE + WHEN supporters.anonymous='t' + OR supporters.name='' + OR supporters.name IS NULL + THEN 'A supporter' + ELSE supporters.name + END AS supporter_name", + 'tickets.quantity', 'tickets.created_at'] + Qx.select(selects.join(',')) .from(:tickets) .left_join(:supporters, 'tickets.supporter_id=supporters.id') .where('tickets.event_id=$id', id: event_id) - .order_by("tickets.created_at desc") + .order_by('tickets.created_at desc') .limit(15) .execute end - end diff --git a/lib/query/query_users.rb b/lib/query/query_users.rb index 94bcb3c2..579a9e72 100644 --- a/lib/query/query_users.rb +++ b/lib/query/query_users.rb @@ -1,40 +1,41 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'psql' require 'qexpr' require 'query/query_email_settings' module QueryUsers - # Return all the nonprofit user emails for a given email notification setting # for notification_type in ['payments', 'campaigns', 'events', 'payouts', 'recurring_donations'] def self.nonprofit_user_emails(np_id, notification_type) - raise ArgumentError.new('Invalid notification type') unless QueryEmailSettings::Settings.include?(notification_type) - Qx.select("users.email") - .from("users") - .join("roles", "roles.user_id=users.id") - .add_join("nonprofits", "roles.host_id=nonprofits.id AND roles.host_type='Nonprofit'") - .add_left_join("email_settings", "email_settings.user_id=users.id") + raise ArgumentError, 'Invalid notification type' unless QueryEmailSettings::Settings.include?(notification_type) + + Qx.select('users.email') + .from('users') + .join('roles', 'roles.user_id=users.id') + .add_join('nonprofits', "roles.host_id=nonprofits.id AND roles.host_type='Nonprofit'") + .add_left_join('email_settings', 'email_settings.user_id=users.id') .where("email_settings.user_id IS NULL OR email_settings.#{notification_type}=TRUE") - .and_where("nonprofits.id=$id", id: np_id) - .group_by("users.email") - .execute.map{|h| h['email']} + .and_where('nonprofits.id=$id', id: np_id) + .group_by('users.email') + .execute.map { |h| h['email'] } end # Return all nonprofit emails regardless of email settings - def self.all_nonprofit_user_emails(np_id, roles=[:nonprofit_admin, :nonprofit_user]) + def self.all_nonprofit_user_emails(np_id, roles = %i[nonprofit_admin nonprofit_user]) Qx.select('users.email').from('users') .join('roles', 'roles.user_id = users.id') .add_join('nonprofits', 'nonprofits.id = roles.host_id AND roles.host_type=\'Nonprofit\'') .where('nonprofits.id=$id', id: np_id) .and_where('roles.name IN ($names)', names: roles) - .execute.map{|h| h['email']} + .execute.map { |h| h['email'] } end # Return an array of email address strings for all users with role of 'super_admin' def self.super_admin_emails - Qx.select("users.email").from("users") - .join("roles", "roles.user_id=users.id AND roles.name='super_admin'") - .ex.map{|h| h['email']} + Qx.select('users.email').from('users') + .join('roles', "roles.user_id=users.id AND roles.name='super_admin'") + .ex.map { |h| h['email'] } end - end diff --git a/lib/queue_donations.rb b/lib/queue_donations.rb index d9eeea03..ccbbd8bf 100644 --- a/lib/queue_donations.rb +++ b/lib/queue_donations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module QueueDonations def self.execute_for_donation(id) @@ -10,15 +12,17 @@ module QueueDonations def self.execute_all donations = fetch_donations return if donations.empty? + donations_ids = donations.collect(&:id) execute(donations) end def self.dry_execute_all -puts "dry push donations to civi" + puts 'dry push donations to civi' donations = fetch_donations return if donations.empty? + donations_ids = donations.collect(&:id) dry_execute(donations) @@ -31,18 +35,16 @@ puts "dry push donations to civi" donations_ids = donations.collect(&:id) set_queued_for_import_at(donations_ids) - rescue Bunny::Exception, Bunny::ClientTimeout, Bunny::ConnectionTimeout Rails.logger.warn "Bunny error: QueueDonations.execute failed for ids #{donations_ids}" - return + nil end def self.dry_execute(donations) push(donations) - rescue Bunny::Exception, Bunny::ClientTimeout, Bunny::ConnectionTimeout Rails.logger.warn "Bunny error: QueueDonations.dry_execute failed for ids #{donations_ids}" - return + nil end def self.push(donations) @@ -68,16 +70,16 @@ puts "dry push donations to civi" def self.set_queued_for_import_at(ids) timestamp = Time.current - Qx.update(:donations). - where('id IN ($ids)', ids: ids). - set(queued_for_import_at: timestamp). - execute + Qx.update(:donations) + .where('id IN ($ids)', ids: ids) + .set(queued_for_import_at: timestamp) + .execute end def self.fetch_donations - Donation. - where('queued_for_import_at IS null'). - includes(:supporter, :nonprofit, :tracking, :payment,:recurring_donation) + Donation + .where('queued_for_import_at IS null') + .includes(:supporter, :nonprofit, :tracking, :payment, :recurring_donation) end def self.prepare_donation_params(donation) @@ -87,7 +89,7 @@ puts "dry push donations to civi" recurring = donation.recurring_donation action_type = :donate - action_technical_type = "cc.wemove.eu:donate" + action_technical_type = 'cc.wemove.eu:donate' action_name = "undefined_#{donation.supporter.locale}" external_id = campaign ? campaign.external_identifier : "cc_default_#{nonprofit.id}" @@ -118,7 +120,8 @@ puts "dry push donations to civi" { email: supporter.email } ], addresses: [ - { zip: supporter.zip_code, country: supporter.country }], + { zip: supporter.zip_code, country: supporter.country } + ] } end @@ -128,26 +131,26 @@ puts "dry push donations to civi" currency: donation.nonprofit.currency, recurring_id: donation.recurring_donation ? "cc_#{donation.recurring_donation.id}" : nil, external_identifier: "cc_#{donation.id}", - type: donation.recurring ? "recurring" : "single" + type: donation.recurring ? 'recurring' : 'single' } if donation.card_id - data = common_data.merge({ - payment_processor: "stripe", + data = common_data.merge( + payment_processor: 'stripe', amount_charged: donation.payment.charge.amount / 100.0, transaction_id: donation.payment.charge.stripe_charge_id, - status: donation.payment.charge.paid? ? "success" : "not_paid" - }) + status: donation.payment.charge.paid? ? 'success' : 'not_paid' + ) elsif donation.direct_debit_detail_id - data = common_data.merge({ - payment_processor: "sepa", + data = common_data.merge( + payment_processor: 'sepa', amount_charged: 0, transaction_id: "cc_#{donation.id}", iban: donation.direct_debit_detail.iban, bic: donation.direct_debit_detail.bic, account_holder: donation.direct_debit_detail.account_holder_name, - status: "success" - }) + status: 'success' + ) end data @@ -158,9 +161,10 @@ puts "dry push donations to civi" id: recurring.id, start: recurring.start_date, time_unit: recurring.time_unit, - active: recurring.active, + active: recurring.active } end + def self.tracking_data(tracking) { source: tracking.utm_source, diff --git a/lib/qx_query_chunker.rb b/lib/qx_query_chunker.rb index cee3ca0b..f2303a06 100644 --- a/lib/qx_query_chunker.rb +++ b/lib/qx_query_chunker.rb @@ -1 +1,3 @@ -QxQueryChunker = QexprQueryChunker \ No newline at end of file +# frozen_string_literal: true + +QxQueryChunker = QexprQueryChunker diff --git a/lib/required_keys.rb b/lib/required_keys.rb index c4d75e53..64b9e0f4 100644 --- a/lib/required_keys.rb +++ b/lib/required_keys.rb @@ -1,12 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # Given a hash and an array of keys # Raise an argument error if any keys are missing from the hash class RequiredKeys - def initialize(hash, keys) - missing = keys.select{|k| hash[k].nil?} - raise ArgumentError.new("Missing keys: #{missing}") if missing.any? + missing = keys.select { |k| hash[k].nil? } + raise ArgumentError, "Missing keys: #{missing}" if missing.any? end end diff --git a/lib/retrieve/retrieve_active_record_items.rb b/lib/retrieve/retrieve_active_record_items.rb index a0b172a9..3a79461f 100644 --- a/lib/retrieve/retrieve_active_record_items.rb +++ b/lib/retrieve/retrieve_active_record_items.rb @@ -1,45 +1,49 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module RetrieveActiveRecordItems - def self.retrieve(data, optional= false) - data.map{|k,v| - our_integer = Integer(v) rescue nil + def self.retrieve(data, optional = false) + data.map do |k, v| + our_integer = begin + Integer(v) + rescue StandardError + nil + end unless (optional && v.nil?) || (our_integer && our_integer > 0) - raise ArgumentError.new("Value '#{v}' for Key '#{k}' is not valid") + raise ArgumentError, "Value '#{v}' for Key '#{k}' is not valid" end - unless k.is_a? Class - raise ArgumentError.new("Key '#{k.to_s}' is not a class") - end + raise ArgumentError, "Key '#{k}' is not a class" unless k.is_a? Class + ret = [] if optional && v.nil? ret = [k, nil] else ret = [k, k.where('id = ?', our_integer).first] - if (ret[1] == nil) - raise ParamValidation::ValidationError.new("ID #{v} is not a valid #{k.to_s}", {key: k}) + if ret[1].nil? + raise ParamValidation::ValidationError.new("ID #{v} is not a valid #{k}", key: k) end end ret - }.to_h + end.to_h end - def self.retrieve_from_keys(input, class_to_key_hash, optional=false) - class_to_key_hash.map{|k,v| - unless k.is_a? Class - raise ArgumentError.new("Key '#{k.to_s}' is not a class") - end + def self.retrieve_from_keys(input, class_to_key_hash, optional = false) + class_to_key_hash.map do |k, v| + raise ArgumentError, "Key '#{k}' is not a class" unless k.is_a? Class + ret = nil begin - item = retrieve({k => input[v]}, optional) + item = retrieve({ k => input[v] }, optional) ret = [v, item[k]] rescue ParamValidation::ValidationError - raise ParamValidation::ValidationError.new("ID #{input[v]} is not a valid #{k.to_s}", {key: v}) + raise ParamValidation::ValidationError.new("ID #{input[v]} is not a valid #{k}", key: v) rescue ArgumentError - raise ParamValidation::ValidationError.new("#{input[v]} is not a valid ID for Key '#{v}'", {key: v}) - rescue - raise ParamValidation::ValidationError.new("#{input[v]} is not a valid ID for Key '#{v}'", {key: v}) + raise ParamValidation::ValidationError.new("#{input[v]} is not a valid ID for Key '#{v}'", key: v) + rescue StandardError + raise ParamValidation::ValidationError.new("#{input[v]} is not a valid ID for Key '#{v}'", key: v) end ret - }.to_h + end.to_h end -end \ No newline at end of file +end diff --git a/lib/scheduled_jobs.rb b/lib/scheduled_jobs.rb index 3d4f992d..e1038afa 100644 --- a/lib/scheduled_jobs.rb +++ b/lib/scheduled_jobs.rb @@ -1,9 +1,8 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' -require 'enumerator' - module ScheduledJobs - # Each of these functions should return an Enumerator # Each value in the enumerator should be a lambda # That way the heroku_scheduled_job task can iterate over each lambda @@ -15,41 +14,38 @@ module ScheduledJobs def self.delete_junk_data # Delete all custom fields with emptly/nil vals del_cfjs_noval = Qx.delete_from(:custom_field_joins) - .where("value IS NULL OR value=''") + .where("value IS NULL OR value=''") # Delete orphaned custom field joins (those should also all have supporters) - del_cfjs_orphaned = Qx.delete_from(:custom_field_joins).where("id IN ($ids)", { - ids: Qx.select("custom_field_joins.id") - .from(:custom_field_joins) - .left_join("supporters", "custom_field_joins.supporter_id=supporters.id") - .where("supporters.id IS NULL") - }) + del_cfjs_orphaned = Qx.delete_from(:custom_field_joins).where('id IN ($ids)', + ids: Qx.select('custom_field_joins.id') + .from(:custom_field_joins) + .left_join('supporters', 'custom_field_joins.supporter_id=supporters.id') + .where('supporters.id IS NULL')) # Delete orphaned tag joins - del_tags_orphaned = Qx.delete_from(:tag_joins).where("id IN ($ids)", { - ids: Qx.select("tag_joins.id") - .from(:tag_joins) - .left_join(:supporters, "tag_joins.supporter_id=supporters.id") - .where("supporters.id IS NULL") - }) + del_tags_orphaned = Qx.delete_from(:tag_joins).where('id IN ($ids)', + ids: Qx.select('tag_joins.id') + .from(:tag_joins) + .left_join(:supporters, 'tag_joins.supporter_id=supporters.id') + .where('supporters.id IS NULL')) - return Enumerator.new do |yielder| + Enumerator.new do |yielder| yielder << lambda do del_cfjs_noval.execute - "Successfully cleaned up custom field joins with no values" + 'Successfully cleaned up custom field joins with no values' end yielder << lambda do del_cfjs_orphaned.execute - "Successfully cleaned up custom field joins that have been orphaned from supporters" + 'Successfully cleaned up custom field joins that have been orphaned from supporters' end yielder << lambda do del_tags_orphaned.execute - "Successfully cleaned up tags that have been orphaned from supporters" + 'Successfully cleaned up tags that have been orphaned from supporters' end end end - def self.pay_recurring_donations - return Enumerator.new do |yielder| + Enumerator.new do |yielder| yielder << lambda do ids = PayRecurringDonation.pay_all_due_with_stripe "Queued jobs to pay #{ids.count} total recurring donations\n Recurring Donation Ids to run are: \n#{ids.join('\n')}" @@ -58,14 +54,14 @@ module ScheduledJobs end def self.update_verification_statuses - return Enumerator.new do |yielder| + Enumerator.new do |yielder| Nonprofit.where(verification_status: 'pending').each do |np| yielder << lambda do acct = Stripe::Account.retrieve(np.stripe_account_id) verified = acct.transfers_enabled && acct.verification.fields_needed.count == 0 np.verification_status = verified ? 'verified' : np.verification_status - NonprofitMailer.failed_verification_notice(np).deliver if np.verification_status != 'verified' - NonprofitMailer.successful_verification_notice(np).deliver if np.verification_status == 'verified' + VerificationFailedJob.perform_later(np) if np.verification_status != 'verified' + VerificationCompletedJob.perform_later(np) if np.verification_status == 'verified' np.save "Status updated for NP #{np.id} as '#{np.verification_status}'" end @@ -74,8 +70,8 @@ module ScheduledJobs end def self.update_np_balances - return Enumerator.new do |yielder| - nps = Nonprofit.where("id IN (?)", Charge.pending.uniq.pluck(:nonprofit_id)) + Enumerator.new do |yielder| + nps = Nonprofit.where('id IN (?)', Charge.pending.uniq.pluck(:nonprofit_id)) nps.each do |np| yielder << lambda do UpdateNonprofit.mark_available_charges(np.id) @@ -86,13 +82,12 @@ module ScheduledJobs end def self.update_pending_payouts - return Enumerator.new do |yielder| + Enumerator.new do |yielder| Payout.pending.includes(:nonprofit).each do |p| yielder << lambda do err = false - p.status = Stripe::Transfer.retrieve(p.stripe_transfer_id, { - stripe_account: p.nonprofit.stripe_account_id - }).status + p.status = Stripe::Transfer.retrieve(p.stripe_transfer_id, + stripe_account: p.nonprofit.stripe_account_id).status p.save "Updated status for NP #{p.nonprofit.id}, payout # #{p.id}" end @@ -101,9 +96,9 @@ module ScheduledJobs end def self.delete_expired_source_tokens - return Enumerator.new do |yielder| + Enumerator.new do |yielder| yielder << lambda do - tokens_deleted = SourceToken.where("expiration > ?", DateTime.now - 1.day).delete_all + tokens_deleted = SourceToken.where('expiration > ?', DateTime.now - 1.day).delete_all "Deleted #{tokens_deleted} source tokens" end end diff --git a/lib/search_vector.rb b/lib/search_vector.rb index 9b190382..dde98873 100644 --- a/lib/search_vector.rb +++ b/lib/search_vector.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module SearchVector + AcceptedTables = %w[supporters payments].freeze - AcceptedTables = ['supporters', 'payments'] - - def self.query(query_string, expr=nil) + def self.query(query_string, expr = nil) (expr || Qexpr.new).where( "to_tsvector('english', coalesce(supporters.name, '') || ' ' || coalesce(supporters.email, '')) @@ plainto_tsquery('english', $search)", - { search: query_string} + search: query_string ) end @@ -27,18 +28,18 @@ module SearchVector , donations.dedication ) AS search_blob" ) - .from(:payments) - .left_outer_join('supporters', 'payments.supporter_id=supporters.id') - .left_outer_join('donations', 'payments.donation_id=donations.id') + .from(:payments) + .left_outer_join('supporters', 'payments.supporter_id=supporters.id') + .left_outer_join('donations', 'payments.donation_id=donations.id') end # Construct of query of ids and search blobs for all supporters # for use in a sub-query def self._supporters_blob_query - fields_subquery = Qexpr.new.select("string_agg(value::text, ' ') AS value", "supporter_id") - .from(:custom_field_joins) - .group_by(:supporter_id) - .as(:custom_field_joins) + fields_subquery = Qexpr.new.select("string_agg(value::text, ' ') AS value", 'supporter_id') + .from(:custom_field_joins) + .group_by(:supporter_id) + .as(:custom_field_joins) Qexpr.new.select( 'supporters.id', "concat_ws(' ' @@ -55,10 +56,9 @@ module SearchVector , payments.towards ) AS search_blob" ) - .from(:supporters) - .left_outer_join(:payments, "payments.supporter_id=supporters.id") - .left_outer_join(:donations, "donations.supporter_id=supporters.id") - .left_outer_join(fields_subquery, "custom_field_joins.supporter_id=supporters.id") + .from(:supporters) + .left_outer_join(:payments, 'payments.supporter_id=supporters.id') + .left_outer_join(:donations, 'donations.supporter_id=supporters.id') + .left_outer_join(fields_subquery, 'custom_field_joins.supporter_id=supporters.id') end - end diff --git a/lib/slug_copy_naming_algorithm.rb b/lib/slug_copy_naming_algorithm.rb index c12bde98..0927265f 100644 --- a/lib/slug_copy_naming_algorithm.rb +++ b/lib/slug_copy_naming_algorithm.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class SlugCopyNamingAlgorithm < CopyNamingAlgorithm - attr_accessor :klass, :nonprofit_id # @param [Class] klass def initialize(klass, nonprofit_id) - @klass = klass - @nonprofit_id = nonprofit_id + @klass = klass + @nonprofit_id = nonprofit_id end def copy_addition - "_copy" + '_copy' end def max_copies @@ -21,8 +22,7 @@ class SlugCopyNamingAlgorithm < CopyNamingAlgorithm end def get_already_used_name_entities(base_name) - end_name = "\\_copy\\_\\d{2}" + end_name = '\\_copy\\_\\d{2}' @klass.method(:where).call('slug SIMILAR TO ? AND nonprofit_id = ? AND (deleted IS NULL OR deleted = false)', base_name + end_name, nonprofit_id).select('slug') end - -end \ No newline at end of file +end diff --git a/lib/slug_nonprofit_naming_algorithm.rb b/lib/slug_nonprofit_naming_algorithm.rb index 010acc3c..7fe78721 100644 --- a/lib/slug_nonprofit_naming_algorithm.rb +++ b/lib/slug_nonprofit_naming_algorithm.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class SlugNonprofitNamingAlgorithm < CopyNamingAlgorithm + attr_accessor :state_slug, :city_slug - attr_accessor :state_slug, :city_slug - - def initialize( state_slug, city_slug) - @state_slug = state_slug - @city_slug = city_slug + def initialize(state_slug, city_slug) + @state_slug = state_slug + @city_slug = city_slug end def copy_addition - "" + '' end def max_copies @@ -25,8 +26,7 @@ class SlugNonprofitNamingAlgorithm < CopyNamingAlgorithm end def get_already_used_name_entities(base_name) - end_name = "\\-\\d{2}" - Nonprofit.method(:where).call('slug SIMILAR TO ? AND state_code_slug = ? AND city_slug = ?', base_name + end_name, @state_slug, @city_slug).select('slug') + end_name = '\\-\\d{2}' + Nonprofit.method(:where).call('slug SIMILAR TO ? AND state_code_slug = ? AND city_slug = ?', base_name + end_name, @state_slug, @city_slug).select('slug') end - -end \ No newline at end of file +end diff --git a/lib/slug_p2p_campaign_naming_algorithm.rb b/lib/slug_p2p_campaign_naming_algorithm.rb index cbbe6aeb..c6bcdba2 100644 --- a/lib/slug_p2p_campaign_naming_algorithm.rb +++ b/lib/slug_p2p_campaign_naming_algorithm.rb @@ -1,14 +1,15 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later class SlugP2pCampaignNamingAlgorithm < CopyNamingAlgorithm - attr_accessor :nonprofit_id # @param [Integer] nonprofit_id def initialize(nonprofit_id) - @nonprofit_id = nonprofit_id + @nonprofit_id = nonprofit_id end def copy_addition - "" + '' end def max_copies @@ -20,8 +21,7 @@ class SlugP2pCampaignNamingAlgorithm < CopyNamingAlgorithm end def get_already_used_name_entities(base_name) - end_name = "\\_\\d{3}" + end_name = '\\_\\d{3}' Campaign.where('slug SIMILAR TO ? AND nonprofit_id = ?', base_name + end_name, nonprofit_id).select('slug') end - -end \ No newline at end of file +end diff --git a/lib/stripe_account.rb b/lib/stripe_account.rb index eaa236da..3883000b 100644 --- a/lib/stripe_account.rb +++ b/lib/stripe_account.rb @@ -1,17 +1,18 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module StripeAccount - # Returns the stripe account ID string def self.find_or_create(nonprofit_id) - ParamValidation.new({:nonprofit_id => nonprofit_id}, {:nonprofit_id => {:required=> true, :is_integer => true}}) - begin - np = Nonprofit.find(nonprofit_id) - rescue => e - raise ParamValidation::ValidationError.new("#{nonprofit_id} is not a valid nonprofit", {:key => :nonprofit_id}) - end + ParamValidation.new({ nonprofit_id: nonprofit_id }, nonprofit_id: { required: true, is_integer: true }) + begin + np = Nonprofit.find(nonprofit_id) + rescue StandardError => e + raise ParamValidation::ValidationError.new("#{nonprofit_id} is not a valid nonprofit", key: :nonprofit_id) + end - if !np['stripe_account_id'] + if !np['stripe_account_id'] return create(np) else return np['stripe_account_id'] @@ -20,32 +21,32 @@ module StripeAccount # np should be a hash with string keys def self.create(np) - ParamValidation.new({:np => np}, {:np => {:required=> true, :is_a => Nonprofit}}) - params = { - managed: true, - email: np['email'].present? ? np['email'] : np.roles.nonprofit_admins.order('created_at ASC').first.user.email, - business_name: np['name'], - legal_entity: { - type: 'company', - address: { - city: np['city'], - state: np['state_code'], - postal_code: np['zip_code'], - country: 'US' - }, - business_name: np['name'], - }, - product_description: 'Nonprofit donations', - transfer_schedule: { interval: 'manual' } - } + ParamValidation.new({ np: np }, np: { required: true, is_a: Nonprofit }) + params = { + managed: true, + email: np['email'].present? ? np['email'] : np.roles.nonprofit_admins.order('created_at ASC').first.user.email, + business_name: np['name'], + legal_entity: { + type: 'company', + address: { + city: np['city'], + state: np['state_code'], + postal_code: np['zip_code'], + country: 'US' + }, + business_name: np['name'] + }, + product_description: 'Nonprofit donations', + transfer_schedule: { interval: 'manual' } + } - if np['website'] && np['website'] =~ URI::regexp - params[:business_url] = np['website'] - end + if np['website'] && np['website'] =~ URI::DEFAULT_PARSER.make_regexp + params[:business_url] = np['website'] + end - acct = Stripe::Account.create(params) + acct = Stripe::Account.create(params) Qx.update(:nonprofits).set(stripe_account_id: acct.id).where(id: np['id']).execute - NonprofitMailer.delay.setup_verification(np['id']) - return acct.id - end + StripeAccountCreateJob.perform_later(Nonprofit.find(np['id'])) + acct.id + end end diff --git a/lib/stripe_utils.rb b/lib/stripe_utils.rb index 820a15ea..7a32c443 100644 --- a/lib/stripe_utils.rb +++ b/lib/stripe_utils.rb @@ -1,35 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'stripe' require 'calculate/calculate_fees' module StripeUtils + # Get the verification status from a stripe object + # Some of our accounts seem to be marked 'Unverified,' but have no + # fields_needed set and have transfers_enabled set to true. So for our system, + # that practically means they are verified. + def self.get_verification_status(stripe_acct) + return 'verified' if stripe_acct.transfers_enabled - # Get the verification status from a stripe object - # Some of our accounts seem to be marked 'Unverified,' but have no - # fields_needed set and have transfers_enabled set to true. So for our system, - # that practically means they are verified. - def self.get_verification_status(stripe_acct) - return 'verified' if stripe_acct.transfers_enabled - return stripe_acct.legal_entity.verification.status - end + stripe_acct.legal_entity.verification.status + end - def self.create_transfer(net_amount, stripe_account_id, currency) - Stripe::Transfer.create({ - amount: net_amount, - currency: currency || Settings.intntl.currencies[0], - recipient: 'self' - }, { - stripe_account: stripe_account_id - }) - end + def self.create_transfer(net_amount, stripe_account_id, currency) + Stripe::Transfer.create({ + amount: net_amount, + currency: currency || Settings.intntl.currencies[0], + recipient: 'self' + }, + stripe_account: stripe_account_id) + end - - def self.create_refund(stripe_charge, amount, reason) - stripe_charge.refunds.create({ - amount: amount, - refund_application_fee: true, - reverse_transfer: true, - reason: reason - }) - end + def self.create_refund(stripe_charge, amount, reason) + stripe_charge.refunds.create( + amount: amount, + refund_application_fee: true, + reverse_transfer: true, + reason: reason + ) + end end diff --git a/lib/swagger-typescript-jquery/README.mustache b/lib/swagger-typescript-jquery/README.mustache deleted file mode 100644 index 31fe0fd5..00000000 --- a/lib/swagger-typescript-jquery/README.mustache +++ /dev/null @@ -1,45 +0,0 @@ -## {{npmName}}@{{npmVersion}} - -This generator creates TypeScript/JavaScript client that utilizes [jQuery](https://jquery.com/). The generated Node module can be used in the following environments: - -Environment -* Node.js -* Webpack -* Browserify - -Language level -* ES5 - you must have a Promises/A+ library installed -* ES6 - -Module system -* CommonJS -* ES6 module system - -It can be used in both TypeScript and JavaScript. In TypeScript, the definition should be automatically resolved via `package.json`. ([Reference](http://www.typescriptlang.org/docs/handbook/typings-for-npm-packages.html)) - -### Building - -To build an compile the typescript sources to javascript use: -``` -npm install -npm run build -``` - -### Publishing - -First build the package then run ```npm publish``` - -### Consuming - -navigate to the folder of your consuming project and run one of the following commands. - -_published:_ - -``` -npm install {{npmName}}@{{npmVersion}} --save -``` - -_unPublished (not recommended):_ - -``` -npm install PATH_TO_GENERATED_PACKAGE --save diff --git a/lib/swagger-typescript-jquery/api.mustache b/lib/swagger-typescript-jquery/api.mustache deleted file mode 100644 index c0935b36..00000000 --- a/lib/swagger-typescript-jquery/api.mustache +++ /dev/null @@ -1,260 +0,0 @@ -{{>licenseInfo}} - -{{#jqueryAlreadyImported}} -declare var $ : any; -{{/jqueryAlreadyImported}} -{{^jqueryAlreadyImported}} -import * as $ from 'jquery'; -{{/jqueryAlreadyImported}} -import * as models from '../model/models'; -import { COLLECTION_FORMATS } from '../variables'; -import { Configuration } from '../configuration'; - -/* tslint:disable:no-unused-variable member-ordering */ - -{{#operations}} - - - -{{#description}} -/** - * {{&description}} - */ -{{/description}} -export class {{classname}} { - protected basePath = '{{{basePath}}}'; - public defaultHeaders: Array = []; - public defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings = null; - public configuration: Configuration = new Configuration(); - - constructor(basePath?: string, configuration?: Configuration, defaultExtraJQueryAjaxSettings?: JQueryAjaxSettings) { - if (basePath) { - this.basePath = basePath; - } - if (configuration) { - this.configuration = configuration; - } - if (defaultExtraJQueryAjaxSettings) { - this.defaultExtraJQueryAjaxSettings = defaultExtraJQueryAjaxSettings; - } - } - - private extendObj(objA: T2, objB: T2): T1|T2 { - for (let key in objB) { - if (objB.hasOwnProperty(key)) { - objA[key] = objB[key]; - } - } - return objA; - } - -{{#operation}} - - /** - * {{¬es}} - {{#summary}} - * @summary {{&summary}} - {{/summary}} - {{#allParams}} - * @param {{paramName}} {{description}} - {{/allParams}} - */ - public {{nickname}}({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}extraJQueryAjaxSettings?: JQueryAjaxSettings): JQueryPromise<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}any{{/returnType}} > { - let localVarPath = this.basePath + '{{{path}}}'{{#pathParams}}.replace('{' + '{{baseName}}' + '}', encodeURIComponent(String({{paramName}}))){{/pathParams}}; - - let queryParameters: any = {}; - let headerParams: any = {}; -{{#hasFormParams}} - let formParams = new FormData(); - let reqHasFile = false; - -{{/hasFormParams}} -{{#allParams}} -{{#required}} - // verify required parameter '{{paramName}}' is not null or undefined - if ({{paramName}} === null || {{paramName}} === undefined) { - throw new Error('Required parameter {{paramName}} was null or undefined when calling {{nickname}}.'); - } - -{{/required}} -{{/allParams}} -{{#queryParams}} - {{#isListContainer}} - if ({{paramName}}) { - {{#isCollectionFormatMulti}} - {{paramName}}.forEach((element: any) => { - queryParameters['{{baseName}}'].push(element); - }); - {{/isCollectionFormatMulti}} - {{^isCollectionFormatMulti}} - queryParameters['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']); - {{/isCollectionFormatMulti}} - } - {{/isListContainer}} - {{^isListContainer}} - if ({{paramName}} !== null && {{paramName}} !== undefined) { - {{#isDateTime}} - queryParameters['{{baseName}}'] = {{paramName}}.toISOString(); - {{/isDateTime}} - {{^isDateTime}} - {{#isDate}} - queryParameters['{{baseName}}'] = {{paramName}}.toISOString(); - {{/isDate}} - {{^isDate}} - queryParameters['{{baseName}}'] = {{paramName}}; - {{/isDate}} - {{/isDateTime}} - } - {{/isListContainer}} -{{/queryParams}} - - localVarPath = localVarPath + "?" + $.param(queryParameters); -{{#formParams}} -{{#isFile}} - reqHasFile = true; - formParams.append("{{baseName}}", {{paramName}}); -{{/isFile}} -{{^isFile}} - {{#isListContainer}} - if ({{paramName}}) { - {{#isCollectionFormatMulti}} - {{paramName}}.forEach((element: any) => { - formParams.append('{{baseName}}', element); - }); - {{/isCollectionFormatMulti}} - {{^isCollectionFormatMulti}} - formParams.append('{{baseName}}', {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}'])); - {{/isCollectionFormatMulti}} - } - {{/isListContainer}} - {{^isListContainer}} - if ({{paramName}} !== null && {{paramName}} !== undefined) { - formParams.append('{{baseName}}', {{paramName}}); - } - {{/isListContainer}} -{{/isFile}} -{{/formParams}} -{{#headerParams}} - {{#isListContainer}} - if ({{paramName}}) { - headerParams['{{baseName}}'] = {{paramName}}.join(COLLECTION_FORMATS['{{collectionFormat}}']); - } - {{/isListContainer}} - {{^isListContainer}} - headerParams['{{baseName}}'] = String({{paramName}}); - {{/isListContainer}} - -{{/headerParams}} - // to determine the Content-Type header - let consumes: string[] = [ - {{#consumes}} - '{{{mediaType}}}'{{#hasMore}}, {{/hasMore}} - {{/consumes}} - ]; - - // to determine the Accept header - let produces: string[] = [ - {{#produces}} - '{{{mediaType}}}'{{#hasMore}}, {{/hasMore}} - {{/produces}} - ]; - -{{#authMethods}} - // authentication ({{name}}) required -{{#isApiKey}} -{{#isKeyInHeader}} - if (this.configuration.apiKey) { - headerParams['{{keyParamName}}'] = this.configuration.apiKey; - } - -{{/isKeyInHeader}} -{{#isKeyInQuery}} - if (this.configuration.apiKey) { - queryParameters.set('{{keyParamName}}', this.configuration.apiKey); - } - -{{/isKeyInQuery}} -{{/isApiKey}} -{{#isBasic}} - // http basic authentication required - if (this.configuration.username || this.configuration.password) { - headerParams['Authorization'] = 'Basic ' + btoa(this.configuration.username + ':' + this.configuration.password); - } - -{{/isBasic}} -{{#isOAuth}} - // oauth required - if (this.configuration.accessToken) { - let accessToken = typeof this.configuration.accessToken === 'function' - ? this.configuration.accessToken() - : this.configuration.accessToken; - headerParams['Authorization'] = 'Bearer ' + accessToken; - } - -{{/isOAuth}} -{{/authMethods}} -{{#hasFormParams}} - if (!reqHasFile) { - headerParams['Content-Type'] = 'application/x-www-form-urlencoded'; - } - -{{/hasFormParams}} - -{{#bodyParam}} - headerParams['Content-Type'] = 'application/json'; - -{{/bodyParam}} - let requestOptions: JQueryAjaxSettings = { - url: localVarPath, - type: '{{httpMethod}}', - headers: headerParams, - processData: false - }; - -{{#bodyParam}} - requestOptions.data = JSON.stringify({{paramName}}); -{{/bodyParam}} - if (headerParams['Content-Type']) { - requestOptions.contentType = headerParams['Content-Type']; - } -{{#hasFormParams}} - requestOptions.data = formParams; - if (reqHasFile) { - requestOptions.contentType = false; - } -{{/hasFormParams}} - - if (extraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, extraJQueryAjaxSettings); - } - - if (this.defaultExtraJQueryAjaxSettings) { - requestOptions = (Object).assign(requestOptions, this.defaultExtraJQueryAjaxSettings); - } - - let dfd = $.Deferred(); - $.ajax(requestOptions).then( - (data: {{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}any{{/returnType}}, textStatus: string, jqXHR: JQueryXHR) => - dfd.resolve(jqXHR, data), - (xhr: JQueryXHR, textStatus: string, errorThrown: string) => { - if(false){} - {{#responses}} - else if (xhr.status == {{{code}}} && {{{code}}} >= 400) - { - dfd.reject(new {{dataType}}Exception(<{{dataType}}>xhr.responseJSON)) - } - - {{/responses}} - else - { - - dfd.reject(errorThrown) - } - } - ); - return dfd.promise(); - } - -{{/operation}} -} -{{/operations}} diff --git a/lib/swagger-typescript-jquery/apis.mustache b/lib/swagger-typescript-jquery/apis.mustache deleted file mode 100644 index 9d3e9234..00000000 --- a/lib/swagger-typescript-jquery/apis.mustache +++ /dev/null @@ -1,9 +0,0 @@ -{{#apiInfo}} -{{#apis}} -{{#operations}} -export * from './{{ classFilename }}'; -import { {{ classname }} } from './{{ classFilename }}'; -{{/operations}} -{{/apis}} -export const APIS = [{{#apis}}{{#operations}}{{ classname }}{{/operations}}{{^-last}}, {{/-last}}{{/apis}}]; -{{/apiInfo}} diff --git a/lib/swagger-typescript-jquery/git_push.sh.mustache b/lib/swagger-typescript-jquery/git_push.sh.mustache deleted file mode 100755 index a2d75234..00000000 --- a/lib/swagger-typescript-jquery/git_push.sh.mustache +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/sh -# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ -# -# Usage example: /bin/sh ./git_push.sh wing328 swagger-petstore-perl "minor update" - -git_user_id=$1 -git_repo_id=$2 -release_note=$3 - -if [ "$git_user_id" = "" ]; then - git_user_id="{{{gitUserId}}}" - echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" -fi - -if [ "$git_repo_id" = "" ]; then - git_repo_id="{{{gitRepoId}}}" - echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" -fi - -if [ "$release_note" = "" ]; then - release_note="{{{releaseNote}}}" - echo "[INFO] No command line input provided. Set \$release_note to $release_note" -fi - -# Initialize the local directory as a Git repository -git init - -# Adds the files in the local repository and stages them for commit. -git add . - -# Commits the tracked changes and prepares them to be pushed to a remote repository. -git commit -m "$release_note" - -# Sets the new remote -git_remote=`git remote` -if [ "$git_remote" = "" ]; then # git remote not defined - - if [ "$GIT_TOKEN" = "" ]; then - echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." - git remote add origin https://github.com/${git_user_id}/${git_repo_id}.git - else - git remote add origin https://${git_user_id}:${GIT_TOKEN}@github.com/${git_user_id}/${git_repo_id}.git - fi - -fi - -git pull origin master - -# Pushes (Forces) the changes in the local repository up to the remote repository -echo "Git pushing to https://github.com/${git_user_id}/${git_repo_id}.git" -git push origin master 2>&1 | grep -v 'To https' - diff --git a/lib/swagger-typescript-jquery/index.mustache b/lib/swagger-typescript-jquery/index.mustache deleted file mode 100644 index d097c728..00000000 --- a/lib/swagger-typescript-jquery/index.mustache +++ /dev/null @@ -1,4 +0,0 @@ -export * from './api/api'; -export * from './model/models'; -export * from './variables'; -export * from './configuration'; \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/licenseInfo.mustache b/lib/swagger-typescript-jquery/licenseInfo.mustache deleted file mode 100644 index bbd8742e..00000000 --- a/lib/swagger-typescript-jquery/licenseInfo.mustache +++ /dev/null @@ -1,11 +0,0 @@ -/** - * {{{appName}}} - * {{{appDescription}}} - * - * {{#version}}OpenAPI spec version: {{{version}}}{{/version}} - * {{#infoEmail}}Contact: {{{infoEmail}}}{{/infoEmail}} - * - * NOTE: This class is auto generated by the swagger code generator program. - * https://github.com/swagger-api/swagger-codegen.git - * Do not edit the class manually. - */ diff --git a/lib/swagger-typescript-jquery/masterApiEntry.mustache b/lib/swagger-typescript-jquery/masterApiEntry.mustache deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/swagger-typescript-jquery/model.mustache b/lib/swagger-typescript-jquery/model.mustache deleted file mode 100644 index 39fa0472..00000000 --- a/lib/swagger-typescript-jquery/model.mustache +++ /dev/null @@ -1,13 +0,0 @@ -{{>licenseInfo}} -import * as models from './models'; -{{#models}} -{{#model}} - -{{#description}} -/** - * {{{description}}} - */ -{{/description}} -{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{>modelGeneric}}{{/isEnum}} -{{/model}} -{{/models}} \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/modelEnum.mustache b/lib/swagger-typescript-jquery/modelEnum.mustache deleted file mode 100644 index 47886484..00000000 --- a/lib/swagger-typescript-jquery/modelEnum.mustache +++ /dev/null @@ -1,12 +0,0 @@ -{{#description}} - /** - * {{{description}}} - */ -{{/description}} -export enum {{classname}} { -{{#allowableValues}} -{{#enumVars}} - {{{name}}} = {{{value}}}{{^-last}},{{/-last}} -{{/enumVars}} -{{/allowableValues}} -} \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/modelGeneric.mustache b/lib/swagger-typescript-jquery/modelGeneric.mustache deleted file mode 100644 index 201f2484..00000000 --- a/lib/swagger-typescript-jquery/modelGeneric.mustache +++ /dev/null @@ -1,45 +0,0 @@ -export interface {{classname}} {{#parent}}extends models.{{{parent}}} {{/parent}}{ -{{#additionalPropertiesType}} - [key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}}; - -{{/additionalPropertiesType}} -{{#vars}} - {{#description}} - /** - * {{{description}}} - */ - {{/description}} - {{name}}{{^required}}?{{/required}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}; - -{{/vars}} -} -export class {{classname}}Exception implements Error{ - - constructor(obj:{{classname}}, message?:string){ - {{#vars}} - this.item = obj; - {{/vars}} - - } - - message: string; - stack: string; - name: string; - - item: {{classname}}; -} - -{{#hasEnums}} -export namespace {{classname}} { -{{#vars}} - {{#isEnum}} - export enum {{enumName}} { - {{#allowableValues}} - {{#enumVars}} - {{{name}}} = {{{value}}}{{^-last}},{{/-last}} - {{/enumVars}} - {{/allowableValues}} - } - {{/isEnum}} -{{/vars}} -}{{/hasEnums}} \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/models.mustache b/lib/swagger-typescript-jquery/models.mustache deleted file mode 100644 index c6e732bf..00000000 --- a/lib/swagger-typescript-jquery/models.mustache +++ /dev/null @@ -1,5 +0,0 @@ -{{#models}} -{{#model}} -export * from './{{{ classname }}}'; -{{/model}} -{{/models}} \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/package.mustache b/lib/swagger-typescript-jquery/package.mustache deleted file mode 100644 index 753b37fb..00000000 --- a/lib/swagger-typescript-jquery/package.mustache +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "{{npmName}}", - "version": "{{npmVersion}}", - "description": "JQuery client for {{npmName}}", - "main": "api.js", - "scripts": { - "build": "tsc" - }, - "author": "Swagger Codegen Contributors", - "license": "MIT", - "dependencies": { - "bluebird": "^3.3.5", - "request": "^2.72.0", - "jquery": "^3.1.1" - }, - "devDependencies": { - "typescript": "2.2.2", - "typings": "^1.3.0" - }{{#npmRepository}}, - "publishConfig":{ - "registry":"{{npmRepository}}" - }{{/npmRepository}} -} diff --git a/lib/swagger-typescript-jquery/tsconfig.mustache b/lib/swagger-typescript-jquery/tsconfig.mustache deleted file mode 100644 index c5228429..00000000 --- a/lib/swagger-typescript-jquery/tsconfig.mustache +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "noImplicitAny": false, - "suppressImplicitAnyIndexErrors": true, - "target": "{{#supportsES6}}ES6{{/supportsES6}}{{^supportsES6}}ES5{{/supportsES6}}", - "moduleResolution": "node", - "removeComments": true, - "sourceMap": true, - "noLib": false, - "declaration": true - }, - "files": [ - "index.ts", - "typings/index.d.ts" - ] -} - diff --git a/lib/swagger-typescript-jquery/typings.mustache b/lib/swagger-typescript-jquery/typings.mustache deleted file mode 100644 index 306cf301..00000000 --- a/lib/swagger-typescript-jquery/typings.mustache +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ambientDependencies": { - "bluebird": "registry:dt/bluebird#2.0.0+20160319051630", - "core-js": "registry:dt/core-js#0.0.0+20160317120654", - "node": "registry:dt/node#4.0.0+20160423143914" - }, - "globalDependencies": { - "jquery": "registry:dt/jquery#1.10.0+20170310222111" - } -} \ No newline at end of file diff --git a/lib/swagger-typescript-jquery/variables.mustache b/lib/swagger-typescript-jquery/variables.mustache deleted file mode 100644 index 505ce935..00000000 --- a/lib/swagger-typescript-jquery/variables.mustache +++ /dev/null @@ -1,7 +0,0 @@ - -export const COLLECTION_FORMATS = { - 'csv': ',', - 'tsv': ' ', - 'ssv': ' ', - 'pipes': '|' -} \ No newline at end of file diff --git a/lib/tasks/bundler_notice.rake b/lib/tasks/bundler_notice.rake deleted file mode 100644 index 23e3265b..00000000 --- a/lib/tasks/bundler_notice.rake +++ /dev/null @@ -1,22 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -desc "generating a notice for bundler" - -# Clear old activerecord sessions tables daily -task :bundler_notice => :environment do - require 'bundler' - require 'httparty' - parser = Bundler::LockfileParser.new(File.read(Rails.root.join("Gemfile.lock"))) - result = parser.specs.map do |spec| - "gem/rubygems/-/#{spec.name}/#{spec.version.to_s}" - end - - @options = { - :headers => { - 'Content-Type' => 'application/json', - 'Accept' => 'application/json' - } - } - - result = HTTParty.post("https://api.clearlydefined.io/notices", @options.merge(body:JSON::generate({coordinates: result}))) - byebug -end \ No newline at end of file diff --git a/lib/tasks/civicrm.rake b/lib/tasks/civicrm.rake index 4b040383..dae3cb1e 100644 --- a/lib/tasks/civicrm.rake +++ b/lib/tasks/civicrm.rake @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'queue_donations' namespace :civicrm do - desc "pushes donation records to CiviCRM" - task :push => :environment do + desc 'pushes donation records to CiviCRM' + task push: :environment do QueueDonations.execute_all end desc "pushes donation records to CiviCRM, but doesn't mark them as pushed (useful for debugging)" - task :dry_run => :environment do + task dry_run: :environment do QueueDonations.dry_execute_all end end diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake deleted file mode 100644 index ca0636b2..00000000 --- a/lib/tasks/database.rake +++ /dev/null @@ -1,31 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -Rake::Task["db:structure:dump"].clear -namespace :db do - namespace :structure do - desc "Overriding the task db:structure:dump task to remove -i option from pg_dump to make postgres 9.5 compatible" - task dump: [:environment, :load_config] do - config = ActiveRecord::Base.configurations[Rails.env] - set_psql_env(config) - filename = File.join(Rails.root, "db", "structure.sql") - database = config["database"] - command = "pg_dump -s -x -O -f #{Shellwords.escape(filename)} #{Shellwords.escape(database)}" - raise 'Error dumping database' unless Kernel.system(command) - - File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" } - if ActiveRecord::Base.connection.supports_migrations? - File.open(filename, "a") do |f| - f.puts ActiveRecord::Base.connection.dump_schema_information - f.print "\n" - end - end - Rake::Task["db:structure:dump"].reenable - end - end - - def set_psql_env(configuration) - ENV['PGHOST'] = configuration['host'] if configuration['host'] - ENV['PGPORT'] = configuration['port'].to_s if configuration['port'] - ENV['PGPASSWORD'] = configuration['password'].to_s if configuration['password'] - ENV['PGUSER'] = configuration['username'].to_s if configuration['username'] - end -end \ No newline at end of file diff --git a/lib/tasks/full_contact.rake b/lib/tasks/full_contact.rake index 7ef13ced..8a6b983a 100644 --- a/lib/tasks/full_contact.rake +++ b/lib/tasks/full_contact.rake @@ -1,12 +1,13 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -desc "For generating Full Contact data" +desc 'For generating Full Contact data' # Clear old activerecord sessions tables daily -task :work_full_contact_queue => :environment do - +task work_full_contact_queue: :environment do loop do - sleep(10) until Qx.select("COUNT(*)").from("full_contact_jobs").execute.first['count'] > 0 - puts "working..." + sleep(10) until Qx.select('COUNT(*)').from('full_contact_jobs').execute.first['count'] > 0 + puts 'working...' begin InsertFullContactInfos.work_queue diff --git a/lib/tasks/health_report.rake b/lib/tasks/health_report.rake index daa1a910..f2ef03cd 100644 --- a/lib/tasks/health_report.rake +++ b/lib/tasks/health_report.rake @@ -1,10 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -desc "For sending an activity report email of what has been happening on the system" +desc 'For sending an activity report email of what has been happening on the system' # Clear old activerecord sessions tables daily -task :send_health_report => :environment do - GenericMailer.admin_notice({ +task send_health_report: :environment do + GenericMailer.admin_notice( body: HealthReport.format_data(HealthReport.query_data), subject: "CommitChange activity report #{Format::Date.to_readable(Time.now)}" - }).deliver + ).deliver_later end diff --git a/lib/tasks/notice.rake b/lib/tasks/notice.rake new file mode 100644 index 00000000..6bac145f --- /dev/null +++ b/lib/tasks/notice.rake @@ -0,0 +1,59 @@ +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + +# Create notice files for dependencies +namespace :notice do + namespace :ruby do + require 'bundler' + require 'httparty' + def get_notice_ruby + parser = Bundler::LockfileParser.new(File.read(Rails.root.join("Gemfile.lock"))) + result = parser.specs.map do |spec| + "gem/rubygems/-/#{spec.name}/#{spec.version.to_s}" + end + + @options = { + :headers => { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + }, + :timeout => 120 + } + result = HTTParty.post("https://api.clearlydefined.io/notices", @options.merge(body:JSON::generate({coordinates: result}))) + end + + desc "generating NOTICE-ruby from ClearlyDefined.io" + task :update do + result = get_notice_ruby + File.write('NOTICE-ruby', result.body) + end + + desc "checking whether NOTICE-ruby matches the one on ClearlyDefined.io" + task :verify do + result = get_notice_ruby + raise "NOTICE-ruby is not up to date. Run bin/rails notice:ruby:update to update the file." if result.body != File.read('NOTICE-ruby') + end + end + + namespace :js do + require 'fileutils' + def get_notice_js + raise "NOTICE-js could not be retrieved from Clearlydefined.io" unless system('yarn noticeme') + File.read('NOTICE') + end + + desc "generating NOTICE-js from ClearlyDefined.io" + task :update do + if (File.exists?('NOTICE')) + File.delete('NOTICE') + end + result = get_notice_js + FileUtils.mv('NOTICE', 'NOTICE-js', force: true) + end + + desc "checking whether NOTICE-js matches the one on ClearlyDefined.io" + task :verify do + result = get_notice_js + raise "NOTICE-js is not up to date. Run bin/rails notice:js:update to update the file." if result != File.read('NOTICE-js') + end + end +end \ No newline at end of file diff --git a/lib/tasks/oapi.rake b/lib/tasks/oapi.rake deleted file mode 100644 index 4bb6cf8d..00000000 --- a/lib/tasks/oapi.rake +++ /dev/null @@ -1,8 +0,0 @@ -require 'grape-swagger/rake/oapi_tasks' -namespace :oapi do - task gen: [:environment] do - ENV['store'] = 'tmp/openapi.json' - GrapeSwagger::Rake::OapiTasks.new(Houdini::API) - Rake::Task['oapi:fetch'].invoke() - end -end \ No newline at end of file diff --git a/lib/tasks/scheduler.rake b/lib/tasks/scheduler.rake index 37bade48..96da0a34 100644 --- a/lib/tasks/scheduler.rake +++ b/lib/tasks/scheduler.rake @@ -1,25 +1,25 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'scheduled_jobs' desc "For use with Heroku's Scheduler add-on" # We use a single rake call so we can catch and send any errors that happen in the job -task :heroku_scheduled_job, [:name] => :environment do |t, args| +task :heroku_scheduled_job, [:name] => :environment do |_t, args| job_name = args[:name] # Fetch all the super admin emails so we can send a report enum = ScheduledJobs.send(job_name) - results = "" + results = '' enum.each do |lamb| - begin - result = lamb.call - results += "Success: #{result}\n" - rescue Exception => e - results += "Failure: #{e}\n" - end + result = lamb.call + results += "Success: #{result}\n" + rescue Exception => e + results += "Failure: #{e}\n" end - GenericMailer.delay.admin_notice({ + GenericMailer.admin_notice( subject: "Scheduled job results on CommitChange for '#{job_name}'", - body: results.empty? ? "No jobs to run today." : results - }) + body: results.empty? ? 'No jobs to run today.' : results + ).deliver_later end diff --git a/lib/tasks/seed.rake b/lib/tasks/seed.rake index 8d72629a..4ec8db2d 100644 --- a/lib/tasks/seed.rake +++ b/lib/tasks/seed.rake @@ -1,16 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later namespace :seed do - - task :np => :environment do - ActiveRecord::Base.transaction do - supers = Role.super_admins.includes(:user).map{|r| r.user} - n = Nonprofit.register(supers.last, name: "Testify #{rand(0..100)}", city: 'Albuquerque', state_code: 'NM') - n.verification_status = 'verified' - n.vetted = true - n.create_billing_subscription({billing_plan: BillingPlan.where(tier: 2).last}) - n.save! - supers.each{|user| user.roles.create(name: :nonprofit_admin, host: n)} - puts "New test nonprofit id: #{n.id}" - end - end + task np: :environment do + ActiveRecord::Base.transaction do + supers = Role.super_admins.includes(:user).map(&:user) + n = Nonprofit.register(supers.last, name: "Testify #{rand(0..100)}", city: 'Albuquerque', state_code: 'NM') + n.verification_status = 'verified' + n.vetted = true + n.create_billing_subscription(billing_plan: BillingPlan.where(tier: 2).last) + n.save! + supers.each { |user| user.roles.create(name: :nonprofit_admin, host: n) } + puts "New test nonprofit id: #{n.id}" + end + end end diff --git a/lib/tasks/settings.rake b/lib/tasks/settings.rake index fcb1fbb0..15391d26 100644 --- a/lib/tasks/settings.rake +++ b/lib/tasks/settings.rake @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later namespace :settings do @@ -5,33 +7,9 @@ namespace :settings do require File.expand_path('../../config/environment.rb', File.dirname(__FILE__)) end - desc "show settings" - task :show => :environment do + desc 'show settings' + task show: :environment do require 'pp' pp Settings.to_hash end - - task :generate_json => :environment do - - cdn_url= URI(Settings.cdn.url) - cdn_url = cdn_url.to_s - if (Settings.button_config&.url) - cdn_url= URI(Settings.button_config.url).to_s - end - c = {button:{url:cdn_url,css:"#{cdn_url}/css/donate-button.v2.css"}} - open(File.expand_path('config/settings.json', Rails.root), 'w') do |f| - f.write(c.to_json) - end - end - - task :combine_translations => 'i18n:js:export' do - js_root = File.expand_path('public/javascripts', Rails.root) - #i18n = File.read(File.join(js_root, 'i18n.js')) - translations = File.read(File.join(js_root, 'translations.js')) - open(File.join(js_root, '_final.js'), 'w') do |f| - f.write("const I18n = require('i18n-js');\n" + translations + "\n window.I18n = I18n") - end - end - end - diff --git a/lib/templates/active_record/application_record/application_record.rb.tt b/lib/templates/active_record/application_record/application_record.rb.tt new file mode 100644 index 00000000..05ba4c2c --- /dev/null +++ b/lib/templates/active_record/application_record/application_record.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% module_namespacing do -%> +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end +<% end -%> diff --git a/lib/templates/active_record/migration/create_table_migration.rb.tt b/lib/templates/active_record/migration/create_table_migration.rb.tt new file mode 100644 index 00000000..ab26dc3a --- /dev/null +++ b/lib/templates/active_record/migration/create_table_migration.rb.tt @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] + def change + create_table :<%= table_name %><%= primary_key_type %> do |t| +<% attributes.each do |attribute| -%> +<% if attribute.password_digest? -%> + t.string :password_digest<%= attribute.inject_options %> +<% elsif attribute.token? -%> + t.string :<%= attribute.name %><%= attribute.inject_options %> +<% elsif attribute.reference? -%> + t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %> +<% elsif !attribute.virtual? -%> + t.<%= attribute.type %> :<%= attribute.name %><%= attribute.inject_options %> +<% end -%> +<% end -%> +<% if options[:timestamps] %> + t.timestamps +<% end -%> + end +<% attributes.select(&:token?).each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true +<% end -%> +<% attributes_with_index.each do |attribute| -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> +<% end -%> + end +end diff --git a/lib/templates/active_record/migration/migration.rb.tt b/lib/templates/active_record/migration/migration.rb.tt new file mode 100644 index 00000000..e171dd81 --- /dev/null +++ b/lib/templates/active_record/migration/migration.rb.tt @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>] +<%- if migration_action == 'add' -%> + def change +<% attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %> + <%- elsif attribute.token? -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true + <%- elsif !attribute.virtual? -%> + add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- if attribute.has_index? -%> + add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> +<%- end -%> + end +<%- elsif migration_action == 'join' -%> + def change + create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t| + <%- attributes.each do |attribute| -%> + <%- if attribute.reference? -%> + t.references :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %> + <%- elsif !attribute.virtual? -%> + <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- end -%> + end + end +<%- else -%> + def change +<% attributes.each do |attribute| -%> +<%- if migration_action -%> + <%- if attribute.reference? -%> + remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %><%= foreign_key_type %> + <%- else -%> + <%- if attribute.has_index? -%> + remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> + <%- end -%> + <%- if !attribute.virtual? -%> + remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> + <%- end -%> + <%- end -%> +<%- end -%> +<%- end -%> + end +<%- end -%> +end diff --git a/lib/templates/active_record/model/model.rb.tt b/lib/templates/active_record/model/model.rb.tt new file mode 100644 index 00000000..34365eb8 --- /dev/null +++ b/lib/templates/active_record/model/model.rb.tt @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% module_namespacing do -%> +class <%= class_name %> < <%= parent_class_name.classify %> +<% attributes.select(&:reference?).each do |attribute| -%> + belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %> +<% end -%> +<% attributes.select(&:rich_text?).each do |attribute| -%> + has_rich_text :<%= attribute.name %> +<% end -%> +<% attributes.select(&:attachment?).each do |attribute| -%> + has_one_attached :<%= attribute.name %> +<% end -%> +<% attributes.select(&:attachments?).each do |attribute| -%> + has_many_attached :<%= attribute.name %> +<% end -%> +<% attributes.select(&:token?).each do |attribute| -%> + has_secure_token<% if attribute.name != "token" %> :<%= attribute.name %><% end %> +<% end -%> +<% if attributes.any?(&:password_digest?) -%> + has_secure_password +<% end -%> +end +<% end -%> diff --git a/lib/templates/active_record/model/module.rb.tt b/lib/templates/active_record/model/module.rb.tt new file mode 100644 index 00000000..bc169149 --- /dev/null +++ b/lib/templates/active_record/model/module.rb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% module_namespacing do -%> +module <%= class_path.map(&:camelize).join('::') %> + def self.table_name_prefix + '<%= namespaced? ? namespaced_class_path.join('_') : class_path.join('_') %>_' + end +end +<% end -%> diff --git a/lib/templates/erb/controller/view.html.erb.tt b/lib/templates/erb/controller/view.html.erb.tt new file mode 100644 index 00000000..cc75205a --- /dev/null +++ b/lib/templates/erb/controller/view.html.erb.tt @@ -0,0 +1,3 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

<%= class_name %>#<%= @action %>

+

Find me in <%= @path %>

diff --git a/lib/templates/erb/mailer/layout.html.erb.tt b/lib/templates/erb/mailer/layout.html.erb.tt new file mode 100644 index 00000000..6e85ad7c --- /dev/null +++ b/lib/templates/erb/mailer/layout.html.erb.tt @@ -0,0 +1,14 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> + + + + + + + + + <%%= yield %> + + diff --git a/lib/templates/erb/mailer/layout.text.erb.tt b/lib/templates/erb/mailer/layout.text.erb.tt new file mode 100644 index 00000000..b4bae3aa --- /dev/null +++ b/lib/templates/erb/mailer/layout.text.erb.tt @@ -0,0 +1,2 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +<%%= yield %> diff --git a/lib/templates/erb/mailer/view.html.erb.tt b/lib/templates/erb/mailer/view.html.erb.tt new file mode 100644 index 00000000..a430ca69 --- /dev/null +++ b/lib/templates/erb/mailer/view.html.erb.tt @@ -0,0 +1,6 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

<%= class_name %>#<%= @action %>

+ +

+ <%%= @greeting %>, find me in <%= @path %> +

diff --git a/lib/templates/erb/mailer/view.text.erb.tt b/lib/templates/erb/mailer/view.text.erb.tt new file mode 100644 index 00000000..4e048e81 --- /dev/null +++ b/lib/templates/erb/mailer/view.text.erb.tt @@ -0,0 +1,4 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +<%= class_name %>#<%= @action %> + +<%%= @greeting %>, find me in <%= @path %> diff --git a/lib/templates/erb/scaffold/_form.html.erb.tt b/lib/templates/erb/scaffold/_form.html.erb.tt new file mode 100644 index 00000000..cca7eb77 --- /dev/null +++ b/lib/templates/erb/scaffold/_form.html.erb.tt @@ -0,0 +1,35 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %> + <%% if <%= singular_table_name %>.errors.any? %> +
+

<%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:

+ +
    + <%% <%= singular_table_name %>.errors.full_messages.each do |message| %> +
  • <%%= message %>
  • + <%% end %> +
+
+ <%% end %> + +<% attributes.each do |attribute| -%> +
+<% if attribute.password_digest? -%> + <%%= form.label :password %> + <%%= form.password_field :password %> +
+ +
+ <%%= form.label :password_confirmation %> + <%%= form.password_field :password_confirmation %> +<% else -%> + <%%= form.label :<%= attribute.column_name %> %> + <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %> +<% end -%> +
+ +<% end -%> +
+ <%%= form.submit %> +
+<%% end %> diff --git a/lib/templates/erb/scaffold/edit.html.erb.tt b/lib/templates/erb/scaffold/edit.html.erb.tt new file mode 100644 index 00000000..cc231a9e --- /dev/null +++ b/lib/templates/erb/scaffold/edit.html.erb.tt @@ -0,0 +1,7 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

Editing <%= singular_table_name.titleize %>

+ +<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> + +<%%= link_to 'Show', @<%= singular_table_name %> %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/lib/templates/erb/scaffold/index.html.erb.tt b/lib/templates/erb/scaffold/index.html.erb.tt new file mode 100644 index 00000000..bfc59d77 --- /dev/null +++ b/lib/templates/erb/scaffold/index.html.erb.tt @@ -0,0 +1,32 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

<%%= notice %>

+ +

<%= plural_table_name.titleize %>

+ + + + +<% attributes.reject(&:password_digest?).each do |attribute| -%> + +<% end -%> + + + + + + <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %> + +<% attributes.reject(&:password_digest?).each do |attribute| -%> + +<% end -%> + + + + + <%% end %> + +
<%= attribute.human_name %>
<%%= <%= singular_table_name %>.<%= attribute.name %> %><%%= link_to 'Show', <%= model_resource_name %> %><%%= link_to 'Edit', edit_<%= singular_route_name %>_path(<%= singular_table_name %>) %><%%= link_to 'Destroy', <%= model_resource_name %>, method: :delete, data: { confirm: 'Are you sure?' } %>
+ +
+ +<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path %> diff --git a/lib/templates/erb/scaffold/new.html.erb.tt b/lib/templates/erb/scaffold/new.html.erb.tt new file mode 100644 index 00000000..9ed89340 --- /dev/null +++ b/lib/templates/erb/scaffold/new.html.erb.tt @@ -0,0 +1,6 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

New <%= singular_table_name.titleize %>

+ +<%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %> + +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/lib/templates/erb/scaffold/show.html.erb.tt b/lib/templates/erb/scaffold/show.html.erb.tt new file mode 100644 index 00000000..e938f045 --- /dev/null +++ b/lib/templates/erb/scaffold/show.html.erb.tt @@ -0,0 +1,12 @@ +<%- # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -%> +

<%%= notice %>

+ +<% attributes.reject(&:password_digest?).each do |attribute| -%> +

+ <%= attribute.human_name %>: + <%%= @<%= singular_table_name %>.<%= attribute.name %> %> +

+ +<% end -%> +<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> | +<%%= link_to 'Back', <%= index_helper %>_path %> diff --git a/app/api/houdini/v1/validations.rb b/lib/templates/factory_bot/model/factories.erb similarity index 50% rename from app/api/houdini/v1/validations.rb rename to lib/templates/factory_bot/model/factories.erb index eb0e60b6..00ab5121 100644 --- a/app/api/houdini/v1/validations.rb +++ b/lib/templates/factory_bot/model/factories.erb @@ -1,2 +1,6 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'houdini/v1/validators/is_equal_to' \ No newline at end of file +FactoryBot.define do +<%= factory_definition.rstrip %> +end diff --git a/lib/templates/rails/assets/javascript.js b/lib/templates/rails/assets/javascript.js new file mode 100644 index 00000000..80d5ae04 --- /dev/null +++ b/lib/templates/rails/assets/javascript.js @@ -0,0 +1,3 @@ +// License: LGPL-3.0-or-later +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/lib/templates/rails/assets/stylesheet.css b/lib/templates/rails/assets/stylesheet.css new file mode 100644 index 00000000..72b4231a --- /dev/null +++ b/lib/templates/rails/assets/stylesheet.css @@ -0,0 +1,5 @@ +/* License: LGPL-3.0-or-later */ +/* + Place all the styles related to the matching controller here. + They will automatically be included in application.css. +*/ diff --git a/lib/templates/rails/controller/controller.rb.tt b/lib/templates/rails/controller/controller.rb.tt new file mode 100644 index 00000000..b5bd889c --- /dev/null +++ b/lib/templates/rails/controller/controller.rb.tt @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= class_name %>Controller < ApplicationController +<% actions.each do |action| -%> + def <%= action %> + end +<%= "\n" unless action == actions.last -%> +<% end -%> +end +<% end -%> diff --git a/lib/templates/rails/generator/%file_name%_generator.rb.tt b/lib/templates/rails/generator/%file_name%_generator.rb.tt new file mode 100644 index 00000000..29902510 --- /dev/null +++ b/lib/templates/rails/generator/%file_name%_generator.rb.tt @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +class <%= class_name %>Generator < Rails::Generators::NamedBase + source_root File.expand_path('templates', __dir__) +end diff --git a/lib/templates/rails/generator/USAGE.tt b/lib/templates/rails/generator/USAGE.tt new file mode 100644 index 00000000..ebdd2a94 --- /dev/null +++ b/lib/templates/rails/generator/USAGE.tt @@ -0,0 +1,8 @@ +Description: + Explain the generator + +Example: + bin/rails generate <%= file_name %> Thing + + This will create: + what/will/it/create diff --git a/gems/grape_devise/spec/dummy/app/controllers/concerns/.keep b/lib/templates/rails/generator/templates/.empty_directory similarity index 100% rename from gems/grape_devise/spec/dummy/app/controllers/concerns/.keep rename to lib/templates/rails/generator/templates/.empty_directory diff --git a/lib/templates/rails/helper/helper.rb.tt b/lib/templates/rails/helper/helper.rb.tt new file mode 100644 index 00000000..499c7d89 --- /dev/null +++ b/lib/templates/rails/helper/helper.rb.tt @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% module_namespacing do -%> +module <%= class_name %>Helper +end +<% end -%> diff --git a/lib/templates/rails/scaffold_controller/api_controller.rb.tt b/lib/templates/rails/scaffold_controller/api_controller.rb.tt new file mode 100644 index 00000000..e8f83972 --- /dev/null +++ b/lib/templates/rails/scaffold_controller/api_controller.rb.tt @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :update, :destroy] + + # GET <%= route_url %> + def index + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> + + render json: <%= "@#{plural_table_name}" %> + end + + # GET <%= route_url %>/1 + def show + render json: <%= "@#{singular_table_name}" %> + end + + # POST <%= route_url %> + def create + @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> + + if @<%= orm_instance.save %> + render json: <%= "@#{singular_table_name}" %>, status: :created, location: <%= "@#{singular_table_name}" %> + else + render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity + end + end + + # PATCH/PUT <%= route_url %>/1 + def update + if @<%= orm_instance.update("#{singular_table_name}_params") %> + render json: <%= "@#{singular_table_name}" %> + else + render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity + end + end + + # DELETE <%= route_url %>/1 + def destroy + @<%= orm_instance.destroy %> + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end + + # Only allow a trusted parameter "white list" through. + def <%= "#{singular_table_name}_params" %> + <%- if attributes_names.empty? -%> + params.fetch(:<%= singular_table_name %>, {}) + <%- else -%> + params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + <%- end -%> + end +end +<% end -%> diff --git a/lib/templates/rails/scaffold_controller/controller.rb.tt b/lib/templates/rails/scaffold_controller/controller.rb.tt new file mode 100644 index 00000000..a25273b7 --- /dev/null +++ b/lib/templates/rails/scaffold_controller/controller.rb.tt @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% if namespaced? -%> +require_dependency "<%= namespaced_path %>/application_controller" + +<% end -%> +<% module_namespacing do -%> +class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] + + # GET <%= route_url %> + def index + @<%= plural_table_name %> = <%= orm_class.all(class_name) %> + end + + # GET <%= route_url %>/1 + def show + end + + # GET <%= route_url %>/new + def new + @<%= singular_table_name %> = <%= orm_class.build(class_name) %> + end + + # GET <%= route_url %>/1/edit + def edit + end + + # POST <%= route_url %> + def create + @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %> + + if @<%= orm_instance.save %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully created.'" %> + else + render :new + end + end + + # PATCH/PUT <%= route_url %>/1 + def update + if @<%= orm_instance.update("#{singular_table_name}_params") %> + redirect_to <%= redirect_resource_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> + else + render :edit + end + end + + # DELETE <%= route_url %>/1 + def destroy + @<%= orm_instance.destroy %> + redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end + + # Only allow a trusted parameter "white list" through. + def <%= "#{singular_table_name}_params" %> + <%- if attributes_names.empty? -%> + params.fetch(:<%= singular_table_name %>, {}) + <%- else -%> + params.require(:<%= singular_table_name %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>) + <%- end -%> + end +end +<% end -%> diff --git a/lib/templates/rails/task/task.rb.tt b/lib/templates/rails/task/task.rb.tt new file mode 100644 index 00000000..ba85a077 --- /dev/null +++ b/lib/templates/rails/task/task.rb.tt @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +namespace :<%= file_name %> do +<% actions.each do |action| -%> + desc "TODO" + task <%= action %>: :environment do + end + +<% end -%> +end diff --git a/lib/templates/rspec/channel/channel_spec.rb.erb.tt b/lib/templates/rspec/channel/channel_spec.rb.erb.tt new file mode 100644 index 00000000..aa067669 --- /dev/null +++ b/lib/templates/rspec/channel/channel_spec.rb.erb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe <%= class_name %>Channel, <%= type_metatag(:channel) %> do + pending "add some examples to (or delete) #{__FILE__}" +end +<% end -%> diff --git a/lib/templates/rspec/controller/controller_spec.rb.tt b/lib/templates/rspec/controller/controller_spec.rb.tt new file mode 100644 index 00000000..e691da01 --- /dev/null +++ b/lib/templates/rspec/controller/controller_spec.rb.tt @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe <%= class_name %>Controller, <%= type_metatag(:controller) %> do + +<% for action in actions -%> + describe "GET #<%= action %>" do + it "returns http success" do + get :<%= action %> + expect(response).to have_http_status(:success) + end + end + +<% end -%> +end +<% end -%> diff --git a/lib/templates/rspec/controller/request_spec.rb.tt b/lib/templates/rspec/controller/request_spec.rb.tt new file mode 100644 index 00000000..3270392f --- /dev/null +++ b/lib/templates/rspec/controller/request_spec.rb.tt @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:request) %> do +<% namespaced_path = regular_class_path.join('/') %> +<% for action in actions -%> + describe "GET /<%= action %>" do + it "returns http success" do + get "<%= "/#{namespaced_path}" if namespaced_path != '' %>/<%= file_name %>/<%= action %>" + expect(response).to have_http_status(:success) + end + end + +<% end -%> +end diff --git a/lib/templates/rspec/controller/routing_spec.rb.tt b/lib/templates/rspec/controller/routing_spec.rb.tt new file mode 100644 index 00000000..9f211e39 --- /dev/null +++ b/lib/templates/rspec/controller/routing_spec.rb.tt @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe '<%= class_name %>Controller', <%= type_metatag(:routing) %> do + describe 'routing' do +<% for action in actions -%> + it 'routes to #<%= action %>' do + expect(get: "/<%= class_name.underscore %>/<%= action %>").to route_to("<%= class_name.underscore %>#<%= action %>") + end +<% end -%> + end +end +<% end -%> diff --git a/lib/templates/rspec/controller/view_spec.rb.tt b/lib/templates/rspec/controller/view_spec.rb.tt new file mode 100644 index 00000000..531bc44b --- /dev/null +++ b/lib/templates/rspec/controller/view_spec.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= file_name %>/<%= @action %>.html.<%= options[:template_engine] %>", <%= type_metatag(:view) %> do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/lib/templates/rspec/feature/feature_singular_spec.rb.tt b/lib/templates/rspec/feature/feature_singular_spec.rb.tt new file mode 100644 index 00000000..67968f78 --- /dev/null +++ b/lib/templates/rspec/feature/feature_singular_spec.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.feature "<%= class_name.singularize %>", <%= type_metatag(:feature) %> do + pending "add some scenarios (or delete) #{__FILE__}" +end diff --git a/lib/templates/rspec/feature/feature_spec.rb.tt b/lib/templates/rspec/feature/feature_spec.rb.tt new file mode 100644 index 00000000..22b6daef --- /dev/null +++ b/lib/templates/rspec/feature/feature_spec.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.feature "<%= class_name.pluralize %>", <%= type_metatag(:feature) %> do + pending "add some scenarios (or delete) #{__FILE__}" +end diff --git a/lib/templates/rspec/generator/generator_spec.rb.tt b/lib/templates/rspec/generator/generator_spec.rb.tt new file mode 100644 index 00000000..1d4a4791 --- /dev/null +++ b/lib/templates/rspec/generator/generator_spec.rb.tt @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:generator) %> do + + pending "add some scenarios (or delete) #{__FILE__}" +end diff --git a/lib/templates/rspec/helper/helper_spec.rb.tt b/lib/templates/rspec/helper/helper_spec.rb.tt new file mode 100644 index 00000000..7c5e51c2 --- /dev/null +++ b/lib/templates/rspec/helper/helper_spec.rb.tt @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the <%= class_name %>Helper. For example: +# +# describe <%= class_name %>Helper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +<% module_namespacing do -%> +RSpec.describe <%= class_name %>Helper, <%= type_metatag(:helper) %> do + pending "add some examples to (or delete) #{__FILE__}" +end +<% end -%> diff --git a/lib/templates/rspec/install/spec/rails_helper.rb.tt b/lib/templates/rspec/install/spec/rails_helper.rb.tt new file mode 100644 index 00000000..4a86c981 --- /dev/null +++ b/lib/templates/rspec/install/spec/rails_helper.rb.tt @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +# This file is copied to spec/ when you run 'rails generate rspec:install' +require 'spec_helper' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../config/environment', __dir__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'rspec/rails' +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } + +<% if RSpec::Rails::FeatureCheck.has_active_record_migration? -%> +# Checks for pending migrations and applies them before tests are run. +# If you are not using ActiveRecord, you can remove these lines. +begin + ActiveRecord::Migration.maintain_test_schema! +rescue ActiveRecord::PendingMigrationError => e + puts e.to_s.strip + exit 1 +end +<% end -%> +RSpec.configure do |config| +<% if RSpec::Rails::FeatureCheck.has_active_record? -%> + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # You can uncomment this line to turn off ActiveRecord support entirely. + # config.use_active_record = false + +<% else -%> + # Remove this line to enable support for ActiveRecord + config.use_active_record = false + + # If you enable ActiveRecord support you should unncomment these lines, + # note if you'd prefer not to run each example within a transaction, you + # should set use_transactional_fixtures to false. + # + # config.fixture_path = "#{::Rails.root}/spec/fixtures" + # config.use_transactional_fixtures = true + +<% end -%> + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, type: :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end diff --git a/lib/templates/rspec/integration/request_spec.rb.tt b/lib/templates/rspec/integration/request_spec.rb.tt new file mode 100644 index 00000000..2106d5a1 --- /dev/null +++ b/lib/templates/rspec/integration/request_spec.rb.tt @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:request) %> do + describe "GET /<%= name.underscore.pluralize %>" do + it "works! (now write some real specs)" do + get <%= index_helper %>_path + expect(response).to have_http_status(200) + end + end +end diff --git a/lib/templates/rspec/job/job_spec.rb.erb.tt b/lib/templates/rspec/job/job_spec.rb.erb.tt new file mode 100644 index 00000000..dbcb7e55 --- /dev/null +++ b/lib/templates/rspec/job/job_spec.rb.erb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe <%= class_name %>Job, <%= type_metatag(:job) %> do + pending "add some examples to (or delete) #{__FILE__}" +end +<% end -%> diff --git a/lib/templates/rspec/mailbox/mailbox_spec.rb.erb.tt b/lib/templates/rspec/mailbox/mailbox_spec.rb.erb.tt new file mode 100644 index 00000000..7280025c --- /dev/null +++ b/lib/templates/rspec/mailbox/mailbox_spec.rb.erb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe <%= class_name %>Mailbox, <%= type_metatag(:mailbox) %> do + pending "add some examples to (or delete) #{__FILE__}" +end +<% end -%> diff --git a/lib/templates/rspec/mailer/fixture b/lib/templates/rspec/mailer/fixture new file mode 100644 index 00000000..171648d6 --- /dev/null +++ b/lib/templates/rspec/mailer/fixture @@ -0,0 +1,3 @@ +<%= class_name %>#<%= @action %> + +Hi, find me in app/views/<%= @path %> diff --git a/lib/templates/rspec/mailer/mailer_spec.rb.tt b/lib/templates/rspec/mailer/mailer_spec.rb.tt new file mode 100644 index 00000000..c72a60ec --- /dev/null +++ b/lib/templates/rspec/mailer/mailer_spec.rb.tt @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require "rails_helper" + +<% module_namespacing do -%> +RSpec.describe <%= Rails.version.to_f >= 5.0 ? class_name.sub(/(Mailer)?$/, 'Mailer') : class_name %>, <%= type_metatag(:mailer) %> do +<% for action in actions -%> + describe "<%= action %>" do + let(:mail) { <%= Rails.version.to_f >= 5.0 ? class_name.sub(/(Mailer)?$/, 'Mailer') : class_name %>.<%= action %> } + + it "renders the headers" do + expect(mail.subject).to eq(<%= action.to_s.humanize.inspect %>) + expect(mail.to).to eq(["to@example.org"]) + expect(mail.from).to eq(["from@example.com"]) + end + + it "renders the body" do + expect(mail.body.encoded).to match("Hi") + end + end + +<% end -%> +<% if actions.blank? -%> + pending "add some examples to (or delete) #{__FILE__}" +<% end -%> +end +<% end -%> diff --git a/lib/templates/rspec/mailer/preview.rb.tt b/lib/templates/rspec/mailer/preview.rb.tt new file mode 100644 index 00000000..d8bfa892 --- /dev/null +++ b/lib/templates/rspec/mailer/preview.rb.tt @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +<% module_namespacing do -%> +# Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %> +class <%= class_name %>Preview < ActionMailer::Preview +<% actions.each do |action| -%> + + # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>/<%= action %> + def <%= action %> + <%= Rails.version.to_f >= 5.0 ? class_name.sub(/(Mailer)?$/, 'Mailer') : class_name %>.<%= action %> + end +<% end -%> + +end +<% end -%> diff --git a/lib/templates/rspec/model/fixtures.yml b/lib/templates/rspec/model/fixtures.yml new file mode 100644 index 00000000..4a8ab4b4 --- /dev/null +++ b/lib/templates/rspec/model/fixtures.yml @@ -0,0 +1,19 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +<% unless attributes.empty? -%> +one: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> + +two: +<% for attribute in attributes -%> + <%= attribute.name %>: <%= attribute.default %> +<% end -%> +<% else -%> +# one: +# column: value +# +# two: +# column: value +<% end -%> diff --git a/lib/templates/rspec/model/model_spec.rb.tt b/lib/templates/rspec/model/model_spec.rb.tt new file mode 100644 index 00000000..5a4acc49 --- /dev/null +++ b/lib/templates/rspec/model/model_spec.rb.tt @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% module_namespacing do -%> +RSpec.describe <%= class_name %>, <%= type_metatag(:model) %> do + pending "add some examples to (or delete) #{__FILE__}" +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/api_controller_spec.rb.tt b/lib/templates/rspec/scaffold/api_controller_spec.rb.tt new file mode 100644 index 00000000..62b5b0b3 --- /dev/null +++ b/lib/templates/rspec/scaffold/api_controller_spec.rb.tt @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to specify the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. +# +# Compared to earlier versions of this generator, there is very limited use of +# stubs and message expectations in this spec. Stubs are only used when there +# is no simpler way to get a handle on the object needed for the example. +# Message expectations are only used when there is no simpler way to specify +# that an instance is receiving a specific message. +# +# Also compared to earlier versions of this generator, there are no longer any +# expectations of assigns and templates rendered. These features have been +# removed from Rails core in Rails 5, but can be added back in via the +# `rails-controller-testing` gem. + +<% module_namespacing do -%> +RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:controller) %> do + + # This should return the minimal set of attributes required to create a valid + # <%= class_name %>. As you add validations to <%= class_name %>, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the session + # in order to pass any filters (e.g. authentication) defined in + # <%= controller_class_name %>Controller. Be sure to keep this updated too. + let(:valid_session) { {} } + +<% unless options[:singleton] -%> + describe "GET #index" do + it "returns a success response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + get :index, params: {}, session: valid_session + expect(response).to be_successful + end + end + +<% end -%> + describe "GET #show" do + it "returns a success response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + get :show, params: {id: <%= file_name %>.to_param}, session: valid_session + expect(response).to be_successful + end + end + + describe "POST #create" do + context "with valid params" do + it "creates a new <%= class_name %>" do + expect { + post :create, params: {<%= ns_file_name %>: valid_attributes}, session: valid_session + }.to change(<%= class_name %>, :count).by(1) + end + + it "renders a JSON response with the new <%= ns_file_name %>" do + post :create, params: {<%= ns_file_name %>: valid_attributes}, session: valid_session + expect(response).to have_http_status(:created) + expect(response.content_type).to eq('application/json') + expect(response.location).to eq(<%= ns_file_name %>_url(<%= class_name %>.last)) + end + end + + context "with invalid params" do + it "renders a JSON response with errors for the new <%= ns_file_name %>" do + post :create, params: {<%= ns_file_name %>: invalid_attributes}, session: valid_session + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to eq('application/json') + end + end + end + + describe "PUT #update" do + context "with valid params" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: new_attributes}, session: valid_session + <%= file_name %>.reload + skip("Add assertions for updated state") + end + + it "renders a JSON response with the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: valid_attributes}, session: valid_session + expect(response).to have_http_status(:ok) + expect(response.content_type).to eq('application/json') + end + end + + context "with invalid params" do + it "renders a JSON response with errors for the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: invalid_attributes}, session: valid_session + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to eq('application/json') + end + end + end + + describe "DELETE #destroy" do + it "destroys the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + expect { + delete :destroy, params: {id: <%= file_name %>.to_param}, session: valid_session + }.to change(<%= class_name %>, :count).by(-1) + end + end + +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/api_request_spec.rb.tt b/lib/templates/rspec/scaffold/api_request_spec.rb.tt new file mode 100644 index 00000000..0fec9c9b --- /dev/null +++ b/lib/templates/rspec/scaffold/api_request_spec.rb.tt @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +<% module_namespacing do -%> +RSpec.describe "/<%= name.underscore.pluralize %>", <%= type_metatag(:request) %> do + # This should return the minimal set of attributes required to create a valid + # <%= class_name %>. As you add validations to <%= class_name %>, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the headers + # in order to pass any filters (e.g. authentication) defined in + # <%= controller_class_name %>Controller, or in your router and rack + # middleware. Be sure to keep this updated too. + let(:valid_headers) { + {} + } + +<% unless options[:singleton] -%> + describe "GET /index" do + it "renders a successful response" do + <%= class_name %>.create! valid_attributes + get <%= index_helper %>_url, headers: valid_headers, as: :json + expect(response).to be_successful + end + end +<% end -%> + + describe "GET /show" do + it "renders a successful response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + get <%= show_helper.tr('@', '') %>, as: :json + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new <%= class_name %>" do + expect { + post <%= index_helper %>_url, + params: { <%= ns_file_name %>: valid_attributes }, headers: valid_headers, as: :json + }.to change(<%= class_name %>, :count).by(1) + end + + it "renders a JSON response with the new <%= ns_file_name %>" do + post <%= index_helper %>_url, + params: { <%= ns_file_name %>: valid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:created) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + + context "with invalid parameters" do + it "does not create a new <%= class_name %>" do + expect { + post <%= index_helper %>_url, + params: { <%= ns_file_name %>: invalid_attributes }, as: :json + }.to change(<%= class_name %>, :count).by(0) + end + + it "renders a JSON response with errors for the new <%= ns_file_name %>" do + post <%= index_helper %>_url, + params: { <%= ns_file_name %>: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to eq("application/json") + end + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, + params: { <%= singular_table_name %>: invalid_attributes }, headers: valid_headers, as: :json + <%= file_name %>.reload + skip("Add assertions for updated state") + end + + it "renders a JSON response with the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, + params: { <%= singular_table_name %>: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:ok) + expect(response.content_type).to eq("application/json") + end + end + + context "with invalid parameters" do + it "renders a JSON response with errors for the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, + params: { <%= singular_table_name %>: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to eq("application/json") + end + end + end + + describe "DELETE /destroy" do + it "destroys the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + expect { + delete <%= show_helper.tr('@', '') %>, headers: valid_headers, as: :json + }.to change(<%= class_name %>, :count).by(-1) + end + end +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/controller_spec.rb.tt b/lib/templates/rspec/scaffold/controller_spec.rb.tt new file mode 100644 index 00000000..f6d16a9b --- /dev/null +++ b/lib/templates/rspec/scaffold/controller_spec.rb.tt @@ -0,0 +1,196 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to specify the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. +# +# Compared to earlier versions of this generator, there is very limited use of +# stubs and message expectations in this spec. Stubs are only used when there +# is no simpler way to get a handle on the object needed for the example. +# Message expectations are only used when there is no simpler way to specify +# that an instance is receiving a specific message. +# +# Also compared to earlier versions of this generator, there are no longer any +# expectations of assigns and templates rendered. These features have been +# removed from Rails core in Rails 5, but can be added back in via the +# `rails-controller-testing` gem. + +<% module_namespacing do -%> +RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:controller) %> do + + # This should return the minimal set of attributes required to create a valid + # <%= class_name %>. As you add validations to <%= class_name %>, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the session + # in order to pass any filters (e.g. authentication) defined in + # <%= controller_class_name %>Controller. Be sure to keep this updated too. + let(:valid_session) { {} } + +<% unless options[:singleton] -%> + describe "GET #index" do + it "returns a success response" do + <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + get :index, {}, valid_session +<% else -%> + get :index, params: {}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + +<% end -%> + describe "GET #show" do + it "returns a success response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + get :show, {id: <%= file_name %>.to_param}, valid_session +<% else -%> + get :show, params: {id: <%= file_name %>.to_param}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + + describe "GET #new" do + it "returns a success response" do +<% if Rails::VERSION::STRING < '5.0' -%> + get :new, {}, valid_session +<% else -%> + get :new, params: {}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + + describe "GET #edit" do + it "returns a success response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + get :edit, {id: <%= file_name %>.to_param}, valid_session +<% else -%> + get :edit, params: {id: <%= file_name %>.to_param}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + + describe "POST #create" do + context "with valid params" do + it "creates a new <%= class_name %>" do + expect { +<% if Rails::VERSION::STRING < '5.0' -%> + post :create, {<%= ns_file_name %>: valid_attributes}, valid_session +<% else -%> + post :create, params: {<%= ns_file_name %>: valid_attributes}, session: valid_session +<% end -%> + }.to change(<%= class_name %>, :count).by(1) + end + + it "redirects to the created <%= ns_file_name %>" do +<% if Rails::VERSION::STRING < '5.0' -%> + post :create, {<%= ns_file_name %>: valid_attributes}, valid_session +<% else -%> + post :create, params: {<%= ns_file_name %>: valid_attributes}, session: valid_session +<% end -%> + expect(response).to redirect_to(<%= class_name %>.last) + end + end + + context "with invalid params" do + it "returns a success response (i.e. to display the 'new' template)" do +<% if Rails::VERSION::STRING < '5.0' -%> + post :create, {<%= ns_file_name %>: invalid_attributes}, valid_session +<% else -%> + post :create, params: {<%= ns_file_name %>: invalid_attributes}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + end + + describe "PUT #update" do + context "with valid params" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + put :update, {id: <%= file_name %>.to_param, <%= ns_file_name %>: new_attributes}, valid_session +<% else -%> + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: new_attributes}, session: valid_session +<% end -%> + <%= file_name %>.reload + skip("Add assertions for updated state") + end + + it "redirects to the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + put :update, {id: <%= file_name %>.to_param, <%= ns_file_name %>: valid_attributes}, valid_session +<% else -%> + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: valid_attributes}, session: valid_session +<% end -%> + expect(response).to redirect_to(<%= file_name %>) + end + end + + context "with invalid params" do + it "returns a success response (i.e. to display the 'edit' template)" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + put :update, {id: <%= file_name %>.to_param, <%= ns_file_name %>: invalid_attributes}, valid_session +<% else -%> + put :update, params: {id: <%= file_name %>.to_param, <%= ns_file_name %>: invalid_attributes}, session: valid_session +<% end -%> + expect(response).to be_successful + end + end + end + + describe "DELETE #destroy" do + it "destroys the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + expect { +<% if Rails::VERSION::STRING < '5.0' -%> + delete :destroy, {id: <%= file_name %>.to_param}, valid_session +<% else -%> + delete :destroy, params: {id: <%= file_name %>.to_param}, session: valid_session +<% end -%> + }.to change(<%= class_name %>, :count).by(-1) + end + + it "redirects to the <%= table_name %> list" do + <%= file_name %> = <%= class_name %>.create! valid_attributes +<% if Rails::VERSION::STRING < '5.0' -%> + delete :destroy, {id: <%= file_name %>.to_param}, valid_session +<% else -%> + delete :destroy, params: {id: <%= file_name %>.to_param}, session: valid_session +<% end -%> + expect(response).to redirect_to(<%= index_helper %>_url) + end + end + +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/edit_spec.rb.tt b/lib/templates/rspec/scaffold/edit_spec.rb.tt new file mode 100644 index 00000000..a7ea64ed --- /dev/null +++ b/lib/templates/rspec/scaffold/edit_spec.rb.tt @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> +RSpec.describe "<%= ns_table_name %>/edit", <%= type_metatag(:view) %> do + before(:each) do + @<%= ns_file_name %> = assign(:<%= ns_file_name %>, <%= class_name %>.create!(<%= '))' if output_attributes.empty? %> +<% output_attributes.each_with_index do |attribute, attribute_index| -%> + <%= attribute.name %>: <%= attribute.default.inspect %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> +<% end -%> +<%= output_attributes.empty? ? "" : " ))\n" -%> + end + + it "renders the edit <%= ns_file_name %> form" do + render + + assert_select "form[action=?][method=?]", <%= ns_file_name %>_path(@<%= ns_file_name %>), "post" do +<% for attribute in output_attributes -%> + <%- name = attribute.respond_to?(:column_name) ? attribute.column_name : attribute.name %> +<% if Rails.version.to_f >= 5.1 -%> + assert_select "<%= attribute.input_type -%>[name=?]", "<%= ns_file_name %>[<%= name %>]" +<% else -%> + assert_select "<%= attribute.input_type -%>#<%= ns_file_name %>_<%= name %>[name=?]", "<%= ns_file_name %>[<%= name %>]" +<% end -%> +<% end -%> + end + end +end diff --git a/lib/templates/rspec/scaffold/index_spec.rb.tt b/lib/templates/rspec/scaffold/index_spec.rb.tt new file mode 100644 index 00000000..cb703f1b --- /dev/null +++ b/lib/templates/rspec/scaffold/index_spec.rb.tt @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> +RSpec.describe "<%= ns_table_name %>/index", <%= type_metatag(:view) %> do + before(:each) do + assign(:<%= table_name %>, [ +<% [1,2].each_with_index do |id, model_index| -%> + <%= class_name %>.create!(<%= output_attributes.empty? ? (model_index == 1 ? ')' : '),') : '' %> +<% output_attributes.each_with_index do |attribute, attribute_index| -%> + <%= attribute.name %>: <%= value_for(attribute) %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> +<% end -%> +<% if !output_attributes.empty? -%> + <%= model_index == 1 ? ')' : '),' %> +<% end -%> +<% end -%> + ]) + end + + it "renders a list of <%= ns_table_name %>" do + render +<% for attribute in output_attributes -%> + assert_select "tr>td", text: <%= value_for(attribute) %>.to_s, count: 2 +<% end -%> + end +end diff --git a/lib/templates/rspec/scaffold/new_spec.rb.tt b/lib/templates/rspec/scaffold/new_spec.rb.tt new file mode 100644 index 00000000..befa8d4d --- /dev/null +++ b/lib/templates/rspec/scaffold/new_spec.rb.tt @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> +RSpec.describe "<%= ns_table_name %>/new", <%= type_metatag(:view) %> do + before(:each) do + assign(:<%= ns_file_name %>, <%= class_name %>.new(<%= '))' if output_attributes.empty? %> +<% output_attributes.each_with_index do |attribute, attribute_index| -%> + <%= attribute.name %>: <%= attribute.default.inspect %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> +<% end -%> +<%= !output_attributes.empty? ? " ))\n end" : " end" %> + + it "renders new <%= ns_file_name %> form" do + render + + assert_select "form[action=?][method=?]", <%= index_helper %>_path, "post" do +<% for attribute in output_attributes -%> + <%- name = attribute.respond_to?(:column_name) ? attribute.column_name : attribute.name %> +<% if Rails.version.to_f >= 5.1 -%> + assert_select "<%= attribute.input_type -%>[name=?]", "<%= ns_file_name %>[<%= name %>]" +<% else -%> + assert_select "<%= attribute.input_type -%>#<%= ns_file_name %>_<%= name %>[name=?]", "<%= ns_file_name %>[<%= name %>]" +<% end -%> +<% end -%> + end + end +end diff --git a/lib/templates/rspec/scaffold/request_spec.rb.tt b/lib/templates/rspec/scaffold/request_spec.rb.tt new file mode 100644 index 00000000..b575879f --- /dev/null +++ b/lib/templates/rspec/scaffold/request_spec.rb.tt @@ -0,0 +1,136 @@ + # frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later + require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +<% module_namespacing do -%> +RSpec.describe "/<%= name.underscore.pluralize %>", <%= type_metatag(:request) %> do + # <%= class_name %>. As you add validations to <%= class_name %>, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + +<% unless options[:singleton] -%> + describe "GET /index" do + it "renders a successful response" do + <%= class_name %>.create! valid_attributes + get <%= index_helper %>_url + expect(response).to be_successful + end + end +<% end -%> + + describe "GET /show" do + it "renders a successful response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + get <%= show_helper.tr('@', '') %> + expect(response).to be_successful + end + end + + describe "GET /new" do + it "renders a successful response" do + get <%= new_helper %> + expect(response).to be_successful + end + end + + describe "GET /edit" do + it "render a successful response" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + get <%= edit_helper.tr('@','') %> + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new <%= class_name %>" do + expect { + post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes } + }.to change(<%= class_name %>, :count).by(1) + end + + it "redirects to the created <%= ns_file_name %>" do + post <%= index_helper %>_url, params: { <%= ns_file_name %>: valid_attributes } + expect(response).to redirect_to(<%= show_helper.gsub("\@#{file_name}", class_name+".last") %>) + end + end + + context "with invalid parameters" do + it "does not create a new <%= class_name %>" do + expect { + post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes } + }.to change(<%= class_name %>, :count).by(0) + end + + it "renders a successful response (i.e. to display the 'new' template)" do + post <%= index_helper %>_url, params: { <%= ns_file_name %>: invalid_attributes } + expect(response).to be_successful + end + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes } + <%= file_name %>.reload + skip("Add assertions for updated state") + end + + it "redirects to the <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: new_attributes } + <%= file_name %>.reload + expect(response).to redirect_to(<%= singular_table_name %>_url(<%= file_name %>)) + end + end + + context "with invalid parameters" do + it "renders a successful response (i.e. to display the 'edit' template)" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + patch <%= show_helper.tr('@', '') %>, params: { <%= singular_table_name %>: invalid_attributes } + expect(response).to be_successful + end + end + end + + describe "DELETE /destroy" do + it "destroys the requested <%= ns_file_name %>" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + expect { + delete <%= show_helper.tr('@', '') %> + }.to change(<%= class_name %>, :count).by(-1) + end + + it "redirects to the <%= table_name %> list" do + <%= file_name %> = <%= class_name %>.create! valid_attributes + delete <%= show_helper.tr('@', '') %> + expect(response).to redirect_to(<%= index_helper %>_url) + end + end +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/routing_spec.rb.tt b/lib/templates/rspec/scaffold/routing_spec.rb.tt new file mode 100644 index 00000000..7b35a6a8 --- /dev/null +++ b/lib/templates/rspec/scaffold/routing_spec.rb.tt @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require "rails_helper" + +<% module_namespacing do -%> +RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:routing) %> do + describe "routing" do +<% unless options[:singleton] -%> + it "routes to #index" do + expect(get: "/<%= ns_table_name %>").to route_to("<%= ns_table_name %>#index") + end + +<% end -%> +<% unless options[:api] -%> + it "routes to #new" do + expect(get: "/<%= ns_table_name %>/new").to route_to("<%= ns_table_name %>#new") + end + +<% end -%> + it "routes to #show" do + expect(get: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#show", id: "1") + end + +<% unless options[:api] -%> + it "routes to #edit" do + expect(get: "/<%= ns_table_name %>/1/edit").to route_to("<%= ns_table_name %>#edit", id: "1") + end + +<% end -%> + + it "routes to #create" do + expect(post: "/<%= ns_table_name %>").to route_to("<%= ns_table_name %>#create") + end + + it "routes to #update via PUT" do + expect(put: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#update", id: "1") + end + + it "routes to #update via PATCH" do + expect(patch: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#update", id: "1") + end + + it "routes to #destroy" do + expect(delete: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#destroy", id: "1") + end + end +end +<% end -%> diff --git a/lib/templates/rspec/scaffold/show_spec.rb.tt b/lib/templates/rspec/scaffold/show_spec.rb.tt new file mode 100644 index 00000000..c6f13948 --- /dev/null +++ b/lib/templates/rspec/scaffold/show_spec.rb.tt @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +<% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> +RSpec.describe "<%= ns_table_name %>/show", <%= type_metatag(:view) %> do + before(:each) do + @<%= ns_file_name %> = assign(:<%= ns_file_name %>, <%= class_name %>.create!(<%= '))' if output_attributes.empty? %> +<% output_attributes.each_with_index do |attribute, attribute_index| -%> + <%= attribute.name %>: <%= value_for(attribute) %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> +<% end -%> +<% if !output_attributes.empty? -%> + )) +<% end -%> + end + + it "renders attributes in

" do + render +<% for attribute in output_attributes -%> + expect(rendered).to match(/<%= raw_value_for(attribute) %>/) +<% end -%> + end +end diff --git a/lib/templates/rspec/system/system_spec.rb.tt b/lib/templates/rspec/system/system_spec.rb.tt new file mode 100644 index 00000000..3e606d7e --- /dev/null +++ b/lib/templates/rspec/system/system_spec.rb.tt @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:system) %> do + before do + driven_by(:rack_test) + end + + pending "add some scenarios (or delete) #{__FILE__}" +end diff --git a/lib/templates/rspec/view/view_spec.rb.tt b/lib/templates/rspec/view/view_spec.rb.tt new file mode 100644 index 00000000..580f5e8d --- /dev/null +++ b/lib/templates/rspec/view/view_spec.rb.tt @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later +require 'rails_helper' + +RSpec.describe "<%= file_path %>/<%= @action %>", <%= type_metatag(:view) %> do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/lib/timespan.rb b/lib/timespan.rb index 305d030e..edb49f8c 100644 --- a/lib/timespan.rb +++ b/lib/timespan.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # For tracking and calculating timespans/time intervals # Relies on activesupport Timespan = Struct.new(:interval, :time_unit) do - - Units = ['week', 'day', 'month', 'year'] - TimeUnits = { + self::Units = %w[week day month year].freeze + self::TimeUnits = { '1_week' => 1.week.ago, '2_weeks' => 2.weeks.ago, '1_month' => 1.month.ago, @@ -13,35 +14,35 @@ Timespan = Struct.new(:interval, :time_unit) do '6_months' => 6.months.ago, '1_year' => 1.year.ago, '2_years' => 2.years.ago - } + }.freeze - # Test if end_date is past start_date by timespan - # eg: later_than_by?(Jun 13th, Jul 14th, 1.month) -> true - # Special case: - # later_than_by?(Jan 31st, Feb 28th, 1.month) -> true - def self.later_than_by?(start_date, end_date, timespan) - return (start_date + timespan) <= end_date - end + # Test if end_date is past start_date by timespan + # eg: later_than_by?(Jun 13th, Jul 14th, 1.month) -> true + # Special case: + # later_than_by?(Jan 31st, Feb 28th, 1.month) -> true + def self.later_than_by?(start_date, end_date, timespan) + (start_date + timespan) <= end_date + end - # Given an Integer (frequency) and a String (time unit), - # return the timespan object (ie. number of seconds) constituting the timespan - # timespan(1, 'minute') -> 60 - # timespan(1, 'month') -> 2592000 - def self.create(interval, time_unit) - raise(ArgumentError, "time_unit must be one of: #{Units}") unless Units.include?(time_unit) - return interval.send(time_unit.to_sym) - end + # Given an Integer (frequency) and a String (time unit), + # return the timespan object (ie. number of seconds) constituting the timespan + # timespan(1, 'minute') -> 60 + # timespan(1, 'month') -> 2592000 + def self.create(interval, time_unit) + raise(ArgumentError, "time_unit must be one of: #{self::Units}") unless self::Units.include?(time_unit) - def self.in_future?(datetime) - datetime > Time.current - end + interval.send(time_unit.to_sym) + end - def self.date_now_or_in_future?(date) - date >= Date.today - end + def self.in_future?(datetime) + datetime > Time.current + end - def self.in_past?(date) - date < Time.current - end + def self.date_now_or_in_future?(date) + date >= Date.today + end + def self.in_past?(date) + date < Time.current + end end diff --git a/lib/update/update_activities.rb b/lib/update/update_activities.rb index 89a96eae..1652d4cf 100644 --- a/lib/update/update_activities.rb +++ b/lib/update/update_activities.rb @@ -1,22 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module UpdateActivities - def self.for_supporter_notes(note) - user_email = Qx.select('email') - .from(:users) - .where(id: note[:user_id]) - .execute - .first['email'] + .from(:users) + .where(id: note[:user_id]) + .execute + .first['email'] Qx.update(:activities) - .set(json_data: {content: note[:content], user_email: user_email}.to_json) + .set(json_data: { content: note[:content], user_email: user_email }.to_json) .timestamps .where(attachment_id: note[:id]) .execute - - end + end end - diff --git a/lib/update/update_billing_subscriptions.rb b/lib/update/update_billing_subscriptions.rb deleted file mode 100644 index 5e15ae16..00000000 --- a/lib/update/update_billing_subscriptions.rb +++ /dev/null @@ -1,12 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later - -module UpdateBillingSubscriptions - - def self.activate_from_trial(np_id) - Qx.update(:billing_subscriptions) - .set(status: 'active') - .timestamps - .where('nonprofit_id=$id', id: np_id) - .execute - end -end diff --git a/lib/update/update_campaign_gift_option.rb b/lib/update/update_campaign_gift_option.rb index ba52f088..73ce1ac4 100644 --- a/lib/update/update_campaign_gift_option.rb +++ b/lib/update/update_campaign_gift_option.rb @@ -1,10 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateCampaignGiftOption - - def self.update gift_option, params - gift_option.update_attributes params - return gift_option - end - + def self.update(gift_option, params) + gift_option.update params + gift_option + end end - diff --git a/lib/update/update_charges.rb b/lib/update/update_charges.rb index 0e3c957b..14450b4f 100644 --- a/lib/update/update_charges.rb +++ b/lib/update/update_charges.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateCharges + def self.disburse_all_with_payments(payment_ids) + Psql.execute(Qexpr.new.update(:charges, status: 'disbursed').where('payment_id IN ($ids)', ids: payment_ids).returning('id', 'status')) + end - def self.disburse_all_with_payments(payment_ids) - Psql.execute(Qexpr.new.update(:charges, status: 'disbursed').where("payment_id IN ($ids)", ids: payment_ids).returning('id', 'status')) - end - - def self.reverse_disburse_all_with_payments(payment_ids) - Charge.where("payment_id IN (?)", payment_ids).update_all(status: 'available') - end + def self.reverse_disburse_all_with_payments(payment_ids) + Charge.where('payment_id IN (?)', payment_ids).update_all(status: 'available') + end end diff --git a/lib/update/update_custom_field_joins.rb b/lib/update/update_custom_field_joins.rb index ef59f0b6..7b755237 100644 --- a/lib/update/update_custom_field_joins.rb +++ b/lib/update/update_custom_field_joins.rb @@ -1,23 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module UpdateCustomFieldJoins - # Delete custom field joins that have the same custom field master # Favor the most recent custom field join def self.delete_dupes(supporter_ids) # Bulk remove duplicate custom field joins, favoring the most recent one ids = Qx.select('ARRAY_AGG(custom_field_joins.id ORDER BY custom_field_joins.created_at DESC) AS ids') - .from(:custom_field_joins) - .where("custom_field_joins.supporter_id IN ($ids)", ids: supporter_ids) - .join("custom_field_masters cfms", "cfms.id = custom_field_joins.custom_field_master_id") - .group_by("cfms.name") - .having("COUNT(custom_field_joins) > 1") - .execute.map{|h| h['ids'][1..-1]}.flatten + .from(:custom_field_joins) + .where('custom_field_joins.supporter_id IN ($ids)', ids: supporter_ids) + .join('custom_field_masters cfms', 'cfms.id = custom_field_joins.custom_field_master_id') + .group_by('cfms.name') + .having('COUNT(custom_field_joins) > 1') + .execute.map { |h| h['ids'][1..-1] }.flatten return unless ids.any? + Qx.delete_from(:custom_field_joins) - .where("id IN ($ids)", ids: ids) + .where('id IN ($ids)', ids: ids) .execute end - end diff --git a/lib/update/update_disputes.rb b/lib/update/update_disputes.rb index 8f73f79c..b33360d3 100644 --- a/lib/update/update_disputes.rb +++ b/lib/update/update_disputes.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateDisputes - def self.disburse_all_with_payments(payment_ids) Psql.execute( - Qexpr.new.update(:disputes, {status: 'lost_and_paid'}).where("payment_id IN ($ids)", ids: payment_ids) + Qexpr.new.update(:disputes, status: 'lost_and_paid').where('payment_id IN ($ids)', ids: payment_ids) ) end end diff --git a/lib/update/update_donation.rb b/lib/update/update_donation.rb index ff680b82..15cb5150 100644 --- a/lib/update/update_donation.rb +++ b/lib/update/update_donation.rb @@ -1,77 +1,75 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateDonation - def self.from_followup(donation, params) donation.designation = params[:designation] if params[:designation].present? donation.dedication = params[:dedication] if params[:dedication].present? donation.comment = params[:comment] if params[:comment].present? donation.save - return donation + donation end # @param [Integer] donation_id the donation for the payment you wish to modify def self.update_payment(donation_id, data) - ParamValidation.new({id:donation_id, data: data}, - { - id: {required: true, is_reference: true}, - data: {required: true, is_hash: true} - }) + ParamValidation.new({ id: donation_id, data: data }, + id: { required: true, is_reference: true }, + data: { required: true, is_hash: true }) existing_payment = Payment.where('donation_id = ?', donation_id).last unless existing_payment raise ParamValidation::ValidationError.new("#{donation_id} is does not correspond to a valid donation", - {key: :id}) + key: :id) end is_offsite = !existing_payment.offsite_payment.nil? validations = { - designation: {is_a: String}, - dedication: {is_a: String}, - comment: {is_a: String}, - campaign_id: {is_reference: true, required:true}, - event_id: {is_reference: true, required: true} + designation: { is_a: String }, + dedication: { is_a: String }, + comment: { is_a: String }, + campaign_id: { is_reference: true, required: true }, + event_id: { is_reference: true, required: true } } if is_offsite # if offline test the other values (fee_total, gross_amount, check_number, date) # - validations.merge!({gross_amount: {is_integer: true, min: 1}, - fee_total: {is_integer: true}, - check_number: {is_a: String}, - date: {can_be_date: true}}) + validations.merge!(gross_amount: { is_integer: true, min: 1 }, + fee_total: { is_integer: true }, + check_number: { is_a: String }, + date: { can_be_date: true }) end ParamValidation.new(data, validations) - set_to_nil = {campaign: data[:campaign_id] == '', event: data[:event_id] == ''} + set_to_nil = { campaign: data[:campaign_id] == '', event: data[:event_id] == '' } # validate campaign and event ids if there and if they belong to nonprofit - if (set_to_nil[:campaign]) + if set_to_nil[:campaign] campaign = nil else campaign = Campaign.where('id = ?', data[:campaign_id]).first unless campaign - raise ParamValidation::ValidationError.new("#{data[:campaign_id]} is not a valid campaign", {key: :campaign_id}) + raise ParamValidation::ValidationError.new("#{data[:campaign_id]} is not a valid campaign", key: :campaign_id) end unless campaign.nonprofit == existing_payment.nonprofit - raise ParamValidation::ValidationError.new("#{data[:campaign_id]} campaign does not belong to this nonprofit for payment #{existing_payment.id}", {key: :campaign_id}) + raise ParamValidation::ValidationError.new("#{data[:campaign_id]} campaign does not belong to this nonprofit for payment #{existing_payment.id}", key: :campaign_id) end end - if (set_to_nil[:event]) + if set_to_nil[:event] event = nil else event = Event.where('id = ?', data[:event_id]).first unless event - raise ParamValidation::ValidationError.new("#{data[:event_id]} is not a valid event", {key: :event_id}) + raise ParamValidation::ValidationError.new("#{data[:event_id]} is not a valid event", key: :event_id) end unless event.nonprofit == existing_payment.nonprofit - raise ParamValidation::ValidationError.new("#{data[:event_id]} event does not belong to this nonprofit for payment #{existing_payment.id}", {key: :event_id}) + raise ParamValidation::ValidationError.new("#{data[:event_id]} event does not belong to this nonprofit for payment #{existing_payment.id}", key: :event_id) end end Qx.transaction do - donation = existing_payment.donation donation.designation = data[:designation] if data[:designation] @@ -87,47 +85,38 @@ module UpdateDonation donation.date = data[:date] if data[:date] end - # edits_to_payments if is_offsite - #if offline, set date, gross_amount, fee_total, net_amount + # if offline, set date, gross_amount, fee_total, net_amount existing_payment.towards = data[:designation] if data[:designation] existing_payment.date = data[:date] if data[:date] existing_payment.gross_amount = data[:gross_amount] if data[:gross_amount] existing_payment.fee_total = data[:fee_total] if data[:fee_total] existing_payment.net_amount = existing_payment.gross_amount - existing_payment.fee_total - if existing_payment.changed? - existing_payment.save! - end + existing_payment.save! if existing_payment.changed? else if donation.designation - Payment.where('donation_id = ?', donation.id).update_all(:towards => donation.designation, updated_at: Time.now) + Payment.where('donation_id = ?', donation.id).update_all(towards: donation.designation, updated_at: Time.now) end end - #if offsite, set check_number, date, gross_amount + # if offsite, set check_number, date, gross_amount if is_offsite offsite_payment = existing_payment.offsite_payment offsite_payment.check_number = data[:check_number] if data[:check_number] offsite_payment.date = data[:date] if data[:date] offsite_payment.gross_amount = data[:gross_amount] if data[:gross_amount] - if offsite_payment.changed? - offsite_payment.save! - end - end - if donation.changed? - donation.save! + offsite_payment.save! if offsite_payment.changed? end + donation.save! if donation.changed? existing_payment.reload ret = donation.attributes ret[:payment] = existing_payment.attributes - if is_offsite - ret[:offsite_payment] = offsite_payment.attributes - end + ret[:offsite_payment] = offsite_payment.attributes if is_offsite return ret end end @@ -140,16 +129,13 @@ module UpdateDonation donation.date = donation.created_at donation.save! - - payments = Payment.where('donation_id = ?', id).includes(:charge) - - payments.each {|p| + payments.each do |p| @payments_corrected.push(p.id) p.date = p.charge.created_at p.save! - } + end donation.save! @@ -159,14 +145,19 @@ module UpdateDonation def self.any_donations_with_created_at_after_date donation_ids = Set.new - CSV.foreach('bad_payments_2.csv').select {|row| true if Integer(row[0]) rescue false}.collect{|row| - is_int = true if Integer(row[0]) rescue false - if (is_int && Float(row[6]) > 0) - donation_ids.add(row[0]) + CSV.foreach('bad_payments_2.csv').select do |row| + true if Integer(row[0]) + rescue StandardError + false + end .collect do |row| + begin + is_int = true if Integer(row[0]) + rescue StandardError + false end - } + donation_ids.add(row[0]) if is_int && Float(row[6]) > 0 + end - return donation_ids + donation_ids end end - diff --git a/lib/update/update_email_lists.rb b/lib/update/update_email_lists.rb index 3f26035a..54dc3ddc 100644 --- a/lib/update/update_email_lists.rb +++ b/lib/update/update_email_lists.rb @@ -1,22 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module UpdateEmailLists - def self.populate_lists_on_mailchimp(npo_id) - lists = Qx.select("tag_master_id", "list_name", "mailchimp_list_id") - .from(:email_lists) - .where(nonprofit_id: npo_id) - .execute - post_data = Qx.select("supporters.email", "email_lists.mailchimp_list_id") - .from("email_lists") - .add_join("tag_masters", "tag_masters.id=email_lists.tag_master_id") - .add_join("tag_joins", "tag_joins.tag_master_id=tag_masters.id") - .add_join("supporters", "supporters.id=tag_joins.supporter_id") - .where("email_lists.nonprofit_id=$id", id: npo_id) - .execute - .map{|h| {method: 'POST', path: "lists/#{h['mailchimp_list_id']}/members", body: {email_address: h['email'], status: 'subscribed'}.to_json}} + lists = Qx.select('tag_master_id', 'list_name', 'mailchimp_list_id') + .from(:email_lists) + .where(nonprofit_id: npo_id) + .execute + post_data = Qx.select('supporters.email', 'email_lists.mailchimp_list_id') + .from('email_lists') + .add_join('tag_masters', 'tag_masters.id=email_lists.tag_master_id') + .add_join('tag_joins', 'tag_joins.tag_master_id=tag_masters.id') + .add_join('supporters', 'supporters.id=tag_joins.supporter_id') + .where('email_lists.nonprofit_id=$id', id: npo_id) + .execute + .map { |h| { method: 'POST', path: "lists/#{h['mailchimp_list_id']}/members", body: { email_address: h['email'], status: 'subscribed' }.to_json } } Mailchimp.perform_batch_operations(npo_id, post_data) end - end diff --git a/lib/update/update_email_settings.rb b/lib/update/update_email_settings.rb index 463a5cca..89956681 100644 --- a/lib/update/update_email_settings.rb +++ b/lib/update/update_email_settings.rb @@ -1,18 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateEmailSettings - def self.save(np_id, user_id, params) es = Psql.execute( Qexpr.new.select(:id).from(:email_settings) - .where("nonprofit_id=$id", id: np_id.to_i) - .where("user_id=$id", id: user_id) + .where('nonprofit_id=$id', id: np_id.to_i) + .where('user_id=$id', id: user_id) ).first if es.nil? - es = Psql.execute(Qexpr.new.insert('email_settings', [{nonprofit_id: np_id, user_id: user_id}], {no_timestamps: true})).first + es = Psql.execute(Qexpr.new.insert('email_settings', [{ nonprofit_id: np_id, user_id: user_id }], no_timestamps: true)).first end Psql.execute( Qexpr.new.update(:email_settings, params) - .where("id=$id", id: es['id']) + .where('id=$id', id: es['id']) .returning('*') ).first end diff --git a/lib/update/update_miscellaneous_np_info.rb b/lib/update/update_miscellaneous_np_info.rb index 1dae749d..2e0b37a6 100644 --- a/lib/update/update_miscellaneous_np_info.rb +++ b/lib/update/update_miscellaneous_np_info.rb @@ -1,23 +1,26 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateMiscellaneousNpInfo def self.update(np_id, misc_settings) - ParamValidation.new({np_id: np_id, misc_settings:misc_settings}, - np_id: {:required => true, :is_integer => true}, - misc_settings: {:required => true, :is_hash => true}) + ParamValidation.new({ np_id: np_id, misc_settings: misc_settings }, + np_id: { required: true, is_integer: true }, + misc_settings: { required: true, is_hash: true }) np = Nonprofit.where('id = ?', np_id).first - raise ParamValidation::ValidationError.new("Nonprofit #{np_id} does not exist", {key: :np_id}) unless np + raise ParamValidation::ValidationError.new("Nonprofit #{np_id} does not exist", key: :np_id) unless np + misc = MiscellaneousNpInfo.where('nonprofit_id = ?', np_id).first unless misc misc = MiscellaneousNpInfo.new misc.nonprofit = np end - if (misc_settings[:donate_again_url].present?) + if misc_settings[:donate_again_url].present? misc.donate_again_url = misc_settings[:donate_again_url] end - if (misc_settings[:change_amount_message].present?) - if (Format::HTML.has_only_empty_tags(misc_settings[:change_amount_message])) - misc.change_amount_message= nil; + if misc_settings[:change_amount_message].present? + if Format::HTML.has_only_empty_tags(misc_settings[:change_amount_message]) + misc.change_amount_message = nil else misc.change_amount_message = misc_settings[:change_amount_message] end @@ -26,4 +29,4 @@ module UpdateMiscellaneousNpInfo misc.save! misc end -end \ No newline at end of file +end diff --git a/lib/update/update_nonprofit.rb b/lib/update/update_nonprofit.rb index 78861949..4d890628 100644 --- a/lib/update/update_nonprofit.rb +++ b/lib/update/update_nonprofit.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'query/query_payments' require 'fetch/stripe/fetch_stripe_account' module UpdateNonprofit - # See the stripe docs for reference: => https://stripe.com/docs/connect/identity-verification - def self.verify_identity(np_id, legal_entity, tos=nil) - np = Qx.select("*").from(:nonprofits).where(id: np_id).execute.first + def self.verify_identity(np_id, legal_entity, tos = nil) + np = Qx.select('*').from(:nonprofits).where(id: np_id).execute.first legal_entity[:address][:country] = 'US' if legal_entity[:address] acct = FetchStripeAccount.with_account_id(np['stripe_account_id']) acct.legal_entity.phone_number = acct.support_phone = legal_entity[:phone_number] if legal_entity[:phone_number] @@ -18,48 +19,45 @@ module UpdateNonprofit acct.legal_entity.personal_id_number = legal_entity[:personal_id_number] if legal_entity[:personal_id_number] acct.legal_entity.type = 'company' acct.legal_entity.business_name = np['name'] - if tos - acct.tos_acceptance = tos - end + acct.tos_acceptance = tos if tos acct.save # Might as well update the nonprofit info if legal_entity[:address] && legal_entity[:business_tax_id] - np = Qx.update(:nonprofits).set({ - address: legal_entity[:address][:line1], - city: legal_entity[:address][:city], - state_code: legal_entity[:address][:state], - zip_code: legal_entity[:address][:postal_code], - ein: legal_entity[:business_tax_id], - verification_status: 'pending', - phone: legal_entity[:phone_number] - }) - .where(id: np_id) - .returning('*') - .execute.first + np = Qx.update(:nonprofits).set( + address: legal_entity[:address][:line1], + city: legal_entity[:address][:city], + state_code: legal_entity[:address][:state], + zip_code: legal_entity[:address][:postal_code], + ein: legal_entity[:business_tax_id], + verification_status: 'pending', + phone: legal_entity[:phone_number] + ) + .where(id: np_id) + .returning('*') + .execute.first else np = Qx.update(:nonprofits).set(verification_status: 'pending').where(id: np_id).returning('*').first end - return np + np end - # Update charges from pending to available if the nonprofit's balance on stripe can accommodate them # First, get net balance on Stripe, then get net balance on CC # Take the difference of those two, and mark as many oldest pending charges as 'available' as are less than or equal to that difference def self.mark_available_charges(npo_id) - stripe_account_id = Qx.select("stripe_account_id").from(:nonprofits).where(id: npo_id).ex.first['stripe_account_id'] + stripe_account_id = Qx.select('stripe_account_id').from(:nonprofits).where(id: npo_id).ex.first['stripe_account_id'] stripe_net_balance = Stripe::Balance.retrieve(stripe_account: stripe_account_id).available.first.amount cc_net_balance = QueryPayments.get_payout_totals(QueryPayments.ids_for_payout(npo_id))['net_amount'] - pending_payments = Qx.select("payments.net_amount", "charges.id AS charge_id") - .from(:payments) - .where("charges.status='pending'") - .and_where("payments.nonprofit_id=$id", id: npo_id) - .join("charges", "charges.payment_id=payments.id") - .order_by("payments.date ASC") - .execute + pending_payments = Qx.select('payments.net_amount', 'charges.id AS charge_id') + .from(:payments) + .where("charges.status='pending'") + .and_where('payments.nonprofit_id=$id', id: npo_id) + .join('charges', 'charges.payment_id=payments.id') + .order_by('payments.date ASC') + .execute return if pending_payments.empty? @@ -69,10 +67,8 @@ module UpdateNonprofit remaining_balance -= payment['net_amount'] true end - end.map{|h| h['charge_id']} + end.map { |h| h['charge_id'] } - Qx.update(:charges).set(status: 'available').where("id IN ($ids)", ids: charge_ids).execute if charge_ids.any? + Qx.update(:charges).set(status: 'available').where('id IN ($ids)', ids: charge_ids).execute if charge_ids.any? end - end - diff --git a/lib/update/update_order.rb b/lib/update/update_order.rb index 23738859..4936da86 100644 --- a/lib/update/update_order.rb +++ b/lib/update/update_order.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module UpdateOrder - # data is an array of hashes of: # - id : id of row to update - # - order: new order of row to update + # - order: new order of row to update def self.with_data(table_name, data) - vals = data.map{|h| "(#{h[:id].to_i}, #{h[:order].to_i})"}.join(", ") + vals = data.map { |h| "(#{h[:id].to_i}, #{h[:order].to_i})" }.join(', ') from_str = "(VALUES #{vals}) AS data(id, \"order\")" - return Qx.update("#{table_name}") + Qx.update(table_name.to_s) .set('"order"="data"."order"') .timestamps .from(from_str) @@ -17,6 +18,4 @@ module UpdateOrder .returning("#{table_name}.order", "#{table_name}.id") .execute end - end - diff --git a/lib/update/update_payouts.rb b/lib/update/update_payouts.rb index 37a795fa..fc4d63b5 100644 --- a/lib/update/update_payouts.rb +++ b/lib/update/update_payouts.rb @@ -1,21 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdatePayouts - def self.reverse_with_stripe(payout_id, status, failure_message) - ParamValidation.new({payout_id:payout_id, status: status, failure_message: failure_message}, { - payout_id: {required: true, is_integer: true}, - status: {included_in:['pending', 'paid', 'canceled', 'failed'], required: true}, - failure_message: {not_blank: true, required: true} - }) + ParamValidation.new({ payout_id: payout_id, status: status, failure_message: failure_message }, + payout_id: { required: true, is_integer: true }, + status: { included_in: %w[pending paid canceled failed], required: true }, + failure_message: { not_blank: true, required: true }) payout = Payout.where('id = ?', payout_id).first unless payout - raise ParamValidation::ValidationError.new("No payout with id number: #{payout_id} ", [{key: :payout_id}]) + raise ParamValidation::ValidationError.new("No payout with id number: #{payout_id} ", [{ key: :payout_id }]) end - payment_ids = payout.payments.select('payments.id').map{|i| i.id}.to_a + payment_ids = payout.payments.select('payments.id').map(&:id).to_a if payment_ids.count < 1 - raise ArgumentError.new("No payments are available to reverse.") + raise ArgumentError, 'No payments are available to reverse.' end + now = Time.current Psql.transaction do @@ -26,16 +27,14 @@ module UpdatePayouts UpdateRefunds.reverse_disburse_all_with_payments(payment_ids) # Mark all disputes as lost_and_paid - #UpdateDisputes.disburse_all_with_payments(payment_ids) + # UpdateDisputes.disburse_all_with_payments(payment_ids) # Get gross total, total fees, net total, and total count # Create the payout record (whether it succeeded on Stripe or not) payout.status = status payout.failure_message = failure_message payout.save! - - #NonprofitMailer.delay.pending_payout_notification(payout['id'].to_i) payout end end -end \ No newline at end of file +end diff --git a/lib/update/update_recurring_donations.rb b/lib/update/update_recurring_donations.rb index 7fb21985..7ade0e09 100644 --- a/lib/update/update_recurring_donations.rb +++ b/lib/update/update_recurring_donations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'query/query_recurring_donations' require 'insert/insert_supporter_notes' @@ -5,61 +7,55 @@ require 'format/date' require 'format/currency' module UpdateRecurringDonations - # Update the card id and name for a given recurring donation (provide rd['donation_id']) def self.update_card_id(rd, token) rd = rd&.with_indifferent_access - ParamValidation.new({rd: rd, token: token}, - { - rd: {is_hash: true, required: true}, - token: {format: UUID::Regex, required: true} - }) + ParamValidation.new({ rd: rd, token: token }, + rd: { is_hash: true, required: true }, + token: { format: UUID::Regex, required: true }) ParamValidation.new(rd, - { - id: {is_reference: true, required: true} - }) + id: { is_reference: true, required: true }) source_token = QuerySourceToken.get_and_increment_source_token(token, nil) tokenizable = source_token.tokenizable - - entities = RetrieveActiveRecordItems.retrieve_from_keys(rd, RecurringDonation => :id ) + entities = RetrieveActiveRecordItems.retrieve_from_keys(rd, RecurringDonation => :id) validate_entities(entities[:id], tokenizable) Qx.transaction do rec_don = entities[:id] donation = rec_don.donation - #TODO This is stupid but the two are used together inconsistently. We should scrap one or the other. + # TODO: This is stupid but the two are used together inconsistently. We should scrap one or the other. donation.card = tokenizable rec_don.card_id = tokenizable rec_don.n_failures = 0 rec_don.save! donation.save! - InsertSupporterNotes.create([{content: "This supporter updated their card for their recurring donation with ID #{rec_don.id}", supporter_id: rec_don.supporter.id, user_id: 540}]) + InsertSupporterNotes.create([{ content: "This supporter updated their card for their recurring donation with ID #{rec_don.id}", supporter_id: rec_don.supporter.id, user_id: 540 }]) end - return QueryRecurringDonations.fetch_for_edit(rd[:id])['recurring_donation'] + QueryRecurringDonations.fetch_for_edit(rd[:id])['recurring_donation'] end # Update the paydate for a given recurring donation (provide rd['id']) def self.update_paydate(rd, paydate) - return ValidationError.new(['Invalid paydate']) unless (1..28).include?(paydate.to_i) - Psql.execute(Qexpr.new.update(:recurring_donations, paydate: paydate).where("id=$id", id: rd['id'])) + return ValidationError.new(['Invalid paydate']) unless (1..28).cover?(paydate.to_i) + + Psql.execute(Qexpr.new.update(:recurring_donations, paydate: paydate).where('id=$id', id: rd['id'])) rd['paydate'] = paydate - return rd + rd end # @param [RecurringDonation] rd # @param [String] token # @param [Integer] amount def self.update_amount(rd, token, amount) - ParamValidation.new({amount: amount, rd: rd, token: token}, - {amount: {is_integer: true, min: 50, required:true}, - rd: {required:true, is_a: RecurringDonation}, - token: {required:true, format: UUID::Regex} - }) + ParamValidation.new({ amount: amount, rd: rd, token: token }, + amount: { is_integer: true, min: 50, required: true }, + rd: { required: true, is_a: RecurringDonation }, + token: { required: true, format: UUID::Regex }) source_token = QuerySourceToken.get_and_increment_source_token(token, nil) tokenizable = source_token.tokenizable @@ -68,63 +64,57 @@ module UpdateRecurringDonations previous_amount = rd.amount donation = rd.donation Qx.transaction do - #TODO This is stupid but the two are used together inconsistently. We should scrap one or the other. + # TODO: This is stupid but the two are used together inconsistently. We should scrap one or the other. rd.card = tokenizable rd.amount = amount - rd.n_failures= 0 + rd.n_failures = 0 donation.card = tokenizable donation.amount = amount rd.save! donation.save! end - EmailJobQueue.queue(JobTypes::NonprofitRecurringDonationChangeAmountJob, rd.id, previous_amount) - EmailJobQueue.queue(JobTypes::DonorRecurringDonationChangeAmountJob,rd.id, previous_amount) + RecurringDonationChangeAmountJob.perform_later(rd, previous_amount) rd end - def self.update_from_start_dates - RecurringDonation.inactive.where("start_date >= ?", Date.today).update_all(active: true) + RecurringDonation.inactive.where('start_date >= ?', Date.today).update_all(active: true) end - def self.update_from_end_dates - RecurringDonation.active.where("end_date < ?", Date.today).update_all(active: false) + RecurringDonation.active.where('end_date < ?', Date.today).update_all(active: false) end - # Cancel a recurring donation (set active='f') and record the supporter/user email who did it - def self.cancel(rd_id, email, dont_notify_nonprofit=false) + def self.cancel(rd_id, email, dont_notify_nonprofit = false) Psql.execute( - Qexpr.new.update(:recurring_donations, { - active: false, - cancelled_by: email, - cancelled_at: Time.current - }) - .where("id=$id", id: rd_id.to_i) + Qexpr.new.update(:recurring_donations, + active: false, + cancelled_by: email, + cancelled_at: Time.current) + .where('id=$id', id: rd_id.to_i) ) rd = QueryRecurringDonations.fetch_for_edit(rd_id)['recurring_donation'] - InsertSupporterNotes.create([{supporter_id: rd['supporter_id'], content: "This supporter's recurring donation for $#{Format::Currency.cents_to_dollars(rd['amount'])} was cancelled by #{rd['cancelled_by']} on #{Format::Date.simple(rd['cancelled_at'])}", user_id: 540}]) - if (!dont_notify_nonprofit) - DonationMailer.delay.nonprofit_recurring_donation_cancellation(rd['donation_id']) + InsertSupporterNotes.create([{ supporter_id: rd['supporter_id'], content: "This supporter's recurring donation for $#{Format::Currency.cents_to_dollars(rd['amount'])} was cancelled by #{rd['cancelled_by']} on #{Format::Date.simple(rd['cancelled_at'])}", user_id: 540 }]) + unless dont_notify_nonprofit + RecurringDonationCancelledJob.perform_later(Donation.find(rd['donation_id'])) end - return rd + rd end - def self.update(rd, params) params = set_defaults(params) if params[:donation] - rd.donation.update_attributes(params[:donation]) + rd.donation.update(params[:donation]) return rd.donation unless rd.donation.valid? + params = params.except(:donation) end - rd.update_attributes(params) - return rd + rd.update(params) + rd end - def self.set_defaults(params) if params[:donation] && params[:donation][:dollars] params[:donation][:amount] = Format::Currency.dollars_to_cents(params[:donation][:dollars]) @@ -141,17 +131,17 @@ module UpdateRecurringDonations params = params.except(:end_date_str) end - return params + params end # @param [RecurringDonation] rd # @param [Card] tokenizable def self.validate_entities(rd, tokenizable) - if (rd.cancelled_at) + if rd.cancelled_at raise ParamValidation::ValidationError.new("Recurring Donation #{rd.id} is already cancelled.", key: :id) end - if (tokenizable.deleted) + if tokenizable.deleted raise ParamValidation::ValidationError.new("Tokenized card #{tokenizable.id} is not valid.", key: :token) end @@ -159,6 +149,4 @@ module UpdateRecurringDonations raise ParamValidation::ValidationError.new("Supporter #{rd.supporter.id} does not own card #{tokenizable.id}", key: :token) end end - end - diff --git a/lib/update/update_refunds.rb b/lib/update/update_refunds.rb index c04cf048..f03f83b5 100644 --- a/lib/update/update_refunds.rb +++ b/lib/update/update_refunds.rb @@ -1,16 +1,17 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateRefunds - - def self.disburse_all_with_payments(payment_ids) + def self.disburse_all_with_payments(payment_ids) expr = Qx.update(:refunds) - .set(disbursed: true) - .timestamps - .where("payment_id IN ($ids)", ids: payment_ids) - .returning('*') - .execute - end + .set(disbursed: true) + .timestamps + .where('payment_id IN ($ids)', ids: payment_ids) + .returning('*') + .execute + end - def self.reverse_disburse_all_with_payments(payment_ids) - Refund.where("payment_id IN (?)", payment_ids).update_all(disbursed:false) - end + def self.reverse_disburse_all_with_payments(payment_ids) + Refund.where('payment_id IN (?)', payment_ids).update_all(disbursed: false) + end end diff --git a/lib/update/update_supporter.rb b/lib/update/update_supporter.rb index c74e379d..9b3e356f 100644 --- a/lib/update/update_supporter.rb +++ b/lib/update/update_supporter.rb @@ -1,18 +1,19 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateSupporter - - def self.from_info(supporter, params) - supporter.update_attributes(params) - #GeocodeModel.delay.geocode(supporter) - return supporter - end + def self.from_info(supporter, params) + supporter.update(params) + # GeocodeModel.delay.geocode(supporter) + supporter + end # Bulk delete, meaning mark all supporters given by a query as deleted='t' def self.bulk_delete(np_id, supporter_ids) Qx.update(:supporters) .set(deleted: true) - .where("id IN ($ids)", ids: supporter_ids) - .and_where("nonprofit_id=$id", id: np_id) + .where('id IN ($ids)', ids: supporter_ids) + .and_where('nonprofit_id=$id', id: np_id) .returning('id') .execute end @@ -20,5 +21,4 @@ module UpdateSupporter def self.general_info(supporter_id, data) Qx.update(:supporters).set(data).where(id: supporter_id).returning('*').ex.last end - end diff --git a/lib/update/update_supporter_notes.rb b/lib/update/update_supporter_notes.rb index 0c0cdd41..99cfbe1c 100644 --- a/lib/update/update_supporter_notes.rb +++ b/lib/update/update_supporter_notes.rb @@ -1,8 +1,9 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'qx' module UpdateSupporterNotes - def self.update(note) Qx.update(:supporter_notes) .set(content: note[:content], user_id: note[:user_id]) @@ -10,17 +11,15 @@ module UpdateSupporterNotes .where(id: note[:id]) .execute UpdateActivities.for_supporter_notes(note) - end + end # sets the deleted column to true on supporter_notes (soft delete) - # and then does a hard delete on the associated activity + # and then does a hard delete on the associated activity def self.delete(id) Qx.update(:supporter_notes) .set(deleted: true) .where(id: id) .execute Qx.delete_from(:activities).where(attachment_id: id).execute - end - + end end - diff --git a/lib/update/update_tickets.rb b/lib/update/update_tickets.rb index 393ba2a9..728c4aeb 100644 --- a/lib/update/update_tickets.rb +++ b/lib/update/update_tickets.rb @@ -1,17 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UpdateTickets + def self.update(data, current_user = nil) + ParamValidation.new(data, + event_id: { required: true, is_reference: true }, + ticket_id: { required: true, is_reference: true }, + token: { format: UUID::Regex }, + bid_id: { is_integer: true }, + # note: nothing to check? - def self.update(data, current_user=nil) - ParamValidation.new(data, { - event_id: {required:true, is_reference: true}, - ticket_id: {required: true, is_reference: true}, - token: {format: UUID::Regex}, - bid_id: {is_integer: true}, - #note: nothing to check? - - checked_in: {included_in: ['true', 'false', true, false]} - - }) + checked_in: { included_in: ['true', 'false', true, false] }) entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Event => :event_id, Ticket => :ticket_id) validate_entities(entities) @@ -78,21 +77,21 @@ module UpdateTickets return { json: { error: "No ticket with id #{ticket_id} at event with id #{event_id}\n #{e.message}" }, status: :unprocessable_entity } rescue ActiveRecord::ActiveRecordError - return { json: { error: "There was a DB error. Please contact support" }, + return { json: { error: 'There was a DB error. Please contact support' }, status: :unprocessable_entity } end end def self.validate_entities(entities) - if (entities[:ticket_id].deleted) + if entities[:ticket_id].deleted raise ParamValidation::ValidationError.new("Ticket ID #{entities[:ticket_id].id} is deleted", key: :ticket_id) end - if (entities[:event_id].deleted) + if entities[:event_id].deleted raise ParamValidation::ValidationError.new("Event ID #{entities[:event_id].id} is deleted", key: :event_id) end - if (entities[:ticket_id].event != entities[:event_id]) + if entities[:ticket_id].event != entities[:event_id] raise ParamValidation::ValidationError.new("Ticket ID #{entities[:ticket_id].id} does not belong to event #{entities[:event_id].id}", key: :ticket_id) end end diff --git a/lib/uuid.rb b/lib/uuid.rb index 4fb82fe5..2bd69d34 100644 --- a/lib/uuid.rb +++ b/lib/uuid.rb @@ -1,4 +1,6 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module UUID - Regex = /\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}?/ -end \ No newline at end of file + Regex = /\{?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}?/.freeze +end diff --git a/lib/validation_error.rb b/lib/validation_error.rb index 1cf68a91..2d5f9aa0 100644 --- a/lib/validation_error.rb +++ b/lib/validation_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later # A generalized, all purpose struct for database validation errors # .errors is simply array of error messages diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 1f1bc541..00000000 --- a/package-lock.json +++ /dev/null @@ -1,24389 +0,0 @@ -{ - "name": "Houdini", - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@babel/core": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.3.3.tgz", - "integrity": "sha512-w445QGI2qd0E0GlSnq6huRZWPMmQGCp5gd5ZWS4hagn0EiwzxD5QMFkpchyusAyVC1n27OKXzQ0/88aVU9n4xQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.3.3", - "@babel/helpers": "^7.2.0", - "@babel/parser": "^7.3.3", - "@babel/template": "^7.2.2", - "@babel/traverse": "^7.2.2", - "@babel/types": "^7.3.3", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.11", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/generator": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz", - "integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==", - "dev": true, - "requires": { - "@babel/types": "^7.3.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.11", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", - "dev": true - }, - "@babel/helper-split-export-declaration": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", - "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@babel/helpers": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.3.1.tgz", - "integrity": "sha512-Q82R3jKsVpUV99mgX50gOPCWwco9Ec5Iln/8Vyu4osNIOQgSrd9RFrQeUvmvddFNoLwMyOUWU+5ckioEKpDoGA==", - "dev": true, - "requires": { - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.5", - "@babel/types": "^7.3.0" - } - }, - "@babel/parser": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz", - "integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==", - "dev": true - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0" - } - }, - "@babel/runtime": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", - "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", - "requires": { - "regenerator-runtime": "^0.13.2" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" - } - } - }, - "@babel/template": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", - "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.2.2", - "@babel/types": "^7.2.2" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/traverse": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", - "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.2.2", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.2.3", - "@babel/types": "^7.2.2", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.10" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz", - "integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" - }, - "dependencies": { - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@jest/console": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", - "integrity": "sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg==", - "dev": true, - "requires": { - "@jest/source-map": "^24.3.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/core": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz", - "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/reporters": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.7.0", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "jest-watcher": "^24.7.1", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.1", - "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", - "dev": true, - "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "expect": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", - "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-regex-util": "^24.3.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-changed-files": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz", - "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "execa": "^1.0.0", - "throat": "^4.0.0" - } - }, - "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", - "realpath-native": "^1.1.0" - } - }, - "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" - } - }, - "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "throat": "^4.0.0" - } - }, - "jest-leak-detector": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", - "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", - "dev": true, - "requires": { - "pretty-format": "^24.7.0" - } - }, - "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-resolve-dependencies": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz", - "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.7.1" - } - }, - "jest-runner": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", - "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.7.1", - "jest-jasmine2": "^24.7.1", - "jest-leak-detector": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - } - }, - "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", - "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "expect": "^24.7.1", - "jest-diff": "^24.7.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.7.0", - "semver": "^5.5.0" - } - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" - } - }, - "jest-watcher": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz", - "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==", - "dev": true, - "requires": { - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.9", - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.1", - "jest-util": "^24.7.1", - "string-length": "^2.0.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pretty-format": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", - "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@jest/environment": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.7.1.tgz", - "integrity": "sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0" - }, - "dependencies": { - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - } - } - }, - "@jest/fake-timers": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.7.1.tgz", - "integrity": "sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/reporters": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz", - "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.2", - "istanbul-api": "^2.1.1", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-source-maps": "^3.0.1", - "jest-haste-map": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "node-notifier": "^5.2.1", - "slash": "^2.0.0", - "source-map": "^0.6.0", - "string-length": "^2.0.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", - "dev": true, - "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "expect": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", - "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-regex-util": "^24.3.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", - "realpath-native": "^1.1.0" - } - }, - "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" - } - }, - "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "throat": "^4.0.0" - } - }, - "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", - "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "expect": "^24.7.1", - "jest-diff": "^24.7.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.7.0", - "semver": "^5.5.0" - } - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pretty-format": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", - "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@jest/source-map": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.3.0.tgz", - "integrity": "sha512-zALZt1t2ou8le/crCeeiRYzvdnTzaIlpOWaet45lNSqNJUnXbppUUFR4ZUAlzgDmKee4Q5P/tKXypI1RiHwgag==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - }, - "dependencies": { - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/test-result": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.7.1.tgz", - "integrity": "sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } - }, - "@jest/test-sequencer": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz", - "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==", - "dev": true, - "requires": { - "@jest/test-result": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", - "dev": true, - "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "expect": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", - "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-regex-util": "^24.3.0" - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", - "realpath-native": "^1.1.0" - } - }, - "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-docblock": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.3.0.tgz", - "integrity": "sha512-nlANmF9Yq1dufhFlKG9rasfQlrY7wINJbo3q01tu56Jv5eBU5jirylhF2O5ZBnLxzOVBGRDz/9NAwNyBtG4Nyg==", - "dev": true, - "requires": { - "detect-newline": "^2.1.0" - } - }, - "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" - } - }, - "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "throat": "^4.0.0" - } - }, - "jest-leak-detector": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", - "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", - "dev": true, - "requires": { - "pretty-format": "^24.7.0" - } - }, - "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-runner": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", - "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.4.2", - "exit": "^0.1.2", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.7.1", - "jest-jasmine2": "^24.7.1", - "jest-leak-detector": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "source-map-support": "^0.5.6", - "throat": "^4.0.0" - } - }, - "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-snapshot": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", - "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "expect": "^24.7.1", - "jest-diff": "^24.7.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.7.0", - "semver": "^5.5.0" - } - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pretty-format": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", - "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.12", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", - "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@jest/transform": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.7.1.tgz", - "integrity": "sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^24.7.0", - "babel-plugin-istanbul": "^5.1.0", - "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-util": "^24.7.1", - "micromatch": "^3.1.10", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "2.4.1" - }, - "dependencies": { - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@jest/types": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz", - "integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/yargs": "^12.0.9" - } - }, - "@sinonjs/commons": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz", - "integrity": "sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", - "dev": true, - "requires": { - "samsam": "1.3.0" - } - }, - "@sinonjs/samsam": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.1.1.tgz", - "integrity": "sha512-ILlwvQUwAiaVBzr3qz8oT1moM7AIUHqUc2UmEjQcH9lLe+E+BZPwUMuc9FFojMswRK4r96x5zDTTrowMLw/vuA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.0.2", - "array-from": "^2.1.1", - "lodash": "^4.17.11" - } - }, - "@types/babel__core": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.1.tgz", - "integrity": "sha512-+hjBtgcFPYyCTo0A15+nxrCVJL7aC6Acg87TXd5OW3QhHswdrOLoles+ldL2Uk8q++7yIfl4tURtztccdeeyOw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.0.2.tgz", - "integrity": "sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/cheerio": { - "version": "0.22.7", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.7.tgz", - "integrity": "sha512-+T9qBbqe/jXtTjzVddArZExahoPPmt8eq3O1ZuCKZXjBVxf/ciUYNXrIDZJEVgYvpELnv6VlPRCfLzufRxpAag==", - "dev": true - }, - "@types/color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.0.tgz", - "integrity": "sha512-5qqtNia+m2I0/85+pd2YzAXaTyKO8j+svirO5aN+XaQJ5+eZ8nx0jPtEWZLxCi50xwYsX10xUHetFzfb1WEs4Q==", - "dev": true, - "requires": { - "@types/color-convert": "*" - } - }, - "@types/color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==", - "dev": true, - "requires": { - "@types/color-name": "*" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/enzyme": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.1.10.tgz", - "integrity": "sha512-90MTwbHmNJybcbHQvSQLbeebROnAXSK216Jl2wo+Yy6Xp0KA7d0NlJcx5nyV03agFUgk7sRmZTTbuhpjMkgNOQ==", - "dev": true, - "requires": { - "@types/cheerio": "*", - "@types/react": "*" - } - }, - "@types/enzyme-to-json": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/enzyme-to-json/-/enzyme-to-json-1.5.1.tgz", - "integrity": "sha512-qv4r6ZxDXMlJoCVklvxFQ+BTvC3DPjvhhFs43ThBH4uvpif1I/bSP9IBHXzo90Skr3JVXvBCm7bFJ88ZbjtpQQ==", - "dev": true, - "requires": { - "@types/enzyme": "*" - } - }, - "@types/es6-promise": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@types/es6-promise/-/es6-promise-3.3.0.tgz", - "integrity": "sha512-ixCIAEkLUKv9movnHKCzx2rzAJgEnSALDXPrOSSwOjWwXFs0ssSZKan+O2e3FExPPCbX+DfA9NcKsbvLuyUlNA==", - "dev": true, - "requires": { - "es6-promise": "*" - } - }, - "@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, - "requires": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", - "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==", - "dev": true - }, - "@types/jest": { - "version": "22.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-22.2.3.tgz", - "integrity": "sha512-e74sM9W/4qqWB6D4TWV9FQk0WoHtX1X4FJpbjxucMSVJHtFjbQOH3H6yp+xno4br0AKG0wz/kPtaN599GUOvAg==", - "dev": true - }, - "@types/jquery": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.1.tgz", - "integrity": "sha512-N3h+rzN518yl2xKrW0o6KKdNmWZ+OwG6SoM5TBEQFF0tTv5wXPEsoOuYQ2Kt3/89XbcSZUJLdjiT/2c3BR/ApQ==", - "dev": true - }, - "@types/jsdom": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-11.0.4.tgz", - "integrity": "sha512-lthMj4kw7Fzs3LjBhQ0+1faAfDrN9GFJZO5Nf/xO7fppFfxqnnQdNR28n0xMGXsx8fTHOPliE1NTkAW1bVLpYw==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^3.0.2" - } - }, - "@types/lodash": { - "version": "4.14.108", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.108.tgz", - "integrity": "sha512-WD2vUOKfBBVHxWUV9iMR9RMfpuf8HquxWeAq2yqGVL7Nc4JW2+sQama0pREMqzNI3Tutj0PyxYUJwuoxxvX+xA==", - "dev": true - }, - "@types/moment-timezone": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@types/moment-timezone/-/moment-timezone-0.5.9.tgz", - "integrity": "sha512-tBf1QR8xAayQfI1xD+SMSNDMxi+aCYKEhjgVXTZt3sgxS2XusNX3jM6jJbFoY/ar1CK/PaYJoPkWs/mwcwgOqw==", - "dev": true, - "requires": { - "moment": ">=2.14.0" - } - }, - "@types/node": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.0.6.tgz", - "integrity": "sha512-2whhQUfDHRBiZ3L54Ulyl1X+fZWbWabxPYRDAsibgOAtE6adwusD15Xv0Bw/D7cPah35Z/wKTdW3iAKsevw1uw==", - "dev": true - }, - "@types/prop-types": { - "version": "15.5.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.5.tgz", - "integrity": "sha512-mOrlCEdwX3seT3n0AXNt4KNPAZZxcsABUHwBgFXOt+nvFUXkxCAO6UBJHPrDxWEa2KDMil86355fjo8jbZ+K0Q==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/q": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz", - "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==", - "dev": true - }, - "@types/react": { - "version": "16.3.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.14.tgz", - "integrity": "sha512-wNUGm49fPl7eE2fnYdF0v5vSOrUMdKMQD/4NwtQRnb6mnPwtkhabmuFz37eq90+hhyfz0pWd38jkZHOcaZ6LGw==", - "dev": true, - "requires": { - "csstype": "^2.2.0" - } - }, - "@types/react-dom": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.0.5.tgz", - "integrity": "sha512-ony2hEYlGXCLWNAWWgbsHR7qVvDbeMRFc5b43+7dhj3n+zXzxz81HV9Yjpc3JD8vLCiwYoSLqFCI6bD0+0zG2Q==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/react": "*" - } - }, - "@types/react-intl": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/react-intl/-/react-intl-2.3.8.tgz", - "integrity": "sha512-dyb2UanAZ3/3pNcqtdnbknHMinKMfQPrqbqGO52eb3xy/5d28zDgEQXFmi9iMwUULwK4BLdLEVetH6Z9gL0bRQ==", - "dev": true - }, - "@types/react-test-renderer": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.0.1.tgz", - "integrity": "sha512-kmNh8g67Ck/y/vp6KX+4JTJXiTGLZBylNhu+R7sm7zcvsrnIGVO6J1zew5inVg428j9f8yHpl68RcYOZXVborQ==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-text-mask": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/@types/react-text-mask/-/react-text-mask-5.4.2.tgz", - "integrity": "sha512-R4h07wAeOPh6xc6E9qYPMgYpeuUJ1w3rs3oWwp9oWIVPtnAGxPGDuCPEs2+ynlP5syeM7heZeZeM8saAHRgENA==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-hP7vUaZMVSWKxo133P8U51U6UZ7+pbY+eAQb8+p6SZ2rB1rj3mOTDgTzhhi+R2SCB4S+sWekAAGoxdiZPG0ReQ==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/sinon": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-4.3.3.tgz", - "integrity": "sha512-Tt7w/ylBS/OEAlSCwzB0Db1KbxnkycP/1UkQpbvKFYoUuRn4uYsC3xh5TRPrOjTy0i8TIkSz1JdNL4GPVdf3KQ==", - "dev": true - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==", - "dev": true - }, - "@types/validator": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-9.4.1.tgz", - "integrity": "sha512-Y8UyLZvBPgckGhEIFCGBPj1tsRbpcZn4rbAp7lUxC3EW/nDR2V6t9LltE+mvDJxQQ+Bg3saE3UAwn6lsG5O1yQ==", - "dev": true - }, - "@types/velocity-animate": { - "version": "1.2.33", - "resolved": "https://registry.npmjs.org/@types/velocity-animate/-/velocity-animate-1.2.33.tgz", - "integrity": "sha512-0UAWZSIAT7QE2kR1tcCJcfed3VY2m7VDbLhXEFt+5yog9mb+JSTAukBJVGOqptm5vuXJhvyOFzVkeNU4mjqcvg==", - "dev": true, - "requires": { - "@types/jquery": "*" - } - }, - "@types/yargs": { - "version": "12.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.12.tgz", - "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", - "dev": true - }, - "JSONStream": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", - "integrity": "sha1-wQI3G27Dp887hHygDCC7D85Mbeo=", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abab": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", - "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - } - }, - "acorn-globals": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", - "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", - "dev": true, - "requires": { - "acorn": "^5.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } - } - }, - "acorn-node": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", - "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", - "dev": true, - "requires": { - "acorn": "^5.4.1", - "xtend": "^4.0.1" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } - } - }, - "adjust-sourcemap-loader": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-1.2.0.tgz", - "integrity": "sha512-958oaHHVEXMvsY7v7cC5gEkNIcoaAVIhZ4mBReYVZJOTP9IgKmzLjIOhTtzpLMu+qriXvLsVjJ155EeInp45IQ==", - "dev": true, - "requires": { - "assert": "^1.3.0", - "camelcase": "^1.2.1", - "loader-utils": "^1.1.0", - "lodash.assign": "^4.0.1", - "lodash.defaults": "^3.1.2", - "object-path": "^0.9.2", - "regex-parser": "^2.2.9" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "lodash.defaults": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-3.1.2.tgz", - "integrity": "sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw=", - "dev": true, - "requires": { - "lodash.assign": "^3.0.0", - "lodash.restparam": "^3.0.0" - }, - "dependencies": { - "lodash.assign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-3.2.0.tgz", - "integrity": "sha1-POnwI0tLIiPilrj6CsH+6OvKZPo=", - "dev": true, - "requires": { - "lodash._baseassign": "^3.0.0", - "lodash._createassigner": "^3.0.0", - "lodash.keys": "^3.0.0" - } - } - } - } - } - }, - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "any-promise": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-0.1.0.tgz", - "integrity": "sha1-gwtoCqflbzNFHUsEnzvYBESY7ic=", - "dev": true - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true - }, - "array-filter": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", - "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=", - "dev": true - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", - "dev": true - }, - "array-map": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", - "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", - "dev": true - }, - "array-reduce": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", - "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "array.prototype.flat": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz", - "integrity": "sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", - "function-bind": "^1.1.1" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "astw": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", - "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - } - }, - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "async-foreach": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atoa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", - "integrity": "sha1-DMDpGkgOc4+SPrwQNnZHF3mzSkk=" - }, - "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", - "dev": true - }, - "attr-binder": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/attr-binder/-/attr-binder-0.3.1.tgz", - "integrity": "sha1-ZRuZ0LoGNDbH25V7CyPI6CxnqyA=" - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "^1.7.6", - "caniuse-db": "^1.0.30000634", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^5.2.16", - "postcss-value-parser": "^3.2.3" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - } - } - }, - "aws-sdk": { - "version": "2.402.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.402.0.tgz", - "integrity": "sha512-cit/4UaNMtVWwkJtxa7gSSrvMURjPpej5guas1Dhaivoz7QcIrAyoOZo49uSdTSPoGG9YtjFy/tSgdSY61BDTQ==", - "requires": { - "buffer": "4.9.1", - "events": "1.1.1", - "ieee754": "1.1.8", - "jmespath": "0.15.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.3.2", - "xml2js": "0.4.19" - }, - "dependencies": { - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - } - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", - "dev": true - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" - } - }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "dev": true, - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - } - }, - "babel-helper-builder-binary-assignment-operator-visitor": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", - "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", - "dev": true, - "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-explode-assignable-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", - "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-helper-remap-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", - "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-loader": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.4.tgz", - "integrity": "sha512-/hbyEvPzBJuGpk9o80R0ZyTej6heEOr59GoEUtn8qFKbnx4cJm9FWES6J/iv644sYgrtVw9JJQkjaLW/bqb5gw==", - "dev": true, - "requires": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-istanbul": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz", - "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.0.0", - "test-exclude": "^5.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "babel-plugin-syntax-async-functions": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", - "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", - "dev": true - }, - "babel-plugin-syntax-exponentiation-operator": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", - "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", - "dev": true - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", - "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", - "dev": true - }, - "babel-plugin-transform-async-to-generator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", - "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", - "dev": true, - "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "lodash": "^4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.2", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", - "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-types": "^6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" - } - }, - "babel-plugin-transform-exponentiation-operator": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", - "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", - "dev": true, - "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "^0.10.0" - } - }, - "babel-plugin-transform-runtime": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", - "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" - } - }, - "babel-preset-env": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", - "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true - }, - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, - "bootstrap": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz", - "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==", - "dev": true - }, - "bootstrap-loader": { - "version": "github:houdiniproject/bootstrap-loader#2e58a107890661e4644a115a635056412c3f9d2d", - "from": "github:houdiniproject/bootstrap-loader#compiled_namespaced", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "escape-regexp": "0.0.1", - "exports-loader": "^0.6.3", - "js-yaml": "^3.7.0", - "loader-utils": "^1.0.2", - "resolve": "^1.1.7", - "semver": "^5.3.0", - "strip-json-comments": "^2.0.1" - }, - "dependencies": { - "exports-loader": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.6.4.tgz", - "integrity": "sha1-1w/GEhl1s1/BKDDPUnVL4nQPyIY=", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "source-map": "0.5.x" - } - } - } - }, - "bootstrap-sass": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/bootstrap-sass/-/bootstrap-sass-3.3.7.tgz", - "integrity": "sha1-ZZbHq0D2Y3OTMjqwvIDQZPxjBJg=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-pack": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.1.0.tgz", - "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", - "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "combine-source-map": "~0.8.0", - "defined": "^1.0.0", - "safe-buffer": "^5.1.1", - "through2": "^2.0.0", - "umd": "^3.0.0" - } - }, - "browser-process-hrtime": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", - "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", - "dev": true - }, - "browser-resolve": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", - "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "browser-split": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/browser-split/-/browser-split-0.0.1.tgz", - "integrity": "sha1-ewl1dPjj6tYG+0Zk5krf3aKYGpM=" - }, - "browserify": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/browserify/-/browserify-13.0.1.tgz", - "integrity": "sha1-03F5y7IiF57Pcw7H5iXpmGd5AtQ=", - "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "assert": "~1.3.0", - "browser-pack": "^6.0.1", - "browser-resolve": "^1.11.0", - "browserify-zlib": "~0.1.2", - "buffer": "^4.1.0", - "concat-stream": "~1.5.1", - "console-browserify": "^1.1.0", - "constants-browserify": "~1.0.0", - "crypto-browserify": "^3.0.0", - "defined": "^1.0.0", - "deps-sort": "^2.0.0", - "domain-browser": "~1.1.0", - "duplexer2": "~0.1.2", - "events": "~1.1.0", - "glob": "^5.0.15", - "has": "^1.0.0", - "htmlescape": "^1.1.0", - "https-browserify": "~0.0.0", - "inherits": "~2.0.1", - "insert-module-globals": "^7.0.0", - "labeled-stream-splicer": "^2.0.0", - "module-deps": "^4.0.2", - "os-browserify": "~0.1.1", - "parents": "^1.0.1", - "path-browserify": "~0.0.0", - "process": "~0.11.0", - "punycode": "^1.3.2", - "querystring-es3": "~0.2.0", - "read-only-stream": "^2.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.4", - "shasum": "^1.0.0", - "shell-quote": "^1.4.3", - "stream-browserify": "^2.0.0", - "stream-http": "^2.0.0", - "string_decoder": "~0.10.0", - "subarg": "^1.0.0", - "syntax-error": "^1.1.1", - "through2": "^2.0.0", - "timers-browserify": "^1.0.1", - "tty-browserify": "~0.0.0", - "url": "~0.11.0", - "util": "~0.10.1", - "vm-browserify": "~0.0.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "assert": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.3.0.tgz", - "integrity": "sha1-A5OaYiWCqBLMICMgoLmlbJuBWEk=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cache-api": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/browserify-cache-api/-/browserify-cache-api-3.0.1.tgz", - "integrity": "sha1-liR+hT8Gj9bg1FzHPwuyzZd47wI=", - "dev": true, - "requires": { - "async": "^1.5.2", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - } - } - }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.1.tgz", - "integrity": "sha512-zy0Cobe3hhgpiOM32Tj7KQ3Vl91m0njwsjzZQK1L+JDf11dzP9qIvjreVinsvXrgfjhStXwUWAEpB9D7Gwmayw==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "browserify-incremental": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/browserify-incremental/-/browserify-incremental-3.1.1.tgz", - "integrity": "sha1-BxPLdYckemMqnwjPG9FpuHi2Koo=", - "dev": true, - "requires": { - "JSONStream": "^0.10.0", - "browserify-cache-api": "^3.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "JSONStream": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", - "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=", - "dev": true, - "requires": { - "jsonparse": "0.0.5", - "through": ">=2.2.7 <3" - } - }, - "jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", - "dev": true - } - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "requires": { - "pako": "~0.2.0" - } - }, - "browserslist": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.7.tgz", - "integrity": "sha512-oYVLxFVqpX9uMhOIQBLtZL+CX4uY8ZpWcjNTaxyWl5rO8yA9SSNikFnAfvk8J3P/7z3BZwNmEqFKaJoYltj3MQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000835", - "electron-to-chromium": "^1.3.45" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "buffer-from": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz", - "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - }, - "dependencies": { - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - } - } - }, - "cached-path-relative": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.2.tgz", - "integrity": "sha512-5r2GqsoEb4qMTTN9J+WzXfjov+hjxT+j3u5K+kIVNIwAd99DLCJE9pBIMP1qVeybV6JiijL385Oz0DcYxfbOIg==", - "dev": true - }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - } - }, - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - }, - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - } - }, - "camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "^1.3.6", - "caniuse-db": "^1.0.30000529", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - } - } - }, - "caniuse-db": { - "version": "1.0.30000839", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000839.tgz", - "integrity": "sha1-VahuQCx0rhcUlwe+o+o5lSIjNJc=", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30000839", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000839.tgz", - "integrity": "sha512-gJZIfmkuy84agOeAZc7WJOexZhisZaBSFk96gkGM6TkH7+1mBfr/MSPnXC8lO0g7guh/ucbswYjruvDbzc6i0g==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "chart.js": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.1.4.tgz", - "integrity": "sha1-ncgQle1cx5QoZeUOTBDPVIqpfwk=", - "requires": { - "chartjs-color": "^2.0.0", - "moment": "^2.10.6" - }, - "dependencies": { - "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" - } - } - }, - "chartjs-color": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz", - "integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=", - "requires": { - "chartjs-color-string": "^0.5.0", - "color-convert": "^0.5.3" - } - }, - "chartjs-color-string": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz", - "integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==", - "requires": { - "color-name": "^1.0.0" - } - }, - "cheerio": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", - "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", - "dev": true, - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash": "^4.15.0", - "parse5": "^3.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", - "dev": true - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "circular-json-es6": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz", - "integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==", - "dev": true - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "^1.1.3" - } - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "clean-webpack-plugin": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", - "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", - "dev": true, - "requires": { - "rimraf": "^2.6.1" - } - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "dev": true - }, - "clone-deep": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", - "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", - "dev": true, - "requires": { - "for-own": "^1.0.0", - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.0", - "shallow-clone": "^1.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", - "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", - "dev": true, - "requires": { - "@types/q": "^1.5.1", - "chalk": "^2.4.1", - "q": "^1.1.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.1.0.tgz", - "integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - }, - "dependencies": { - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-string": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", - "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - } - } - }, - "color-convert": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", - "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "^1.0.0" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "^0.11.0", - "css-color-names": "0.0.4", - "has": "^1.0.1" - }, - "dependencies": { - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - } - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "combine-source-map": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.8.0.tgz", - "integrity": "sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos=", - "dev": true, - "requires": { - "convert-source-map": "~1.1.0", - "inline-source-map": "~0.6.0", - "lodash.memoize": "~3.0.3", - "source-map": "~0.5.3" - }, - "dependencies": { - "convert-source-map": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", - "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=", - "dev": true - } - } - }, - "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", - "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "commons.css": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/commons.css/-/commons.css-0.1.8.tgz", - "integrity": "sha1-y7/20biWQazW+sHxp2I5cQHqseU=" - }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "dev": true - }, - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=" - }, - "compression-webpack-plugin": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-1.1.11.tgz", - "integrity": "sha512-ZVWKrTQhtOP7rDx3M/koXTnRm/iwcYbuCdV+i4lZfAIe32Mov7vUVM0+8Vpz4q0xH+TBUZxq+rM8nhtkDH50YQ==", - "dev": true, - "requires": { - "cacache": "^10.0.1", - "find-cache-dir": "^1.0.0", - "neo-async": "^2.5.0", - "serialize-javascript": "^1.4.0", - "webpack-sources": "^1.0.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "concat-stream": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", - "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "~2.0.0", - "typedarray": "~0.0.5" - }, - "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "contra": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz", - "integrity": "sha1-9TveQtfltZhcrk2ZqNYQUm3o8o0=", - "requires": { - "atoa": "1.0.0", - "ticky": "1.0.1" - } - }, - "convert-source-map": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz", - "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=", - "dev": true - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookiejar": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.1.tgz", - "integrity": "sha1-PRJ1L2rfaKiS8zJDNJK9WBK7Zo8=" - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "copy-webpack-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz", - "integrity": "sha512-OlTo6DYg0XfTKOF8eLf79wcHm4Ut10xU2cRBRPMW/NA5F9VMjZGTfRHWDIYC3s+1kObGYrBLshXWU1K0hILkNQ==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - } - }, - "core-js": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.6.tgz", - "integrity": "sha512-lQUVfQi0aLix2xpyjrrJEvfuYCqPc/HwmTKsC/VNf8q0zsjX7SQZtp4+oRONN5Tsur9GDETPjj+Ub2iDiGZfSQ==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cosmiconfig": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", - "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", - "dev": true, - "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.4.3", - "minimist": "^1.2.0", - "object-assign": "^4.1.0", - "os-homedir": "^1.0.1", - "parse-json": "^2.2.0", - "require-from-string": "^1.1.0" - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cropperjs": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.0.0-beta.2.tgz", - "integrity": "sha1-/cXogEfX+J1S1EWjIoEzorV6y88=" - }, - "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" - } - }, - "crossvent": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", - "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=", - "requires": { - "custom-event": "1.0.0" - } - }, - "cryptiles": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.4.tgz", - "integrity": "sha512-8I1sgZHfVwcSOY6mSGpVU3lw/GSIZvusg8dD2+OGehCJpOhQRLNcH0qb9upQnOH4XhgxxFJSg6E2kx95deb1Tw==", - "dev": true, - "requires": { - "boom": "5.x.x" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "css": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/css/-/css-2.2.3.tgz", - "integrity": "sha512-0W171WccAjQGGTKLhw4m2nnl0zPHUlTO/I8td4XzJgIB8Hg3ZZx71qT4G4eX8OVsSiaAKiUMy73E3nsbPlg2DQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "source-map": "^0.1.38", - "source-map-resolve": "^0.5.1", - "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "css-color-function": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/css-color-function/-/css-color-function-1.3.3.tgz", - "integrity": "sha1-jtJMLAIFBzM5+voAS8jBQfzLKC4=", - "dev": true, - "requires": { - "balanced-match": "0.1.0", - "color": "^0.11.0", - "debug": "^3.1.0", - "rgb": "~0.1.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.1.0.tgz", - "integrity": "sha1-tQS9BYabOSWd0MXvw12EMXbczEo=", - "dev": true - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-declaration-sorter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", - "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", - "dev": true, - "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "css-loader": { - "version": "0.28.11", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", - "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", - "dev": true, - "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "cssnano": "^3.10.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash.camelcase": "^4.3.0", - "object-assign": "^4.1.1", - "postcss": "^5.0.6", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "^1.1.2" - } - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "^6.3.1", - "decamelize": "^1.1.2", - "defined": "^1.0.0", - "has": "^1.0.1", - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-calc": "^5.2.0", - "postcss-colormin": "^2.1.8", - "postcss-convert-values": "^2.3.4", - "postcss-discard-comments": "^2.0.4", - "postcss-discard-duplicates": "^2.0.1", - "postcss-discard-empty": "^2.0.1", - "postcss-discard-overridden": "^0.1.1", - "postcss-discard-unused": "^2.2.1", - "postcss-filter-plugins": "^2.0.0", - "postcss-merge-idents": "^2.1.5", - "postcss-merge-longhand": "^2.0.1", - "postcss-merge-rules": "^2.0.3", - "postcss-minify-font-values": "^1.0.2", - "postcss-minify-gradients": "^1.0.1", - "postcss-minify-params": "^1.0.4", - "postcss-minify-selectors": "^2.0.4", - "postcss-normalize-charset": "^1.1.0", - "postcss-normalize-url": "^3.0.7", - "postcss-ordered-values": "^2.1.0", - "postcss-reduce-idents": "^2.2.2", - "postcss-reduce-initial": "^1.0.0", - "postcss-reduce-transforms": "^1.0.3", - "postcss-svgo": "^2.1.1", - "postcss-unique-selectors": "^2.0.2", - "postcss-value-parser": "^3.2.3", - "postcss-zindex": "^2.0.1" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "^1.0.9", - "source-map": "^0.5.3" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" - } - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "prepend-http": "^1.0.0", - "query-string": "^4.1.0", - "sort-keys": "^1.0.0" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "^1.0.5", - "postcss": "^5.0.13", - "postcss-value-parser": "^3.2.3" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "^5.0.11", - "postcss-value-parser": "^3.1.2" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "^5.0.14" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "^5.0.16" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "^1.5.2", - "caniuse-api": "^1.5.2", - "postcss": "^5.0.4", - "postcss-selector-parser": "^2.2.2", - "vendors": "^1.0.0" - } - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "^5.0.12", - "postcss-value-parser": "^3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.2", - "postcss-value-parser": "^3.0.2", - "uniqs": "^2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.2", - "has": "^1.0.1", - "postcss": "^5.0.14", - "postcss-selector-parser": "^2.0.0" - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "^5.0.5" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^1.4.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.1" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.8", - "postcss-value-parser": "^3.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "^2.0.0", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "svgo": "^0.7.0" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - } - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "~1.0.1", - "colors": "~1.1.2", - "csso": "~2.3.1", - "js-yaml": "~3.7.0", - "mkdirp": "~0.5.1", - "sax": "~1.2.1", - "whet.extend": "~0.9.9" - } - } - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-select-base-adapter": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", - "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", - "dev": true - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" - }, - "dependencies": { - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - } - } - }, - "css-tree": { - "version": "1.0.0-alpha.28", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", - "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - } - }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=", - "dev": true - }, - "css-url-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/css-url-regex/-/css-url-regex-1.1.0.tgz", - "integrity": "sha1-g4NCMMyfdMRX3lnuvRVD/uuDt+w=", - "dev": true - }, - "css-what": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", - "integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==", - "dev": true - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz", - "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==", - "dev": true, - "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.7", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dev": true, - "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-preset-default": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz", - "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==", - "dev": true, - "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.1", - "postcss-colormin": "^4.0.3", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.2", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.11", - "postcss-merge-rules": "^4.0.3", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.2", - "postcss-minify-params": "^4.0.2", - "postcss-minify-selectors": "^4.0.2", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.2", - "postcss-normalize-positions": "^4.0.2", - "postcss-normalize-repeat-style": "^4.0.2", - "postcss-normalize-string": "^4.0.2", - "postcss-normalize-timing-functions": "^4.0.2", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.2", - "postcss-ordered-values": "^4.1.2", - "postcss-reduce-initial": "^4.0.3", - "postcss-reduce-transforms": "^4.0.2", - "postcss-svgo": "^4.0.2", - "postcss-unique-selectors": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", - "dev": true, - "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - } - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "dev": true, - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-util-get-arguments": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", - "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=", - "dev": true - }, - "cssnano-util-get-match": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", - "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=", - "dev": true - }, - "cssnano-util-raw-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", - "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cssnano-util-same-parent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", - "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", - "dev": true - }, - "csso": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", - "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", - "dev": true, - "requires": { - "css-tree": "1.0.0-alpha.29" - }, - "dependencies": { - "css-tree": { - "version": "1.0.0-alpha.29", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", - "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", - "dev": true, - "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" - } - } - } - }, - "cssom": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", - "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", - "dev": true - }, - "cssstyle": { - "version": "0.2.37", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", - "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", - "dev": true, - "requires": { - "cssom": "0.3.x" - } - }, - "csstype": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.0.tgz", - "integrity": "sha512-KyGTo7/Y1lIK+JIfZ4LNEFdjN5lYwmJPpiP2fJbZF9LL95sY4CvUibobzzyXVsiuiD/oKcCzGZfT7WWZxHvkCw==", - "dev": true - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "data-tooltip": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/data-tooltip/-/data-tooltip-0.0.1.tgz", - "integrity": "sha1-LS8jCI5Dkv/wY58ZVIkXAJg+Y8Q=" - }, - "data-urls": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz", - "integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==", - "dev": true, - "requires": { - "abab": "^1.0.4", - "whatwg-mimetype": "^2.0.0", - "whatwg-url": "^6.4.0" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.0.tgz", - "integrity": "sha512-ZbfWJq/wN1Z273o7mUSjILYqehAktR2NVoSrOukDkU9kg2v/Uv89yU4Cvz8seJeAmtN5oqiefKq8FPuXOboqLw==", - "requires": { - "is-arguments": "^1.0.4", - "is-date-object": "^1.0.1", - "is-regex": "^1.0.4", - "object-is": "^1.0.1", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.2.0" - }, - "dependencies": { - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "deep-equal-ident": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz", - "integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=", - "dev": true, - "requires": { - "lodash.isequal": "^3.0" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", - "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true - }, - "deps-sort": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", - "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", - "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "shasum": "^1.0.0", - "subarg": "^1.0.0", - "through2": "^2.0.0" - } - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", - "dev": true - }, - "detective": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", - "integrity": "sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig==", - "dev": true, - "requires": { - "acorn": "^5.2.1", - "defined": "^1.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - } - } - }, - "dezalgo": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", - "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "dir-glob": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", - "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", - "dev": true - }, - "dom-helpers": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", - "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", - "requires": { - "@babel/runtime": "^7.1.2" - } - }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", - "dev": true, - "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - } - } - }, - "dom-walk": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotize": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dotize/-/dotize-0.2.0.tgz", - "integrity": "sha1-aeUvSisTNExW/yPHA8MHSi1uV8c=" - }, - "dragula": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.1.tgz", - "integrity": "sha1-xE6BHbxC3FxlTatxx3AymVFE5oc=", - "requires": { - "contra": "1.9.4", - "crossvent": "1.5.4" - } - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "electron-to-chromium": { - "version": "1.3.45", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.45.tgz", - "integrity": "sha1-RYrBscXHYM6IEaFtK/vZfsMLr7g=", - "dev": true - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "enzyme": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.8.0.tgz", - "integrity": "sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw==", - "dev": true, - "requires": { - "array.prototype.flat": "^1.2.1", - "cheerio": "^1.0.0-rc.2", - "function.prototype.name": "^1.1.0", - "has": "^1.0.3", - "is-boolean-object": "^1.0.0", - "is-callable": "^1.1.4", - "is-number-object": "^1.0.3", - "is-string": "^1.0.4", - "is-subset": "^0.1.1", - "lodash.escape": "^4.0.1", - "lodash.isequal": "^4.5.0", - "object-inspect": "^1.6.0", - "object-is": "^1.0.1", - "object.assign": "^4.1.0", - "object.entries": "^1.0.4", - "object.values": "^1.0.4", - "raf": "^3.4.0", - "rst-selector-parser": "^2.2.3", - "string.prototype.trim": "^1.1.2" - }, - "dependencies": { - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", - "dev": true - } - } - }, - "enzyme-adapter-react-16": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz", - "integrity": "sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg==", - "dev": true, - "requires": { - "enzyme-adapter-utils": "^1.10.0", - "function.prototype.name": "^1.1.0", - "object.assign": "^4.1.0", - "object.values": "^1.1.0", - "prop-types": "^15.6.2", - "react-is": "^16.7.0", - "react-test-renderer": "^16.0.0-0" - }, - "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - } - } - }, - "enzyme-adapter-utils": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz", - "integrity": "sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA==", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.0", - "object.assign": "^4.1.0", - "object.fromentries": "^2.0.0", - "prop-types": "^15.6.2", - "semver": "^5.6.0" - }, - "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, - "semver": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", - "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "dev": true - } - } - }, - "enzyme-matchers": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-7.0.1.tgz", - "integrity": "sha512-1HmUK3frVSt7kim8icdx5LimuQm+DlklBRfy+dSlKd4SRxwlDGEM1LYTxL21/2kUZNl1XVUT5k5mec/D3k5jWw==", - "dev": true, - "requires": { - "circular-json-es6": "^2.0.1", - "deep-equal-ident": "^1.1.1" - } - }, - "enzyme-to-json": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.3.tgz", - "integrity": "sha1-7eRZOPswnNh+vUOG9gx1RSVRWgc=", - "dev": true, - "requires": { - "lodash": "^4.17.4" - } - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "error": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/error/-/error-4.4.0.tgz", - "integrity": "sha1-v2n/JR+0onnBmtzNqmth6Q2b8So=", - "requires": { - "camelize": "^1.0.0", - "string-template": "~0.2.0", - "xtend": "~4.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", - "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } - }, - "es5-ext": { - "version": "0.10.42", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz", - "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==", - "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" - }, - "dependencies": { - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true - } - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-promise": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.1.1.tgz", - "integrity": "sha1-A+jzxyl5KOVHjWqx0GQyUVB73t0=" - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "escape-regexp": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/escape-regexp/-/escape-regexp-0.0.1.tgz", - "integrity": "sha1-9EvaEtRbvfnLf4Yu5+SCez3TIlQ=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", - "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", - "dev": true, - "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", - "dev": true - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "ev-emitter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.0.3.tgz", - "integrity": "sha1-7l73S27SjZEbMt3L/JeGf1gJN7w=" - }, - "ev-store": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ev-store/-/ev-store-7.0.0.tgz", - "integrity": "sha1-GrDH+CE2UF3XSzHRdwHLK+bSZVg=", - "requires": { - "individual": "^3.0.0" - } - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "eventemitter3": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", - "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "exports-loader": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.7.0.tgz", - "integrity": "sha512-RKwCrO4A6IiKm0pG3c9V46JxIHcDplwwGJn6+JJ1RcVnh/WSGJa0xkmk5cRVtgOPzCAtTMGj2F7nluh9L0vpSA==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "source-map": "0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", - "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", - "dev": true - } - } - }, - "expose-loader": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-0.7.5.tgz", - "integrity": "sha512-iPowgKUZkTPX5PznYsmifVj9Bob0w2wTHVkt/eYNPSzyebkUgIedmskf/kcfEIWpiWjg3JRjnW+a17XypySMuw==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extract-text-webpack-plugin": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz", - "integrity": "sha512-bt/LZ4m5Rqt/Crl2HiKuAl/oqg0psx1tsTLkvWbJen1CtD+fftkZhMaQ9HOtY2gWsl2Wq+sABmMVi9z3DhKWQQ==", - "dev": true, - "requires": { - "async": "^2.4.1", - "loader-utils": "^1.1.0", - "schema-utils": "^0.3.0", - "webpack-sources": "^1.0.1" - }, - "dependencies": { - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "dev": true, - "requires": { - "lodash": "^4.14.0" - } - } - } - }, - "extract-zip": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", - "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", - "dev": true, - "requires": { - "concat-stream": "1.6.2", - "debug": "2.6.9", - "mkdirp": "0.5.1", - "yauzl": "2.4.1" - }, - "dependencies": { - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - }, - "fast-diff": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", - "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "fb-watchman": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", - "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", - "dev": true, - "requires": { - "bser": "^2.0.0" - } - }, - "fbjs": { - "version": "0.8.16", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", - "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.9" - }, - "dependencies": { - "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" - } - } - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "ff-core": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/ff-core/-/ff-core-0.0.2.tgz", - "integrity": "sha1-rP/kiMxhpD/CPxoDfQOmbCvj93s=", - "requires": { - "flimflam-render": "0.1.4", - "flyd": "0.2.2", - "form-serialize": "0.7.1", - "ramda": "0.21.0", - "snabbdom": "0.4.2" - }, - "dependencies": { - "form-serialize": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/form-serialize/-/form-serialize-0.7.1.tgz", - "integrity": "sha1-SPdON9BB3k99FA3SxUs3//jS6EM=" - }, - "snabbdom": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.4.2.tgz", - "integrity": "sha1-rbFSJ0AAmIB2CYlltI0Dqw6x4KY=" - } - } - }, - "ff-dashboard": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/ff-dashboard/-/ff-dashboard-0.0.4.tgz", - "integrity": "sha1-jsXtgSjWWT/IwvYuS0Sjvit6pho=", - "requires": { - "flimflam-render": "0.1.4", - "flyd": "0.2.2", - "flyd-ajax": "0.1.0", - "flyd-flatmap": "1.0.2", - "form-serialize": "^0.7.1", - "ramda": "0.22.1", - "snabbdom": "0.5.3" - }, - "dependencies": { - "flyd-ajax": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/flyd-ajax/-/flyd-ajax-0.1.0.tgz", - "integrity": "sha1-C5QAq50eOq6gHcdRyhxegDXKX5U=", - "requires": { - "flyd": "0.2.2", - "ramda": "0.21.0" - }, - "dependencies": { - "ramda": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz", - "integrity": "sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=" - } - } - }, - "form-serialize": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/form-serialize/-/form-serialize-0.7.2.tgz", - "integrity": "sha1-sKL/DCICb7bT0VydM/beakMuRzI=" - }, - "ramda": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.22.1.tgz", - "integrity": "sha1-Ax2gw99BfFszyWI0dX6zcDPzag4=" - }, - "snabbdom": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.5.3.tgz", - "integrity": "sha1-yLFPisPbsOg9i1bBRokCU1o7okE=" - } - } - }, - "ff-file-uploader": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/ff-file-uploader/-/ff-file-uploader-0.0.6.tgz", - "integrity": "sha1-KRuzT3dE/J+Fjsv5hNI9o2jyYkk=", - "requires": { - "flimflam-render": "0.2.1", - "flyd": "0.2.2", - "ramda": "0.23.0", - "snabbdom": "0.6.1" - }, - "dependencies": { - "flimflam-render": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/flimflam-render/-/flimflam-render-0.2.1.tgz", - "integrity": "sha1-XOwIie9VjfTr7cy7Gc5ogWEnY/U=", - "requires": { - "flyd": "0.2.2", - "ramda": "0.23.0", - "snabbdom": "0.6.3" - }, - "dependencies": { - "snabbdom": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.6.3.tgz", - "integrity": "sha1-XqpVp0seVC5f/75rjq8XCwxLpTM=" - } - } - }, - "ramda": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.23.0.tgz", - "integrity": "sha1-zNE//3NJepOXTj6GMnv9h71ujis=" - }, - "snabbdom": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.6.1.tgz", - "integrity": "sha1-sAb7lD3CcQshK/VkCq9XBMFSP5o=" - } - } - }, - "file-loader": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", - "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" - }, - "dependencies": { - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - } - }, - "fj-curry": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fj-curry/-/fj-curry-1.0.0.tgz", - "integrity": "sha1-JyWhdRJtKeNrTyNg6LU87bgZMdE=" - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "flimflam": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/flimflam/-/flimflam-0.7.5.tgz", - "integrity": "sha1-/xKpr6eRpma6+MwGn1A1zp3vk3c=", - "requires": { - "flyd": "0.2.4", - "form-serialize": "0.7.1", - "ramda": "0.23.0", - "snabbdom": "0.6.5", - "snabbdom-merge": "0.0.4" - }, - "dependencies": { - "flyd": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/flyd/-/flyd-0.2.4.tgz", - "integrity": "sha1-BBTIN8NPrmmxJ3dSzwBDgu/7mQA=", - "requires": { - "ramda": "^0.19.1" - }, - "dependencies": { - "ramda": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.19.1.tgz", - "integrity": "sha1-icStaXJl/2sfrOnyhkOeJSDWZ5w=" - } - } - }, - "form-serialize": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/form-serialize/-/form-serialize-0.7.1.tgz", - "integrity": "sha1-SPdON9BB3k99FA3SxUs3//jS6EM=" - }, - "ramda": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.23.0.tgz", - "integrity": "sha1-zNE//3NJepOXTj6GMnv9h71ujis=" - }, - "snabbdom": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.6.5.tgz", - "integrity": "sha1-AbDLqNYj7KGeVwh2YwwSwFeDAGY=" - } - } - }, - "flimflam-docs-scaffold": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/flimflam-docs-scaffold/-/flimflam-docs-scaffold-0.0.8.tgz", - "integrity": "sha1-26xIKACtHIV3T1SO/umR3zmIZsw=", - "requires": { - "flimflam": "0.7.1", - "flyd-url": "0.0.1", - "imagesloaded": "4.1.1", - "ramda": "0.23.0" - }, - "dependencies": { - "flimflam": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/flimflam/-/flimflam-0.7.1.tgz", - "integrity": "sha1-RdhiUBgzdXheFcWY6OqEsuEIvr4=", - "requires": { - "flyd": "0.2.4", - "form-serialize": "0.7.1", - "ramda": "0.23.0", - "snabbdom": "0.6.5", - "snabbdom-merge": "0.0.4" - } - }, - "flyd": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/flyd/-/flyd-0.2.4.tgz", - "integrity": "sha1-BBTIN8NPrmmxJ3dSzwBDgu/7mQA=", - "requires": { - "ramda": "^0.19.1" - }, - "dependencies": { - "ramda": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.19.1.tgz", - "integrity": "sha1-icStaXJl/2sfrOnyhkOeJSDWZ5w=" - } - } - }, - "form-serialize": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/form-serialize/-/form-serialize-0.7.1.tgz", - "integrity": "sha1-SPdON9BB3k99FA3SxUs3//jS6EM=" - }, - "ramda": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.23.0.tgz", - "integrity": "sha1-zNE//3NJepOXTj6GMnv9h71ujis=" - }, - "snabbdom": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.6.5.tgz", - "integrity": "sha1-AbDLqNYj7KGeVwh2YwwSwFeDAGY=" - } - } - }, - "flimflam-render": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/flimflam-render/-/flimflam-render-0.1.4.tgz", - "integrity": "sha1-4Hfx2zHqYhSUlHjP41dziy1TH0k=", - "requires": { - "flyd": "0.2.1", - "ramda": "0.19.1" - }, - "dependencies": { - "flyd": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/flyd/-/flyd-0.2.1.tgz", - "integrity": "sha1-tLPyIJ1NUpW8r3Hm4P8XP9/z1U0=", - "requires": { - "ramda": "^0.17.0" - }, - "dependencies": { - "ramda": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.17.1.tgz", - "integrity": "sha1-TBmBR9OrVOjBUlXxFzDiEW9uYHM=" - } - } - }, - "ramda": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.19.1.tgz", - "integrity": "sha1-icStaXJl/2sfrOnyhkOeJSDWZ5w=" - } - } - }, - "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" - } - }, - "flyd": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/flyd/-/flyd-0.2.2.tgz", - "integrity": "sha1-t6CoUdWdfK5Lap+7yhyrJxmrBvA=", - "requires": { - "ramda": "^0.19.1" - }, - "dependencies": { - "ramda": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.19.1.tgz", - "integrity": "sha1-icStaXJl/2sfrOnyhkOeJSDWZ5w=" - } - } - }, - "flyd-ajax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/flyd-ajax/-/flyd-ajax-0.2.0.tgz", - "integrity": "sha1-x6Q02oChJOuBGuudEZM5Ub+I0t8=", - "requires": { - "flyd": "0.2.2", - "ramda": "0.21.0" - } - }, - "flyd-flatmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flyd-flatmap/-/flyd-flatmap-1.0.2.tgz", - "integrity": "sha1-wxx+Ln3d4BDjn984cHklidDvel8=", - "requires": { - "flyd": "0.1.x" - }, - "dependencies": { - "flyd": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/flyd/-/flyd-0.1.17.tgz", - "integrity": "sha1-a1aXDgFaeKpFH0M0D+BkzaW1deA=", - "requires": { - "ramda": "^0.17.0" - } - }, - "ramda": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.17.1.tgz", - "integrity": "sha1-TBmBR9OrVOjBUlXxFzDiEW9uYHM=" - } - } - }, - "flyd-url": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/flyd-url/-/flyd-url-0.0.1.tgz", - "integrity": "sha1-8PvfSOy3aO+bbZsyyzfccv+Pr4k=", - "requires": { - "flyd": "0.2.2", - "url": "0.11.0" - } - }, - "flyd-zip": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/flyd-zip/-/flyd-zip-0.0.1.tgz", - "integrity": "sha1-XPAYWVZG1XGhpmatKjJSHRo07sE=", - "requires": { - "flyd": "0.2.2", - "ramda": "0.21.0" - } - }, - "focus-group": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/focus-group/-/focus-group-0.3.1.tgz", - "integrity": "sha1-4PMu2GsNq91v/OvfiY7LMuR/7c4=" - }, - "focus-trap": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-3.0.0.tgz", - "integrity": "sha512-jTFblf0tLWbleGjj2JZsAKbgtZTdL1uC48L8FcmSDl4c2vDoU4NycN1kgV5vJhuq1mxNFkw7uWZ1JAGlINWvyw==", - "requires": { - "tabbable": "^3.1.0", - "xtend": "^4.0.1" - } - }, - "focus-trap-react": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/focus-trap-react/-/focus-trap-react-4.0.1.tgz", - "integrity": "sha512-UUZKVEn5cFbF6yUnW7lbXNW0iqN617ShSqYKgxctUvWw1wuylLtyVmC0RmPQNnJ/U+zoKc/djb0tZMs0uN/0QQ==", - "requires": { - "focus-trap": "^3.0.0" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "form-serialize": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/form-serialize/-/form-serialize-0.7.0.tgz", - "integrity": "sha1-trbumKpHoZzlA2q8KvleNbdTnq4=" - }, - "format-number": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/format-number/-/format-number-2.0.2.tgz", - "integrity": "sha1-i+Kqb6bvXsQ4MIkrHa7nJwX5tGI=" - }, - "formidable": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz", - "integrity": "sha1-Kz9MQRy7X91pXESEPiojUUpDIxo=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz", - "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "is-callable": "^1.1.3" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "requires": { - "globule": "^1.0.0" - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" - } - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - }, - "dependencies": { - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "globule": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", - "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", - "dev": true, - "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true - }, - "handlebars": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", - "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", - "dev": true, - "requires": { - "neo-async": "^2.6.0", - "optimist": "^0.6.1", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "uglify-js": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", - "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==", - "dev": true, - "optional": true, - "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" - } - } - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", - "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", - "dev": true, - "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "requires": { - "function-bind": "^1.0.2" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "hasha": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", - "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", - "dev": true, - "requires": { - "is-stream": "^1.0.1", - "pinkie-promise": "^2.0.0" - } - }, - "hex-color-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", - "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hoek": { - "version": "4.2.1", - "resolved": "http://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, - "hoist-non-react-statics": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", - "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", - "requires": { - "react-is": "^16.7.0" - } - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz", - "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==" - }, - "hsl-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", - "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=", - "dev": true - }, - "hsla-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz", - "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=", - "dev": true - }, - "html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "htmlescape": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", - "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", - "dev": true - }, - "htmlparser2": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", - "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.0", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", - "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", - "dev": true - }, - "i18n-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.0.3.tgz", - "integrity": "sha512-u144MQhV/8mz4Y5wP86SQAWMwS8gpe/JavIa9hugSI4WreezGgbhJPdk2Q60KcdIltKLiNefGtHNh1N8SSmQqQ==" - }, - "iban": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/iban/-/iban-0.0.8.tgz", - "integrity": "sha1-TyPSOtUEoxmjqfduyQkHu0E8rhg=" - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "icss-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", - "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", - "dev": true, - "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "ieee754": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.11.tgz", - "integrity": "sha512-VhDzCKN7K8ufStx/CLj5/PDTMgph+qwN5Pkd5i0sGnVwk56zJ0lkT8Qzi1xqWLS0Wp29DgDtNeS7v8/wMoZeHg==" - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.8.tgz", - "integrity": "sha512-pUh+xUQQhQzevjRHHFqqcTy0/dP/kS9I8HSrUydhihjuD09W6ldVWFtIrwhXdUJHis3i2rZNqEHpZH/cbinFbg==", - "dev": true - }, - "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true - }, - "imagesloaded": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.1.tgz", - "integrity": "sha1-U7W2ZhU2CFClomSxKT5/TQbTvVE=", - "requires": { - "ev-emitter": "~1.0.0" - } - }, - "immutable": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.5.tgz", - "integrity": "sha1-VX0D5cKtuXn0ze5JRUQ0wJw2EOQ=" - }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "imports-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.8.0.tgz", - "integrity": "sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ==", - "dev": true, - "requires": { - "loader-utils": "^1.0.2", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "in-publish": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", - "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "individual": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/individual/-/individual-3.0.0.tgz", - "integrity": "sha1-58pPhfiVewGHNPKFdQ3CLsL5hi0=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inline-source-map": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", - "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", - "dev": true, - "requires": { - "source-map": "~0.5.3" - } - }, - "insert-module-globals": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.6.tgz", - "integrity": "sha512-R3sidKJr3SsggqQQ5cEwQb3pWG8RNx0UnpyeiOSR6jorRIeAOzH2gkTWnNdMnyRiVbjrG047K7UCtlMkQ1Mo9w==", - "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "combine-source-map": "^0.8.0", - "concat-stream": "^1.6.1", - "is-buffer": "^1.1.0", - "lexical-scope": "^1.2.0", - "path-is-absolute": "^1.0.1", - "process": "~0.11.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - } - } - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "intl-format-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-2.1.0.tgz", - "integrity": "sha1-BKNp/sv61tpgBbrh8UMzMy3PkxY=" - }, - "intl-messageformat": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-2.2.0.tgz", - "integrity": "sha1-NFvNRt5jC3aDMwwuUhd/9eq0hPw=", - "requires": { - "intl-messageformat-parser": "1.4.0" - } - }, - "intl-messageformat-parser": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-1.4.0.tgz", - "integrity": "sha1-tD1FqXRoytvkQzHXS7Ho3qRPwHU=" - }, - "intl-relativeformat": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/intl-relativeformat/-/intl-relativeformat-2.1.0.tgz", - "integrity": "sha1-AQ8RBYAiUfQKxH0OPhogE0iiVd8=", - "requires": { - "intl-messageformat": "^2.0.0" - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arguments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", - "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-boolean-object": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz", - "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" - }, - "is-color-stop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz", - "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", - "dev": true, - "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-generator-fn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.0.0.tgz", - "integrity": "sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==", - "dev": true - }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-number-object": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz", - "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=", - "dev": true - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" - }, - "is-odd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", - "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", - "dev": true, - "requires": { - "is-number": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - } - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-string": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz", - "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=", - "dev": true - }, - "is-subset": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", - "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=", - "dev": true - }, - "is-svg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", - "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", - "dev": true, - "requires": { - "html-comment-regex": "^1.1.0" - } - }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isnumeric": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/isnumeric/-/isnumeric-0.2.0.tgz", - "integrity": "sha1-ojR7o2DeGeM9D/1ZD933dVy/LmQ=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-api": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.5.tgz", - "integrity": "sha512-meYk1BwDp59Pfse1TvPrkKYgVqAufbdBLEVoqvu/hLLKSaQ054ZTksbNepyc223tMnWdm6AdK2URIJJRqdP87g==", - "dev": true, - "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.4", - "istanbul-lib-hook": "^2.0.6", - "istanbul-lib-instrument": "^3.2.0", - "istanbul-lib-report": "^2.0.7", - "istanbul-lib-source-maps": "^3.0.5", - "istanbul-reports": "^2.2.3", - "js-yaml": "^3.13.0", - "make-dir": "^2.1.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - }, - "dependencies": { - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.2.0.tgz", - "integrity": "sha512-06IM3xShbNW4NgZv5AP4QH0oHqf1/ivFo8eFys0ZjPXHGldHJQWb3riYOKXqmOqfxXBfxu4B+g/iuhOPZH0RJg==", - "dev": true, - "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.4", - "semver": "^6.0.0" - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.5.tgz", - "integrity": "sha512-eDhZ7r6r1d1zQPVZehLc3D0K14vRba/eBYkz3rw16DLOrrTzve9RmnkcwrrkWVgO1FL3EK5knujVe5S8QHE9xw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.4", - "make-dir": "^2.1.0", - "rimraf": "^2.6.2", - "source-map": "^0.6.1" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - } - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.0.0.tgz", - "integrity": "sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", - "dev": true - }, - "istanbul-lib-hook": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.6.tgz", - "integrity": "sha512-829DKONApZ7UCiPXcOYWSgkFXa4+vNYoNOt3F+4uDJLKL1OotAoVwvThoEj1i8jmOj7odbYcR3rnaHu+QroaXg==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, - "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", - "dev": true, - "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" - } - }, - "istanbul-lib-report": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.7.tgz", - "integrity": "sha512-wLH6beJBFbRBLiTlMOBxmb85cnVM1Vyl36N48e4e/aTKSM3WbOx7zbVIH1SQ537fhhsPbX0/C5JB4qsmyRXXyA==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^2.0.4", - "make-dir": "^2.1.0", - "supports-color": "^6.0.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", - "dev": true - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "rimraf": "^2.6.2", - "source-map": "^0.6.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.3.tgz", - "integrity": "sha512-T6EbPuc8Cb620LWAYyZ4D8SSn06dY9i1+IgUX2lTH8gbwflMc9Obd33zHTyNX653ybjpamAHS9toKS3E6cGhTw==", - "dev": true, - "requires": { - "handlebars": "^4.1.0" - } - }, - "jest": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.1.0.tgz", - "integrity": "sha512-+q91L65kypqklvlRFfXfdzUKyngQLOcwGhXQaLmVHv+d09LkNXuBuGxlofTFW42XMzu3giIcChchTsCNUjQ78A==", - "dev": true, - "requires": { - "import-local": "^2.0.0", - "jest-cli": "^24.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@cnakazawa/watch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", - "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", - "dev": true, - "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/babel__core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.6.0", - "chalk": "^2.4.2", - "slash": "^2.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz", - "integrity": "sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w==", - "dev": true, - "requires": { - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-jest": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz", - "integrity": "sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.6.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "dev": true, - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "diff-sequences": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true - }, - "exec-sh": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", - "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "expect": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", - "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-regex-util": "^24.3.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "jest-cli": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz", - "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==", - "dev": true, - "requires": { - "@jest/core": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "import-local": "^2.0.0", - "is-ci": "^2.0.0", - "jest-config": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "prompts": "^2.0.1", - "realpath-native": "^1.1.0", - "yargs": "^12.0.2" - }, - "dependencies": { - "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", - "realpath-native": "^1.1.0" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" - } - } - } - }, - "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - } - } - }, - "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - } - } - }, - "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", - "jsdom": "^11.5.1" - } - }, - "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" - } - }, - "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "co": "^4.6.0", - "expect": "^24.7.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", - "throat": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", - "chalk": "^2.0.1", - "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", - "realpath-native": "^1.1.0" - } - }, - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - }, - "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "anymatch": "^2.0.0", - "fb-watchman": "^2.0.0", - "fsevents": "^1.2.7", - "graceful-fs": "^4.1.15", - "invariant": "^2.2.4", - "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", - "jest-worker": "^24.6.0", - "micromatch": "^3.1.10", - "sane": "^4.0.3", - "walker": "^1.0.7" - } - }, - "jest-regex-util": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.3.0.tgz", - "integrity": "sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg==", - "dev": true - }, - "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/yargs": "^12.0.2", - "chalk": "^2.0.1", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", - "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "realpath-native": "^1.1.0", - "slash": "^2.0.0", - "strip-bom": "^3.0.0", - "yargs": "^12.0.2" - } - }, - "jest-snapshot": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", - "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^24.7.0", - "chalk": "^2.0.1", - "expect": "^24.7.1", - "jest-diff": "^24.7.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^24.7.0", - "semver": "^5.5.0" - } - }, - "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "camelcase": "^5.0.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "leven": "^2.1.0", - "pretty-format": "^24.7.0" - } - }, - "jest-worker": { - "version": "24.6.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.6.0.tgz", - "integrity": "sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ==", - "dev": true, - "requires": { - "merge-stream": "^1.0.1", - "supports-color": "^6.1.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", - "dev": true, - "requires": { - "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" - }, - "dependencies": { - "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true - } - } - }, - "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "browser-resolve": "^1.11.3", - "chalk": "^2.0.1", - "jest-pnp-resolver": "^1.2.1", - "realpath-native": "^1.1.0" - } - }, - "jest-serializer": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.4.0.tgz", - "integrity": "sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q==", - "dev": true - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pretty-format": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", - "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0", - "ansi-regex": "^4.0.0", - "ansi-styles": "^3.2.0", - "react-is": "^16.8.4" - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "react-is": { - "version": "16.8.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", - "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - }, - "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - } - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "12.0.5", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", - "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", - "dev": true, - "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.1" - } - }, - "yargs-parser": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", - "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "jest-environment-enzyme": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/jest-environment-enzyme/-/jest-environment-enzyme-7.0.2.tgz", - "integrity": "sha512-cBBWX3ZCR4JJR6ml6cTYLH1fdBZ2QEbywY6yrkgTP6OZgE+Nh6Agu0g99uJha2F36+zRFS/xkk8AIEAJvLtzQQ==", - "dev": true, - "requires": { - "jest-environment-jsdom": "^24.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", - "dev": true, - "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", - "jsdom": "^11.5.1" - } - }, - "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", - "dev": true, - "requires": { - "@jest/types": "^24.7.0" - } - }, - "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", - "dev": true, - "requires": { - "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", - "callsites": "^3.0.0", - "chalk": "^2.0.1", - "graceful-fs": "^4.1.15", - "is-ci": "^2.0.0", - "mkdirp": "^0.5.1", - "slash": "^2.0.0", - "source-map": "^0.6.0" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "jest-enzyme": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/jest-enzyme/-/jest-enzyme-7.0.1.tgz", - "integrity": "sha512-IpHjNin+7bsRthciMMo5HVA9TTKZCsBkOPsS4qdIZvImZx94no362wwPeustn0SY/REyXm39wHPsORknuXUWmA==", - "dev": true, - "requires": { - "enzyme-matchers": "^7.0.1", - "enzyme-to-json": "^3.3.0", - "jest-environment-enzyme": "^7.0.1" - } - }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true - }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" - }, - "jquery": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.11.1.tgz", - "integrity": "sha1-tuyShZARLr7Wnh5Jy/0AJczWDds=" - }, - "jquery.cookie": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha1-1j3OIJ6raR/mMxbbCMqeR+D5OFs=" - }, - "js-base64": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.3.tgz", - "integrity": "sha512-H7ErYLM34CvDMto3GbD6xD0JLUGYXR3QTcH6B/tr4Hi/QpSThnCsIp+Sy5FRTw3B0d6py4HcNkW7nO/wdtGWEw==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "jsdom": { - "version": "11.10.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.10.0.tgz", - "integrity": "sha512-x5No5FpJgBg3j5aBwA8ka6eGuS5IxbC8FOkmyccKvObtFT0bDMict/LOxINZsZGZSfGdNomLZ/qRV9Bpq/GIBA==", - "dev": true, - "requires": { - "abab": "^1.0.4", - "acorn": "^5.3.0", - "acorn-globals": "^4.1.0", - "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": ">= 0.2.37 < 0.3.0", - "data-urls": "^1.0.0", - "domexception": "^1.0.0", - "escodegen": "^1.9.0", - "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.2.0", - "nwmatcher": "^1.4.3", - "parse5": "4.0.0", - "pn": "^1.1.0", - "request": "^2.83.0", - "request-promise-native": "^1.0.5", - "sax": "^1.2.4", - "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.3", - "w3c-hr-time": "^1.0.1", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.0", - "ws": "^4.0.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "1.0.6", - "mime-types": "^2.1.12" - } - }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "dev": true, - "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" - } - }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" - } - }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "request": { - "version": "2.86.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.86.0.tgz", - "integrity": "sha512-BQZih67o9r+Ys94tcIW4S7Uu8pthjrQVxhsZ/weOwHbDfACxvIyvnAbzFQxjy1jMtvFSzv5zf4my6cZsJBbVzw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", - "dev": true - } - } - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json-stable-stringify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", - "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", - "dev": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", - "dev": true - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "kleur": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.2.tgz", - "integrity": "sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q==", - "dev": true - }, - "labeled-stream-splicer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz", - "integrity": "sha512-MC94mHZRvJ3LfykJlTUipBqenZz1pacOZEMhhQ8dMGcDHs0SBE5GbsavUXV7YtP3icBW17W0Zy1I0lfASmo9Pg==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "isarray": "^2.0.4", - "stream-splicer": "^2.0.0" - }, - "dependencies": { - "isarray": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", - "integrity": "sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA==", - "dev": true - } - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "left-pad": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", - "dev": true - }, - "less": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/less/-/less-3.0.4.tgz", - "integrity": "sha512-q3SyEnPKbk9zh4l36PGeW2fgynKu+FpbhiUNx/yaiBUQ3V0CbACCgb9FzYWcRgI2DJlP6eI4jc8XPrCTi55YcQ==", - "dev": true, - "requires": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "optional": true - }, - "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", - "dev": true, - "optional": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true, - "optional": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "optional": true - }, - "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", - "dev": true, - "optional": true, - "requires": { - "mime-db": "~1.37.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "optional": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "optional": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true, - "optional": true - } - } - }, - "less-loader": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", - "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", - "dev": true, - "requires": { - "clone": "^2.1.1", - "loader-utils": "^1.1.0", - "pify": "^3.0.0" - }, - "dependencies": { - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lexical-scope": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", - "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", - "dev": true, - "requires": { - "astw": "^2.0.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "lodash": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", - "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==" - }, - "lodash._baseassign": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", - "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", - "dev": true, - "requires": { - "lodash._basecopy": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._basecopy": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", - "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", - "dev": true - }, - "lodash._baseisequal": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz", - "integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=", - "dev": true, - "requires": { - "lodash.isarray": "^3.0.0", - "lodash.istypedarray": "^3.0.0", - "lodash.keys": "^3.0.0" - } - }, - "lodash._bindcallback": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz", - "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=", - "dev": true - }, - "lodash._createassigner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz", - "integrity": "sha1-g4pbri/aymOsIt7o4Z+k5taXCxE=", - "dev": true, - "requires": { - "lodash._bindcallback": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash.restparam": "^3.0.0" - } - }, - "lodash._getnative": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", - "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", - "dev": true - }, - "lodash._isiterateecall": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", - "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", - "dev": true - }, - "lodash._reinterpolate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", - "dev": true - }, - "lodash.assign": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", - "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", - "dev": true - }, - "lodash.escape": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", - "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=", - "dev": true - }, - "lodash.flattendeep": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", - "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", - "dev": true - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", - "dev": true - }, - "lodash.isarray": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", - "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", - "dev": true - }, - "lodash.isequal": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz", - "integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=", - "dev": true, - "requires": { - "lodash._baseisequal": "^3.0.0", - "lodash._bindcallback": "^3.0.0" - } - }, - "lodash.istypedarray": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz", - "integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" - } - }, - "lodash.memoize": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", - "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", - "dev": true - }, - "lodash.restparam": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", - "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", - "dev": true - }, - "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0", - "lodash.templatesettings": "^4.0.0" - } - }, - "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", - "dev": true, - "requires": { - "lodash._reinterpolate": "~3.0.0" - } - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "lolex": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.6.0.tgz", - "integrity": "sha512-e1UtIo1pbrIqEXib/yMjHciyqkng5lc0rrIbytgjmRgDR9+2ceNIAcwOWSgylRjoEP9VdVguCSRwnNmlbnOUwA==", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "requires": { - "js-tokens": "^3.0.0" - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "requires": { - "p-defer": "^1.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "marked": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz", - "integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==" - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "mdn-data": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", - "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", - "dev": true - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, - "merge-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", - "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "methods": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.0.1.tgz", - "integrity": "sha1-dbyRlD3/19oDfPPusO1zoAN80Us=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", - "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "dev": true, - "requires": { - "mime-db": "~1.33.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "requires": { - "dom-walk": "^0.1.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "dev": true, - "requires": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", - "dev": true - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mobx": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-4.3.1.tgz", - "integrity": "sha1-M05aq0kWsdQ/D682BaZLG0s8y40=" - }, - "mobx-react": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.3.tgz", - "integrity": "sha512-WC8yFlwvJ91hy8j6CrydAuFteUafcuvdITFQeHl3LRIf5ayfT/4W3M/byhEYD2BcJWejeXr8y4Rh2H26RunCRQ==", - "requires": { - "hoist-non-react-statics": "^3.0.0", - "react-lifecycles-compat": "^3.0.2" - } - }, - "mobx-react-devtools": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mobx-react-devtools/-/mobx-react-devtools-5.0.1.tgz", - "integrity": "sha512-Uv86FWqVmmqaEs3oBOCs7AJDKZL8LIYG43ZRMP/xVX0PB/ytY3hdj16kXZ/RdbFLESEKws/+IyVboj0nCesd6w==" - }, - "mobx-react-form": { - "version": "github:houdiniproject/mobx-react-form#08048878a4f01da8cfdc8dad417b70766357dfdc", - "from": "github:houdiniproject/mobx-react-form#our_fix", - "requires": { - "babel-plugin-transform-runtime": "^6.23.0", - "lodash": "^4.16.2" - } - }, - "mobx-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mobx-utils/-/mobx-utils-5.0.1.tgz", - "integrity": "sha512-L4nUNOK2LJ1ca/jc7jQyMGhPHyBJ7yFi1P6k+4FfQgxT50IBMSGQTuxSUFFuSSz0pVU0SM5jbwPGrlJW3TWvEA==" - }, - "module-deps": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", - "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", - "dev": true, - "requires": { - "JSONStream": "^1.0.3", - "browser-resolve": "^1.7.0", - "cached-path-relative": "^1.0.0", - "concat-stream": "~1.5.0", - "defined": "^1.0.0", - "detective": "^4.0.0", - "duplexer2": "^0.1.2", - "inherits": "^2.0.1", - "parents": "^1.0.0", - "readable-stream": "^2.0.2", - "resolve": "^1.1.3", - "stream-combiner2": "^1.1.1", - "subarg": "^1.0.0", - "through2": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "moment": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", - "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" - }, - "moment-range": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/moment-range/-/moment-range-2.2.0.tgz", - "integrity": "sha1-sCV1d4pKxQpld3k59cXXV/g/zYU=" - }, - "moment-timezone": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz", - "integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==", - "requires": { - "moment": ">= 2.9.0" - } - }, - "moo": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz", - "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==", - "dev": true - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", - "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-odd": "^2.0.0", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nearley": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz", - "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==", - "dev": true, - "requires": { - "commander": "^2.19.0", - "moo": "^0.4.3", - "railroad-diagrams": "^1.0.0", - "randexp": "0.4.6", - "semver": "^5.4.1" - } - }, - "neo-async": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", - "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", - "dev": true - }, - "next-tick": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz", - "integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0=" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "nise": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.8.tgz", - "integrity": "sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^3.1.0", - "just-extend": "^4.0.2", - "lolex": "^2.3.2", - "path-to-regexp": "^1.7.0", - "text-encoding": "^0.6.4" - }, - "dependencies": { - "@sinonjs/formatio": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.1.0.tgz", - "integrity": "sha512-ZAR2bPHOl4Xg6eklUGpsdiIJ4+J1SNag1DHHrG/73Uz/nVwXqjgUtRPLoS+aVyieN9cSbc0E4LsU984tWcDyNg==", - "dev": true, - "requires": { - "@sinonjs/samsam": "^2 || ^3" - } - }, - "just-extend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.0.2.tgz", - "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", - "dev": true - } - } - }, - "no-case": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", - "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", - "requires": { - "lower-case": "^1.1.1" - } - }, - "no-scroll": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/no-scroll/-/no-scroll-2.1.1.tgz", - "integrity": "sha512-YTzGAJOo/B6hkodeT5SKKHpOhAzjMfkUCCXjLJwjWk2F4/InIg+HbdH9kmT7bKpleDuqLZDTRy2OdNtAj0IVyQ==" - }, - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-gyp": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", - "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "dev": true, - "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" - }, - "dependencies": { - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true - } - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - } - } - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz", - "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==", - "dev": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "node-releases": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.19.tgz", - "integrity": "sha512-SH/B4WwovHbulIALsQllAVwqZZD1kPmKCqrhGfR29dXjLAVZMHvBjD3S6nL9D/J9QkmZ1R92/0wCMDKXUUvyyA==", - "dev": true, - "requires": { - "semver": "^5.3.0" - } - }, - "node-sass": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", - "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", - "dev": true, - "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.11", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" - }, - "dependencies": { - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true - } - } - }, - "nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz", - "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", - "dev": true - }, - "noticeme": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/noticeme/-/noticeme-0.0.2.tgz", - "integrity": "sha512-W+NWW3pApNeYCR+7jBcxQHtEcxRkHTNF2tlT62NyxpNMi1XCpBMaSmtVHarP+WebXTTEK6/03JexHazdhlRuRQ==", - "requires": { - "node-fetch": "^2.6.0", - "read-package-tree": "^5.2.2" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - } - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "nwmatcher": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", - "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "dev": true - }, - "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" - }, - "object-keys": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", - "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" - }, - "object-path": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz", - "integrity": "sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.entries": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", - "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "dependencies": { - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", - "dev": true - } - } - }, - "object.fromentries": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz", - "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.11.0", - "function-bind": "^1.1.1", - "has": "^1.0.1" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "dependencies": { - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.0" - } - }, - "object-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", - "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==", - "dev": true - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onecolor": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/onecolor/-/onecolor-2.4.2.tgz", - "integrity": "sha1-pT7D/xccNEYBbdUhDRobVEv32HQ=", - "dev": true - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - }, - "dependencies": { - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } - } - }, - "os-browserify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", - "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "dev": true, - "requires": { - "lcid": "^1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", - "dev": true - }, - "p-each-series": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", - "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", - "dev": true, - "requires": { - "p-reduce": "^1.0.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-is-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", - "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", - "dev": true - }, - "p-limit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", - "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-reduce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", - "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", - "dev": true - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "parchment": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", - "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" - }, - "parents": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", - "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", - "dev": true, - "requires": { - "path-platform": "~0.11.15" - } - }, - "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "parse5": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", - "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "parsleyjs": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/parsleyjs/-/parsleyjs-2.0.7.tgz", - "integrity": "sha1-eaOR1HJ1w0EDJ6xrlyUdfcxmj28=" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "path-platform": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", - "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "dev": true, - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - } - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "percent": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/percent/-/percent-1.1.1.tgz", - "integrity": "sha1-DnwS+uCOJzlQYGrhEWsUz0LyqB4=" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "phantomjs-prebuilt": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", - "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3", - "extract-zip": "^1.6.5", - "fs-extra": "^1.0.0", - "hasha": "^2.2.0", - "kew": "^0.7.0", - "progress": "^1.1.8", - "request": "^2.81.0", - "request-progress": "^2.0.1", - "which": "^1.2.10" - }, - "dependencies": { - "es6-promise": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", - "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", - "dev": true - } - } - }, - "phone-formatter": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/phone-formatter/-/phone-formatter-0.0.2.tgz", - "integrity": "sha1-82JsfSdIYPAU9w9DqHVmoWsOes4=" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pikaday": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.3.2.tgz", - "integrity": "sha1-jwsXZZ4RfaVojCfvjjT5pZaZxpQ=", - "requires": { - "moment": "2.x" - } - }, - "pikaday-time": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pikaday-time/-/pikaday-time-1.5.1.tgz", - "integrity": "sha1-AuyKTXRB7TY87WSrnIBoEYE7kaM=", - "requires": { - "moment": "2.x" - } - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pixrem": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pixrem/-/pixrem-3.0.2.tgz", - "integrity": "sha1-MNG6+0w73Ojpu0vVahOYVhkyDDQ=", - "dev": true, - "requires": { - "browserslist": "^1.0.0", - "postcss": "^5.0.0", - "reduce-css-calc": "^1.2.7" - }, - "dependencies": { - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "^1.0.30000639", - "electron-to-chromium": "^1.2.7" - } - } - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "pleeease-filters": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/pleeease-filters/-/pleeease-filters-3.0.1.tgz", - "integrity": "sha1-Tf4OjxBGYTUXxktyi8gGCKfr8i8=", - "dev": true, - "requires": { - "onecolor": "~2.4.0", - "postcss": "^5.0.4" - } - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "js-base64": "^2.1.9", - "source-map": "^0.5.6", - "supports-color": "^3.2.3" - }, - "dependencies": { - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - } - } - }, - "postcss-apply": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/postcss-apply/-/postcss-apply-0.3.0.tgz", - "integrity": "sha1-ovN8W9+ogeTBX08kXsDNlt0ucNU=", - "dev": true, - "requires": { - "balanced-match": "^0.4.1", - "postcss": "^5.0.21" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "postcss-attribute-case-insensitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-1.0.1.tgz", - "integrity": "sha1-zrc3d+EGFn6yM/GTjJvZ8uaXMI0=", - "dev": true, - "requires": { - "postcss": "^5.1.1", - "postcss-selector-parser": "^2.2.0" - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "^5.0.2", - "postcss-message-helpers": "^2.0.0", - "reduce-css-calc": "^1.2.6" - } - }, - "postcss-color-function": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-function/-/postcss-color-function-2.0.1.tgz", - "integrity": "sha1-mtIm9VDop8f4uKd4YFRbbdf1UkE=", - "dev": true, - "requires": { - "css-color-function": "^1.2.0", - "postcss": "^5.0.4", - "postcss-message-helpers": "^2.0.0", - "postcss-value-parser": "^3.3.0" - } - }, - "postcss-color-gray": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-3.0.1.tgz", - "integrity": "sha1-dEMu3mbdg7HRNjVlxos3bhj/Z3A=", - "dev": true, - "requires": { - "color": "^0.11.3", - "postcss": "^5.0.4", - "postcss-message-helpers": "^2.0.0", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - } - } - }, - "postcss-color-hex-alpha": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-2.0.0.tgz", - "integrity": "sha1-RP1uyt5mAoZIyIHLZQTNy/3GzQk=", - "dev": true, - "requires": { - "color": "^0.10.1", - "postcss": "^5.0.4", - "postcss-message-helpers": "^2.0.0" - }, - "dependencies": { - "color": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/color/-/color-0.10.1.tgz", - "integrity": "sha1-wEGI34KiCd3rzOzazT7DIPGTc58=", - "dev": true, - "requires": { - "color-convert": "^0.5.3", - "color-string": "^0.3.0" - } - } - } - }, - "postcss-color-hsl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-color-hsl/-/postcss-color-hsl-1.0.5.tgz", - "integrity": "sha1-9Tuxw0gxDOMHrYnjGBqGRzi15oc=", - "dev": true, - "requires": { - "postcss": "^5.2.0", - "postcss-value-parser": "^3.3.0", - "units-css": "^0.4.0" - } - }, - "postcss-color-hwb": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-hwb/-/postcss-color-hwb-2.0.1.tgz", - "integrity": "sha1-1jr6+bcMtZX5AKKcn+V78qMvq+w=", - "dev": true, - "requires": { - "color": "^0.11.4", - "postcss": "^5.0.4", - "postcss-message-helpers": "^2.0.0", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - } - } - }, - "postcss-color-rebeccapurple": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-2.0.1.tgz", - "integrity": "sha1-dMZETny7fYVhO19yht96SRYIRRw=", - "dev": true, - "requires": { - "color": "^0.11.4", - "postcss": "^5.0.4" - }, - "dependencies": { - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "^1.0.2", - "color-convert": "^1.3.0", - "color-string": "^0.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - } - } - }, - "postcss-color-rgb": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/postcss-color-rgb/-/postcss-color-rgb-1.1.4.tgz", - "integrity": "sha1-8pJD4i6OjBNDRHQJI3LUzmBb6Lw=", - "dev": true, - "requires": { - "postcss": "^5.2.0", - "postcss-value-parser": "^3.3.0" - } - }, - "postcss-color-rgba-fallback": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-color-rgba-fallback/-/postcss-color-rgba-fallback-2.2.0.tgz", - "integrity": "sha1-bSlJG+WZCpMXPUfnx29YELCUAro=", - "dev": true, - "requires": { - "postcss": "^5.0.0", - "postcss-value-parser": "^3.0.2", - "rgb-hex": "^1.0.0" - } - }, - "postcss-colormin": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz", - "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-convert-values": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", - "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-cssnext": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/postcss-cssnext/-/postcss-cssnext-2.11.0.tgz", - "integrity": "sha1-MeaPAB5AlgTacDtm3hS4uMjJ8rE=", - "dev": true, - "requires": { - "autoprefixer": "^6.0.2", - "caniuse-api": "^1.5.3", - "chalk": "^1.1.1", - "pixrem": "^3.0.0", - "pleeease-filters": "^3.0.0", - "postcss": "^5.0.4", - "postcss-apply": "^0.3.0", - "postcss-attribute-case-insensitive": "^1.0.1", - "postcss-calc": "^5.0.0", - "postcss-color-function": "^2.0.0", - "postcss-color-gray": "^3.0.0", - "postcss-color-hex-alpha": "^2.0.0", - "postcss-color-hsl": "^1.0.5", - "postcss-color-hwb": "^2.0.0", - "postcss-color-rebeccapurple": "^2.0.0", - "postcss-color-rgb": "^1.1.4", - "postcss-color-rgba-fallback": "^2.0.0", - "postcss-custom-media": "^5.0.0", - "postcss-custom-properties": "^5.0.0", - "postcss-custom-selectors": "^3.0.0", - "postcss-font-family-system-ui": "^1.0.1", - "postcss-font-variant": "^2.0.0", - "postcss-image-set-polyfill": "^0.3.3", - "postcss-initial": "^1.3.1", - "postcss-media-minmax": "^2.1.0", - "postcss-nesting": "^2.0.5", - "postcss-pseudo-class-any-link": "^1.0.0", - "postcss-pseudoelements": "^3.0.0", - "postcss-replace-overflow-wrap": "^1.0.0", - "postcss-selector-matches": "^2.0.0", - "postcss-selector-not": "^2.0.0" - } - }, - "postcss-custom-media": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-5.0.1.tgz", - "integrity": "sha1-E40loYS/LrVN4S1VpsAcMKnYvYE=", - "dev": true, - "requires": { - "postcss": "^5.0.0" - } - }, - "postcss-custom-properties": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-5.0.2.tgz", - "integrity": "sha1-lxnXjy2pz59TgQrrwj1GVhMKzrE=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2", - "postcss": "^5.0.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "postcss-custom-selectors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-3.0.0.tgz", - "integrity": "sha1-j4Ekn17Qeo0JF89qOf5bBWt/lqw=", - "dev": true, - "requires": { - "balanced-match": "^0.2.0", - "postcss": "^5.0.0", - "postcss-selector-matches": "^2.0.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.1.tgz", - "integrity": "sha1-e8ZYtL7WHu5CStdPdfXD4sTfPMc=", - "dev": true - } - } - }, - "postcss-discard-comments": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", - "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-duplicates": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", - "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-empty": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", - "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-overridden": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", - "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "^5.0.14", - "uniqs": "^2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", - "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-font-family-system-ui": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/postcss-font-family-system-ui/-/postcss-font-family-system-ui-1.0.2.tgz", - "integrity": "sha1-PhpeP7fjHl6ecUOcyw6AFFVpJ8c=", - "dev": true, - "requires": { - "lodash": "^4.17.4", - "postcss": "^5.2.12", - "postcss-value-parser": "^3.3.0" - } - }, - "postcss-font-variant": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-2.0.1.tgz", - "integrity": "sha1-fKKRA/WfoCyjrOLKIrL3VoU9Tvg=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-image-set-polyfill": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/postcss-image-set-polyfill/-/postcss-image-set-polyfill-0.3.5.tgz", - "integrity": "sha1-Dxk0E3AM8fgr05Bm7wFtZaShgYE=", - "dev": true, - "requires": { - "postcss": "^6.0.1", - "postcss-media-query-parser": "^0.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-import": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-9.1.0.tgz", - "integrity": "sha1-lf6YdqHnmvSfvcNYnwH+WqfMHoA=", - "dev": true, - "requires": { - "object-assign": "^4.0.1", - "postcss": "^5.0.14", - "postcss-value-parser": "^3.2.3", - "promise-each": "^2.2.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - } - }, - "postcss-initial": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-1.5.3.tgz", - "integrity": "sha1-IMPpHJaCLdsb7UlQjbltVrrDd9A=", - "dev": true, - "requires": { - "lodash.template": "^4.2.4", - "postcss": "^5.0.19" - } - }, - "postcss-load-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", - "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0", - "postcss-load-options": "^1.2.0", - "postcss-load-plugins": "^2.3.0" - } - }, - "postcss-load-options": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", - "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.0", - "object-assign": "^4.1.0" - } - }, - "postcss-load-plugins": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", - "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", - "dev": true, - "requires": { - "cosmiconfig": "^2.1.1", - "object-assign": "^4.1.0" - } - }, - "postcss-loader": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.5.tgz", - "integrity": "sha512-pV7kB5neJ0/1tZ8L1uGOBNTVBCSCXQoIsZMsrwvO8V2rKGa2tBl/f80GGVxow2jJnRJ2w1ocx693EKhZAb9Isg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "postcss": "^6.0.0", - "postcss-load-config": "^1.2.0", - "schema-utils": "^0.4.0" - }, - "dependencies": { - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-media-minmax": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-2.1.2.tgz", - "integrity": "sha1-RExc+JJqteT9iiUJ6Sl+dRZJzfg=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", - "dev": true - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.10", - "postcss-value-parser": "^3.1.1" - } - }, - "postcss-merge-longhand": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", - "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", - "dev": true, - "requires": { - "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-merge-rules": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", - "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", - "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-gradients": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", - "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-params": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", - "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-minify-selectors": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", - "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", - "dev": true, - "requires": { - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "6.0.22", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.22.tgz", - "integrity": "sha512-Toc9lLoUASwGqxBSJGTVcOQiDqjK+Z2XlWBg+IgYwQMY9vA2f7iMpXVc1GpPcfTSyM5lkxNo0oDwDRO+wm7XHA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-nesting": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-2.3.1.tgz", - "integrity": "sha1-lKa2pO9wf77CCof+5clXdZtOAc8=", - "dev": true, - "requires": { - "postcss": "^5.0.19" - } - }, - "postcss-normalize-charset": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", - "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", - "dev": true, - "requires": { - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-display-values": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", - "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-positions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", - "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-repeat-style": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", - "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-string": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", - "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", - "dev": true, - "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-timing-functions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", - "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-unicode": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", - "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", - "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", - "dev": true, - "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-normalize-whitespace": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", - "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", - "dev": true, - "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-ordered-values": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", - "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", - "dev": true, - "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-pseudo-class-any-link": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-1.0.0.tgz", - "integrity": "sha1-kDI5GWQB0zX+c6x1YYb6YuaTryY=", - "dev": true, - "requires": { - "postcss": "^5.0.3", - "postcss-selector-parser": "^1.1.4" - }, - "dependencies": { - "postcss-selector-parser": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-1.3.3.tgz", - "integrity": "sha1-0u4Z33pk+O8hwacchvfUg1yIwoE=", - "dev": true, - "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - } - } - }, - "postcss-pseudoelements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-pseudoelements/-/postcss-pseudoelements-3.0.0.tgz", - "integrity": "sha1-bGghd8eQC6BTtt8X+MWQKEx7i7w=", - "dev": true, - "requires": { - "postcss": "^5.0.4" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "^5.0.4", - "postcss-value-parser": "^3.0.2" - } - }, - "postcss-reduce-initial": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", - "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-api": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-reduce-transforms": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", - "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", - "dev": true, - "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-replace-overflow-wrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-1.0.0.tgz", - "integrity": "sha1-8KA7Meq5Y2ppNr/SEOKu8bQ0pkM=", - "dev": true, - "requires": { - "postcss": "^5.0.16" - } - }, - "postcss-selector-matches": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-2.0.5.tgz", - "integrity": "sha1-+g9Dvle2jneqTNEYBwI0kqExAn8=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2", - "postcss": "^5.0.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "postcss-selector-not": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-2.0.0.tgz", - "integrity": "sha1-xzrSGj91I0vuf+4mnhVP1qhpeY0=", - "dev": true, - "requires": { - "balanced-match": "^0.2.0", - "postcss": "^5.0.0" - }, - "dependencies": { - "balanced-match": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.1.tgz", - "integrity": "sha1-e8ZYtL7WHu5CStdPdfXD4sTfPMc=", - "dev": true - } - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "^1.0.2", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-svgo": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz", - "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==", - "dev": true, - "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-unique-selectors": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", - "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", - "dev": true, - "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "^1.0.1", - "postcss": "^5.0.4", - "uniqs": "^2.0.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, - "promise-each": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/promise-each/-/promise-each-2.2.0.tgz", - "integrity": "sha1-M1MXTv8mlEgQN+BOAfd6oPttG2A=", - "dev": true, - "requires": { - "any-promise": "^0.1.0" - } - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "prompts": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.3.tgz", - "integrity": "sha512-H8oWEoRZpybm6NV4to9/1limhttEo13xK62pNvn2JzY0MA03p7s0OjtmhXyon3uJmxiJJVSuUwEJFFssI3eBiQ==", - "dev": true, - "requires": { - "kleur": "^3.0.2", - "sisteransi": "^1.0.0" - } - }, - "prop-types": { - "version": "15.6.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.1.tgz", - "integrity": "sha512-4ec7bY1Y66LymSUOH/zARVYObB23AT2h8cf6e/O6ZALB/N0sqZFEx7rq6EYPX2MkOdKORuooI/H5k9TlR4q7kQ==", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" - } - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", - "dev": true - }, - "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.0.tgz", - "integrity": "sha512-UWi0klDoq8xtVzlMRgENV9F7iCTZExaJQSQL187UXsxpk9NnrKGqTqqUNYAKGOzucSOxs2+jUnRNI+rLviPhJg==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "query-string": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.2.2.tgz", - "integrity": "sha1-iIpvy292Bwujny8wJchwmd76FkU=", - "requires": { - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "quill": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", - "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", - "requires": { - "clone": "^2.1.1", - "deep-equal": "^1.0.1", - "eventemitter3": "^2.0.3", - "extend": "^3.0.2", - "parchment": "^1.1.4", - "quill-delta": "^3.6.2" - }, - "dependencies": { - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" - } - } - }, - "quill-delta": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", - "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", - "requires": { - "deep-equal": "^1.0.1", - "extend": "^3.0.2", - "fast-diff": "1.1.2" - } - }, - "raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "dev": true, - "requires": { - "performance-now": "^2.1.0" - } - }, - "railroad-diagrams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz", - "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=", - "dev": true - }, - "ramda": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz", - "integrity": "sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=" - }, - "randexp": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz", - "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==", - "dev": true, - "requires": { - "discontinuous-range": "1.0.0", - "ret": "~0.1.10" - } - }, - "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/react/-/react-16.3.2.tgz", - "integrity": "sha512-o5GPdkhciQ3cEph6qgvYB7LTOHw/GB0qRI6ZFNugj49qJCFfgHwVNjZ5u+b7nif4vOeMIOuYj3CeYe2IBD74lg==", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" - } - }, - "react-aria-modal": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/react-aria-modal/-/react-aria-modal-3.0.1.tgz", - "integrity": "sha512-H0pV5ITXe6JCQAUGoSiSncfTeGunImT8KFzLLMisuzOzsG4X7GZQJGcjk6uSwZmvwZpHL+EjMwRD+vJZx3nG8g==", - "requires": { - "focus-trap-react": "^4.0.0", - "no-scroll": "^2.1.1", - "react-displace": "^2.3.0" - } - }, - "react-displace": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/react-displace/-/react-displace-2.3.0.tgz", - "integrity": "sha512-T8g/lyn3IX8kxLO4k4vJ/oIO9G72pRTc9GYslqKsfPcN4gY5+FYR5OHxeTH1skPjVylJrveGE3OC2qCt3BuHeA==" - }, - "react-dom": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.3.2.tgz", - "integrity": "sha512-MMPko3zYncNrz/7gG17wJWUREZDvskZHXOwbttzl0F0L3wDmToyuETuo/r8Y5yvDejwYcRyWI1lvVBjLJWFwKA==", - "requires": { - "fbjs": "^0.8.16", - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.0" - } - }, - "react-intl": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-2.4.0.tgz", - "integrity": "sha1-ZsFNyd+ac7L7v71gIXJugKYT6xU=", - "requires": { - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.0.0", - "invariant": "^2.1.1" - } - }, - "react-is": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz", - "integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA==" - }, - "react-lifecycles-compat": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" - }, - "react-test-renderer": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.1.tgz", - "integrity": "sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg==", - "dev": true, - "requires": { - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "react-is": "^16.8.1", - "scheduler": "^0.13.1" - }, - "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - } - } - }, - "react-text-mask": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/react-text-mask/-/react-text-mask-5.4.1.tgz", - "integrity": "sha512-mGx1n0x0skktwsKcpgAGnrgExFlXDflbWEHh8PC1UaQP8+EdHkW7JYczTghsBo2rnVwU9/4A9x/2bVt3HY46/w==", - "requires": { - "prop-types": "^15.5.6" - } - }, - "react-transition-group": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", - "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", - "requires": { - "dom-helpers": "^3.4.0", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" - }, - "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - } - } - }, - "read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", - "dev": true, - "requires": { - "pify": "^2.3.0" - } - }, - "read-only-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", - "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "read-package-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz", - "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", - "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "npm-normalize-package-bin": "^1.0.0" - } - }, - "read-package-tree": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", - "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", - "requires": { - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "util-promisify": "^2.1.0" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" - } - }, - "realpath-native": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", - "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", - "dev": true, - "requires": { - "util.promisify": "^1.0.0" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "reduce-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", - "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=" - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2", - "math-expression-evaluator": "^1.2.14", - "reduce-function-call": "^1.0.1" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "^0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regex-parser": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.9.tgz", - "integrity": "sha512-VncXxOF6uFlYog5prG2j+e2UGJeam5MfNiJnB/qEgo4KTnMm2XrELCg4rNZ6IlaEUZnGlb8aB6lXowCRQtTkkA==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "requires": { - "define-properties": "^1.1.2" - } - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", - "dev": true - }, - "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", - "dev": true, - "requires": { - "mime-db": "~1.37.0" - } - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true - } - } - }, - "request-progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", - "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } - }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "dev": true, - "requires": { - "lodash": "^4.13.1" - } - }, - "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "dev": true, - "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "^1.1.0", - "tough-cookie": ">=2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", - "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, - "resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", - "dev": true, - "requires": { - "resolve-from": "^3.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - } - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "resolve-url-loader": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-2.3.0.tgz", - "integrity": "sha512-RaEUWgF/B6aTg9VKaOv2o6dfm5f75/lGh8S+SQwoMcBm48WkA2nhLR+V7KEawkxXjU4lLB16IVeHCe7F69nyVw==", - "dev": true, - "requires": { - "adjust-sourcemap-loader": "^1.1.0", - "camelcase": "^4.1.0", - "convert-source-map": "^1.5.1", - "loader-utils": "^1.1.0", - "lodash.defaults": "^4.0.0", - "rework": "^1.0.1", - "rework-visit": "^1.0.0", - "source-map": "^0.5.7", - "urix": "^0.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rework": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz", - "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=", - "dev": true, - "requires": { - "convert-source-map": "^0.3.3", - "css": "^2.0.0" - }, - "dependencies": { - "convert-source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", - "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", - "dev": true - } - } - }, - "rework-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz", - "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=", - "dev": true - }, - "rgb": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/rgb/-/rgb-0.1.0.tgz", - "integrity": "sha1-vieykej+/+rBvZlylyG/pA/AN7U=", - "dev": true - }, - "rgb-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgb-hex/-/rgb-hex-1.0.0.tgz", - "integrity": "sha1-v6+M2c2RZLWibXHrTxWgllMks8E=", - "dev": true - }, - "rgb-regex": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz", - "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=", - "dev": true - }, - "rgba-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", - "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "rst-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", - "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=", - "dev": true, - "requires": { - "lodash.flattendeep": "^4.4.0", - "nearley": "^2.7.10" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, - "sass-graph": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", - "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", - "dev": true, - "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" - } - }, - "sass-loader": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.0.1.tgz", - "integrity": "sha512-MeVVJFejJELlAbA7jrRchi88PGP6U9yIfqyiG+bBC4a9s2PX+ulJB9h8bbEohtPBfZmlLhNZ0opQM9hovRXvlw==", - "dev": true, - "requires": { - "clone-deep": "^2.0.1", - "loader-utils": "^1.0.1", - "lodash.tail": "^4.1.1", - "neo-async": "^2.5.0", - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" - }, - "scheduler": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz", - "integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==", - "dev": true, - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "^5.0.0" - } - }, - "scss-tokenizer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", - "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", - "dev": true, - "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": ">=0.0.4" - } - } - } - }, - "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" - }, - "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shallow-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", - "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", - "dev": true, - "requires": { - "is-extendable": "^0.1.1", - "kind-of": "^5.0.0", - "mixin-object": "^2.0.1" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "shasum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", - "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", - "dev": true, - "requires": { - "json-stable-stringify": "~0.0.0", - "sha.js": "~2.4.4" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shell-quote": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", - "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", - "dev": true, - "requires": { - "array-filter": "~0.0.0", - "array-map": "~0.0.0", - "array-reduce": "~0.0.0", - "jsonify": "~0.0.0" - } - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true - }, - "shuffle-array": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/shuffle-array/-/shuffle-array-1.0.1.tgz", - "integrity": "sha1-xP88/nTRb5NzBZIwGyXmV3sSiYs=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - } - } - }, - "sinon": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-5.0.7.tgz", - "integrity": "sha512-GvNLrwpvLZ8jIMZBUhHGUZDq5wlUdceJWyHvZDmqBxnjazpxY1L0FNbGBX6VpcOEoQ8Q4XMWFzm2myJMvx+VjA==", - "dev": true, - "requires": { - "@sinonjs/formatio": "^2.0.0", - "diff": "^3.1.0", - "lodash.get": "^4.4.2", - "lolex": "^2.2.0", - "nise": "^1.2.0", - "supports-color": "^5.1.0", - "type-detect": "^4.0.5" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "snabbdom": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.3.0.tgz", - "integrity": "sha1-Tr2NW9fQRPcm0AvoLykOeuWONX8=" - }, - "snabbdom-merge": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/snabbdom-merge/-/snabbdom-merge-0.0.4.tgz", - "integrity": "sha1-CuzIN0uJKpqNPAH3osSTBZw8VB0=", - "requires": { - "ramda": "0.23.0", - "snabbdom": "0.6.4" - }, - "dependencies": { - "ramda": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.23.0.tgz", - "integrity": "sha1-zNE//3NJepOXTj6GMnv9h71ujis=" - }, - "snabbdom": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-0.6.4.tgz", - "integrity": "sha1-bPjGWKUdeGm9JxA0KW72M+YSYlc=" - } - } - }, - "snake-case": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", - "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", - "requires": { - "no-case": "^2.2.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "^0.5.6" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "sshpk": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", - "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "dev": true - }, - "stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha1-1PM6tU6OOHeLDKXP07OvsS22hiA=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stdout-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", - "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", - "dev": true, - "requires": { - "readable-stream": "^2.0.1" - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", - "dev": true, - "requires": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" - } - }, - "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.2.tgz", - "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "stream-splicer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", - "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.2" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" - }, - "string-length": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", - "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", - "dev": true, - "requires": { - "astral-regex": "^1.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string-replace-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-replace-loader/-/string-replace-loader-2.1.1.tgz", - "integrity": "sha512-0Nvw1LDclF45AFNuYPcD2Jvkv0mwb/dQSnJZMvhqGrT+zzmrpG3OJFD600qfQfNUd5aqfp7fCm2mQMfF7zLbyQ==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" - }, - "dependencies": { - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "string-replace-webpack-plugin": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/string-replace-webpack-plugin/-/string-replace-webpack-plugin-0.1.3.tgz", - "integrity": "sha1-c8ZX51nWbP6Arh4M8JGqJW0OcVw=", - "dev": true, - "requires": { - "async": "~0.2.10", - "css-loader": "^0.9.1", - "file-loader": "^0.8.1", - "loader-utils": "~0.2.3", - "style-loader": "^0.8.3" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - }, - "css-loader": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.9.1.tgz", - "integrity": "sha1-LhqgDOfjDvLGp6SzAKCAp8l54Nw=", - "dev": true, - "optional": true, - "requires": { - "csso": "1.3.x", - "loader-utils": "~0.2.2", - "source-map": "~0.1.38" - } - }, - "csso": { - "version": "1.3.12", - "resolved": "https://registry.npmjs.org/csso/-/csso-1.3.12.tgz", - "integrity": "sha1-/GKGlKLTiTiqrEmWdTIY/TEc254=", - "dev": true, - "optional": true - }, - "file-loader": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.8.5.tgz", - "integrity": "sha1-knXQMf54DyfUf19K8CvUNxPMFRs=", - "dev": true, - "optional": true, - "requires": { - "loader-utils": "~0.2.5" - } - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "dev": true, - "optional": true, - "requires": { - "amdefine": ">=0.0.4" - } - }, - "style-loader": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.8.3.tgz", - "integrity": "sha1-9Pkut9tjdodI8VBlzWcA9aHIU1c=", - "dev": true, - "optional": true, - "requires": { - "loader-utils": "^0.2.5" - } - } - } - }, - "string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=" - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string.prototype.trim": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", - "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.0", - "function-bind": "^1.0.2" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "style-loader": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz", - "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" - }, - "dependencies": { - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "stylehacks": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", - "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", - "dev": true, - "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "browserslist": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.6.0.tgz", - "integrity": "sha512-Jk0YFwXBuMOOol8n6FhgkDzn3mY9PYLYGk29zybF05SbRTsMgPqmTNeQQhOghCxq5oFqAXE3u4sYddr4C0uRhg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30000967", - "electron-to-chromium": "^1.3.133", - "node-releases": "^1.1.19" - } - }, - "caniuse-lite": { - "version": "1.0.30000969", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000969.tgz", - "integrity": "sha512-Kus0yxkoAJgVc0bax7S4gLSlFifCa7MnSZL9p9VuS/HIKEL4seaqh28KIQAAO50cD/rJ5CiJkJFapkdDAlhFxQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "electron-to-chromium": { - "version": "1.3.134", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.134.tgz", - "integrity": "sha512-C3uK2SrtWg/gSWaluLHWSHjyebVZCe4ZC0NVgTAoTq8tCR9FareRK5T7R7AS/nPZShtlEcjVMX1kQ8wi4nU68w==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "dev": true, - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - } - }, - "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", - "dev": true, - "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "^1.1.0" - } - }, - "superagent": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.1.0.tgz", - "integrity": "sha1-JPyU+OVlIeRHpVc2wYi20AKfibU=", - "requires": { - "component-emitter": "1.1.2", - "cookiejar": "2.0.1", - "debug": "2", - "extend": "1.2.1", - "form-data": "0.1.3", - "formidable": "1.0.14", - "methods": "1.0.1", - "mime": "1.2.11", - "qs": "2.3.3", - "readable-stream": "1.0.27-1", - "reduce-component": "1.0.1" - }, - "dependencies": { - "combined-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", - "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", - "requires": { - "delayed-stream": "0.0.5" - } - }, - "delayed-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", - "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=" - }, - "extend": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", - "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" - }, - "form-data": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.3.tgz", - "integrity": "sha1-TuQ0bm61Ni6DRKAgdb2NvYxzc+o=", - "requires": { - "async": "~0.9.0", - "combined-stream": "~0.0.4", - "mime": "~1.2.11" - } - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "qs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", - "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=" - }, - "readable-stream": { - "version": "1.0.27-1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", - "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "svgo": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.2.tgz", - "integrity": "sha512-rAfulcwp2D9jjdGu+0CuqlrAUin6bBWrpoqXWwKDZZZJfXcUXQSxLJOFJCQCSA0x0pP2U0TxSlJu2ROq5Bq6qA==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "coa": "^2.0.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "^0.1.1", - "css-tree": "1.0.0-alpha.28", - "css-url-regex": "^1.1.0", - "csso": "^3.5.1", - "js-yaml": "^3.13.1", - "mkdirp": "~0.5.1", - "object.values": "^1.1.0", - "sax": "~1.2.4", - "stable": "^0.1.8", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "css-select": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.0.2.tgz", - "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", - "dev": true, - "requires": { - "boolbase": "^1.0.0", - "css-what": "^2.1.2", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true - }, - "syntax-error": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", - "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", - "dev": true, - "requires": { - "acorn-node": "^1.2.0" - } - }, - "tabbable": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-3.1.1.tgz", - "integrity": "sha512-583MHIOwictf7+zbxqO/L5fBqMN6Li4SJ1XTKQA9WzHRA7c2BB+D+Ny7Y6kGqU2u+rHK59+oRzrBvMU53aZz+A==" - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "tar": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", - "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" - } - }, - "test-exclude": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", - "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^1.0.1" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", - "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", - "dev": true - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, - "text-mask": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/text-mask/-/text-mask-0.0.2.tgz", - "integrity": "sha1-EvsYFqw9ueoin6agTiK8ZDpw788=" - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", - "dev": true - }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", - "dev": true, - "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" - } - }, - "ticky": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", - "integrity": "sha1-t8+nHnaPHJAAxJe5FRswlHxQ5G0=" - }, - "timers-browserify": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", - "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", - "dev": true, - "requires": { - "process": "~0.11.0" - }, - "dependencies": { - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - } - } - }, - "timsort": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", - "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", - "dev": true - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", - "dev": true, - "requires": { - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - } - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - } - } - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "requires": { - "glob": "^7.1.2" - } - }, - "ts-jest": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.0.0.tgz", - "integrity": "sha512-o8BO3TkMREpAATaFTrXkovMsCpBl2z4NDBoLJuWZcJJj1ijI49UnvDMfVpj+iogn/Jl8Pbhuei5nc/Ti+frEHw==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "json5": "2.x", - "make-error": "1.x", - "mkdirp": "0.x", - "resolve": "1.x", - "semver": "^5.5", - "yargs-parser": "10.x" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "json5": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", - "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "ts-loader": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-3.5.0.tgz", - "integrity": "sha512-JTia3kObhTk36wPFgy0RnkZReiusYx7Le9IhcUWRrCTcFcr6Dy1zGsFd3x8DG4gevlbN65knI8W50FfoykXcng==", - "dev": true, - "requires": { - "chalk": "^2.3.0", - "enhanced-resolve": "^3.0.0", - "loader-utils": "^1.0.2", - "micromatch": "^3.1.4", - "semver": "^5.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", - "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, - "requires": { - "color-name": "^1.1.1" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", - "dev": true - }, - "ua-parser-js": { - "version": "0.7.18", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.18.tgz", - "integrity": "sha512-LtzwHlVHwFGTptfNSgezHp7WUlwiqb0gA9AALRbKaERfxwJoiX0A73QbTToxteIAuIaFshhgIZfqK8s7clqgnA==" - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - } - }, - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" - } - }, - "umd": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.3.tgz", - "integrity": "sha512-4IcGSufhFshvLNcMCV80UnQVlZ5pMOC8mvNPForqwA4+lzYQuetTESLDQkeLmihq8bRcnpbQa48Wb8Lh16/xow==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "units-css": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/units-css/-/units-css-0.4.0.tgz", - "integrity": "sha1-1iKGU6UZg9fBb/KPi53Dsf/tOgc=", - "dev": true, - "requires": { - "isnumeric": "^0.2.0", - "viewport-dimensions": "^0.2.0" - } - }, - "unquote": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", - "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "upath": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.5.tgz", - "integrity": "sha512-qbKn90aDQ0YEwvXoLqj0oiuUYroLX2lVHZ+b+xwjozFasAOC4GneDq5+OaIG5Zj+jFmbz/uO+f7a9qxjktJQww==", - "dev": true - }, - "uri-js": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.1.tgz", - "integrity": "sha512-jpKCA3HjsBfSDOEgxRDAxQCNyHfCPSbq57PqCkd3gAyBuPb3IWxw54EHncqESznIdqSetHfw3D7ylThu2Kcc9A==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true - } - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "url-loader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.0.1.tgz", - "integrity": "sha512-rAonpHy7231fmweBKUFe0bYnlGDty77E+fm53NZdij7j/YOpyGzc7ttqG1nAXl3aRs0k41o0PC3TvGXQiw2Zvw==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^0.4.3" - }, - "dependencies": { - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - }, - "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util-promisify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", - "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", - "requires": { - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "uuid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.2.tgz", - "integrity": "sha1-SL1WmPBnfjx5AaHEbvFbFkN5RyY=" - }, - "v8-compile-cache": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", - "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "validator": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", - "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" - }, - "vdom-thunk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vdom-thunk/-/vdom-thunk-3.0.0.tgz", - "integrity": "sha1-NmIVbfbuZNoufWOe1bTwHa+HWBI=" - }, - "velocity-animate": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/velocity-animate/-/velocity-animate-1.5.2.tgz", - "integrity": "sha512-m6EXlCAMetKztO1ppBhGU1/1MR3IiEevO6ESq6rcrSQ3Q77xYSW13jkfXW88o4xMrkXJhy/U7j4wFR/twMB0Eg==" - }, - "velocity-react": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/velocity-react/-/velocity-react-1.4.1.tgz", - "integrity": "sha512-ZyXBm+9C/6kNUNyc+aeNKEhtTu/Mn+OfpsNBGuTxU8S2DUcis/KQL0rTN6jWL+7ygdOrun18qhheNZTA7YERmg==", - "requires": { - "lodash": "^4.17.5", - "prop-types": "^15.5.8", - "react-transition-group": "^2.0.0", - "velocity-animate": "^1.4.0" - } - }, - "vendors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz", - "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==", - "dev": true - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } - } - }, - "view-script": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/view-script/-/view-script-0.3.6.tgz", - "integrity": "sha1-ZRf4LeBHgda5f8kDCmOqN8V34rI=" - }, - "viewport-dimensions": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz", - "integrity": "sha1-3nQHR9tTh/0XJfUXXpG6x2r982w=", - "dev": true - }, - "virtual-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/virtual-dom/-/virtual-dom-2.1.1.tgz", - "integrity": "sha1-gO2i1IG57eDASRGM78tKBfIdE3U=", - "requires": { - "browser-split": "0.0.1", - "error": "^4.3.0", - "ev-store": "^7.0.0", - "global": "^4.3.0", - "is-object": "^1.0.1", - "next-tick": "^0.2.2", - "x-is-array": "0.1.0", - "x-is-string": "0.1.0" - } - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "vvvview": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/vvvview/-/vvvview-0.4.3.tgz", - "integrity": "sha1-1FB61xKSqONUaTFLPJJAJJZn4VE=", - "requires": { - "fj-curry": "1.0.0", - "virtual-dom": "2.1.1" - } - }, - "w3c-hr-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", - "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, - "requires": { - "browser-process-hrtime": "^0.1.2" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "requires": { - "makeerror": "1.0.x" - } - }, - "watchpack": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", - "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", - "dev": true, - "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - } - }, - "chokidar": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", - "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.1.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "webpack": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.11.0.tgz", - "integrity": "sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg==", - "dev": true, - "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" - }, - "dependencies": { - "acorn": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", - "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", - "dev": true - }, - "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", - "uri-js": "^4.2.1" - } - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "dev": true, - "requires": { - "lodash": "^4.14.0" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - } - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - } - } - } - }, - "webpack-cli": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.8.tgz", - "integrity": "sha512-RANYSXwikSWINjHMd/mtesblNSpjpDLoYTBtP99n1RhXqVI/wxN40Auqy42I7y4xrbmRBoA5Zy5E0JSBD5XRhw==", - "dev": true, - "requires": { - "chalk": "2.4.2", - "cross-spawn": "6.0.5", - "enhanced-resolve": "4.1.0", - "findup-sync": "3.0.0", - "global-modules": "2.0.0", - "import-local": "2.0.0", - "interpret": "1.2.0", - "loader-utils": "1.2.3", - "supports-color": "6.1.0", - "v8-compile-cache": "2.0.3", - "yargs": "13.2.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "interpret": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", - "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", - "dev": true - }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.0.0" - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", - "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^2.0.0", - "p-is-promise": "^2.0.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "os-locale": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", - "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", - "dev": true, - "requires": { - "execa": "^1.0.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } - }, - "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yargs": { - "version": "13.2.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", - "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "os-locale": "^3.1.0", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "webpack-merge": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.2.tgz", - "integrity": "sha512-/0QYwW/H1N/CdXYA2PNPVbsxO3u2Fpz34vs72xm03SRfg6bMNGfMJIQEpQjKRvkG2JvT6oRJFpDtSrwbX8Jzvw==", - "dev": true, - "requires": { - "lodash": "^4.17.5" - } - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "webpack-sweet-entry": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/webpack-sweet-entry/-/webpack-sweet-entry-1.1.5.tgz", - "integrity": "sha512-AbGUO7qPh305dBpolzAi32z8+SPt9BzJRxKTpLJvHUtPuu1YawX1n3e19Uhrd658rglZhN2NRBSml5W9ePsXdQ==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", - "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.19" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - } - } - }, - "whatwg-fetch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", - "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" - }, - "whatwg-mimetype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", - "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", - "dev": true - }, - "whatwg-url": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz", - "integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", - "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "ws": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", - "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0" - } - }, - "x-is-array": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-array/-/x-is-array-0.1.0.tgz", - "integrity": "sha1-3lIBcdR7P0FvVYfWKbidJrEtwp0=" - }, - "x-is-string": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", - "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", - "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", - "dev": true, - "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", - "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", - "dev": true, - "requires": { - "camelcase": "^3.0.0" - }, - "dependencies": { - "camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", - "dev": true - } - } - }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", - "dev": true, - "requires": { - "fd-slicer": "~1.0.1" - } - } - } -} diff --git a/package.json b/package.json index 31bb4ff7..f77cac88 100644 --- a/package.json +++ b/package.json @@ -6,20 +6,19 @@ "url": "https://github.com/houdiniproject/houdini" }, "scripts": { - "watch": "export HOUDINI_WATCH=1; script/build.sh", - "build": "script/build.sh", - "build-all": "script/compile-assets.sh && npm run build", - "ci-build-all": "script/compile-assets.sh && npm ci && npm run build", - "test": "rake -v spec && npm run build && npx jest", - "export-button-config": "bundle exec rake settings:generate_json", - "export-i18n": "bundle exec rake settings:combine_translations", - "generate-openapi": "rake oapi:gen", - "get-codegen-cli": "script/download_codegen_cli.sh", - "generate-api-js": "rm -rf javascripts/api && npm run get-codegen-cli && npm run generate-openapi && java -jar .bin/swagger-codegen-cli.jar generate -l 'typescript-jquery' -i tmp/openapi.json -o javascripts/api --config 'swagger.json' -t lib/swagger-typescript-jquery", - "webpack": "webpack", + "build-all": "yarn install --frozen-lockfile && script/compile-assets.sh && yarn build", + "ci-build-all": "script/compile-assets.sh && yarn build", + "test": "rake -v spec && yarn build && yarn jest", "jest": "jest" }, "devDependencies": { + "@babel/core": "^7.0.0", + "@babel/plugin-proposal-decorators": "^7.10.0", + "@babel/preset-env": "^7.7.1", + "@babel/preset-react": "^7.10.0", + "@babel/preset-typescript": "^7.9.0", + "@rails/webpacker": "^5.1.1", + "@types/activestorage": "^5.2.2", "@types/color": "^3.0.0", "@types/enzyme": "^3.1.9", "@types/enzyme-to-json": "^1.5.1", @@ -31,65 +30,41 @@ "@types/lodash": "^4.14.106", "@types/moment-timezone": "^0.5.9", "@types/prop-types": "^15.5.5", - "@types/react": "^16.1.0", - "@types/react-dom": "^16.0.5", + "@types/react": "^16.9.35", + "@types/react-dom": "^16.9.8", "@types/react-intl": "^2.3.7", "@types/react-test-renderer": "^16.0.1", "@types/react-text-mask": "^5.4.2", "@types/react-transition-group": "^2.9.0", "@types/sinon": "^4.3.3", "@types/validator": "^9.4.1", - "@types/velocity-animate": "^1.2.33", - "babel-core": "^6.26.0", - "babel-loader": "^7.1.4", - "babel-preset-env": "^1.6.1", - "babel-preset-es2015": "^6.24.1", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "bootstrap": "^3.4.1", - "bootstrap-loader": "github:houdiniproject/bootstrap-loader#compiled_namespaced", + "bootstrap-loader": "github:houdiniproject/bootstrap-loader#compiled_namespaced_3", "bootstrap-sass": "^3.3.7", - "browserify": "13.0.1", - "browserify-incremental": "3.1.1", - "clean-webpack-plugin": "^0.1.19", - "compression-webpack-plugin": "^1.1.11", - "copy-webpack-plugin": "^4.5.1", "css-loader": "^0.28.10", - "cssnano": "^4.1.10", "enzyme": "^3.8.0", "enzyme-adapter-react-16": "^1.9.1", "enzyme-to-json": "^3.3.3", - "exports-loader": "^0.7.0", - "expose-loader": "^0.7.5", - "extract-text-webpack-plugin": "^3.0.2", - "file-loader": "^1.1.11", - "imports-loader": "^0.8.0", + "fork-ts-checker-webpack-plugin": "^4.1.6", "jest": "^24.1.0", "jest-enzyme": "^7.0.1", "jsdom": "^11.10.0", - "less": "^3.0.4", - "less-loader": "^4.1.0", - "lodash": "^4.17.14", "node-sass": "^4.12.0", "phantomjs-prebuilt": "^2.1.16", - "postcss-cssnext": "^2.9.0", - "postcss-import": "^9.1.0", - "postcss-loader": "^2.1.1", "resolve-url-loader": "^2.3.0", "sass-loader": "^7.0.1", "sinon": "^5.0.7", - "string-replace-loader": "^2.1.1", - "string-replace-webpack-plugin": "^0.1.3", "style-loader": "^0.21.0", "ts-jest": "^24.0.0", - "ts-loader": "^3", - "typescript": "^2.9.2", - "uglifyjs-webpack-plugin": "^0.4.6", + "typescript": "^3.9.3", "url-loader": "^1.0.1", - "webpack": "^3", - "webpack-cli": "^3.3.8", - "webpack-merge": "^4.1.2", - "webpack-sweet-entry": "^1.1.4" + "webpack": "^4.0.0", + "webpack-cli": "^3.3.11", + "webpack-dev-server": "^3.11.0" }, "dependencies": { + "@rails/activestorage": "^6.0.2-2", "attr-binder": "0.3.1", "aws-sdk": "^2.402.0", "chart.js": "2.1.4", @@ -121,7 +96,9 @@ "immutable": "3.7.5", "jquery": "1.11.1", "jquery.cookie": "1.4.1", + "lodash": "^4.17.15", "marked": "^0.7.0", + "mini-css-extract-plugin": "^0.8.0", "mobx": "^4.3.1", "mobx-react": "^5.4.3", "mobx-react-devtools": "^5.0.1", @@ -139,6 +116,7 @@ "pikaday-time": "1.5.1", "query-string": "4.2.2", "quill": "^1.3.6", + "rails-erb-loader": "^5.5.2", "ramda": "^0.21.0", "react": "^16.2.0", "react-aria-modal": "^3.0.1", @@ -154,7 +132,6 @@ "uuid": "2.0.2", "validator": "^9.4.1", "vdom-thunk": "3.0.0", - "velocity-react": "^1.4.1", "view-script": "0.3.6", "virtual-dom": "2.1.1", "vvvview": "0.4.3" @@ -175,6 +152,11 @@ "transform": { "^.+\\.tsx?$": "ts-jest" }, + "testPathIgnorePatterns": [ + "/node_modules/", + "/config/webpack/test.js", + "/vendor/" + ], "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", "moduleFileExtensions": [ "ts", diff --git a/postcss.config.js b/postcss.config.js index 5e3b3032..aa5998a8 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,8 +1,12 @@ -// License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later module.exports = { - plugins: { - 'postcss-import': {}, - 'postcss-cssnext': {}, - 'cssnano': {} - }, -}; + plugins: [ + require('postcss-import'), + require('postcss-flexbugs-fixes'), + require('postcss-preset-env')({ + autoprefixer: { + flexbox: 'no-2009' + }, + stage: 3 + }) + ] +} diff --git a/script/build.sh b/script/build.sh deleted file mode 100755 index cd0626ba..00000000 --- a/script/build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - - -( -echo $HOUDINI_WATCH -set -e -set -o pipefail -export DATABASE_URL=${BUILD_DATABASE_URL:-postgres://admin:password@db/commitchange_development} -echo $DATABASE_URL -npm run export-button-config && npm run export-i18n && npm run generate-api-js - -if [ -n "$HOUDINI_WATCH" ]; -then - echo "we're gonna watch!!!" - $(npm bin)/webpack --watch -else - echo "we're gonna build!!!" - NODE_ENV=production $(npm bin)/webpack -p -fi -) \ No newline at end of file diff --git a/script/build/debian/java.sh b/script/build/debian/java.sh deleted file mode 100755 index 983907ce..00000000 --- a/script/build/debian/java.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash -set -e - -apt-get -yy install default-jre \ No newline at end of file diff --git a/script/build/debian/node.sh b/script/build/debian/node.sh index c013317a..13a9d745 100755 --- a/script/build/debian/node.sh +++ b/script/build/debian/node.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash set -e -curl -sL https://deb.nodesource.com/setup_10.x | bash - -apt-get update -qq && apt-get install -y nodejs -npm install npm@^6 -g -npm install -g icu4c-data@64l \ No newline at end of file +curl -sL https://deb.nodesource.com/setup_13.x | bash - +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + +apt-get update -qq && apt-get install -y nodejs yarn diff --git a/script/debian_setup.sh b/script/debian_setup.sh index d36dfa9c..df295c31 100755 --- a/script/debian_setup.sh +++ b/script/debian_setup.sh @@ -1,8 +1,11 @@ #!/usr/bin/env bash set -e -curl -sL https://deb.nodesource.com/setup_9.x | bash - +curl -sL https://deb.nodesource.com/setup_13.x | bash - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - -apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs postgresql-9.6 default-jre -npm install npm@^6 -g \ No newline at end of file + +curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - +echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + +apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs postgresql-9.6 default-jre yarn \ No newline at end of file diff --git a/script/delayed_job b/script/delayed_job index edf19598..4f1b0b2b 100755 --- a/script/delayed_job +++ b/script/delayed_job @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) require 'delayed/command' diff --git a/script/download_codegen_cli.sh b/script/download_codegen_cli.sh deleted file mode 100755 index 22c41648..00000000 --- a/script/download_codegen_cli.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -if [ ! -e .bin/swagger-codegen-cli.jar ] -then - curl -L https://repo1.maven.org/content/repositories/releases/io/swagger/swagger-codegen-cli/2.3.1/swagger-codegen-cli-2.3.1.jar -o .bin/swagger-codegen-cli.jar --create-dirs -fi diff --git a/script/pg_restore_local_from_production.sh b/script/pg_restore_local_from_production.sh index eb9a877d..f8383d3b 100755 --- a/script/pg_restore_local_from_production.sh +++ b/script/pg_restore_local_from_production.sh @@ -1,4 +1,4 @@ #!/bin/bash set -e -pg_restore --verbose --clean --no-acl --no-owner -h db -U admin -d commitchange_development latest.dump +pg_restore --verbose --clean --no-acl --no-owner -h localhost -U houdini_user -d commitchange_development latest.dump diff --git a/script/rails b/script/rails index c5b1430b..12b00eff 100755 --- a/script/rails +++ b/script/rails @@ -1,11 +1,13 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. if ENV['RAILS_ENV'] == 'test' require 'simplecov' SimpleCov.start 'rails' - puts "required simplecov" + puts 'required simplecov' end -APP_PATH = File.expand_path('../../config/application', __FILE__) -require File.expand_path('../../config/boot', __FILE__) +APP_PATH = File.expand_path('../config/application', __dir__) +require File.expand_path('../config/boot', __dir__) require 'rails/commands' diff --git a/script/test.sh b/script/test.sh index 192ff103..01f488e4 100755 --- a/script/test.sh +++ b/script/test.sh @@ -1,2 +1,2 @@ #!/bin/bash -rake db:create db:structure:load db:migrate && RAILS_ENV=test rake db:create db:structure:load test:prepare && rake spec && npm run ci-build-all && npm run jest \ No newline at end of file +yarn ci && rake db:create db:structure:load db:migrate && RAILS_ENV=test rake db:create db:structure:load test:prepare && rake spec && yarn run build-all && yarn jest diff --git a/spec/api/houdini/nonprofit_spec.rb b/spec/api/houdini/nonprofit_spec.rb deleted file mode 100644 index daddc8ba..00000000 --- a/spec/api/houdini/nonprofit_spec.rb +++ /dev/null @@ -1,214 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'rails_helper' - -describe Houdini::V1::Nonprofit, :type => :controller do - describe :get do - - end - - describe :post do - around {|e| - @old_bp =Settings.default_bp - e.run - Settings.default_bp = @old_bp - - } - def expect_validation_errors(actual, input) - expected_errors = input.with_indifferent_access[:errors] - expect(actual["errors"]).to match_array expected_errors - end - - def create_errors(*wrapper_params) - output = totally_empty_errors - wrapper_params.each {|i| output[:errors].push(h(params: [i], messages: gr_e('presence')))} - output - end - - def h(h = {}) - h.with_indifferent_access - end - - let(:totally_empty_errors) { - { - errors: - [ - h(params: ["nonprofit[name]"], messages: gr_e("presence", "blank")), - h(params: ["nonprofit[zip_code]"], messages: gr_e("presence", "blank")), - h(params: ["nonprofit[state_code]"], messages: gr_e("presence", "blank")), - h(params: ["nonprofit[city]"], messages: gr_e("presence", "blank")), - - h(params: ["user[name]"], messages: gr_e("presence", "blank")), - h(params: ["user[email]"], messages: gr_e("presence", "blank")), - h(params: ["user[password]"], messages: gr_e("presence", "blank")), - h(params: ["user[password_confirmation]"], messages: gr_e("presence", "blank")), - ] - - - }.with_indifferent_access - } - describe 'authorization' do - around {|e| - Rails.configuration.action_controller.allow_forgery_protection = true - e.run - Rails.configuration.action_controller.allow_forgery_protection = false - } - it 'rejects csrf' do - - xhr :post, '/api/v1/nonprofit' - expect(response.code).to eq "401" - end - end - it 'validates nothing' do - input = {} - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "400" - expect_validation_errors(JSON.parse(response.body), create_errors("nonprofit", "user")) - end - - it 'validates url, email, phone ' do - input = { - nonprofit: { - email: "noemeila", - phone: "notphone", - url: "" - }} - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "400" - expected = create_errors("user") - expected[:errors].push(h(params:["nonprofit[email]"], messages: gr_e("regexp"))) - #expected[:errors].push(h(params:["nonprofit[phone]"], messages: gr_e("regexp"))) - #expected[:errors].push(h(params:["nonprofit[url]"], messages: gr_e("regexp"))) - - expect_validation_errors(JSON.parse(response.body), expected) - end - - it 'should reject unmatching passwords ' do - input = { - - user: { - email: "wmeil@email.com", - name: "name", - password: 'password', - password_confirmation: 'doesn\'t match' - } - } - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "400" - expect(JSON.parse(response.body)['errors']).to include(h(params:["user[password]", "user[password_confirmation]"], messages: gr_e("is_equal_to"))) - - end - - it 'attempts to make a slug copy and returns the proper errors' do - force_create(:nonprofit, slug: "n", state_code_slug: "wi", city_slug: "appleton") - input = { - nonprofit: {name: "n", state_code: "WI", city: "appleton", zip_code: 54915}, - user: {name: "Name", email: "em@em.com", password: "12345678", password_confirmation: "12345678"} - } - - expect_any_instance_of(SlugNonprofitNamingAlgorithm).to receive(:create_copy_name).and_raise(UnableToCreateNameCopyError.new) - - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "400" - - expect_validation_errors(JSON.parse(response.body), { - errors: [ - h( - params:["nonprofit[name]"], - messages:["has an invalid slug. Contact support for help."] - ) - ] - }) - end - - it 'errors on attempt to add user with email that already exists' do - force_create(:user, email: 'em@em.com') - - input = { - nonprofit: {name: "n", state_code: "WI", city: "appleton", zip_code: 54915}, - user: {name: "Name", email: "em@em.com", password: "12345678", password_confirmation: "12345678"} - } - - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "400" - - expect_validation_errors(JSON.parse(response.body), { - errors: [ - h( - params:["user[email]"], - messages:["has already been taken"] - ) - ] - }) - - - end - - it "succeeds" do - force_create(:nonprofit, slug: "n", state_code_slug: "wi", city_slug: "appleton") - input = { - nonprofit: {name: "n", state_code: "WI", city: "appleton", zip_code: 54915, url: 'www.cs.c', website: 'www.cs.c'}, - user: {name: "Name", email: "em@em.com", password: "12345678", password_confirmation: "12345678"} - } - - bp = force_create(:billing_plan) - Settings.default_bp.id = bp.id - - #expect(Houdini::V1::Nonprofit).to receive(:sign_in) - - xhr :post, '/api/v1/nonprofit', input - expect(response.code).to eq "201" - - our_np = Nonprofit.all[1] - expected_np = { - name: "n", - state_code: "WI", - city: "appleton", - zip_code: "54915", - state_code_slug: "wi", - city_slug: "appleton", - slug: "n-00", - website: 'http://www.cs.c' - }.with_indifferent_access - - expected_np = our_np.attributes.with_indifferent_access.merge(expected_np) - expect(our_np.attributes).to eq expected_np - - expect(our_np.billing_subscription.billing_plan).to eq bp - - response_body = { - id: our_np.id - }.with_indifferent_access - - expect(JSON.parse(response.body)).to eq response_body - - user = User.first - expected_user = { - email: "em@em.com", - name: "Name" - } - - expected_user = user.attributes.with_indifferent_access.merge(expected_user) - expect(our_np.roles.nonprofit_admins.count).to eq 1 - expect(our_np.roles.nonprofit_admins.first.user.attributes).to eq expected_user - - - end - - - end -end - - -def find_error_message(json, field_name) - errors = json['errors'] - - error = errors.select {|i| i["params"].any? {|j| j == field_name}}.first - return error if !error - return error["messages"] - -end - -def gr_e(*keys) - keys.map {|i| I18n.translate("grape.errors.messages." + i, locale: 'en')} - -end \ No newline at end of file diff --git a/spec/api/support/api_shared_user_verification.rb b/spec/api/support/api_shared_user_verification.rb deleted file mode 100644 index ade50170..00000000 --- a/spec/api/support/api_shared_user_verification.rb +++ /dev/null @@ -1,179 +0,0 @@ -# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later -require 'controllers/support/general_shared_user_context' -RSpec.shared_context :api_shared_user_verification do - include_context :general_shared_user_context - let(:user_as_np_admin) { - __create_admin(nonprofit) - } - - let(:user_as_other_np_admin) { - __create_admin(other_nonprofit) - } - - let(:user_as_np_associate){ - __create_associate(nonprofit) - } - - let(:user_as_other_np_associate){ - __create_associate(other_nonprofit) - } - - let(:unauth_user) { - force_create(:user) - } - - - let(:campaign_editor) { - __create(:campaign_editor, campaign) - } - - let(:confirmed_user){ - force_create(:user, confirmed_at: Time.current) - } - - let(:event_editor) { - __create(:event_editor,event) - } - - let(:super_admin) { - __create(:super_admin, other_nonprofit) - } - - let(:user_with_profile) { - u = force_create(:user) - force_create(:profile, user: u) - u - } - - let(:all_users) do - {:user_as_np_admin => user_as_np_admin, - :user_as_other_np_admin => user_as_other_np_admin, - :user_as_np_associate => user_as_np_associate, - :user_as_other_np_associate => user_as_other_np_associate, - :unauth_user => unauth_user, - :campaign_editor => campaign_editor, - :event_editor => event_editor, - :super_admin => super_admin, - :user_with_profile => user_with_profile - } - end - - let(:roles__open_to_all) do - [nil, :user_as_np_admin, - :user_as_other_np_admin, - :user_as_np_associate, - :user_as_other_np_associate, - :unauth_user, - :campaign_editor, - :event_editor, - :super_admin, - :user_with_profile - ] - end - - let(:roles__open_to_np_associate) do - [:user_as_np_admin, - - :user_as_np_associate, - - :super_admin - - ] - end - - def __create(name, host) - u = force_create(:user) - force_create(:role, user: u, name: name, host:host) - u - end - - def __create_admin(host) - u = force_create(:user) - force_create(:role, user: u, name: :nonprofit_admin, host:host) - u - end - - def __create_associate(host) - u = force_create(:user) - force_create(:role, user: u, name: :nonprofit_associate, host:host) - u - end - - def sign_in(user_to_signin) - post_via_redirect 'users/sign_in', 'user[email]' => user_to_signin.email, 'user[password]' => user_to_signin.password, format: "json" - end - - def sign_out - send(:get, 'users/sign_out') - end - - def send(method, *args) - case method - when :get - return xhr(:get, *args) - when :post - return xhr(:post, *args) - when :delete - return xhr(:delete, *args) - when :put - return xhr(:put, *args) - end - end - - def accept(user_to_signin:, method:, action:, args:) - new_user = user_to_signin - if (user_to_signin != nil && user_to_signin.is_a?(OpenStruct)) - new_user = user_to_signin.value - end - sign_in new_user if new_user - # allows us to run the helpers but ignore what the controller action does - # - send(method, action, args) - expect(response.status).to eq(200), "expcted success for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ":" : "")} #{new_user&.attributes}" - sign_out - end - - def reject(user_to_signin:, method:, action:, args:) - - new_user = user_to_signin - if (user_to_signin != nil && user_to_signin.is_a?(OpenStruct)) - new_user = user_to_signin.value - end - sign_in new_user if new_user - send(method, action, args) - expect(response.status).to eq(401), "expected failure for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ":" : "")} #{new_user&.attributes}" - sign_out - end - - alias_method :redirects_to, :reject - - def run_authorization_tests(details, &block) - @method = details[:method] - @successful_users = details[:successful_users] - @action = details[:action] - @block_to_get_arguments_to_run = block || ->(_) {} #no-op - accept_test_for_nil = false - all_users.each do |k,v| - os = OpenStruct.new - os.key = k - os.value = v - - if k.nil? - accept(user_to_signin: nil, method:@method, action: @action, args: @block_to_get_arguments_to_run.call(v)) - accept_test_for_nil = true - end - if @successful_users.include? k - accept(user_to_signin: os, method:@method, action: @action, args: @block_to_get_arguments_to_run.call(v)) - else - reject(user_to_signin: os, method:@method, action: @action, args: @block_to_get_arguments_to_run.call(v)) - end - end - - unless accept_test_for_nil - reject(user_to_signin: nil, method:@method, action: @action, args: @block_to_get_arguments_to_run.call(nil)) - end - end - -end - - diff --git a/spec/controllers/api/nonprofits_controller_spec.rb b/spec/controllers/api/nonprofits_controller_spec.rb new file mode 100644 index 00000000..86913965 --- /dev/null +++ b/spec/controllers/api/nonprofits_controller_spec.rb @@ -0,0 +1,61 @@ +require 'rails_helper' + +describe Api::NonprofitsController, type: :request do + + let(:user) { create(:user)} + let(:nonprofit_admin_role) do + role = user.roles.build(host: nonprofit, name: 'nonprofit_admin') + role.save! + role + end + let(:nonprofit) {create(:nm_justice)} + + describe 'get' do + end + + describe 'create' do + around(:each) do |example| + @old_bp = Settings.default_bp + example.run + Settings.default_bp = @old_bp + end + + it 'validates and returns correct errors' do + input = {} + post '/api/nonprofits', params: input, xhr: true + expect(response).to have_http_status :unprocessable_entity + expect(response.parsed_body['errors'].keys).to match_array ['name', 'city', 'state_code', 'slug', 'user_id'] + end + + it 'succeeds' do + input = { name: 'n', state_code: 'WI', city: 'appleton', zip_code: 54_915, user_id: user.id, phone: '920-555-5555' } + sign_in user + bp = force_create(:billing_plan) + Settings.default_bp.id = bp.id + + sign_in user + + post '/api/nonprofits', params: input, xhr: true + expect(response).to have_http_status :created + + expected_np = { + name: 'n', + state_code: 'WI', + city: 'appleton', + zip_code: '54915', + state_code_slug: 'wi', + city_slug: 'appleton', + slug: 'n', + phone: '920-555-5555', + email: nil, + website: nil, + urls: {plain_url: "http://www.example.com/nonprofits/1", slug_url: "http://www.example.com/wi/appleton/n"} + }.with_indifferent_access + + expect(response.parsed_body['id']).to be > 0 + expect(response.parsed_body.except('id')).to eq expected_np + end + end +end + + diff --git a/spec/controllers/aws_presigned_posts_spec.rb b/spec/controllers/aws_presigned_posts_spec.rb index 51e71c04..d36a52f3 100644 --- a/spec/controllers/aws_presigned_posts_spec.rb +++ b/spec/controllers/aws_presigned_posts_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe AwsPresignedPostsController, :type => :controller do +describe AwsPresignedPostsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do @@ -11,4 +13,4 @@ describe AwsPresignedPostsController, :type => :controller do end end end -end \ No newline at end of file +end diff --git a/spec/controllers/billing_subscriptions_spec.rb b/spec/controllers/billing_subscriptions_spec.rb index 0ec55691..493080db 100644 --- a/spec/controllers/billing_subscriptions_spec.rb +++ b/spec/controllers/billing_subscriptions_spec.rb @@ -1,24 +1,23 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe BillingSubscriptionsController, :type => :controller do +describe BillingSubscriptionsController, type: :controller do describe 'authorization' do include_context :shared_user_context - describe 'create_trial' do - include_context :open_to_np_admin, :post, :create_trial, nonprofit_id: :__our_np - end describe 'create' do - include_context :open_to_np_admin, :post, :create, nonprofit_id: :__our_np + include_context :open_to_np_admin, :post, :create, nonprofit_id: :__our_np end describe 'cancel' do - include_context :open_to_np_admin, :post, :cancel, nonprofit_id: :__our_np + include_context :open_to_np_admin, :post, :cancel, nonprofit_id: :__our_np end describe 'cancellation' do - include_context :open_to_np_admin, :get, :cancellation, nonprofit_id: :__our_np + include_context :open_to_np_admin, :get, :cancellation, nonprofit_id: :__our_np, without_json_view: true end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaign_gift_options_spec.rb b/spec/controllers/campaign_gift_options_spec.rb index d2878734..11bbbcb9 100644 --- a/spec/controllers/campaign_gift_options_spec.rb +++ b/spec/controllers/campaign_gift_options_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe CampaignGiftOptionsController, :type => :controller do +describe CampaignGiftOptionsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'reject unauthorized' do @@ -11,14 +13,14 @@ describe CampaignGiftOptionsController, :type => :controller do end describe 'update' do - include_context :open_to_campaign_editor, :put, :update, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :put, :update, nonprofit_id: :__our_np, campaign_id: :__our_campaign, id: '1' end describe 'destroy' do - include_context :open_to_campaign_editor, :delete, :destroy, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :delete, :destroy, nonprofit_id: :__our_np, campaign_id: :__our_campaign, id: '1' end describe 'update_order' do - include_context :open_to_campaign_editor, :put, :update_order, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :put, :update_order, nonprofit_id: :__our_np, campaign_id: :__our_campaign, id: '1' end end @@ -28,8 +30,8 @@ describe CampaignGiftOptionsController, :type => :controller do end describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, campaign_id: :__our_campaign, id: '1' end end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaign_gifts_spec.rb b/spec/controllers/campaign_gifts_spec.rb index 06f3f599..52356cd7 100644 --- a/spec/controllers/campaign_gifts_spec.rb +++ b/spec/controllers/campaign_gifts_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe CampaignGiftsController, :type => :controller do +describe CampaignGiftsController, type: :controller do describe 'authorization' do include_context :shared_user_context @@ -12,4 +14,4 @@ describe CampaignGiftsController, :type => :controller do end end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaigns/campaign_gift_options_spec.rb b/spec/controllers/campaigns/campaign_gift_options_spec.rb index 9627d7fc..3bbad02a 100644 --- a/spec/controllers/campaigns/campaign_gift_options_spec.rb +++ b/spec/controllers/campaigns/campaign_gift_options_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Campaigns::CampaignGiftOptionsController, :type => :controller do +describe Campaigns::CampaignGiftOptionsController, type: :controller do describe 'authorization' do include_context :shared_user_context - describe 'reject unauthorized users' do + describe 'accept all' do describe 'index' do - include_context :open_to_campaign_editor, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign end end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaigns/donations_spec.rb b/spec/controllers/campaigns/donations_spec.rb index 11c40336..06be9a01 100644 --- a/spec/controllers/campaigns/donations_spec.rb +++ b/spec/controllers/campaigns/donations_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Campaigns::DonationsController, :type => :controller do +describe Campaigns::DonationsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'reject unauthorized' do describe 'index' do - include_context :open_to_campaign_editor, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign end end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaigns/supporters_spec.rb b/spec/controllers/campaigns/supporters_spec.rb index fcaf2d22..20208be3 100644 --- a/spec/controllers/campaigns/supporters_spec.rb +++ b/spec/controllers/campaigns/supporters_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Campaigns::SupportersController, :type => :controller do +describe Campaigns::SupportersController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'reject unauthorized' do describe 'index' do - include_context :open_to_campaign_editor, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :get, :index, nonprofit_id: :__our_np, campaign_id: :__our_campaign, without_json_view: true end end end -end \ No newline at end of file +end diff --git a/spec/controllers/campaigns_spec.rb b/spec/controllers/campaigns_spec.rb index a7cf07f8..fb0c0fb0 100644 --- a/spec/controllers/campaigns_spec.rb +++ b/spec/controllers/campaigns_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe CampaignsController, :type => :controller do +describe CampaignsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do @@ -15,51 +17,76 @@ describe CampaignsController, :type => :controller do end describe 'duplicate' do - include_context :open_to_confirmed_users, :post, :duplicate, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_confirmed_users, :post, :duplicate, nonprofit_id: :__our_np, id: :__our_campaign end describe 'update' do - include_context :open_to_campaign_editor, :put, :update, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :put, :update, nonprofit_id: :__our_np, id: :__our_campaign end describe 'soft_delete' do - include_context :open_to_campaign_editor, :delete, :soft_delete, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_campaign_editor, :delete, :soft_delete, nonprofit_id: :__our_np, id: :__our_campaign end - end describe 'open to all' do describe 'index' do - include_context :open_to_all, :get, :index, nonprofit_id: :__our_np + include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, id: :__our_campaign, without_json_view: true end describe 'activities' do - include_context :open_to_all, :get, :activities, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :activities, nonprofit_id: :__our_np, id: :__our_campaign end describe 'metrics' do - include_context :open_to_all, :get, :metrics, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :metrics, nonprofit_id: :__our_np, id: :__our_campaign end describe 'timeline' do - include_context :open_to_all, :get, :timeline, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :timeline, nonprofit_id: :__our_np, id: :__our_campaign end describe 'totals' do - include_context :open_to_all, :get, :totals, nonprofit_id: :__our_np, campaign_id: :__our_campaign + include_context :open_to_all, :get, :totals, nonprofit_id: :__our_np, id: :__our_campaign end describe 'peer_to_peer' do - include_context :open_to_all, :get, :peer_to_peer, nonprofit_id: :__our_np + include_context :open_to_all, :get, :peer_to_peer, nonprofit_id: :__our_np, without_json_view: true end - - - - - end end -end \ No newline at end of file + + describe 'routes' do + it 'routes campaigns#index' do + expect(get: '/nonprofits/5/campaigns/4').to(route_to(controller: 'campaigns', action: 'show', nonprofit_id: '5', id: '4')) + end + end + + describe 'index' do + render_views + let(:nonprofit) { force_create(:nm_justice, published: true)} + let(:campaign) { force_create(:campaign, nonprofit: nonprofit, name: "simplename", goal_amount: 444)} + before(:each) do + + campaign + get(:index, params: {nonprofit_id: nonprofit.id, format: :json}) + end + + it 'has ok status' do + expect(response).to have_http_status(:ok) + end + + it 'has correct items' do + body = JSON::parse(response.body) + expect(body).to eq({data: [{id: campaign.id, + name: 'simplename', + total_raised: 0, + goal_amount: 444, + url: "http://test.host/nm/albuquerque/new-mexico-equality/campaigns/slug_#{campaign.id}" + }]}.with_indifferent_access) + end + end +end diff --git a/spec/controllers/cards_spec.rb b/spec/controllers/cards_spec.rb index 3aac440b..8fd67de8 100644 --- a/spec/controllers/cards_spec.rb +++ b/spec/controllers/cards_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe CardsController, :type => :controller do +describe CardsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'accept all' do describe 'create' do - include_context :open_to_all, :post, :create, nonprofit_id: :__our_np + include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end end end -end \ No newline at end of file +end diff --git a/spec/controllers/direct_debit_details_spec.rb b/spec/controllers/direct_debit_details_spec.rb index 44f29060..72c068d6 100644 --- a/spec/controllers/direct_debit_details_spec.rb +++ b/spec/controllers/direct_debit_details_spec.rb @@ -1,16 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe DirectDebitDetailsController, :type => :controller do +describe DirectDebitDetailsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'open to all' do describe 'create' do - include_context :open_to_all, :post, :create, nonprofit_id: :__our_np + include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/email_settings_spec.rb b/spec/controllers/email_settings_spec.rb index 9b7c49dd..dcc76e12 100644 --- a/spec/controllers/email_settings_spec.rb +++ b/spec/controllers/email_settings_spec.rb @@ -1,21 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe EmailSettingsController, :type => :controller do +describe EmailSettingsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'create' do - include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np + include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np end describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np end - end - - end -end \ No newline at end of file +end diff --git a/spec/controllers/emails_spec.rb b/spec/controllers/emails_spec.rb index 26d1eb40..cc0c3d00 100644 --- a/spec/controllers/emails_spec.rb +++ b/spec/controllers/emails_spec.rb @@ -1,16 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe EmailsController, :type => :controller do +describe EmailsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'create' do include_context :open_to_registered, :post, :create end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/event_discounts_spec.rb b/spec/controllers/event_discounts_spec.rb index 37cfcaba..b3775bfc 100644 --- a/spec/controllers/event_discounts_spec.rb +++ b/spec/controllers/event_discounts_spec.rb @@ -1,30 +1,30 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe EventDiscountsController, :type => :controller do +describe EventDiscountsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'create' do - include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event end - + describe 'update' do - include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event, id: '2' end - + describe 'destroy' do - include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event, id: '2' end - - end - + describe 'open to all' do describe 'index' do - include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, event_id: :__our_event, id: '2' end end end -end \ No newline at end of file +end diff --git a/spec/controllers/events_spec.rb b/spec/controllers/events_spec.rb index 7bef9f29..e24e7e98 100644 --- a/spec/controllers/events_spec.rb +++ b/spec/controllers/events_spec.rb @@ -1,53 +1,51 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe EventsController, :type => :controller do +describe EventsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'create' do - include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, id: :__our_event end describe 'update' do - include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, id: :__our_event end describe 'duplicate' do - include_context :open_to_event_editor, :post, :duplicate, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :post, :duplicate, nonprofit_id: :__our_np, id: :__our_event end describe 'soft_delete' do - include_context :open_to_event_editor, :delete, :soft_delete, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :delete, :soft_delete, nonprofit_id: :__our_np, event_id: :__our_event end describe 'stats' do - include_context :open_to_event_editor, :get, :stats, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :get, :stats, nonprofit_id: :__our_np, id: :__our_event, without_json_view: true end describe 'name_and_id' do - include_context :open_to_np_associate, :get, :name_and_id, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :name_and_id, nonprofit_id: :__our_np end end describe 'open to all' do describe 'index' do - include_context :open_to_all, :get, :index, nonprofit_id: :__our_np + include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'listings' do - include_context :open_to_all, :get, :listings, nonprofit_id: :__our_np + include_context :open_to_all, :get, :listings, nonprofit_id: :__our_np end describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np + include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, id: :__our_event, without_json_view: true end describe 'activities' do - include_context :open_to_all, :get, :activities, nonprofit_id: :__our_np + include_context :open_to_all, :get, :activities, nonprofit_id: :__our_np, id: :__our_event end describe 'metrics' do - include_context :open_to_all, :get, :metrics, nonprofit_id: :__our_np + include_context :open_to_all, :get, :metrics, nonprofit_id: :__our_np, id: :__our_event end - - - end - -end \ No newline at end of file +end diff --git a/spec/controllers/front_spec.rb b/spec/controllers/front_spec.rb index b64291c6..0161360b 100644 --- a/spec/controllers/front_spec.rb +++ b/spec/controllers/front_spec.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe FrontController, :type => :controller do +describe FrontController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'accept all' do describe 'index' do - include_context :open_to_all, :get, :index + include_context :open_to_all, :get, :index end end end it 'index redirects to onboard with no non-profits' do - get( :index) + get(:index) expect(response).to redirect_to onboard_url end @@ -37,6 +39,5 @@ describe FrontController, :type => :controller do get(:index) expect(response).to redirect_to profile_url(unauth_user.profile.id) end - end -end \ No newline at end of file +end diff --git a/spec/controllers/image_attachments_spec.rb b/spec/controllers/image_attachments_spec.rb index c38ec151..cfd6dab1 100644 --- a/spec/controllers/image_attachments_spec.rb +++ b/spec/controllers/image_attachments_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe ImageAttachmentsController, :type => :controller do +describe ImageAttachmentsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do @@ -15,4 +17,4 @@ describe ImageAttachmentsController, :type => :controller do end end end -end \ No newline at end of file +end diff --git a/spec/controllers/maps_spec.rb b/spec/controllers/maps_spec.rb index 6fcebd00..f2712aa5 100644 --- a/spec/controllers/maps_spec.rb +++ b/spec/controllers/maps_spec.rb @@ -1,30 +1,30 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe MapsController, :type => :controller do +describe MapsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'all_supporters' do - include_context :open_to_super_admin, :get, :all_supporters + include_context :open_to_super_admin, :get, :all_supporters, without_json_view: true end describe 'all_npo_supporters' do - include_context :open_to_np_associate, :get, :all_npo_supporters, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :all_npo_supporters, nonprofit_id: :__our_np, without_json_view: true end describe 'specific_npo_supporters' do - include_context :open_to_np_associate, :get, :specific_npo_supporters, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :specific_npo_supporters, nonprofit_id: :__our_np, without_json_view: true end end describe 'open_to_all' do describe 'all_npos' do - include_context :open_to_all, :get, :all_npos, nonprofit_id: :__our_np + include_context :open_to_all, :get, :all_npos, nonprofit_id: :__our_np, without_json_view: true end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/activities_spec.rb b/spec/controllers/nonprofits/activities_spec.rb index 3b59f248..dd284891 100644 --- a/spec/controllers/nonprofits/activities_spec.rb +++ b/spec/controllers/nonprofits/activities_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::ActivitiesController, :type => :controller do +describe Nonprofits::ActivitiesController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'get' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, supporter_id: 1111 end end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/bank_accounts_spec.rb b/spec/controllers/nonprofits/bank_accounts_spec.rb index a9a9c10b..76db830c 100644 --- a/spec/controllers/nonprofits/bank_accounts_spec.rb +++ b/spec/controllers/nonprofits/bank_accounts_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::BankAccountsController, :type => :controller do +describe Nonprofits::BankAccountsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'create' do @@ -10,7 +12,7 @@ describe Nonprofits::BankAccountsController, :type => :controller do end describe 'confirmation' do - include_context :open_to_np_admin, :get, :confirmation, nonprofit_id: :__our_np + include_context :open_to_np_admin, :get, :confirmation, nonprofit_id: :__our_np, without_json_view: true end describe 'confirm' do @@ -18,7 +20,7 @@ describe Nonprofits::BankAccountsController, :type => :controller do end describe 'cancellation' do - include_context :open_to_np_admin, :get, :cancellation, nonprofit_id: :__our_np + include_context :open_to_np_admin, :get, :cancellation, nonprofit_id: :__our_np, without_json_view: true end describe 'cancel' do @@ -29,4 +31,4 @@ describe Nonprofits::BankAccountsController, :type => :controller do include_context :open_to_np_admin, :post, :resend_confirmation, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/button_spec.rb b/spec/controllers/nonprofits/button_spec.rb index cd60b48e..9d859523 100644 --- a/spec/controllers/nonprofits/button_spec.rb +++ b/spec/controllers/nonprofits/button_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::ButtonController, :type => :controller do +describe Nonprofits::ButtonController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'send_code' do @@ -10,17 +12,15 @@ describe Nonprofits::ButtonController, :type => :controller do end describe 'basic' do - include_context :open_to_registered, :get, :basic, nonprofit_id: :__our_np + include_context :open_to_registered, :get, :basic, nonprofit_id: :__our_np, without_json_view: true end describe 'guided' do - include_context :open_to_registered, :get, :guided, nonprofit_id: :__our_np + include_context :open_to_registered, :get, :guided, nonprofit_id: :__our_np, without_json_view: true end describe 'advanced' do - include_context :open_to_registered, :get, :advanced, nonprofit_id: :__our_np + include_context :open_to_registered, :get, :advanced, nonprofit_id: :__our_np, without_json_view: true end - - end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/cards_spec.rb b/spec/controllers/nonprofits/cards_spec.rb index bf539727..3aee6a58 100644 --- a/spec/controllers/nonprofits/cards_spec.rb +++ b/spec/controllers/nonprofits/cards_spec.rb @@ -1,16 +1,18 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::CardsController, :type => :controller do +describe Nonprofits::CardsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'show' do - include_context :open_to_np_associate, :get, :edit, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :edit, nonprofit_id: :__our_np, without_json_view: true end describe 'create' do include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/charges_spec.rb b/spec/controllers/nonprofits/charges_spec.rb index 0bc2f4fd..d6325ed5 100644 --- a/spec/controllers/nonprofits/charges_spec.rb +++ b/spec/controllers/nonprofits/charges_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::ChargesController, :type => :controller do +describe Nonprofits::ChargesController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'get' do include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/custom_field_masters_spec.rb b/spec/controllers/nonprofits/custom_field_masters_spec.rb index dcdb1f63..585d6685 100644 --- a/spec/controllers/nonprofits/custom_field_masters_spec.rb +++ b/spec/controllers/nonprofits/custom_field_masters_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::CustomFieldMastersController, :type => :controller do +describe Nonprofits::CustomFieldMastersController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do - describe 'get payments' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + describe 'get custom field masters' do + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'create' do @@ -14,7 +16,7 @@ describe Nonprofits::CustomFieldMastersController, :type => :controller do end describe 'destroy' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/custom_fields_joins_spec.rb b/spec/controllers/nonprofits/custom_fields_joins_spec.rb index c6c02cc3..ebbab658 100644 --- a/spec/controllers/nonprofits/custom_fields_joins_spec.rb +++ b/spec/controllers/nonprofits/custom_fields_joins_spec.rb @@ -1,20 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::CustomFieldJoinsController, :type => :controller do +describe Nonprofits::CustomFieldJoinsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do - describe 'index ' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + describe 'index' do + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, supporter_id: 1, without_json_view: true end describe 'modify' do - include_context :open_to_np_associate, :post, :modify, nonprofit_id: :__our_np + include_context :open_to_np_associate, :post, :modify, nonprofit_id: :__our_np, id: '1' end describe 'destroy' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1', supporter_id: 1 end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/donations_spec.rb b/spec/controllers/nonprofits/donations_spec.rb index 24e45032..f9c85070 100644 --- a/spec/controllers/nonprofits/donations_spec.rb +++ b/spec/controllers/nonprofits/donations_spec.rb @@ -1,25 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' require 'controllers/support/new_controller_user_context' require 'support/contexts/shared_donation_charge_context' -describe Nonprofits::DonationsController, :type => :controller do - +describe Nonprofits::DonationsController, type: :controller do describe 'rejects unauthenticated users' do describe 'index' do include_context :shared_user_context - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, id: '1' end - - describe 'update' do include_context :shared_user_context - include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np, id: '1' end - - end describe 'accept all users' do describe 'create' do @@ -27,23 +24,24 @@ describe Nonprofits::DonationsController, :type => :controller do end describe 'follow up' do - include_context :open_to_all, :put, :followup, nonprofit_id: :__our_np + include_context :open_to_all, :put, :followup, nonprofit_id: :__our_np, id: '1' end end end -describe 'Nonprofits::DonationsController::create_offsite', :type => :request do +describe '.create_offsite', type: :request do describe 'create_offsite' do include_context :shared_donation_charge_context - include_context :new_controller_user_context + include_context :general_shared_user_context + require 'support/contexts/general_shared_user_context.rb' - it 'reject non-campaign editors (and np authorized folks)' do - run_authorization_tests({method: :post, action: "/nonprofits/#{nonprofit.id}/donations/create_offsite", - successful_users: roles__open_to_campaign_editor}) do |_| - {nonprofit_id: nonprofit.id, - donation: {campaign_id: campaign.id}} - end - end - #include_context :open_to_np_associate, :post, :create_offsite, nonprofit_id: :__our_np + # it 'reject non-campaign editors (and np authorized folks)', :type => :request do + # run_authorization_tests({method: :post, action: "/nonprofits/#{nonprofit.id}/donations/create_offsite", + # successful_users: roles__open_to_campaign_editor}) do |_| + # {nonprofit_id: nonprofit.id, + # donation: {campaign_id: campaign.id}} + # end + # end + # include_context :open_to_np_associate, :post, :create_offsite, nonprofit_id: :__our_np end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/email_lists_spec.rb b/spec/controllers/nonprofits/email_lists_spec.rb index 737f3e62..6c094e2e 100644 --- a/spec/controllers/nonprofits/email_lists_spec.rb +++ b/spec/controllers/nonprofits/email_lists_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::EmailListsController, :type => :controller do +describe Nonprofits::EmailListsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'index' do @@ -13,4 +15,4 @@ describe Nonprofits::EmailListsController, :type => :controller do include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/imports_spec.rb b/spec/controllers/nonprofits/imports_spec.rb index 93f2f833..ac599957 100644 --- a/spec/controllers/nonprofits/imports_spec.rb +++ b/spec/controllers/nonprofits/imports_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::ImportsController, :type => :controller do +describe Nonprofits::ImportsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'create' do include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/miscellaneous_np_infos_spec.rb b/spec/controllers/nonprofits/miscellaneous_np_infos_spec.rb index 2c6f7720..7fb3d53d 100644 --- a/spec/controllers/nonprofits/miscellaneous_np_infos_spec.rb +++ b/spec/controllers/nonprofits/miscellaneous_np_infos_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::MiscellaneousNpInfosController, :type => :controller do +describe Nonprofits::MiscellaneousNpInfosController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'show' do @@ -13,4 +15,4 @@ describe Nonprofits::MiscellaneousNpInfosController, :type => :controller do include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/nonprofit_keys_spec.rb b/spec/controllers/nonprofits/nonprofit_keys_spec.rb index 13ed8c12..0832490d 100644 --- a/spec/controllers/nonprofits/nonprofit_keys_spec.rb +++ b/spec/controllers/nonprofits/nonprofit_keys_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::NonprofitKeysController, :type => :controller do +describe Nonprofits::NonprofitKeysController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'index' do @@ -17,4 +19,4 @@ describe Nonprofits::NonprofitKeysController, :type => :controller do include_context :open_to_np_associate, :get, :mailchimp_landing, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/payments_spec.rb b/spec/controllers/nonprofits/payments_spec.rb index a23537d8..ed3696f2 100644 --- a/spec/controllers/nonprofits/payments_spec.rb +++ b/spec/controllers/nonprofits/payments_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::PaymentsController, :type => :controller do +describe Nonprofits::PaymentsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'get payments' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'export payments' do @@ -14,15 +16,15 @@ describe Nonprofits::PaymentsController, :type => :controller do end describe 'show payments' do - include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np, id: '1', with_status: 200 end describe 'update' do - include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np, id: '1' end describe 'destroy payment' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/payouts_spec.rb b/spec/controllers/nonprofits/payouts_spec.rb index 677b4625..1160aeab 100644 --- a/spec/controllers/nonprofits/payouts_spec.rb +++ b/spec/controllers/nonprofits/payouts_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::PayoutsController, :type => :controller do +describe Nonprofits::PayoutsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'create' do @@ -10,13 +12,11 @@ describe Nonprofits::PayoutsController, :type => :controller do end describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'show' do - include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np, id: '1' end - - end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/recurring_donations_spec.rb b/spec/controllers/nonprofits/recurring_donations_spec.rb index a218a1f4..4a23e1c0 100644 --- a/spec/controllers/nonprofits/recurring_donations_spec.rb +++ b/spec/controllers/nonprofits/recurring_donations_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::RecurringDonationsController, :type => :controller do +describe Nonprofits::RecurringDonationsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'export' do @@ -14,18 +16,16 @@ describe Nonprofits::RecurringDonationsController, :type => :controller do end describe 'show' do - include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np, id: '1', with_status: 200 end describe 'destroy' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end describe 'update' do - include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np, id: '1' end - - end describe 'open for all' do @@ -33,4 +33,4 @@ describe Nonprofits::RecurringDonationsController, :type => :controller do include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/reports_spec.rb b/spec/controllers/nonprofits/reports_spec.rb index 203d51e2..d401b78f 100644 --- a/spec/controllers/nonprofits/reports_spec.rb +++ b/spec/controllers/nonprofits/reports_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::ReportsController, :type => :controller do +describe Nonprofits::ReportsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'end_of_year' do @@ -13,4 +15,4 @@ describe Nonprofits::ReportsController, :type => :controller do include_context :open_to_np_associate, :get, :end_of_year_custom, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/supporter_emails_spec.rb b/spec/controllers/nonprofits/supporter_emails_spec.rb index 66410728..681aa5f2 100644 --- a/spec/controllers/nonprofits/supporter_emails_spec.rb +++ b/spec/controllers/nonprofits/supporter_emails_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::SupporterEmailsController, :type => :controller do +describe Nonprofits::SupporterEmailsController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'create' do @@ -13,4 +15,4 @@ describe Nonprofits::SupporterEmailsController, :type => :controller do include_context :open_to_np_associate, :post, :gmail, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/supporters_spec.rb b/spec/controllers/nonprofits/supporters_spec.rb index 0c3e473c..6b57d812 100644 --- a/spec/controllers/nonprofits/supporters_spec.rb +++ b/spec/controllers/nonprofits/supporters_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::SupportersController, :type => :controller do +describe Nonprofits::SupportersController, type: :controller do include_context :shared_user_context describe 'rejects unauthenticated users' do describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, without_json_view: true end describe 'index_metrics' do @@ -14,23 +16,23 @@ describe Nonprofits::SupportersController, :type => :controller do end describe 'show' do - include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :show, nonprofit_id: :__our_np, id: '1' end describe 'email_address' do - include_context :open_to_np_associate, :get, :email_address, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :email_address, nonprofit_id: :__our_np, id: '1' end describe 'full_contact' do - include_context :open_to_np_associate, :get, :full_contact, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :full_contact, nonprofit_id: :__our_np, id: '1' end describe 'info_card' do - include_context :open_to_np_associate, :get, :info_card, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :info_card, nonprofit_id: :__our_np, id: '1' end describe 'update' do - include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np, id: '1' end describe 'bulk_delete' do @@ -47,4 +49,4 @@ describe Nonprofits::SupportersController, :type => :controller do include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/tag_joins_spec.rb b/spec/controllers/nonprofits/tag_joins_spec.rb index 927fef46..70138d13 100644 --- a/spec/controllers/nonprofits/tag_joins_spec.rb +++ b/spec/controllers/nonprofits/tag_joins_spec.rb @@ -1,22 +1,22 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::TagJoinsController, :type => :controller do +describe Nonprofits::TagJoinsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np, supporter_id: 1 end - + describe 'modify' do - include_context :open_to_np_associate, :post, :modify, nonprofit_id: :__our_np + include_context :open_to_np_associate, :post, :modify, nonprofit_id: :__our_np, id: '1' end - + describe 'destroy' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1', supporter_id: 2 end - - end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/tag_masters_spec.rb b/spec/controllers/nonprofits/tag_masters_spec.rb index 5ad258a6..7239c435 100644 --- a/spec/controllers/nonprofits/tag_masters_spec.rb +++ b/spec/controllers/nonprofits/tag_masters_spec.rb @@ -1,22 +1,24 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::TagMastersController, :type => :controller do +describe Nonprofits::TagMastersController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'index' do - include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :index, nonprofit_id: :__our_np end describe 'create' do - include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np + include_context :open_to_np_associate, :post, :create, nonprofit_id: :__our_np end describe 'destroy' do - include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_associate, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits/trackings_spec.rb b/spec/controllers/nonprofits/trackings_spec.rb index fd27c4f9..4ec0311c 100644 --- a/spec/controllers/nonprofits/trackings_spec.rb +++ b/spec/controllers/nonprofits/trackings_spec.rb @@ -1,12 +1,14 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe Nonprofits::TrackingsController, :type => :controller do +describe Nonprofits::TrackingsController, type: :controller do include_context :shared_user_context describe 'open to all' do describe 'create' do - include_context :open_to_all, :post, :create, nonprofit_id: :__our_np + include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end end -end \ No newline at end of file +end diff --git a/spec/controllers/nonprofits_spec.rb b/spec/controllers/nonprofits_spec.rb index ec4099c3..54a92096 100644 --- a/spec/controllers/nonprofits_spec.rb +++ b/spec/controllers/nonprofits_spec.rb @@ -1,80 +1,73 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe NonprofitsController, :type => :controller do +describe NonprofitsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'update' do - include_context :open_to_np_associate, :put, :update, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :update, id: :__our_np end describe 'dashboard' do - include_context :open_to_np_associate, :get, :dashboard, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :dashboard, id: :__our_np, without_json_view: true end describe 'dashboard_metrics' do - include_context :open_to_np_associate, :get, :dashboard_metrics, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :dashboard_metrics, id: :__our_np end describe 'verify_identity' do - include_context :open_to_np_associate, :put, :verify_identity, nonprofit_id: :__our_np + include_context :open_to_np_associate, :put, :verify_identity, id: :__our_np end describe 'recurring_donation_stats' do - include_context :open_to_np_associate, :get, :recurring_donation_stats, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :recurring_donation_stats, id: :__our_np end describe 'profile_todos' do - include_context :open_to_np_associate, :get, :profile_todos, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :profile_todos, id: :__our_np end describe 'dashboard_todos' do - include_context :open_to_np_associate, :get, :dashboard_todos, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :dashboard_todos, id: :__our_np end describe 'payment_history' do - include_context :open_to_np_associate, :get, :payment_history, nonprofit_id: :__our_np + include_context :open_to_np_associate, :get, :payment_history, id: :__our_np end - - describe 'destroy' do - include_context :open_to_super_admin, :delete, :destroy + include_context :open_to_super_admin, :delete, :destroy, id: :__our_np end - - - - - - - end describe 'open to all' do describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np + include_context :open_to_all, :get, :show, id: :__our_np, without_json_view: true end describe 'create' do - include_context :open_to_all, :post, :create, nonprofit_id: :__our_np + include_context :open_to_all, :post, :create, nonprofit_id: :__our_np end describe 'btn' do - include_context :open_to_all, :get, :btn, nonprofit_id: :__our_np + include_context :open_to_all, :get, :btn, id: :__our_np, without_json_view: true end describe 'supporter_form' do - include_context :open_to_all, :get, :supporter_form, nonprofit_id: :__our_np + include_context :open_to_all, :get, :supporter_form, id: :__our_np, without_json_view: true end describe 'custom_supporter' do - include_context :open_to_all, :post, :custom_supporter, nonprofit_id: :__our_np + include_context :open_to_all, :post, :custom_supporter, id: :__our_np end describe 'donate' do - include_context :open_to_all, :get, :donate, nonprofit_id: :__our_np + include_context :open_to_all, :get, :donate, id: :__our_np, without_json_view: true end describe 'search' do @@ -82,4 +75,4 @@ describe NonprofitsController, :type => :controller do end end end -end \ No newline at end of file +end diff --git a/spec/controllers/onboard_controller_spec.rb b/spec/controllers/onboard_controller_spec.rb index 0471da11..7e7b0dcc 100644 --- a/spec/controllers/onboard_controller_spec.rb +++ b/spec/controllers/onboard_controller_spec.rb @@ -1,5 +1,6 @@ +# frozen_string_literal: true + require 'rails_helper' -RSpec.describe OnboardController, :type => :controller do - +RSpec.describe OnboardController, type: :controller do end diff --git a/spec/controllers/profiles_spec.rb b/spec/controllers/profiles_spec.rb index 181fa3a1..624712f3 100644 --- a/spec/controllers/profiles_spec.rb +++ b/spec/controllers/profiles_spec.rb @@ -1,28 +1,30 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe ProfilesController, :type => :controller do +describe ProfilesController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'update' do - include_context :open_to_profile_owner, :put, :update, id: :__our_profile + include_context :open_to_profile_owner, :put, :update, id: :__our_profile end describe 'fundraisers' do - include_context :open_to_profile_owner, :get, :fundraisers, id: :__our_profile + include_context :open_to_profile_owner, :get, :fundraisers, id: :__our_profile, without_json_view: true end describe 'donations_history' do - include_context :open_to_profile_owner, :get, :donations_history, id: :__our_profile + include_context :open_to_profile_owner, :get, :donations_history, id: :__our_profile, without_json_view: true end end describe 'open to all' do describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np + include_context :open_to_all, :get, :show, id: :__our_np, without_json_view: true end end end -end \ No newline at end of file +end diff --git a/spec/controllers/recurring_donations_spec.rb b/spec/controllers/recurring_donations_spec.rb index 5876b6e0..48e6e33e 100644 --- a/spec/controllers/recurring_donations_spec.rb +++ b/spec/controllers/recurring_donations_spec.rb @@ -1,28 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe RecurringDonationsController, :type => :controller do +describe RecurringDonationsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'open to all (note: edit token is checked inside methods)' do describe 'edit' do - include_context :open_to_all, :get, :edit, nonprofit_id: :__our_np + include_context :open_to_all, :get, :edit, nonprofit_id: :__our_np, id: '1', without_json_view: true end describe 'destroy' do - include_context :open_to_all, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_all, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end describe 'update' do - include_context :open_to_all, :put, :update, nonprofit_id: :__our_np + include_context :open_to_all, :put, :update, nonprofit_id: :__our_np, id: '1' end describe 'update_amount' do - include_context :open_to_all, :put, :update_amount, nonprofit_id: :__our_np + include_context :open_to_all, :put, :update_amount, nonprofit_id: :__our_np, id: '1' end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/roles_spec.rb b/spec/controllers/roles_spec.rb index cfc87a30..364a17c7 100644 --- a/spec/controllers/roles_spec.rb +++ b/spec/controllers/roles_spec.rb @@ -1,20 +1,20 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe RolesController, :type => :controller do +describe RolesController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'create' do - include_context :open_to_np_admin, :post, :create, nonprofit_id: :__our_np + include_context :open_to_np_admin, :post, :create, nonprofit_id: :__our_np end - + describe 'destroy' do - include_context :open_to_np_admin, :delete, :destroy, nonprofit_id: :__our_np + include_context :open_to_np_admin, :delete, :destroy, nonprofit_id: :__our_np, id: '1' end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/settings_spec.rb b/spec/controllers/settings_spec.rb index 31ac7c28..58cf5c74 100644 --- a/spec/controllers/settings_spec.rb +++ b/spec/controllers/settings_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe SettingsController, :type => :controller do +describe SettingsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'index' do - include_context :open_to_registered, :get, :index, nonprofit_id: :__our_np + include_context :open_to_registered, :get, :index, nonprofit_id: :__our_np, without_json_view: true end end end -end \ No newline at end of file +end diff --git a/spec/controllers/static_controller_spec.rb b/spec/controllers/static_controller_spec.rb index dc1a1a88..52813da8 100644 --- a/spec/controllers/static_controller_spec.rb +++ b/spec/controllers/static_controller_spec.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' -RSpec.describe StaticController, :type => :controller do - describe ".ccs" do +RSpec.describe StaticController, type: :controller do + describe '.ccs' do around(:each) do |example| example.run Settings.reload! @@ -11,36 +13,37 @@ RSpec.describe StaticController, :type => :controller do describe 'local_tar_gz' do before (:each) do Settings.merge!( - { - ccs: { - ccs_method: 'local_tar_gz', - } - }) + ccs: { + ccs_method: 'local_tar_gz' + } + ) end - it 'fails on git archive' do expect(Kernel).to receive(:system).and_return(false) get('ccs') expect(response.status).to eq 500 end - end - it 'setup github' do - Settings.merge!( - { - ccs: { - ccs_method: 'github', - options: { - account: 'account', - repo: 'repo' - } - } - }) - expect(File).to receive(:read).with("#{Rails.root}/CCS_HASH").and_return("hash\n") - get('ccs') - expect(response).to redirect_to "https://github.com/account/repo/tree/hash" + describe 'github' do + before (:each) do + Settings.merge!( + ccs: { + ccs_method: 'github', + options: { + account: 'account', + repo: 'repo' + } + } + ) + end + + it 'setup github' do + expect(File).to receive(:read).with("#{Rails.root}/CCS_HASH").and_return("hash\n") + get('ccs') + expect(response).to redirect_to 'https://github.com/account/repo/tree/hash' + end end end end diff --git a/spec/controllers/super_admins_spec.rb b/spec/controllers/super_admins_spec.rb index af6e5c46..f452740c 100644 --- a/spec/controllers/super_admins_spec.rb +++ b/spec/controllers/super_admins_spec.rb @@ -1,28 +1,28 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe SuperAdminsController, :type => :controller do +describe SuperAdminsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'search_nonprofits' do - include_context :open_to_super_admin, :get, :search_nonprofits + include_context :open_to_super_admin, :get, :search_nonprofits end describe 'search_profiles' do - include_context :open_to_super_admin, :get, :search_profiles + include_context :open_to_super_admin, :get, :search_profiles end describe 'search_fullcontact' do - include_context :open_to_super_admin, :get, :search_fullcontact + include_context :open_to_super_admin, :get, :search_fullcontact end describe 'index' do - include_context :open_to_super_admin, :get, :index + include_context :open_to_super_admin, :get, :index, without_json_view: true end - - end end -end \ No newline at end of file +end diff --git a/spec/controllers/support/new_controller_user_context.rb b/spec/controllers/support/new_controller_user_context.rb index 7c08bf81..37fb793d 100644 --- a/spec/controllers/support/new_controller_user_context.rb +++ b/spec/controllers/support/new_controller_user_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'support/contexts/general_shared_user_context' @@ -5,7 +7,7 @@ RSpec.shared_context :new_controller_user_context do include_context :general_shared_user_context def sign_in(user_to_signin) - post_via_redirect 'users/sign_in', 'user[email]' => user_to_signin.email, 'user[password]' => user_to_signin.password, format: "json" + post_via_redirect 'users/sign_in', 'user[email]' => user_to_signin.email, 'user[password]' => user_to_signin.password, format: 'json' end def sign_out @@ -15,19 +17,19 @@ RSpec.shared_context :new_controller_user_context do def send(method, *args) case method when :get - return xhr(:get, *args) + xhr(:get, *args) when :post - return xhr(:post, *args) + xhr(:post, *args) when :delete - return xhr(:delete, *args) + xhr(:delete, *args) when :put - return xhr(:put, *args) + xhr(:put, *args) end end def accept(user_to_signin:, method:, action:, args:) new_user = user_to_signin - if (user_to_signin != nil && user_to_signin.is_a?(OpenStruct)) + if !user_to_signin.nil? && user_to_signin.is_a?(OpenStruct) new_user = user_to_signin.value end sign_in new_user if new_user @@ -35,19 +37,18 @@ RSpec.shared_context :new_controller_user_context do # expect_any_instance_of(described_class).to receive(action).and_return(ActionController::TestResponse.new(200)) # expect_any_instance_of(described_class).to receive(:render).and_return(nil) send(method, action, args) - expect(response.status).to_not eq(302), "expected success for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ":" : "")} #{new_user&.attributes}" + expect(response.status).to_not eq(302), "expected success for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ':' : '')} #{new_user&.attributes}" sign_out end def reject(user_to_signin:, method:, action:, args:) - new_user = user_to_signin - if (user_to_signin != nil && user_to_signin.is_a?(OpenStruct)) + if !user_to_signin.nil? && user_to_signin.is_a?(OpenStruct) new_user = user_to_signin.value end sign_in new_user if new_user send(method, action, args) - expect(response.status).to eq(302), "expected failure for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ":" : "")} #{new_user&.attributes}" + expect(response.status).to eq(302), "expected failure for user: #{(user_to_signin.is_a?(OpenStruct) ? user_to_signin.key.to_s + ':' : '')} #{new_user&.attributes}" sign_out end -end \ No newline at end of file +end diff --git a/spec/controllers/support/shared_user_context.rb b/spec/controllers/support/shared_user_context.rb index 7517f4a1..e5c02ad9 100644 --- a/spec/controllers/support/shared_user_context.rb +++ b/spec/controllers/support/shared_user_context.rb @@ -1,150 +1,165 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later - RSpec.shared_context :shared_user_context do + let(:nonprofit) { force_create(:nm_justice, published: true) } + let(:other_nonprofit) { force_create(:fv_poverty) } - - let(:nonprofit) {force_create(:nonprofit, published:true)} - let(:other_nonprofit) { force_create(:nonprofit)} - - - let(:user_as_np_admin) { + let(:user_as_np_admin) do __create_admin(nonprofit) - } + end - - let(:user_as_other_np_admin) { + let(:user_as_other_np_admin) do __create_admin(other_nonprofit) - } + end - let(:user_as_np_associate){ + let(:user_as_np_associate) do __create_associate(nonprofit) - } + end - let(:user_as_other_np_associate){ + let(:user_as_other_np_associate) do __create_associate(other_nonprofit) - } + end - let(:unauth_user) { + let(:unauth_user) do force_create(:user) - } + end - let(:campaign) {force_create(:campaign, nonprofit: nonprofit)} - let(:campaign_editor) { + let(:campaign) { force_create(:campaign, nonprofit: nonprofit) } + let(:campaign_editor) do __create(:campaign_editor, campaign) - } + end - let(:confirmed_user){ + let(:confirmed_user) do force_create(:user, confirmed_at: Time.current) - } + end - let(:event) { + let(:event) do force_create(:event, nonprofit: nonprofit) - } + end - let(:event_editor) { - __create(:event_editor,event) - } + let(:event_editor) do + __create(:event_editor, event) + end - let(:super_admin) { - __create(:super_admin, other_nonprofit) - } + let(:super_admin) do + __create(:super_admin, other_nonprofit) + end - let(:user_with_profile) { + let(:user_with_profile) do u = force_create(:user) force_create(:profile, user: u) u - } - - + end def __create(name, host) u = force_create(:user) - force_create(:role, user: u, name: name, host:host) + force_create(:role, user: u, name: name, host: host) u end def __create_admin(host) u = force_create(:user) - force_create(:role, user: u, name: :nonprofit_admin, host:host) + force_create(:role, user: u, name: :nonprofit_admin, host: host) u end def __create_associate(host) u = force_create(:user) - force_create(:role, user: u, name: :nonprofit_associate, host:host) + force_create(:role, user: u, name: :nonprofit_associate, host: host) u end def send(method, *args) case method - when :get - return get(*args) - when :post - return post(*args) - when :delete - return delete(*args) - when :put - return put(*args) + when :get + get(*args) + when :post + post(*args) + when :delete + delete(*args) + when :put + put(*args) end end def accept(user_to_signin, method, action, *args) + test_variables = collect_test_variables(args) + request.accept = 'application/json' unless test_variables[:without_json_view] sign_in user_to_signin if user_to_signin # allows us to run the helpers but ignore what the controller action does - # - expect_any_instance_of(described_class).to receive(action).and_return(ActionController::TestResponse.new(200)) - expect_any_instance_of(described_class).to receive(:render).and_return(nil) - send(method, action, *args) - expect(response.status).to eq 200 + + if test_variables[:without_json_view] + expect_any_instance_of(described_class).to receive(action).and_return(ActionDispatch::IntegrationTest.new(200)) + expect_any_instance_of(described_class).to receive(:render).and_return(nil) + send(method, action, reduce_params(*args)) + expect(response.status).to eq 200 + else + expect_any_instance_of(described_class).to receive(action).and_return(ActionDispatch::IntegrationTest.new(204)) + send(method, action, reduce_params(*args)) + expect(response.status).to eq(test_variables[:with_status] || 204) + end end def reject(user_to_signin, method, action, *args) sign_in user_to_signin if user_to_signin - send(method, action, *args) + send(method, action, reduce_params(*args)) expect(response.status).to eq 302 end alias_method :redirects_to, :reject - def fix_args( *args) + def reduce_params(*args) + { params: args.reduce({}, :merge) } + end + + def collect_test_variables(*args) + test_vars = {} + args.collect do |items| + if items.kind_of?(Array) + items.each do |k, v| + test_vars.merge!(k.slice(:without_json_view, :with_status)) if k.kind_of?(Hash) + end + end + end + return test_vars + end + + def fix_args(*args) replacements = { - __our_np: nonprofit.id, - __our_campaign: campaign.id, - __our_event: event.id, - __our_profile: user_with_profile.profile.id + __our_np: nonprofit.id, + __our_campaign: campaign.id, + __our_event: event.id, + __our_profile: user_with_profile.profile.id } - args.collect{|i| + args.collect do |i| ret = i if replacements[i] ret = replacements[i] elsif i.is_a? Hash - ret = i.collect{|k,v | + ret = i.collect do |k, v| ret_v = v - if replacements[v] - ret_v = replacements[v] - end + ret_v = replacements[v] if replacements[v] - [k,ret_v] - }.to_h + [k, ret_v] + end.to_h end ret - }.to_a + end.to_a end - - end RSpec.shared_context :open_to_all do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'accepts no user' do accept(nil, method, action, *fixed_args) end @@ -188,14 +203,13 @@ RSpec.shared_context :open_to_all do |method, action, *args| it 'accept profile user' do accept(user_with_profile, method, action, *fixed_args) end - end RSpec.shared_context :open_to_np_associate do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -242,12 +256,11 @@ RSpec.shared_context :open_to_np_associate do |method, action, *args| end end - RSpec.shared_context :open_to_np_admin do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -295,9 +308,9 @@ end RSpec.shared_context :open_to_registered do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -343,12 +356,11 @@ RSpec.shared_context :open_to_registered do |method, action, *args| end end - RSpec.shared_context :open_to_campaign_editor do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -392,14 +404,13 @@ RSpec.shared_context :open_to_campaign_editor do |method, action, *args| it 'rejects profile user' do reject(user_with_profile, method, action, *fixed_args) end - end RSpec.shared_context :open_to_confirmed_users do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -443,14 +454,13 @@ RSpec.shared_context :open_to_confirmed_users do |method, action, *args| it 'rejects profile user' do reject(user_with_profile, method, action, *fixed_args) end - end RSpec.shared_context :open_to_event_editor do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -497,9 +507,9 @@ end RSpec.shared_context :open_to_super_admin do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -546,12 +556,11 @@ RSpec.shared_context :open_to_super_admin do |method, action, *args| end end - RSpec.shared_context :open_to_profile_owner do |method, action, *args| include_context :shared_user_context - let(:fixed_args){ - fix_args( *args) - } + let(:fixed_args) do + fix_args(*args) + end it 'rejects no user' do reject(nil, method, action, *fixed_args) @@ -596,4 +605,4 @@ RSpec.shared_context :open_to_profile_owner do |method, action, *args| it 'accepts profile user' do accept(user_with_profile, method, action, *fixed_args) end -end \ No newline at end of file +end diff --git a/spec/controllers/ticket_levels_spec.rb b/spec/controllers/ticket_levels_spec.rb index 53687df7..7fb63bd9 100644 --- a/spec/controllers/ticket_levels_spec.rb +++ b/spec/controllers/ticket_levels_spec.rb @@ -1,34 +1,35 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe TicketLevelsController, :type => :controller do +describe TicketLevelsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do describe 'create' do - include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event, id: '1' end describe 'update' do - include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event, id: '1' end describe 'update_order' do - include_context :open_to_event_editor, :put, :update_order, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :put, :update_order, nonprofit_id: :__our_np, event_id: :__our_event end describe 'destroy' do - include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event, id: '1' end - end describe 'open to all' do describe 'show' do - include_context :open_to_all, :get, :show, nonprofit_id: :__our_np + include_context :open_to_all, :get, :show, nonprofit_id: :__our_np, event_id: :__our_event, id: '2' end describe 'index' do - include_context :open_to_all, :get, :index, nonprofit_id: :__our_np + include_context :open_to_all, :get, :index, nonprofit_id: :__our_np, event_id: :__our_event end end end -end \ No newline at end of file +end diff --git a/spec/controllers/tickets_spec.rb b/spec/controllers/tickets_spec.rb index 6bda138d..fd79bb3c 100644 --- a/spec/controllers/tickets_spec.rb +++ b/spec/controllers/tickets_spec.rb @@ -1,39 +1,38 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' require 'controllers/support/shared_user_context' -describe TicketsController, :type => :controller do +describe TicketsController, type: :controller do describe 'authorization' do include_context :shared_user_context describe 'rejects unauthorized users' do - describe 'update' do - include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event - end describe 'index' do - include_context :open_to_event_editor, :get, :index, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :get, :index, nonprofit_id: :__our_np, event_id: :__our_event, without_json_view: true end + describe 'update' do + include_context :open_to_event_editor, :put, :update, nonprofit_id: :__our_np, event_id: :__our_event, id: 1111 + end describe 'destroy' do - include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_event_editor, :delete, :destroy, nonprofit_id: :__our_np, event_id: :__our_event, id: 1111 end describe 'delete_card_for_ticket' do - include_context :open_to_np_associate, :post, :delete_card_for_ticket, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_np_associate, :post, :delete_card_for_ticket, nonprofit_id: :__our_np, event_id: :__our_event, id: 11_111 end - - end describe 'open to all' do describe 'create' do - include_context :open_to_all, :post, :create, nonprofit_id: :__our_np + include_context :open_to_all, :post, :create, nonprofit_id: :__our_np, event_id: :__our_event end describe 'add_note' do - include_context :open_to_all, :put, :add_note, nonprofit_id: :__our_np, event_id: :__our_event + include_context :open_to_all, :put, :add_note, nonprofit_id: :__our_np, event_id: :__our_event, id: 1111 end - end end -end \ No newline at end of file +end diff --git a/spec/cve/cve_2014_2538_spec.rb b/spec/cve/cve_2014_2538_spec.rb index 53fcb21e..bc2b49ed 100644 --- a/spec/cve/cve_2014_2538_spec.rb +++ b/spec/cve/cve_2014_2538_spec.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + # License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later require 'rails_helper' -require "rack/ssl" +require 'rack/ssl' describe Rack::SSL do describe '.call' do - it "invalid uri returns 404" do + it 'invalid uri returns 404' do def test_invalid_uri_returns_404 # Can't test this with Rack::Test because it fails on the URI before it # even gets to Rack::SSL. Other webservers will pass this URI through. ssl = Rack::SSL.new(nil) - resp = ssl.call('PATH_INFO' => "https://example.org/path/