diff --git a/.github/workflows/build_baseimage_with_gems.yml b/.github/workflows/build_baseimage_with_gems.yml index 3487e838c..e6f72594a 100644 --- a/.github/workflows/build_baseimage_with_gems.yml +++ b/.github/workflows/build_baseimage_with_gems.yml @@ -17,7 +17,7 @@ jobs: steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Login to container registry env: diff --git a/.github/workflows/build_deploy_staging.yml b/.github/workflows/build_deploy_staging.yml index e339353f2..b854ce820 100644 --- a/.github/workflows/build_deploy_staging.yml +++ b/.github/workflows/build_deploy_staging.yml @@ -67,7 +67,7 @@ jobs: - name: Set EPP port run: echo "EPP_PORT=${PR_REF:(-3)}" >> $GITHUB_ENV - + - name: Set config files for build env: ST_APP: ${{ secrets.ST_APPLICATION_YML}} @@ -101,7 +101,7 @@ jobs: sed -i -e 's/{certfile_path, "\/opt\/shared\/ca\/certs\/cert.pem"},/{certfile_path, "\/opt\/shared\/ca\/certs\/tls.crt"},/' sys.config sed -i -e 's/{keyfile_path, "\/opt\/shared\/ca\/certs\/key.pem"},/{keyfile_path, "\/opt\/shared\/ca\/certs\/tls.key"}]},/' sys.config sed -i -e 's/{crlfile_path, "\/opt\/shared\/ca\/certs\/key.pem"}]},//' sys.config - + - name: Build proxy image run: | cd epp_proxy @@ -143,7 +143,9 @@ jobs: chmod 0600 kubeconfig - name: Install Open VPN - run: sudo apt-get install openvpn + run: | + sudo apt-get update + sudo apt-get install openvpn - name: Deploy from remote server timeout-minutes: 5 diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index dd71c4bee..d50636e26 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -22,7 +22,7 @@ jobs: continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} @@ -79,7 +79,7 @@ jobs: - name: Save coverage run: ./cc-test-reporter format-coverage --output coverage/codeclimate.${{ matrix.ruby }}.json - - uses: actions/upload-artifact@v2.3.1 + - uses: actions/upload-artifact@v3.0.0 with: name: coverage-${{ matrix.ruby }} path: coverage/codeclimate.${{ matrix.ruby }}.json @@ -104,7 +104,7 @@ jobs: - name: Give test coverage reporter executable permissions run: chmod +x cc-test-reporter - - uses: actions/download-artifact@v2.1.0 + - uses: actions/download-artifact@v3.0.0 with: name: coverage-${{ matrix.ruby }} path: coverage diff --git a/.gitignore b/.gitignore index 3f23f1277..1c4a85f46 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /config/application.yml /config/environments/development.rb /config/deploy.rb +/config/master.key /.idea # Do not commit one. Instead, download the latest from https://github.com/internetee/style-guide. diff --git a/CHANGELOG.md b/CHANGELOG.md index 893557f80..944496e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,72 @@ +17.05.2022 +* removed unnecessary contact validation on contact create [#2376](https://github.com/internetee/registry/pull/2376) +* Refactored email validation job [#2369](https://github.com/internetee/registry/pull/2369) +* Job for deprecated validation events removal [#2374](https://github.com/internetee/registry/issues/2374) + +09.05.2022 +* test for auction view [#2373](https://github.com/internetee/registry/pull/2373) + +06.05.2022 +* refactored out the contact_code_cache from domain_contacts model [#2370](https://github.com/internetee/registry/issues/2370) + +28.04.2022 +* Fixed ns and dnssec validation error messages [#2296](https://github.com/internetee/registry/issues/2296) +* Added status notes to REPP domain info output [#2331](https://github.com/internetee/registry/issues/2331) +* Added auction list view to admin for improved ahandling of upcoming enlgish auction feature [#2341](https://github.com/internetee/registry/pull/2341) + +27.04.2022 +* Refactored email validation - reducing dns requests [#2364](https://github.com/internetee/registry/issues/2364) + +21.04.2022 +* Delay renovate Ruby version updates for 60 days [#2361](https://github.com/internetee/registry/issues/2361) + +20.04.2022 +* Contacts with disclosed attributes can now be updated [#2340](https://github.com/internetee/registry/issues/2340) +* Legacy code fix [#2360](https://github.com/internetee/registry/pull/2360) + +19.04.2022 +* Rolled back ruby version to 3.0.3 [#2358](https://github.com/internetee/registry/pull/2358) + +18.04.2022 +* Fixed error 2005 epp syntax issue [#2338](https://github.com/internetee/registry/issues/2338) +* Fixed poll issue with email validations [#2343](https://github.com/internetee/registry/issues/2343) +* Removed registrant portal code from registry project [#2350](https://github.com/internetee/registry/issues/2350) + +14.04.2022 +* Removed legacy email verification code [#2349](https://github.com/internetee/registry/issues/2349) + +06.04.2022 +* Contact email validation on domain update [#2213](https://github.com/internetee/registry/issues/2213) + +05.04.2022 +* Automatic contact name update poll messages are now grouped together into one change poll message [#2307](https://github.com/internetee/registry/issues/2307) +* Status notes are now added to status elements of epp xml [#2211](https://github.com/internetee/registry/issues/2211) +* Admin: Wildcard search improvements [#499](https://github.com/internetee/registry/issues/499) +* Admin: CSV download fix for history view [#2275](https://github.com/internetee/registry/issues/2275) +* Admin: CSV output fix for diman, contact, invoices and account views [2303](https://github.com/internetee/registry/issues/2303) +* Admin: registrar dropdown is searchable in invoice creation view [#2313](https://github.com/internetee/registry/issues/2313) +* Admin: Refactored CSV generation [#2321](https://github.com/internetee/registry/issues/2321) +* Removed legacy migration jobs [#2090](https://github.com/internetee/registry/issues/2090) + +04.04.2022 +* Upload-artifact update to 3.0.0 [#2301](https://github.com/internetee/registry/pull/2301) +* data_migrate update to 8.0 [#2302](https://github.com/internetee/registry/pull/2302) + +01.04.2022 +* Pg update to 1.3.5 [#2328](https://github.com/internetee/registry/pull/2328) +* Puma update to 5.6.4 [#2327](https://github.com/internetee/registry/pull/2327) + +31.03.2022 +* Sidekiq update to 6.4.1 [#2322](https://github.com/internetee/registry/pull/2322) + +25.03.2022 +* Bulk change of business contacts' names requires now user confirmation [#2309](https://github.com/internetee/registry/pull/2309) + +23.02.2022 +* FD notes are updated when basis for FD changes [#2216](https://github.com/internetee/registry/issues/2216) +* Admin: date filter end date in domain hostory is now inclusive [#2274](https://github.com/internetee/registry/issues/2274) +* Job for finding and removing disputed statuses from the domains that should not have it [#2281](https://github.com/internetee/registry/issues/2281) + 09.02.2022 * DNSSEC key validation [#1897](https://github.com/internetee/registry/issues/1897) diff --git a/Gemfile b/Gemfile index b5ae07f77..c907eb267 100644 --- a/Gemfile +++ b/Gemfile @@ -17,9 +17,9 @@ gem 'figaro', '~> 1.2' # model related gem 'paper_trail', '~> 12.1' -gem 'pg', '1.3.2' +gem 'pg', '1.3.5' # 1.8 is for Rails < 5.0 -gem 'ransack', '~> 2.5.0' +gem 'ransack', '~> 2.6.0' gem 'truemail', '~> 2.4' # validates email by regexp, mail server existence and address existence gem 'validates_email_format_of', '1.6.3' # validates email against RFC 2822 and RFC 3696 @@ -40,7 +40,7 @@ gem 'select2-rails', '4.0.13' # for autocomplete gem 'selectize-rails', '0.12.6' # include selectize.js for select # registry specfic -gem 'data_migrate', '~> 7.0' +gem 'data_migrate', '~> 8.0' gem 'dnsruby', '~> 1.61' gem 'isikukood' # for EE-id validation gem 'money-rails' @@ -70,7 +70,7 @@ gem 'jquery-ui-rails', '6.0.1' gem 'pdfkit' gem 'que' gem 'que-web' -gem 'sidekiq' +gem 'sidekiq', '>= 6.4.1' gem 'company_register', github: 'internetee/company_register', branch: 'master' diff --git a/Gemfile.lock b/Gemfile.lock index 5eaba3a62..033c9a88b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -193,7 +193,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.9) + concurrent-ruby (1.1.10) connection_pool (2.2.5) countries (4.0.1) i18n_data (~> 0.13.0) @@ -205,7 +205,7 @@ GEM daemons-rails (1.2.1) daemons multi_json (~> 1.0) - data_migrate (7.0.2) + data_migrate (8.0.0) activerecord (>= 5.0) railties (>= 5.0) database_cleaner (2.0.1) @@ -258,7 +258,7 @@ GEM httpi (2.4.5) rack socksify - i18n (1.8.11) + i18n (1.10.0) concurrent-ruby (~> 1.0) i18n_data (0.13.0) isikukood (0.1.2) @@ -289,7 +289,7 @@ GEM kaminari-core (1.2.1) libxml-ruby (3.2.1) logger (1.4.3) - loofah (2.12.0) + loofah (2.16.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) @@ -303,7 +303,7 @@ GEM nokogiri (~> 1) rake mini_mime (1.1.1) - mini_portile2 (2.7.1) + mini_portile2 (2.8.0) minitest (5.15.0) monetize (1.9.4) money (~> 6.12) @@ -324,10 +324,10 @@ GEM newrelic_rpm (= 8.1.0) newrelic_rpm (8.1.0) nio4r (2.5.8) - nokogiri (1.13.0) - mini_portile2 (~> 2.7.0) + nokogiri (1.13.4) + mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogiri (1.13.0-x86_64-linux) + nokogiri (1.13.4-x86_64-linux) racc (~> 1.4) nori (2.6.0) omniauth (1.9.1) @@ -351,7 +351,7 @@ GEM activerecord (>= 5.2) request_store (~> 1.1) pdfkit (0.8.5) - pg (1.3.2) + pg (1.3.5) pg_query (2.1.2) google-protobuf (>= 3.17.1) pghero (2.8.1) @@ -360,7 +360,7 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (4.0.6) - puma (5.6.2) + puma (5.6.4) nio4r (~> 2.0) que (0.14.3) que-web (0.7.2) @@ -406,9 +406,9 @@ GEM rake (>= 0.13) thor (~> 1.0) rake (13.0.6) - ransack (2.5.0) - activerecord (>= 5.2.4) - activesupport (>= 5.2.4) + ransack (2.6.0) + activerecord (>= 6.0.4) + activesupport (>= 6.0.4) i18n rbtree3 (0.6.0) redis (4.6.0) @@ -449,7 +449,7 @@ GEM selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) - sidekiq (6.4.0) + sidekiq (6.4.1) connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.2.0) @@ -480,7 +480,7 @@ GEM attr_required (>= 0.0.5) httpclient (>= 2.4) temple (0.8.2) - thor (1.1.0) + thor (1.2.1) tilt (2.0.10) truemail (2.4.9) simpleidn (~> 0.2.1) @@ -524,7 +524,7 @@ GEM wkhtmltopdf-binary (0.12.5.4) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.5.2) + zeitwerk (2.5.4) PLATFORMS ruby @@ -544,7 +544,7 @@ DEPENDENCIES company_register! countries daemons-rails (= 1.2.1) - data_migrate (~> 7.0) + data_migrate (~> 8.0) database_cleaner devise (~> 4.8) digidoc_client! @@ -574,7 +574,7 @@ DEPENDENCIES omniauth-tara! paper_trail (~> 12.1) pdfkit - pg (= 1.3.2) + pg (= 1.3.5) pg_query (>= 0.9.0) pghero pry (= 0.14.1) @@ -582,13 +582,13 @@ DEPENDENCIES que que-web rails (~> 6.1.4) - ransack (~> 2.5.0) + ransack (~> 2.6.0) rest-client rexml sass-rails select2-rails (= 4.0.13) selectize-rails (= 0.12.6) - sidekiq + sidekiq (>= 6.4.1) simplecov (= 0.17.1) simpleidn (= 0.2.1) spy @@ -601,4 +601,4 @@ DEPENDENCIES wkhtmltopdf-binary (~> 0.12.5.1) BUNDLED WITH - 2.2.31 + 2.3.9 diff --git a/README.md b/README.md index d0766bd15..2d5b058cc 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Documentation ------------- * [EPP documentation](/doc/epp) -* [EPP request-response examples](/doc/epp-examples.md) +* [EPP request-response examples](/doc/epp_examples.md) * [REPP documentation](https://internetee.github.io/repp-apidoc/) Installation diff --git a/app/assets/stylesheets/registrant-manifest.sass b/app/assets/stylesheets/registrant-manifest.sass deleted file mode 100644 index 6d0a281fe..000000000 --- a/app/assets/stylesheets/registrant-manifest.sass +++ /dev/null @@ -1,11 +0,0 @@ -//= require 'registrant/registrant-bootstrap' -//= require 'jquery-ui/datepicker' -//= require 'select2' -//= require 'select2-bootstrap' -@import shared/fonts -@import shared/general -@import forms -@import typeaheadjs -@import selectize -@import selectize.bootstrap3 -@import registrant/registrant diff --git a/app/assets/stylesheets/registrant/registrant-bootstrap.sass b/app/assets/stylesheets/registrant/registrant-bootstrap.sass deleted file mode 100644 index 08f6eb984..000000000 --- a/app/assets/stylesheets/registrant/registrant-bootstrap.sass +++ /dev/null @@ -1,19 +0,0 @@ -$brand-primary: #7EA82F -$navbar-default-bg: #7EA82F -$navbar-default-brand-color: #fff -$navbar-default-link-color: #fff -$border-radius-base: 2px -$body-bg: #F8F8F8 -$container-large-desktop: 1040px -$font-family-sans-serif: 'EtelkaLightProRegular', Arial, Helvetica, sans-serif -$font-family-serif: 'EtelkaLightProBold', Georgia, "Times New Roman", Times, serif -$font-size-h1: 26px -$navbar-default-link-active-color: #333 - -@import 'bootstrap-sprockets' -@import 'bootstrap' -@import 'shared/general-bootstrap' - -// Support rails error element -.field_with_errors - @extend .has-error diff --git a/app/assets/stylesheets/registrant/registrant.sass b/app/assets/stylesheets/registrant/registrant.sass deleted file mode 100644 index ebe9f4974..000000000 --- a/app/assets/stylesheets/registrant/registrant.sass +++ /dev/null @@ -1,44 +0,0 @@ -html - position: relative - min-height: 100% - overflow-y: scroll - -body - padding-bottom: 130px - -body > .container - height: 100% - background: #fff - padding: 60px 30px 30px 30px - -h1, h2, h3, h4 - margin-bottom: 0px !important - -// Commented out, default 20px is needed on forms -// hr - // margin-top: 10px !important - // margin-bottom: 10px !important - -.navbar li - font-weight: bold - -.footer - position: absolute - bottom: 0 - width: 100% - height: 130px - background: image_url('bg.jpg') - color: white !important - background-size: 100% - -.confirmation - padding: 40px 0 20px 0 - .column-keys - text-align: right - width: 49% - float: left - .column-values - float: right - font-weight: bold - text-align: left - width: 49% diff --git a/app/controllers/admin/account_activities_controller.rb b/app/controllers/admin/account_activities_controller.rb index ebd44e28e..452acaee1 100644 --- a/app/controllers/admin/account_activities_controller.rb +++ b/app/controllers/admin/account_activities_controller.rb @@ -35,7 +35,8 @@ module Admin respond_to do |format| format.html format.csv do - send_data @q.result.to_csv, filename: "account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv" + raw_csv = CsvGenerator.generate_csv(@q.result) + send_data raw_csv, filename: "account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv" end end diff --git a/app/controllers/admin/auctions_controller.rb b/app/controllers/admin/auctions_controller.rb new file mode 100644 index 000000000..c1023b705 --- /dev/null +++ b/app/controllers/admin/auctions_controller.rb @@ -0,0 +1,142 @@ +module Admin + class AuctionsController < BaseController + load_and_authorize_resource + + def index + params[:q] ||= {} + + @auctions = Auction.with_domain_name(params[:domain_matches]) + .with_status(params[:statuses_contains]) + .with_start_created_at_date(params[:created_at_start]) + .with_end_created_at_date(params[:created_at_end]) + .order(created_at: :desc) + + @auction = Auction.new + + normalize_search_parameters do + @q = @auctions.ransack(PartialSearchFormatter.format(params[:q])) + @auctions = @q.result.page(params[:page]) + end + + @auctions = @auctions.per(params[:results_per_page_auction]) if params[:results_per_page_auction].to_i.positive? + + domains = ReservedDomain.all.order(:name) + q = domains.ransack(PartialSearchFormatter.format(params[:q])) + @domains = q.result.page(params[:page]) + @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? + + render_by_format('admin/auctions/index', 'auctions') + end + + def create + auction = Auction.new(domain: params[:domain], status: Auction.statuses[:started], platform: 'manual') + + if domain_exists_in_blocked_disputed_and_registered?(params[:domain]) + flash[:alert] = "Adding #{params[:domain]} failed - domain registered or regsitration is blocked" + redirect_to admin_auctions_path and return + end + + result = check_availability(params[:domain])[0] + if result[:avail].zero? + flash[:alert] = "Cannot generate domain. Reason: #{result[:reason]}" + redirect_to admin_auctions_path and return + end + + if auction.save + reserved_domain = auction.domain if remove_from_reserved(auction) + flash[:notice] = "Auction #{params[:domain]} created. + #{reserved_domain.present? ? 'These domain will be removed from reserved list: ' + reserved_domain : ' '}" + else + flash[:alert] = 'Something goes wrong' + end + + redirect_to admin_auctions_path + end + + def upload_spreadsheet + if params[:q].nil? + flash[:alert] = 'No file upload! Look at the left of upload button!' + redirect_to admin_auctions_path and return + end + + filename = params[:q][:file] + table = CSV.parse(File.read(filename), headers: true) + + failed_names = [] + reserved_domains = [] + + if validate_table(table) + table.each do |row| + record = row.to_h + + if domain_exists_in_blocked_disputed_and_registered?(record['name']) + failed_names << record['name'] + + next + end + + result = check_availability(record['name'])[0] + if result[:avail].zero? + failed_names << record['name'] + + next + end + + auction = Auction.new(domain: record['name'], status: Auction.statuses[:started], platform: 'manual') + flag = remove_from_reserved(auction) if auction.save! + reserved_domains << auction.domain if flag + end + + message_template = "Domains added! + #{reserved_domains.present? ? + 'These domains will be removed from reserved list: ' + reserved_domains.join(' ') + '! ' + : '! '} + #{failed_names.present? ? 'These domains were ignored: ' + failed_names.join(' ') : '!'}" + + flash[:notice] = message_template + else + flash[:alert] = "Invalid CSV format. Should be column with 'name' where is the list of name of domains!" + end + + redirect_to admin_auctions_path + end + + private + + def check_availability(domain_name) + Epp::Domain.check_availability(domain_name) + end + + def domain_exists_in_blocked_disputed_and_registered?(domain_name) + Domain.exists?(name: domain_name) || + BlockedDomain.exists?(name: domain_name) || + Dispute.exists?(domain_name: domain_name) || + Auction.exists?(domain: domain_name) + end + + def validate_table(table) + first_row = table.headers + first_row.include? 'name' + end + + def remove_from_reserved(auction) + domain = ReservedDomain.find_by(name: auction.domain) + + domain.destroy if domain.present? + end + + def normalize_search_parameters + ca_cache = params[:q][:valid_to_lteq] + begin + end_time = params[:q][:valid_to_lteq].try(:to_date) + params[:q][:valid_to_lteq] = end_time.try(:end_of_day) + rescue + logger.warn('Invalid date') + end + + yield + + params[:q][:valid_to_lteq] = ca_cache + end + end +end diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 016c0a750..56806ba3e 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -26,7 +26,7 @@ module Admin respond_to do |format| format.html { render page } format.csv do - raw_csv = @q.result.to_csv + raw_csv = CsvGenerator.generate_csv(@q.result) send_data raw_csv, filename: "#{filename}_#{Time.zone.now.to_formatted_s(:number)}.csv", type: "#{Mime[:csv]}; charset=utf-8" diff --git a/app/controllers/admin/blocked_domains_controller.rb b/app/controllers/admin/blocked_domains_controller.rb index 268eaca73..c039c5de1 100644 --- a/app/controllers/admin/blocked_domains_controller.rb +++ b/app/controllers/admin/blocked_domains_controller.rb @@ -5,7 +5,7 @@ module Admin def index params[:q] ||= {} domains = BlockedDomain.all.order(:name) - @q = domains.ransack(params[:q]) + @q = domains.ransack(PartialSearchFormatter.format(params[:q])) @domains = @q.result.page(params[:page]) @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? @@ -13,13 +13,10 @@ module Admin end def new - @domain = BlockedDomain.new - end def create - @domain = BlockedDomain.new(blocked_domain_params) if @domain.save @@ -29,18 +26,16 @@ module Admin flash.now[:alert] = I18n.t('failed_to_add_domain') render 'new' end - end def delete - if BlockedDomain.find(params[:id]).destroy flash[:notice] = I18n.t('domain_deleted') - redirect_to admin_blocked_domains_path else flash.now[:alert] = I18n.t('failed_to_delete_domain') - redirect_to admin_blocked_domains_path end + + redirect_to admin_blocked_domains_path end def blocked_domain_params diff --git a/app/controllers/admin/contact_versions_controller.rb b/app/controllers/admin/contact_versions_controller.rb index 8ded131c0..43d43d5e8 100644 --- a/app/controllers/admin/contact_versions_controller.rb +++ b/app/controllers/admin/contact_versions_controller.rb @@ -1,29 +1,15 @@ module Admin class ContactVersionsController < BaseController - include ObjectVersionsHelper + include ApplicationHelper load_and_authorize_resource class: Version::ContactVersion def index params[:q] ||= {} - search_params = params[:q].deep_dup.except(:created_at_gteq, :created_at_lteq) - - where_s = '1=1' - - search_params.each do |key, value| - next if value.empty? - - where_s += case key - when 'event' - " AND event = '#{value}'" - else - create_where_string(key, value) - end - end - - versions = Version::ContactVersion.includes(:item).where(where_s).order(created_at: :desc, id: :desc) - @q = versions.ransack(params[:q]) + search_params = PartialSearchFormatter.format(fix_date_params) + versions = Version::ContactVersion.includes(:item).order(created_at: :desc, id: :desc) + @q = versions.ransack(polymorphic_association(search_params)) @versions = @q.result.page(params[:page]) @versions = @versions.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? @@ -53,8 +39,23 @@ module Admin render json: Version::ContactVersion.search_by_query(params[:q]) end - def create_where_string(key, value) - " AND object->>'#{key}' ~* '#{value}'" + private + + def polymorphic_association(search_params) + record_type = {} + fields = %w[name code ident] + fields.each { |field| record_type[:"item_of_Contact_type_#{field}_matches"] = search_params[:"#{field}_matches"] } + + record_type + end + + def fix_date_params + params_copy = params[:q].deep_dup + if params_copy['created_at_lteq'].present? + params_copy['created_at_lteq'] = Date.parse(params_copy['created_at_lteq']) + 1.day + end + + params_copy end end end diff --git a/app/controllers/admin/contacts_controller.rb b/app/controllers/admin/contacts_controller.rb index 192b86242..73ead7b25 100644 --- a/app/controllers/admin/contacts_controller.rb +++ b/app/controllers/admin/contacts_controller.rb @@ -9,10 +9,8 @@ module Admin params[:q] ||= {} search_params = params[:q].deep_dup - if search_params[:domain_contacts_type_in].is_a?(Array) && - search_params[:domain_contacts_type_in].delete('registrant') - search_params[:registrant_domains_id_not_null] = 1 - end + search_params[:registrant_domains_id_not_null] = 1 if search_params[:domain_contacts_type_in].is_a?(Array) && + search_params[:domain_contacts_type_in].delete('registrant') contacts = Contact.includes(:registrar).joins(:registrar) .select('contacts.*, registrars.name as registrars_name') @@ -20,7 +18,7 @@ module Admin contacts = filter_by_flags(contacts) normalize_search_parameters do - @q = contacts.ransack(search_params) + @q = contacts.ransack(PartialSearchFormatter.format(search_params)) @contacts = @q.result.distinct.page(params[:page]) end @@ -33,7 +31,7 @@ module Admin if params[:only_no_country_code].eql?('1') contacts = contacts.where("ident_country_code is null or ident_country_code=''") end - contacts = contacts.email_verification_failed if params[:email_verification_failed].eql?('1') + contacts end @@ -41,8 +39,7 @@ module Admin render json: Contact.search_by_query(params[:q]) end - def edit - end + def edit; end def update cp = ignore_empty_statuses diff --git a/app/controllers/admin/domain_versions_controller.rb b/app/controllers/admin/domain_versions_controller.rb index bdff4b085..c82347ff9 100644 --- a/app/controllers/admin/domain_versions_controller.rb +++ b/app/controllers/admin/domain_versions_controller.rb @@ -1,7 +1,5 @@ module Admin class DomainVersionsController < BaseController - include ObjectVersionsHelper - load_and_authorize_resource class: Version::DomainVersion def index @@ -44,7 +42,7 @@ module Admin where_s += ' AND 1=0' if registrars == [] versions = Version::DomainVersion.includes(:item).where(where_s).order(created_at: :desc, id: :desc) - @q = versions.ransack(params[:q]) + @q = versions.ransack(fix_date_params) @versions = @q.result.page(params[:page]) @versions = @versions.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? @@ -77,5 +75,15 @@ module Admin def create_where_string(key, value) " AND object->>'#{key}' ~* '#{value}'" end + + private + + def fix_date_params + params_copy = params[:q].deep_dup + created_at = params_copy['created_at_lteq'] + params_copy['created_at_lteq'] = Date.parse(created_at) + 1.day if created_at.present? + + params_copy + end end end diff --git a/app/controllers/admin/domains_controller.rb b/app/controllers/admin/domains_controller.rb index 20f47da02..707b40654 100644 --- a/app/controllers/admin/domains_controller.rb +++ b/app/controllers/admin/domains_controller.rb @@ -17,18 +17,10 @@ module Admin end normalize_search_parameters do - @q = domains.ransack(params[:q]) + @q = domains.ransack(PartialSearchFormatter.format(params[:q])) @domains = @q.result.page(params[:page]) - (redirect_to [:admin, @domains.first] and return if @domains.count == 1 && params[:q][:name_matches].present?) - if @domains.count.zero? && params[:q][:name_matches] !~ /^%.+%$/ - # if we do not get any results, add wildcards to the name field and search again - n_cache = params[:q][:name_matches] - params[:q][:name_matches] = "%#{params[:q][:name_matches]}%" - @q = domains.ransack(params[:q]) - @domains = @q.result.page(params[:page]) - params[:q][:name_matches] = n_cache # we don't want to show wildcards in search form - end end + @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? render_by_format('admin/domains/index', 'domains') @@ -95,7 +87,7 @@ module Admin def build_associations @server_statuses = @domain.statuses.select { |x| DomainStatus::SERVER_STATUSES.include?(x) } @server_statuses = [nil] if @server_statuses.empty? - @other_statuses = @domain.statuses.select { |x| !DomainStatus::SERVER_STATUSES.include?(x) } + @other_statuses = @domain.statuses.reject { |x| DomainStatus::SERVER_STATUSES.include?(x) } end def ignore_empty_statuses diff --git a/app/controllers/admin/epp_logs_controller.rb b/app/controllers/admin/epp_logs_controller.rb index 279ff1ab8..021c90379 100644 --- a/app/controllers/admin/epp_logs_controller.rb +++ b/app/controllers/admin/epp_logs_controller.rb @@ -5,7 +5,7 @@ module Admin # rubocop:disable Metrics/MethodLength def index - @q = ApiLog::EppLog.ransack(params[:q]) + @q = ApiLog::EppLog.ransack(PartialSearchFormatter.format(params[:q])) @q.sorts = 'id desc' if @q.sorts.empty? @epp_logs = @q.result @@ -29,16 +29,19 @@ module Admin def set_default_dates params[:q] ||= {} + return unless default_dates? - if params[:q][:created_at_gteq].nil? && params[:q][:created_at_lteq].nil? && params[:created_after].present? - default_date = params[:created_after] + default_date = params[:created_after] + default_date = 'today' unless %w[today tomorrow yesterday].include?(default_date) - if !['today', 'tomorrow', 'yesterday'].include?(default_date) - default_date = 'today' - end + params[:q][:created_at_gteq] = Date.send(default_date).strftime("%Y-%m-%d") + end - params[:q][:created_at_gteq] = Date.send(default_date).strftime("%Y-%m-%d") - end + private + + def default_dates? + params[:q] ||= {} + params[:q][:created_at_gteq].nil? && params[:q][:created_at_lteq].nil? && params[:created_after].present? end end end diff --git a/app/controllers/admin/invoices_controller.rb b/app/controllers/admin/invoices_controller.rb index 68db492d8..223257605 100644 --- a/app/controllers/admin/invoices_controller.rb +++ b/app/controllers/admin/invoices_controller.rb @@ -7,8 +7,8 @@ module Admin end def create - r = Registrar.find_by(id: deposit_params[:registrar_id]) - @deposit = Deposit.new(deposit_params.merge(registrar: r)) + registrar = Registrar.find(deposit_params[:registrar_id]) + @deposit = Deposit.new(deposit_params.merge(registrar: registrar)) @invoice = @deposit.issue_prepayment_invoice if @invoice&.persisted? diff --git a/app/controllers/admin/repp_logs_controller.rb b/app/controllers/admin/repp_logs_controller.rb index 028dbffc8..abb8415bc 100644 --- a/app/controllers/admin/repp_logs_controller.rb +++ b/app/controllers/admin/repp_logs_controller.rb @@ -5,7 +5,7 @@ module Admin # rubocop:disable Metrics/MethodLength def index - @q = ApiLog::ReppLog.ransack(params[:q]) + @q = ApiLog::ReppLog.ransack(PartialSearchFormatter.format(params[:q])) @q.sorts = 'id desc' if @q.sorts.empty? @repp_logs = @q.result @@ -31,17 +31,19 @@ module Admin def set_default_dates params[:q] ||= {} + return unless default_dates? - if params[:q][:created_at_gteq].nil? && params[:q][:created_at_lteq].nil? && params[:created_after].present? + default_date = params[:created_after] + default_date = 'today' unless %w[today tomorrow yesterday].include?(default_date) - default_date = params[:created_after] + params[:q][:created_at_gteq] = Date.send(default_date).strftime("%Y-%m-%d") + end - if !['today', 'tomorrow', 'yesterday'].include?(default_date) - default_date = 'today' - end + private - params[:q][:created_at_gteq] = Date.send(default_date).strftime("%Y-%m-%d") - end + def default_dates? + params[:q] ||= {} + params[:q][:created_at_gteq].nil? && params[:q][:created_at_lteq].nil? && params[:created_after].present? end end end diff --git a/app/controllers/admin/reserved_domains_controller.rb b/app/controllers/admin/reserved_domains_controller.rb index 35a81842d..20957dec4 100644 --- a/app/controllers/admin/reserved_domains_controller.rb +++ b/app/controllers/admin/reserved_domains_controller.rb @@ -6,7 +6,7 @@ module Admin def index params[:q] ||= {} domains = ReservedDomain.all.order(:name) - @q = domains.ransack(params[:q]) + @q = domains.ransack(PartialSearchFormatter.format(params[:q])) @domains = @q.result.page(params[:page]) @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i.positive? @@ -17,11 +17,9 @@ module Admin @domain = ReservedDomain.new end - def edit - end + def edit; end def create - @domain = ReservedDomain.new(reserved_domain_params) if @domain.save @@ -31,34 +29,48 @@ module Admin flash.now[:alert] = I18n.t('failed_to_add_domain') render 'new' end - end def update - if @domain.update(reserved_domain_params) flash[:notice] = I18n.t('domain_updated') else flash.now[:alert] = I18n.t('failed_to_update_domain') end - render 'edit' + render 'edit' end def delete - if ReservedDomain.find(params[:id]).destroy flash[:notice] = I18n.t('domain_deleted') - redirect_to admin_reserved_domains_path else flash.now[:alert] = I18n.t('failed_to_delete_domain') - redirect_to admin_reserved_domains_path end + redirect_to admin_reserved_domains_path + end + + def release_to_auction + redirect_to admin_reserved_domains_path and return if params[:reserved_elements].nil? + + reserved_domains_ids = params[:reserved_elements][:domain_ids] + reserved_domains = ReservedDomain.where(id: reserved_domains_ids) + + reserved_domains.each do |domain| + Auction.create!(domain: domain.name, status: Auction.statuses[:started], platform: 'manual') + domain.destroy! + end + + redirect_to admin_auctions_path end private + def reserved_checked_elements + # params.require(:reserved_elements).permit(:name, :password) + end + def reserved_domain_params params.require(:reserved_domain).permit(:name, :password) end diff --git a/app/controllers/api/v1/auctions_controller.rb b/app/controllers/api/v1/auctions_controller.rb index de8e94442..9a01f4e68 100644 --- a/app/controllers/api/v1/auctions_controller.rb +++ b/app/controllers/api/v1/auctions_controller.rb @@ -44,7 +44,7 @@ module Api private def serializable_hash(auction) - { id: auction.uuid, domain: auction.domain, status: auction.status } + { id: auction.uuid, domain: auction.domain, status: auction.status, platform: auction.platform } end def serializable_hash_for_update_action(auction) diff --git a/app/controllers/api/v1/registrant/contacts_controller.rb b/app/controllers/api/v1/registrant/contacts_controller.rb index 30096ab8a..b196c567a 100644 --- a/app/controllers/api/v1/registrant/contacts_controller.rb +++ b/app/controllers/api/v1/registrant/contacts_controller.rb @@ -34,6 +34,17 @@ module Api end end + def do_need_update_contacts + result = current_registrant_user.do_need_update_contacts? + render json: { update_contacts: result[:result], counter: result[:counter] } + end + + def update_contacts + contacts = current_registrant_user.update_contacts + + render json: { message: 'get it', contacts: contacts } + end + def update logger.debug 'Received update request' logger.debug params diff --git a/app/controllers/registrant_controller.rb b/app/controllers/registrant_controller.rb deleted file mode 100644 index 1e97281e7..000000000 --- a/app/controllers/registrant_controller.rb +++ /dev/null @@ -1,37 +0,0 @@ -class RegistrantController < ApplicationController - before_action :authenticate_registrant_user! - before_action :set_paper_trail_whodunnit - layout 'registrant/application' - - include Registrant::ApplicationHelper - - helper_method :head_title_sufix - - def head_title_sufix - t(:registrant_head_title_sufix) - end - - private - - def current_ability - @current_ability ||= Ability.new(current_registrant_user, request.remote_ip) - end - - def user_for_paper_trail - current_registrant_user.present? ? current_registrant_user.id_role_username : 'anonymous' - end - - def current_user_contacts - current_registrant_user.contacts - rescue CompanyRegister::NotAvailableError - flash.now[:notice] = t('registrant.company_register_unavailable') - current_registrant_user.direct_contacts - end - - def current_user_domains - current_registrant_user.domains - rescue CompanyRegister::NotAvailableError - flash.now[:notice] = t('registrant.company_register_unavailable') - current_registrant_user.direct_domains - end -end diff --git a/app/controllers/registrar/account_activities_controller.rb b/app/controllers/registrar/account_activities_controller.rb index 55e53f8fc..0ad8c3d5a 100644 --- a/app/controllers/registrar/account_activities_controller.rb +++ b/app/controllers/registrar/account_activities_controller.rb @@ -20,7 +20,8 @@ class Registrar respond_to do |format| format.html { @account_activities = @q.result.page(params[:page]) } format.csv do - send_data @q.result.to_csv, filename: "account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv" + raw_csv = CsvGenerator.generate_csv(@q.result) + send_data raw_csv, filename: "account_activities_#{Time.zone.now.to_formatted_s(:number)}.csv" end end diff --git a/app/controllers/registrar/contacts_controller.rb b/app/controllers/registrar/contacts_controller.rb index ec4ce5129..812e278e5 100644 --- a/app/controllers/registrar/contacts_controller.rb +++ b/app/controllers/registrar/contacts_controller.rb @@ -40,7 +40,7 @@ class Registrar @contacts = @contacts.per(contacts_per_page) if contacts_per_page.positive? end format.csv do - raw_csv = contacts.to_csv + raw_csv = CsvGenerator.generate_csv(contacts) send_data raw_csv, filename: 'contacts.csv', type: "#{Mime[:csv]}; charset=utf-8" end format.pdf do diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index e5ab59fa2..3347f5d38 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -88,14 +88,16 @@ class Registrar @domain_params[:period] = Depp::Domain.default_period end + # rubocop:disable Metrics/CognitiveComplexity def create authorize! :create, Depp::Domain @domain_params = domain_params.to_h @data = @domain.create(@domain_params) - if response_ok? + if @data && response_ok? redirect_to info_registrar_domains_url(domain_name: @domain_params[:name]) else + flash[:alert] = t('.email_error_message') unless @emails_check_result render 'new' end end @@ -113,13 +115,15 @@ class Registrar @data = @domain.update(@domain_params) @dispute = Dispute.active.find_by(domain_name: @domain_params[:name]) - if response_ok? + if @data && response_ok? redirect_to info_registrar_domains_url(domain_name: @domain_params[:name]) else + flash[:alert] = t('.email_error_message') unless @emails_check_result params[:domain_name] = @domain_params[:name] render 'new' end end + # rubocop:enable Metrics/CognitiveComplexity def delete authorize! :delete, Depp::Domain diff --git a/app/controllers/repp/v1/domains/contacts_controller.rb b/app/controllers/repp/v1/domains/contacts_controller.rb index 4c89243c7..b41b3a378 100644 --- a/app/controllers/repp/v1/domains/contacts_controller.rb +++ b/app/controllers/repp/v1/domains/contacts_controller.rb @@ -14,8 +14,8 @@ module Repp api :GET, '/repp/v1/domains/:domain_name/contacts' desc "View domain's admin and tech contacts" def index - admin_contacts = @domain.admin_domain_contacts.pluck(:contact_code_cache) - tech_contacts = @domain.tech_domain_contacts.pluck(:contact_code_cache) + admin_contacts = @domain.admin_domain_contacts.map(&:contact).pluck(:code) + tech_contacts = @domain.tech_domain_contacts.map(&:contact).pluck(:code) data = { admin_contacts: admin_contacts, tech_contacts: tech_contacts } render_success(data: data) @@ -38,7 +38,6 @@ module Repp def cta(action = 'add') params[:contacts].each { |c| c[:action] = action } action = Actions::DomainUpdate.new(@domain, contact_create_params, false) - # rubocop:disable Style/AndOr handle_errors(@domain) and return unless action.call # rubocop:enable Style/AndOr diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3de98b88c..122fc40d4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -10,27 +10,11 @@ module ApplicationHelper end def ident_for(contact) - if contact.is_a? Hash - ident_country_code = contact[:ident_country_code] - ident_type = contact[:ident_type] - ident = contact[:ident] - else - ident_country_code = contact.ident_country_code - ident_type = contact.ident_type - ident = contact.ident - end + ident = contact.ident + description = "[#{contact.ident_country_code} #{contact.ident_type}]" + description.prepend("#{ident} ") if ident.present? - case ident_type - when 'birthday' - "#{ident} [#{ident_country_code} #{ident_type}]" - else - if ident.present? - "#{ident} [#{ident_country_code} #{ident_type}]" - else - "[#{ident_country_code} #{ident_type}]" - end - - end + description end def current_commit_link diff --git a/app/helpers/auction_helper.rb b/app/helpers/auction_helper.rb new file mode 100644 index 000000000..25cf463af --- /dev/null +++ b/app/helpers/auction_helper.rb @@ -0,0 +1,19 @@ +module AuctionHelper + include ActionView::Helpers::TagHelper + + def colorize_auction(auction) + case auction.status + when 'started' then render_status_black(auction.domain) + when 'awaiting_payment' then render_status_black(auction.domain) + else render_status_green(auction.domain) + end + end + + def render_status_black(name) + tag.span name.to_s, style: 'color: black;' + end + + def render_status_green(name) + tag.span name.to_s, style: 'color: green;' + end +end diff --git a/app/helpers/object_versions_helper.rb b/app/helpers/object_versions_helper.rb deleted file mode 100644 index dae357cf6..000000000 --- a/app/helpers/object_versions_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ObjectVersionsHelper - def attach_existing_fields(version, new_object) - version.object_changes.to_h.each do |key, value| - method_name = "#{key}=".to_sym - new_object.public_send(method_name, event_value(version, value)) if new_object.respond_to?(method_name) - end - end - - def only_present_fields(version, model) - field_names = model.column_names - version.object.to_h.select { |key, _value| field_names.include?(key) } - end - - private - - def event_value(version, val) - version.event == 'destroy' ? val.first : val.last - end -end diff --git a/app/helpers/registrant/application_helper.rb b/app/helpers/registrant/application_helper.rb deleted file mode 100644 index 6451f91a2..000000000 --- a/app/helpers/registrant/application_helper.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Registrant::ApplicationHelper - def env_style - return '' if unstable_env.nil? - "background-image: url(#{image_path("registrar/bg-#{unstable_env}.png")});" - end -end diff --git a/app/interactions/actions/contact_create.rb b/app/interactions/actions/contact_create.rb index d31af2e38..f3e6560b8 100644 --- a/app/interactions/actions/contact_create.rb +++ b/app/interactions/actions/contact_create.rb @@ -19,12 +19,12 @@ module Actions def maybe_change_email return if Rails.env.test? - [:regex, :mx].each do |m| + %i[regex mx].each do |m| result = Actions::SimpleMailValidator.run(email: contact.email, level: m) - next if result - contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error)) + err_text = "email '#{contact.email}' didn't pass validation" + contact.add_epp_error('2005', nil, nil, "#{I18n.t(:parameter_value_syntax_error)} #{err_text}") @error = true return end @@ -84,6 +84,7 @@ module Actions return false if @error contact.generate_code + contact.email_history = contact.email contact.save end end diff --git a/app/interactions/actions/contact_update.rb b/app/interactions/actions/contact_update.rb index d9a136dd3..bfa3f3b7c 100644 --- a/app/interactions/actions/contact_update.rb +++ b/app/interactions/actions/contact_update.rb @@ -23,11 +23,12 @@ module Actions def maybe_change_email return if Rails.env.test? - [:regex, :mx].each do |m| + %i[regex mx].each do |m| result = Actions::SimpleMailValidator.run(email: @new_attributes[:email], level: m) next if result - contact.add_epp_error('2005', nil, "email didn't pass validation", I18n.t(:parameter_value_syntax_error)) + err_text = "email '#{new_attributes[:email]}' didn't pass validation" + contact.add_epp_error('2005', nil, nil, "#{I18n.t(:parameter_value_syntax_error)} #{err_text}") @error = true return end @@ -36,12 +37,11 @@ module Actions end def maybe_filtering_old_failed_records - if contact.validation_events.count > 1 - contact.validation_events.order!(created_at: :asc) - while contact.validation_events.count >= 1 - contact.validation_events.first.destroy - end - end + validation_events = contact.validation_events + return unless validation_events.count > 1 + + validation_events.order!(created_at: :asc) + validation_events.first.destroy while validation_events.count >= 1 end def maybe_remove_address @@ -112,10 +112,12 @@ module Actions email_changed = contact.will_save_change_to_email? old_email = contact.email_was + contact.email_history = old_email updated = contact.save - if updated && email_changed && contact.registrant? - ContactMailer.email_changed(contact: contact, old_email: old_email).deliver_now + if updated && email_changed + contact.validation_events.where('event_data @> ?', { 'email': old_email }.to_json).destroy_all + ContactMailer.email_changed(contact: contact, old_email: old_email).deliver_now if contact.registrant? end updated diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index 8fd25df0f..970d74b3b 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -120,7 +120,7 @@ module Actions contact = Contact.find_by(code: contact_code) arr = admin ? @admin_contacts : @tech_contacts if contact - arr << { contact_id: contact.id, contact_code_cache: contact.code } + arr << { contact_id: contact.id, contact_code: contact.code } else domain.add_epp_error('2303', 'contact', contact_code, %i[domain_contacts not_found]) end diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index 40b7876f6..a86b038ff 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -45,6 +45,10 @@ module Actions def assign_new_registrant domain.add_epp_error('2306', nil, nil, %i[registrant cannot_be_missing]) unless params[:registrant][:code] + contact_code = params[:registrant][:code] + contact = Contact.find_by(code: contact_code) + validate_email(contact.email) + regt = Registrant.find_by(code: params[:registrant][:code]) unless regt domain.add_epp_error('2303', 'registrant', params[:registrant], %i[registrant not_found]) @@ -120,9 +124,35 @@ module Actions @dnskeys << { id: dnkey.id, _destroy: 1 } if dnkey end + def start_validate_email(props) + contact = Contact.find_by(code: props[0][:contact_code]) + + return if contact.nil? + + validate_email(contact.email) + end + + def validate_email(email) + return true if Rails.env.test? + + %i[regex mx].each do |m| + result = Actions::SimpleMailValidator.run(email: email, level: m) + next if result + + err_text = "email #{email} didn't pass validation" + domain.add_epp_error('2005', nil, nil, "#{I18n.t(:parameter_value_syntax_error)} #{err_text}") + @error = true + return + end + + true + end + def assign_admin_contact_changes props = gather_domain_contacts(params[:contacts].select { |c| c[:type] == 'admin' }) + start_validate_email(props) if props.present? + if props.any? && domain.admin_change_prohibited? domain.add_epp_error('2304', 'admin', DomainStatus::SERVER_ADMIN_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) @@ -136,6 +166,8 @@ module Actions props = gather_domain_contacts(params[:contacts].select { |c| c[:type] == 'tech' }, admin: false) + start_validate_email(props) if props.present? + if props.any? && domain.tech_change_prohibited? domain.add_epp_error('2304', 'tech', DomainStatus::SERVER_TECH_CHANGE_PROHIBITED, I18n.t(:object_status_prohibits_operation)) @@ -173,7 +205,7 @@ module Actions domain.add_epp_error('2306', 'contact', code, %i[domain_contacts admin_contact_can_be_only_private_person]) else - add ? { contact_id: obj.id, contact_code_cache: obj.code } : { id: obj.id, _destroy: 1 } + add ? { contact_id: obj.id, contact_code: obj.code } : { id: obj.id, _destroy: 1 } end end diff --git a/app/interactions/actions/email_check.rb b/app/interactions/actions/email_check.rb index 248952a47..c16c035ed 100644 --- a/app/interactions/actions/email_check.rb +++ b/app/interactions/actions/email_check.rb @@ -31,31 +31,27 @@ module Actions Rails.env.test? && check_level == 'smtp' ? :mx : check_level.to_sym end + def destroy_old_validations(validation_events, minimum_size, check_level) + return unless validation_events.count > minimum_size && @check_level == check_level + + validation_events.order!(created_at: :asc) + validation_events.first.destroy while validation_events.count > minimum_size + end + def filtering_old_failed_records(result) - if @check_level == "mx" && !result.success && validation_eventable.validation_events.count > 3 - validation_eventable.validation_events.order!(created_at: :asc) - while validation_eventable.validation_events.count > 3 - validation_eventable.validation_events.first.destroy - end - end + events = validation_eventable.validation_events - if @check_level == "mx" && result.success && validation_eventable.validation_events.count > 1 - validation_eventable.validation_events.order!(created_at: :asc) - while validation_eventable.validation_events.count > 1 - validation_eventable.validation_events.first.destroy - end - end + destroy_old_validations(events, ValidationEvent::MX_CHECK, 'mx') unless result.success - if @check_level == "smtp" && validation_eventable.validation_events.count > 1 - validation_eventable.validation_events.order!(created_at: :asc) - while validation_eventable.validation_events.count > 1 - validation_eventable.validation_events.first.destroy - end - end + destroy_old_validations(events, ValidationEvent::REDEEM_EVENTS_COUNT_BY_LEVEL[:mx], 'mx') if result.success + + destroy_old_validations(events, ValidationEvent::REDEEM_EVENTS_COUNT_BY_LEVEL[:smtp], 'smtp') end def save_result(result) - if !result.success && @check_level == "mx" + contacts = Contact.where(email: email) + + if !result.success && @check_level == 'mx' result_validation = Actions::AAndAaaaEmailValidation.call(email: @email, value: 'A') output_a_and_aaaa_validation_results(email: @email, result: result_validation, @@ -65,11 +61,13 @@ module Actions output_a_and_aaaa_validation_results(email: @email, result: result_validation, type: 'AAAA') + result.success = result_validation.present? + end - result_validation.present? ? result.success = true : result.success = false - validation_eventable.validation_events.create(validation_event_attrs(result)) - else - validation_eventable.validation_events.create(validation_event_attrs(result)) + contacts.find_in_batches(batch_size: 500) do |contact_batches| + contact_batches.each do |contact| + contact.validation_events.create(validation_event_attrs(result)) + end end rescue ActiveRecord::RecordNotSaved logger.info "Cannot save validation result for #{log_object_id}" @@ -97,8 +95,7 @@ module Actions when 'AAAA' ress = dns.getresources domain, Resolv::DNS::Resource::IN::AAAA end - - result = ress.map { |r| r.address } + result = ress.map(&:address) end result diff --git a/app/interactions/domains/force_delete/notify_registrar.rb b/app/interactions/domains/force_delete/notify_registrar.rb index e4aa48976..25d59bf29 100644 --- a/app/interactions/domains/force_delete/notify_registrar.rb +++ b/app/interactions/domains/force_delete/notify_registrar.rb @@ -6,18 +6,26 @@ module Domains end def notify_without_email - domain.registrar.notifications.create!(text: I18n.t('force_delete_set_on_domain', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date)) + template = I18n.t('force_delete_set_on_domain', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date) + + return if domain.registrar.notifications.last.text.include? template + + domain.registrar.notifications.create!(text: template) end def notify_with_email - domain.registrar.notifications.create!(text: I18n.t('force_delete_auto_email', - domain_name: domain.name, - outzone_date: domain.outzone_date, - purge_date: domain.purge_date, - email: email)) + template = I18n.t('force_delete_auto_email', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date, + email: email) + + return if domain.registrar.notifications.last.text.include? template + + domain.registrar.notifications.create!(text: template) end end end diff --git a/app/interactions/domains/force_delete_email/base.rb b/app/interactions/domains/force_delete_email/base.rb index f22244045..d75749b50 100644 --- a/app/interactions/domains/force_delete_email/base.rb +++ b/app/interactions/domains/force_delete_email/base.rb @@ -11,6 +11,8 @@ module Domains domains = domain_contacts.map(&:domain).flatten + Domain.where(registrant_id: registrant_ids) + return if expired_or_hold_domains_exists?(domains) + domains.each do |domain| next if domain.expired? @@ -20,6 +22,12 @@ module Domains private + def expired_or_hold_domains_exists?(domains) + domains.any? do |domain| + domain.statuses.include?(DomainStatus::SERVER_HOLD) && email.include?(domain.name) + end + end + def before_execute_force_delete(domain) if domain.force_delete_scheduled? && !domain.status_notes[DomainStatus::FORCE_DELETE].nil? added_additional_email_into_notes(domain) @@ -28,6 +36,18 @@ module Domains end end + def notify_registrar(domain) + template = I18n.t('force_delete_auto_email', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date, + email: domain.status_notes[DomainStatus::FORCE_DELETE]) + + return if domain.registrar.notifications.last.text.include? template + + domain.registrar.notifications.create!(text: template) + end + def process_force_delete(domain) domain.schedule_force_delete(type: :soft, notify_by_email: true, @@ -39,6 +59,8 @@ module Domains def added_additional_email_into_notes(domain) return if domain.status_notes[DomainStatus::FORCE_DELETE].include? email + # notify_registrar(domain) + domain.status_notes[DomainStatus::FORCE_DELETE].concat(" #{email}") domain.save(validate: false) end diff --git a/app/jobs/check_force_delete_job.rb b/app/jobs/check_force_delete_job.rb new file mode 100644 index 000000000..5fb3408f1 --- /dev/null +++ b/app/jobs/check_force_delete_job.rb @@ -0,0 +1,43 @@ +class CheckForceDeleteJob < ApplicationJob + def perform(contact_ids) + contacts = Contact.find(contact_ids) + + contacts.each do |contact| + email = contact.email + + if contact.need_to_start_force_delete? + Domains::ForceDeleteEmail::Base.run(email: email) + elsif contact.need_to_lift_force_delete? + domain_list(email).each { |domain| refresh_status_notes(contact, domain) } + end + end + end + + private + + def refresh_status_notes(contact, domain) + force_delete_emails = domain.status_notes[DomainStatus::FORCE_DELETE] + return unless force_delete_emails + + force_delete_emails.slice!(contact.email_history) + force_delete_emails.lstrip! + domain.save(validate: false) + + notify_registrar(domain) unless force_delete_emails.empty? + end + + def domain_list(email) + domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten + registrant_ids = Registrant.where(email: email).pluck(:id) + + (domain_contacts.map(&:domain).flatten + Domain.where(registrant_id: registrant_ids)).uniq + end + + def notify_registrar(domain) + domain.registrar.notifications.create!(text: I18n.t('force_delete_auto_email', + domain_name: domain.name, + outzone_date: domain.outzone_date, + purge_date: domain.purge_date, + email: domain.status_notes[DomainStatus::FORCE_DELETE])) + end +end diff --git a/app/jobs/dispute_status_update_job.rb b/app/jobs/dispute_status_update_job.rb index 5e42f29e1..3051b0ce3 100644 --- a/app/jobs/dispute_status_update_job.rb +++ b/app/jobs/dispute_status_update_job.rb @@ -1,6 +1,7 @@ class DisputeStatusUpdateJob < ApplicationJob - def perform(logger: Logger.new($stdout)) + def perform(logger: Logger.new($stdout), include_closed: false) @logger = logger + @include_closed = include_closed @backlog = { 'activated': 0, 'closed': 0, 'activate_fail': [], 'close_fail': [] } .with_indifferent_access @@ -15,7 +16,11 @@ class DisputeStatusUpdateJob < ApplicationJob end def close_disputes - disputes = Dispute.where(closed: nil).where('expires_at < ?', Time.zone.today).all + disputes = if @include_closed + Dispute.where('expires_at < ?', Time.zone.today).all + else + Dispute.where(closed: nil).where('expires_at < ?', Time.zone.today).all + end @logger.info "DisputeStatusUpdateJob - Found #{disputes.count} closable disputes" disputes.each do |dispute| process_dispute(dispute, closing: true) diff --git a/app/jobs/migrate_before_force_delete_statuses_job.rb b/app/jobs/migrate_before_force_delete_statuses_job.rb deleted file mode 100644 index 7e6a1e1c0..000000000 --- a/app/jobs/migrate_before_force_delete_statuses_job.rb +++ /dev/null @@ -1,36 +0,0 @@ -class MigrateBeforeForceDeleteStatusesJob < ApplicationJob - def perform - logger.info 'Ran MigrateBeforeForceDeleteStatusesJob!' - - domains = Domain.where.not(statuses_before_force_delete: nil) - logger.info "Total domains are #{domains.count}" - - interate_domain_in_batches(domains) - end - - private - - def interate_domain_in_batches(domains) - count = 0 - - domains.find_in_batches do |domain_batches| - count += domain_batches.count - logger.info "Proccesing #{count} domains of #{domains.count}" - domain_batches.each do |domain| - migrate_data_to_statuses_history(domain) - end - end - end - - def migrate_data_to_statuses_history(domain) - domain.update(force_delete_domain_statuses_history: domain.statuses_before_force_delete) - rescue StandardError => e - logger.warn "#{domain.name} crashed!" - logger.warn e.to_s - raise e - end - - def logger - @logger ||= Logger.new(Rails.root.join('log/migrate_before_force_delete_statuses.log')) - end -end diff --git a/app/jobs/migrate_statuses_to_domain_history_job.rb b/app/jobs/migrate_statuses_to_domain_history_job.rb deleted file mode 100644 index f35c986b6..000000000 --- a/app/jobs/migrate_statuses_to_domain_history_job.rb +++ /dev/null @@ -1,36 +0,0 @@ -class MigrateStatusesToDomainHistoryJob < ApplicationJob - def perform - logger.info 'Ran MigrateStatusesToDomainHistoryJob!' - - domains = Domain.where(locked_by_registrant_at: nil) - logger.info "Total domains are #{domains.count}" - - interate_domain_in_batches(domains) - end - - private - - def interate_domain_in_batches(domains) - count = 0 - - domains.find_in_batches do |domain_batches| - count += domain_batches.count - logger.info "Proccesing #{count} domains of #{domains.count}" - domain_batches.each do |domain| - migrate_data_to_admin_store_field(domain) - end - end - end - - def migrate_data_to_admin_store_field(domain) - domain.update(admin_store_statuses_history: domain.statuses) - rescue StandardError => e - logger.warn "#{domain.name} crashed!" - logger.warn e.to_s - raise e - end - - def logger - @logger ||= Logger.new(Rails.root.join('log/migrate_statuses_to_domain_history.log')) - end -end diff --git a/app/jobs/nameserver_record_validation_job.rb b/app/jobs/nameserver_record_validation_job.rb index 7dd2211a4..f737c0f36 100644 --- a/app/jobs/nameserver_record_validation_job.rb +++ b/app/jobs/nameserver_record_validation_job.rb @@ -64,12 +64,11 @@ class NameserverRecordValidationJob < ApplicationJob nameserver.save end - def add_nameserver_to_failed(nameserver:, reason:) - if nameserver.validation_counter.nil? - nameserver.validation_counter = 1 - else - nameserver.validation_counter = nameserver.validation_counter + 1 - end + def add_nameserver_to_failed(nameserver:, reason:, result_reason:) + return cname_case_handle(nameserver: nameserver, reason: reason) if result_reason == 'cname' + + nameserver.validation_counter = 1 if nameserver.validation_counter.nil? + nameserver.validation_counter = nameserver.validation_counter + 1 nameserver.failed_validation_reason = reason nameserver.save @@ -77,15 +76,25 @@ class NameserverRecordValidationJob < ApplicationJob failed_log(text: reason, nameserver: nameserver, domain: nameserver.domain) if nameserver.failed_validation? end + def cname_case_handle(nameserver:, reason:) + nameserver.validation_datetime = Time.zone.now + nameserver.failed_validation_reason = reason + nameserver.save + + failed_log(text: reason, nameserver: nameserver, domain: nameserver.domain) + end + def parse_result(result, nameserver) domain = Domain.find(nameserver.domain_id) - text = "" + text = '' case result[:reason] when 'answer' - text = "No any answer comes from **#{nameserver.hostname}**. Nameserver not exist" + text = "DNS Server **#{nameserver.hostname}** not responding" when 'serial' - text = "Serial number for nameserver hostname **#{nameserver.hostname}** doesn't present. SOA validation failed." + text = "Serial number for nameserver hostname **#{nameserver.hostname}** of #{nameserver.domain.name} doesn't present in zone. SOA validation failed." + when 'cname' + text = "Warning: SOA record expected but CNAME found instead. This setup can lead to unexpected errors when using the domain: hostname - **#{nameserver.hostname}** of #{nameserver.domain.name}" when 'not found' text = "Seems nameserver hostname **#{nameserver.hostname}** doesn't exist" when 'exception' @@ -97,7 +106,7 @@ class NameserverRecordValidationJob < ApplicationJob end logger.info text - add_nameserver_to_failed(nameserver: nameserver, reason: text) + add_nameserver_to_failed(nameserver: nameserver, reason: text, result_reason: result[:reason]) false end diff --git a/app/jobs/validate_dnssec_job.rb b/app/jobs/validate_dnssec_job.rb index 623513768..69168efbb 100644 --- a/app/jobs/validate_dnssec_job.rb +++ b/app/jobs/validate_dnssec_job.rb @@ -36,7 +36,7 @@ class ValidateDnssecJob < ApplicationJob domain.nameservers.each do |n| next unless n.validated? - validate(hostname: n.hostname, domain: domain) + validate(nameserver: n, domain: domain) notify_contacts(domain) logger.info "----------------------------" @@ -54,25 +54,26 @@ class ValidateDnssecJob < ApplicationJob # ContactNotification.notify_tech_contact(domain: domain, reason: 'dnssec') end - def validate(hostname:, domain:, type: 'DNSKEY', klass: 'IN') - resolver = prepare_validator(hostname) + def validate(nameserver:, domain:, type: 'DNSKEY', klass: 'IN') + resolver = prepare_validator(nameserver.hostname) answer = resolver.query(domain.name, type, klass) - return logger.info "no any data for #{domain.name} | hostname - #{hostname}" if answer.nil? + return logger.info "no any data for #{domain.name} | hostname - #{nameserver.hostname}" if answer.nil? logger.info "-----------" - logger.info "data for domain name - #{domain.name} | hostname - #{hostname}" + logger.info "data for domain name - #{domain.name} | hostname - #{nameserver.hostname}" logger.info "-----------" response_container = parse_response(answer) - compare_dnssec_data(response_container: response_container, domain: domain) + compare_dnssec_data(response_container: response_container, domain: domain, nameserver: nameserver) rescue Exception => e - logger.error "#{e.message} - domain name: #{domain.name} - hostname: #{hostname}" + logger.error "#{e.message} - domain name: #{domain.name} - hostname: #{nameserver.hostname}" + nameserver.update(failed_validation_reason: "#{e.message} - domain name: #{domain.name} - hostname: #{nameserver.hostname}") nil end - def compare_dnssec_data(response_container:, domain:) + def compare_dnssec_data(response_container:, domain:, nameserver:) domain.dnskeys.each do |key| next unless key.flags.to_s == '257' next if key.validation_datetime.present? @@ -82,11 +83,15 @@ class ValidateDnssecJob < ApplicationJob if flag key.validation_datetime = Time.zone.now + key.failed_validation_reason = nil key.save + nameserver.failed_validation_reason = nil + nameserver.save logger.info text + " ------->> succesfully!" else - logger.info text + " ------->> not found in zone!" + key.update!(failed_validation_reason: text + " not found in zone! Domain name - #{domain.name}. Hostname - #{nameserver.hostname}") + logger.info text + " ------->> not found in zone! Domain name - #{domain.name}. Hostname - #{nameserver.hostname}" end end end @@ -133,10 +138,11 @@ class ValidateDnssecJob < ApplicationJob inner_resolver.nameserver = nameserver inner_resolver.packet_timeout = timeouts.to_i inner_resolver.query_timeout = timeouts.to_i - resolver = Dnsruby::Recursor.new(inner_resolver) - resolver.dnssec = true + # resolver = Dnsruby::Recursor.new(inner_resolver) + # resolver.dnssec = true - resolver + # resolver + inner_resolver end def logger diff --git a/app/jobs/verify_emails_job.rb b/app/jobs/verify_emails_job.rb index 4b9b98fb7..7274fcad4 100644 --- a/app/jobs/verify_emails_job.rb +++ b/app/jobs/verify_emails_job.rb @@ -1,8 +1,13 @@ class VerifyEmailsJob < ApplicationJob discard_on StandardError - def perform(contact:, check_level: 'mx') - contact_not_found(contact.id) unless contact + def perform(email:, check_level: 'mx') + contact = Contact.find_by(email: email) + + return Rails.logger.info "No found #{email} contact" if contact.nil? + + return unless filter_check_level(contact) + validate_check_level(check_level) action = Actions::EmailCheck.new(email: contact.email, validation_eventable: contact, @@ -32,4 +37,17 @@ class VerifyEmailsJob < ApplicationJob def valid_check_levels ValidationEvent::VALID_CHECK_LEVELS end + + def get_validation_results(contact) + ValidationEvent.where(created_at: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day) + end + + def filter_check_level(contact) + return true unless contact.validation_events.exists? + + data = contact.validation_events.order(created_at: :asc).last + return true if data.successful? && data.created_at < (Time.zone.now - ValidationEvent::VALIDATION_PERIOD) + + !(data.failed? && data.event_data['check_level'] == 'regex') + end end diff --git a/app/lib/to_csv.rb b/app/lib/to_csv.rb deleted file mode 100644 index 32c288978..000000000 --- a/app/lib/to_csv.rb +++ /dev/null @@ -1,10 +0,0 @@ -module ToCsv - def to_csv - CSV.generate do |csv| - csv << column_names - all.find_each do |item| - csv << item.attributes.values_at(*column_names) - end - end - end -end diff --git a/app/models/ability.rb b/app/models/ability.rb index baa26e4cb..bc2caa6ba 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -12,7 +12,7 @@ class Ability @user.roles&.each { |role| send(role) } when 'ApiUser' @user.roles&.each { |role| send(role) } - when 'RegistrantUser' + when 'RegistrantUser' static_registrant end @@ -95,6 +95,7 @@ class Ability can :manage, User can :manage, ApiUser can :manage, AdminUser + can :manage, Auction can :manage, Certificate can :manage, LegalDocument can :manage, BankStatement diff --git a/app/models/account.rb b/app/models/account.rb index fe0820888..7639c61dd 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,5 +1,4 @@ class Account < ApplicationRecord - extend ToCsv include Versions belongs_to :registrar, required: true @@ -12,4 +11,12 @@ class Account < ApplicationRecord def activities account_activities end + + def as_csv_row + [id, balance, currency, registrar] + end + + def self.csv_header + ['Id', 'Balance', 'Currency', 'Registrar'] + end end diff --git a/app/models/account_activity.rb b/app/models/account_activity.rb index 05873a2a6..38fa70358 100644 --- a/app/models/account_activity.rb +++ b/app/models/account_activity.rb @@ -11,6 +11,7 @@ class AccountActivity < ApplicationRecord UPDATE_CREDIT = 'update_credit'.freeze after_create :update_balance + def update_balance account.balance += sum account.save @@ -19,23 +20,17 @@ class AccountActivity < ApplicationRecord save end + def as_csv_row + [account.registrar.try(:code), description, I18n.t(activity_type), I18n.l(created_at), sum] + end + class << self def types_for_select [CREATE, RENEW, ADD_CREDIT, UPDATE_CREDIT].map { |x| [I18n.t(x), x] } end - def to_csv - attributes = %w(description activity_type created_at sum) - - CSV.generate(headers: true) do |csv| - csv << %w(registrar description activity_type receipt_date sum) - - all.each do |x| - attrs = [x.account.registrar.try(:code)] - attrs += attributes.map { |attr| x.send(attr) } - csv << attrs - end - end + def csv_header + ['Registrar', 'Description', 'Activity Type', 'Receipt Date', 'Sum'] end end end diff --git a/app/models/action.rb b/app/models/action.rb index ac5ee7f72..8a822f867 100644 --- a/app/models/action.rb +++ b/app/models/action.rb @@ -2,18 +2,40 @@ class Action < ApplicationRecord has_paper_trail versions: { class_name: 'Version::ActionVersion' } belongs_to :user - belongs_to :contact + belongs_to :contact, optional: true + has_many :subactions, class_name: 'Action', + foreign_key: 'bulk_action_id', + inverse_of: :bulk_action, + dependent: :destroy + belongs_to :bulk_action, class_name: 'Action', optional: true validates :operation, inclusion: { in: proc { |action| action.class.valid_operations } } class << self def valid_operations - %w[update] + %w[update bulk_update] end end def notification_key - raise 'Action object is missing' unless contact + raise 'Action object is missing' unless bulk_action? || contact + "contact_#{operation}".to_sym end + + def bulk_action? + !!subactions.exists? + end + + def to_non_available_contact_codes + return [] unless bulk_action? + + subactions.map do |a| + { + code: a.contact.code, + avail: 0, + reason: 'in use', + } + end + end end diff --git a/app/models/api_log/epp_log.rb b/app/models/api_log/epp_log.rb index feed1ecad..c89569be5 100644 --- a/app/models/api_log/epp_log.rb +++ b/app/models/api_log/epp_log.rb @@ -1,5 +1,3 @@ module ApiLog - class EppLog < Db - extend ToCsv - end + class EppLog < Db; end end diff --git a/app/models/api_log/repp_log.rb b/app/models/api_log/repp_log.rb index 62dcee238..540a9043a 100644 --- a/app/models/api_log/repp_log.rb +++ b/app/models/api_log/repp_log.rb @@ -1,5 +1,3 @@ module ApiLog - class ReppLog < Db - extend ToCsv - end + class ReppLog < Db; end end diff --git a/app/models/auction.rb b/app/models/auction.rb index 791184d60..465a827ec 100644 --- a/app/models/auction.rb +++ b/app/models/auction.rb @@ -9,11 +9,30 @@ class Auction < ApplicationRecord domain_not_registered: 'domain_not_registered', } + enum platform: %i[auto manual] + PENDING_STATUSES = [statuses[:started], statuses[:awaiting_payment], statuses[:payment_received]].freeze + private_constant :PENDING_STATUSES + scope :with_status, ->(status) { + where(status: status) if status.present? + } + + scope :with_start_created_at_date, ->(start_created_at) { + where('created_at >= ?', start_created_at) if start_created_at.present? + } + + scope :with_end_created_at_date, ->(end_created_at) { + where('created_at <= ?', end_created_at) if end_created_at.present? + } + + scope :with_domain_name, ->(domain_name) { + where('domain ilike ?', "%#{domain_name.strip}%") if domain_name.present? + } + def self.pending(domain_name) find_by(domain: domain_name.to_s, status: PENDING_STATUSES) end diff --git a/app/models/blocked_domain.rb b/app/models/blocked_domain.rb index fb50a8b52..f82faa771 100644 --- a/app/models/blocked_domain.rb +++ b/app/models/blocked_domain.rb @@ -1,6 +1,5 @@ class BlockedDomain < ApplicationRecord include Versions - extend ToCsv before_save :generate_data after_destroy :remove_data diff --git a/app/models/bulk_action.rb b/app/models/bulk_action.rb new file mode 100644 index 000000000..9c98ee2db --- /dev/null +++ b/app/models/bulk_action.rb @@ -0,0 +1 @@ +class BulkAction < Action; end diff --git a/app/models/concerns/contact/disclosable.rb b/app/models/concerns/contact/disclosable.rb deleted file mode 100644 index a61b240b1..000000000 --- a/app/models/concerns/contact/disclosable.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Contact::Disclosable - extend ActiveSupport::Concern - - class_methods do - attr_accessor :disclosable_attributes - end - - included do - self.disclosable_attributes = %w[name email] - validate :validate_disclosed_attributes - end - - private - - def validate_disclosed_attributes - return if disclosed_attributes.empty? - - has_undisclosable_attributes = (disclosed_attributes - self.class.disclosable_attributes) - .any? - errors.add(:disclosed_attributes, :invalid) if has_undisclosable_attributes - end -end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index 3fa3bf627..7dd309c41 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -34,13 +34,7 @@ module Domain::ForceDelete reason = explicit&.downcase return reason if %w[invalid_email invalid_phone].include?(reason) - if contact_emails_verification_failed.present? - 'invalid_email' - elsif registrant.org? - 'legal_person' - else - 'private_person' - end + registrant.org? ? 'legal_person' : 'private_person' end def force_delete_scheduled? diff --git a/app/models/concerns/email_verifable.rb b/app/models/concerns/email_verifable.rb index 4f9b4ffeb..be9603031 100644 --- a/app/models/concerns/email_verifable.rb +++ b/app/models/concerns/email_verifable.rb @@ -5,10 +5,6 @@ module EmailVerifable scope :recently_not_validated, -> { where.not(id: ValidationEvent.validated_ids_by(name)) } end - def email_verification_failed? - need_to_start_force_delete? - end - def validate_email_data(level:, count:) validation_events.order(created_at: :desc).limit(count).all? do |event| event.check_level == level.to_s && event.failed? @@ -18,9 +14,7 @@ module EmailVerifable def need_to_start_force_delete? flag = false ValidationEvent::INVALID_EVENTS_COUNT_BY_LEVEL.each do |level, count| - if validation_events.count >= count && validate_email_data(level: level, count: count) - flag = true - end + flag = true if validation_events.count >= count && validate_email_data(level: level, count: count) end flag diff --git a/app/models/contact.rb b/app/models/contact.rb index 84d4ba962..2be404997 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -7,7 +7,6 @@ class Contact < ApplicationRecord include UserEvents include Contact::Transferable include Contact::Identical - include Contact::Disclosable include Contact::Archivable include EmailVerifable @@ -16,7 +15,7 @@ class Contact < ApplicationRecord has_many :domain_contacts has_many :domains, through: :domain_contacts has_many :legal_documents, as: :documentable - has_many :validation_events, as: :validation_eventable + has_many :validation_events, as: :validation_eventable, dependent: :destroy has_many :registrant_domains, class_name: 'Domain', foreign_key: 'registrant_id' has_many :actions, dependent: :destroy @@ -25,10 +24,18 @@ class Contact < ApplicationRecord alias_attribute :kind, :ident_type alias_attribute :copy_from_id, :original_id # Old attribute name; for PaperTrail - scope :email_verification_failed, lambda { - joins('LEFT JOIN email_address_verifications emv ON contacts.email = emv.email') - .where('success = false and verified_at IS NOT NULL') - } + scope :with_different_company_name, (lambda do |company| + where("ident = ? AND ident_country_code = 'EE' AND name != ?", + company.registration_number, + company.company_name) + end) + + scope :with_different_registrant_name, (lambda do |user| + where('ident = ? AND ident_country_code = ? AND UPPER(name) != UPPER(?)', + user.ident, + user.country.alpha2, + user.username) + end) NAME_REGEXP = /([\u00A1-\u00B3\u00B5-\u00BF\u0021-\u0026\u0028-\u002C\u003A-\u0040]| [\u005B-\u005F\u007B-\u007E\u2040-\u206F\u20A0-\u20BF\u2100-\u218F])/x @@ -42,7 +49,7 @@ class Contact < ApplicationRecord validates :phone, presence: true, e164: true, phone: true - validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? } + # validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? } validates :code, uniqueness: { message: :epp_id_taken }, @@ -188,15 +195,6 @@ class Contact < ApplicationRecord ] end - def to_csv - CSV.generate do |csv| - csv << column_names - all.each do |contact| - csv << contact.attributes.values_at(*column_names) - end - end - end - def pdf(html) kit = PDFKit.new(html) kit.to_pdf @@ -569,4 +567,27 @@ class Contact < ApplicationRecord def deletable? !linked? end + + def ident_human_description + description = "[#{ident_country_code} #{ident_type}]" + description.prepend("#{ident} ") if ident.present? + + description + end + + def as_csv_row + [ + name, + code, + ident_human_description, + email, + created_at.to_formatted_s(:db), + registrar, + phone, + ] + end + + def self.csv_header + ['Name', 'ID', 'Ident', 'E-mail', 'Created at', 'Registrar', 'Phone'] + end end diff --git a/app/models/depp/contact.rb b/app/models/depp/contact.rb index 48e49d5e4..7007bcd00 100644 --- a/app/models/depp/contact.rb +++ b/app/models/depp/contact.rb @@ -5,7 +5,8 @@ module Depp attr_accessor :id, :name, :email, :phone, :org_name, :ident, :ident_type, :ident_country_code, :street, :city, :zip, :state, :country_code, - :password, :legal_document, :statuses, :code + :password, :legal_document, :statuses, :code, + :email_history DISABLED = 'Disabled' DISCLOSURE_TYPES = [DISABLED, '1', '0'] diff --git a/app/models/dispute.rb b/app/models/dispute.rb index cbf93566a..f5a948355 100644 --- a/app/models/dispute.rb +++ b/app/models/dispute.rb @@ -1,5 +1,4 @@ class Dispute < ApplicationRecord - extend ToCsv include WhoisStatusPopulate validates :domain_name, :password, :starts_at, :expires_at, presence: true before_validation :fill_empty_passwords, :set_expiry_date diff --git a/app/models/dns/domain_name.rb b/app/models/dns/domain_name.rb index bceb4433b..6c68c3797 100644 --- a/app/models/dns/domain_name.rb +++ b/app/models/dns/domain_name.rb @@ -35,6 +35,7 @@ module DNS def sell_at_auction auction = Auction.new auction.domain = name + auction.platform = 'auto' auction.start ToStdout.msg "Created the auction: #{auction.inspect}" update_whois_from_auction(auction) diff --git a/app/models/domain.rb b/app/models/domain.rb index 873216cf1..68c5bc611 100644 --- a/app/models/domain.rb +++ b/app/models/domain.rb @@ -161,14 +161,6 @@ class Domain < ApplicationRecord attribute: 'hostname' } - validates :tech_domain_contacts, uniqueness_multi: { - attribute: 'contact_code_cache' - } - - validates :admin_domain_contacts, uniqueness_multi: { - attribute: 'contact_code_cache' - } - validates :dnskeys, uniqueness_multi: { attribute: 'public_key' } @@ -289,21 +281,6 @@ class Domain < ApplicationRecord ) end - def to_csv - CSV.generate do |csv| - headers = column_names.dup - swap_elements(headers, [[0, 1], [1, 5]]) - headers[0] = 'Domain' - headers[1] = headers[1].humanize - csv << headers - all.find_each do |item| - row = item.attributes.values_at(*column_names) - swap_elements(row, [[0, 1], [1, 5]]) - csv << row - end - end - end - private def registrant_user_domains_by_registrant(registrant_user) @@ -749,8 +726,36 @@ class Domain < ApplicationRecord DNS::DomainName.new(name) end - def contact_emails_verification_failed - contacts.select(&:email_verification_failed?)&.map(&:email)&.uniq + def as_csv_row + [ + name, + registrant_name, + valid_to.to_formatted_s(:db), + registrar, + created_at.to_formatted_s(:db), + statuses, + contacts.pluck(:code), + force_delete_date, + force_delete_data, + ] + end + + def registrant_name + return registrant.name if registrant + + ver = Version::ContactVersion.where(item_id: registrant_id).last + contact = Contact.all_versions_for([registrant_id], created_at).first + + contact = ObjectVersionsParser.new(ver).parse if contact.nil? && ver + + contact.try(:name) || 'Deleted' + end + + def self.csv_header + [ + 'Domain', 'Registrant', 'Valid to', 'Registrar', 'Created at', + 'Statuses', 'Contacts code', 'Force delete date', 'Force delete data' + ] end def self.pdf(html) diff --git a/app/models/domain_contact.rb b/app/models/domain_contact.rb index 910f4e445..4a791493f 100644 --- a/app/models/domain_contact.rb +++ b/app/models/domain_contact.rb @@ -6,32 +6,21 @@ class DomainContact < ApplicationRecord belongs_to :contact belongs_to :domain + validates :contact, presence: true + + after_destroy :update_contact attr_accessor :value_typeahead + attr_writer :contact_code self.ignored_columns = %w[legacy_domain_id legacy_contact_id] - def epp_code_map - { - '2302' => [ - [:contact_code_cache, :taken, { value: { obj: 'contact', val: contact_code_cache } }] - ] - } - end - def name return 'Tech' if type == 'TechDomainContact' return 'Admin' if type == 'AdminDomainContact' + '' end - validates :contact, presence: true - - before_save :update_contact_code_cache - def update_contact_code_cache - self.contact_code_cache = contact.code - end - - after_destroy :update_contact def update_contact Contact.find(contact_id).save end diff --git a/app/models/epp/contact.rb b/app/models/epp/contact.rb index 614be201b..35691d789 100644 --- a/app/models/epp/contact.rb +++ b/app/models/epp/contact.rb @@ -47,7 +47,7 @@ class Epp::Contact < Contact codes = codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" } res = [] - codes.map { |c| c.include?(':') ? c : "#{reg}:#{c}" }.map { |c| c.strip.upcase }.each do |x| + codes.map { |c| c.strip.upcase }.each do |x| c = find_by_epp_code(x) res << (c ? { code: c.code, avail: 0, reason: 'in use' } : { code: x, avail: 1 }) end diff --git a/app/models/invoice.rb b/app/models/invoice.rb index df8b7aff8..6ba3a158d 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -3,7 +3,6 @@ class Invoice < ApplicationRecord include Invoice::Cancellable include Invoice::Payable include Invoice::BookKeeping - extend ToCsv belongs_to :buyer, class_name: 'Registrar' has_one :account_activity @@ -117,6 +116,23 @@ class Invoice < ApplicationRecord e_invoice_sent_at.present? end + def as_csv_row + [ + number, + buyer, + cancelled? ? I18n.t(:cancelled) : due_date, + receipt_date_status, + issue_date, + total, + currency, + seller_name, + ] + end + + def self.csv_header + ['Number', 'Buyer', 'Due Date', 'Receipt Date', 'Issue Date', 'Total', 'Currency', 'Seller Name'] + end + def self.create_from_transaction!(transaction) registrar_user = Registrar.find_by(reference_no: transaction.parsed_ref_number) return unless registrar_user @@ -128,6 +144,16 @@ class Invoice < ApplicationRecord private + def receipt_date_status + if paid? + receipt_date + elsif cancelled? + I18n.t(:cancelled) + else + I18n.t(:unpaid) + end + end + def apply_default_buyer_vat_no self.buyer_vat_no = buyer.vat_no end diff --git a/app/models/notification.rb b/app/models/notification.rb index 07e824367..c9af66c56 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -2,7 +2,7 @@ class Notification < ApplicationRecord include Versions # version/notification_version.rb belongs_to :registrar - belongs_to :action + belongs_to :action, optional: true scope :unread, -> { where(read: false) } diff --git a/app/models/registrant_user.rb b/app/models/registrant_user.rb index 5fe508125..073ab3214 100644 --- a/app/models/registrant_user.rb +++ b/app/models/registrant_user.rb @@ -20,49 +20,45 @@ class RegistrantUser < User def companies(company_register = CompanyRegister::Client.new) return [] if ident.include?('-') - companies = company_register.representation_rights(citizen_personal_code: ident, - citizen_country_code: country.alpha3) - - companies = update_contacts_before_receive(companies) - companies + company_register.representation_rights(citizen_personal_code: ident, + citizen_country_code: country.alpha3) rescue CompanyRegister::NotAvailableError - return [] + [] end - def update_contacts_before_receive(companies) - return [] if companies.blank? + def do_need_update_contacts? + counter = 0 + + counter += Contact.with_different_registrant_name(self).size companies.each do |company| - contacts = Contact.where(ident: company.registration_number, ident_country_code: 'EE') - - next if contacts.blank? - - contacts.each do |contact| - next if company.company_name == contact.name - - update_company_name(contact: contact, company: company) - end + counter += Contact.with_different_company_name(company).size end - companies + return { result: true, counter: counter } if counter.positive? + + { result: false, counter: 0 } end - def update_company_name(contact:, company:) - old_contact_name = contact.name - contact.name = company.company_name + # rubocop:disable Metrics/MethodLength + def update_contacts + user = self + contacts = [] + contacts.concat(Contact.with_different_registrant_name(user).each do |c| + c.write_attribute(:name, user.username) + end) + companies.each do |company| + contacts.concat(Contact.with_different_company_name(company).each do |c| + c.write_attribute(:name, company.company_name) + end) + end - contact.save(validate: false) + return [] if contacts.blank? - notify_registrar_data_updated(company_name: company.company_name, - old_contact_name: old_contact_name, - contact: contact) - end - - def notify_registrar_data_updated(company_name:, old_contact_name:, contact:) - contact.registrar.notifications.create!( - text: "Contact update: #{contact.id} name updated from #{old_contact_name} to #{company_name} by the registry" - ) + group_and_bulk_update(contacts) + contacts end + # rubocop:enable Metrics/MethodLength def contacts(representable: true) Contact.registrant_user_contacts(self, representable: representable) @@ -100,17 +96,6 @@ class RegistrantUser < User username.split.second end - def update_related_contacts - contacts = Contact.where(ident: ident, ident_country_code: country.alpha2) - .where('UPPER(name) != UPPER(?)', username) - - contacts.each do |contact| - contact.update(name: username) - action = actions.create!(contact: contact, operation: :update) - contact.registrar.notify(action) - end - end - class << self def find_or_create_by_api_data(user_data = {}) return false unless user_data[:ident] @@ -147,9 +132,27 @@ class RegistrantUser < User user = find_or_create_by(registrant_ident: "#{user_data[:country_code]}-#{user_data[:ident]}") user.username = "#{user_data[:first_name]} #{user_data[:last_name]}" user.save - - user.update_related_contacts user end end + + private + + def group_and_bulk_update(contacts) + contacts.group_by(&:registrar_id).each do |registrar_id, reg_contacts| + bulk_action, action = actions.create!(operation: :bulk_update) if reg_contacts.size > 1 + reg_contacts.each do |c| + if c.save(validate: false) + action = actions.create!(contact: c, operation: :update, bulk_action_id: bulk_action&.id) + end + end + notify_registrar_contacts_updated(action: bulk_action || action, + registrar_id: registrar_id) + end + end + + def notify_registrar_contacts_updated(action:, registrar_id:) + registrar = Registrar.find(registrar_id) + registrar&.notify(action) + end end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 8517bd6fe..aafc391cd 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -34,8 +34,8 @@ class Registrar < ApplicationRecord attribute :vat_rate, ::Type::VatRate.new after_initialize :set_defaults - validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? } - validate :correct_billing_email_format + # validate :correct_email_format, if: proc { |c| c.will_save_change_to_email? } + # validate :correct_billing_email_format alias_attribute :contact_email, :email @@ -218,8 +218,15 @@ class Registrar < ApplicationRecord end def notify(action) - text = I18n.t("notifications.texts.#{action.notification_key}", contact: action.contact.code) - notifications.create!(text: text) + text = I18n.t("notifications.texts.#{action.notification_key}", contact: action.contact&.code, + count: action.subactions&.count) + if action.bulk_action? + notifications.create!(text: text, action_id: action.id, + attached_obj_type: 'BulkAction', + attached_obj_id: action.id) + else + notifications.create!(text: text) + end end def e_invoice_iban diff --git a/app/models/reserved_domain.rb b/app/models/reserved_domain.rb index 5d585e5d9..10d5edd11 100644 --- a/app/models/reserved_domain.rb +++ b/app/models/reserved_domain.rb @@ -1,5 +1,4 @@ class ReservedDomain < ApplicationRecord - extend ToCsv include Versions # version/reserved_domain_version.rb include WhoisStatusPopulate before_save :fill_empty_passwords diff --git a/app/models/validation_event.rb b/app/models/validation_event.rb index 69fd660e5..3e7670fac 100644 --- a/app/models/validation_event.rb +++ b/app/models/validation_event.rb @@ -35,8 +35,6 @@ class ValidationEvent < ApplicationRecord scope :smtp, -> { where('event_data @> ?', { 'check_level': 'smtp' }.to_json) } scope :by_object, ->(object) { where(validation_eventable: object) } - after_create :check_for_force_delete - def self.validated_ids_by(klass) old_records .successful @@ -59,28 +57,4 @@ class ValidationEvent < ApplicationRecord def object validation_eventable end - - def check_for_force_delete - if object.need_to_start_force_delete? - start_force_delete - elsif object.need_to_lift_force_delete? - lift_force_delete - end - end - - def start_force_delete - Domains::ForceDeleteEmail::Base.run(email: email) - end - - def lift_force_delete - # domain_contacts = Contact.where(email: email).map(&:domain_contacts).flatten - # registrant_ids = Registrant.where(email: email).pluck(:id) - # - # domains = domain_contacts.map(&:domain).flatten + - # Domain.where(registrant_id: registrant_ids) - # - # domains.each do |domain| - # Domains::ForceDeleteLift::Base.run(domain: domain) - # end - end end diff --git a/app/models/version/contact_version.rb b/app/models/version/contact_version.rb index 95bd4b677..d3ff1130c 100644 --- a/app/models/version/contact_version.rb +++ b/app/models/version/contact_version.rb @@ -1,7 +1,23 @@ class Version::ContactVersion < PaperTrail::Version - extend ToCsv include VersionSession self.table_name = :log_contacts self.sequence_name = :log_contacts_id_seq + + def as_csv_row + contact = ObjectVersionsParser.new(self).parse + + [ + contact.name, + contact.code, + contact.ident_human_description, + contact.registrar, + event, + created_at.to_formatted_s(:db) + ] + end + + def self.csv_header + ['Name', 'ID', 'Ident', 'Registrar', 'Action', 'Created at'] + end end diff --git a/app/models/version/domain_version.rb b/app/models/version/domain_version.rb index 2c6848d4b..e84fc9da2 100644 --- a/app/models/version/domain_version.rb +++ b/app/models/version/domain_version.rb @@ -1,5 +1,4 @@ class Version::DomainVersion < PaperTrail::Version - extend ToCsv include VersionSession self.table_name = :log_domains @@ -7,6 +6,18 @@ class Version::DomainVersion < PaperTrail::Version scope :deleted, -> { where(event: 'destroy') } + def as_csv_row + domain = ObjectVersionsParser.new(self).parse + + [ + domain.name, + domain.registrant_name, + domain.registrar, + event, + created_at.to_formatted_s(:db) + ] + end + def self.was_contact_linked?(contact_id) sql = <<-SQL SELECT @@ -43,4 +54,8 @@ class Version::DomainVersion < PaperTrail::Version count_by_sql(sql).nonzero? end + + def self.csv_header + ['Name', 'Registrant', 'Registrar', 'Action', 'Created at'] + end end diff --git a/app/presenters/domain_presenter.rb b/app/presenters/domain_presenter.rb index 812881742..f19bbb62f 100644 --- a/app/presenters/domain_presenter.rb +++ b/app/presenters/domain_presenter.rb @@ -52,10 +52,6 @@ class DomainPresenter end end - def contact_emails_verification_failed - domain.contact_emails_verification_failed.join(', ') - end - def remove_registry_lock_btn return unless domain.locked_by_registrant? diff --git a/app/services/csv_generator.rb b/app/services/csv_generator.rb new file mode 100644 index 000000000..d92beeddc --- /dev/null +++ b/app/services/csv_generator.rb @@ -0,0 +1,29 @@ +class CsvGenerator + class << self + def generate_csv(objects) + class_name = objects.first.class + return default_generation(objects) unless custom_csv?(class_name) + + CSV.generate do |csv| + csv << class_name.csv_header + objects.each { |object| csv << object.as_csv_row } + end + end + + private + + def default_generation(objects) + CSV.generate do |csv| + csv << objects.column_names + objects.all.find_each { |object| csv << object.attributes.values_at(*objects.column_names) } + end + end + + def custom_csv?(class_name) + [ + Version::DomainVersion, Version::ContactVersion, Domain, + Contact, Invoice, Account, AccountActivity + ].include?(class_name) + end + end +end diff --git a/app/services/nameserver_validator.rb b/app/services/nameserver_validator.rb index c45003cd3..6436ba363 100644 --- a/app/services/nameserver_validator.rb +++ b/app/services/nameserver_validator.rb @@ -35,8 +35,14 @@ module NameserverValidator return { result: false, reason: 'answer' } if result.answer.empty? + decision = result.answer.any? do |a| + a.type == 'CNAME' + end + + return { result: false, reason: 'cname' } if decision + decision = result.answer.all? do |a| - a.serial.present? + a.instance_variable_defined? '@serial' end return { result: false, reason: 'serial' } unless decision diff --git a/app/services/object_versions_parser.rb b/app/services/object_versions_parser.rb new file mode 100644 index 000000000..5871a6bd6 --- /dev/null +++ b/app/services/object_versions_parser.rb @@ -0,0 +1,28 @@ +class ObjectVersionsParser + def initialize(version) + @version = version + end + + def parse + model = @version.item_type.constantize + attributes = only_present_fields(model) + history_object = model.new(attributes) + attach_existing_fields(history_object) unless @version.event == 'destroy' + + history_object + end + + private + + def attach_existing_fields(history_object) + @version.object_changes.to_h.each do |key, value| + method_name = "#{key}=".to_sym + history_object.public_send(method_name, value.last) if history_object.respond_to?(method_name) + end + end + + def only_present_fields(model) + field_names = model.column_names + @version.object.to_h.select { |key, _value| field_names.include?(key) } + end +end diff --git a/app/services/partial_search_formatter.rb b/app/services/partial_search_formatter.rb new file mode 100644 index 000000000..af0c7978d --- /dev/null +++ b/app/services/partial_search_formatter.rb @@ -0,0 +1,13 @@ +class PartialSearchFormatter + def self.format(params) + search_params = params.deep_dup + + search_params.each do |key, value| + next unless key.include?('matches') && value.present? + + value << '%' + end + + search_params + end +end diff --git a/app/views/admin/auctions/_modal.html.erb b/app/views/admin/auctions/_modal.html.erb new file mode 100644 index 000000000..e57d2139d --- /dev/null +++ b/app/views/admin/auctions/_modal.html.erb @@ -0,0 +1,15 @@ +
\ No newline at end of file diff --git a/app/views/admin/auctions/index.html.erb b/app/views/admin/auctions/index.html.erb new file mode 100644 index 000000000..85d533fa7 --- /dev/null +++ b/app/views/admin/auctions/index.html.erb @@ -0,0 +1,155 @@ ++ <%= sort_link(@q, 'domain') %> + | ++ <%= sort_link(@q, 'status') %> + | ++ <%= sort_link(@q, 'created_at') %> + | ++ <%= sort_link(@q, 'registration_code') %> + | ++ <%= sort_link(@q, 'registration_deadline') %> + | ++ <%= sort_link(@q, 'platform', 'Type') %> + | +
---|---|---|---|---|---|
<%= colorize_auction(auction) %> | +<%= auction.status %> | +<%= auction.created_at %> | +<%= auction.registration_code %> | +<%= auction.registration_deadline %> | +<%= auction.platform.nil? ? 'auto' : auction.platform %> | +
+ <%= check_box_tag :check_all %> + | ++ <%= sort_link(@q, 'name') %> + | ++ <%= sort_link(@q, 'password') %> + | ++ <%= sort_link(@q, 'created_at', t(:created_at)) %> + | ++ <%= sort_link(@q, 'updated_at', t(:updated_at)) %> + | ++ <%= t(:actions) %> + | +
---|---|---|---|---|---|
+ <%= f.check_box :domain_ids, { multiple: true }, x.id, nil %> + | ++ <%= x.name %> + | ++ <%= x.password %> + | ++ <%= l(x.created_at, format: :short) %> + | ++ <%= l(x.updated_at, format: :short) %> + | ++ <%= link_to(t(:edit_pw), edit_admin_reserved_domain_path(id: x.id), class: 'btn btn-primary btn-xs') %> + <%= link_to(t(:delete), delete_admin_reserved_domain_path(id: x.id), data: { confirm: t(:are_you_sure) }, class: 'btn btn-danger btn-xs') %> + | +
Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt
- -Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>.
- -Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.
- -Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.
- -Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.
- -Lisaküsimuste korral võtke palun ühendust oma registripidajaga:
-<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> - -<%= render 'mailers/shared/signatures/signature.et.html' %> - -Dear registrant/administrative contact of .ee domain,
- -Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.
- -Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.
- -Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants
- -If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.
- -Should you have additional questions, please contact your registrar:
-<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> - -<%= render 'mailers/shared/signatures/signature.en.html' %> -Уважаемый регистрант/административный контакт домена .ee
- -Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.
- -Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.
- -Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов
- -Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.
- -В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: -<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>
- -<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb deleted file mode 100644 index 8d2fc58ce..000000000 --- a/app/views/mailers/domain_delete_mailer/forced/invalid_email.text.erb +++ /dev/null @@ -1,47 +0,0 @@ -Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt
- -Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontaktandmed on puudulikud - eposti aadress <%= @domain.contact_emails_verification_failed %>
- -Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.
- -Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.
- -Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.
- -Lisaküsimuste korral võtke palun ühendust oma registripidajaga:
-<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> - -<%= render 'mailers/shared/signatures/signature.et.html' %> - -Dear registrant/administrative contact of .ee domain,
- -Estonian Internet Foundation has learned that contact data of the domain <%= @domain.name %> s invalid - email(s) <%= @domain.contact_emails_verification_failed %>.
- -Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.
- -Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants
- -If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.
- -Should you have additional questions, please contact your registrar:
-<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> - -<%= render 'mailers/shared/signatures/signature.en.html' %> -Уважаемый регистрант/административный контакт домена .ee
- -Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @registrant.reg_no %> неверны - электронная почта <%= @domain.contact_emails_verification_failed %>.
- -Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.
- -Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов
- -Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.
- -В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: - <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>
- -<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/registrar/domains/partials/_statuses.haml b/app/views/registrar/domains/partials/_statuses.haml index 57ecc4105..125309caf 100644 --- a/app/views/registrar/domains/partials/_statuses.haml +++ b/app/views/registrar/domains/partials/_statuses.haml @@ -6,7 +6,7 @@ %thead %tr %th{class: 'col-xs-6'}= t(:status) - %th{class: 'col-xs-6'}= t(:description) + %th{class: 'col-xs-6'}= t(:notes) %tbody - @data.css('status').each do |x| %tr diff --git a/config/locales/en.yml b/config/locales/en.yml index 2f7e8a0aa..9c396cbde 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -96,8 +96,6 @@ en: contact: blank: 'Contact was not found' taken: 'Contact already exists on this domain' - contact_code_cache: - taken: 'Contact already exists on this domain' domain_status: attributes: @@ -189,6 +187,7 @@ en: log_out: 'Log out (%{user})' system: 'System' domains: 'Domains' + auctions: 'Auctions' registrars: 'Registrars' valid_to: 'Valid to' name: 'Name' @@ -371,7 +370,7 @@ en: sim_error: 'SIM application error' internal_error: 'Internal error' client_side_status_editing_error: 'Parameter value policy error. Client-side object status management not supported' - parameter_value_syntax_error: 'Parameter value syntax error: ' + parameter_value_syntax_error: 'Parameter value syntax error:' # DEPP activemodel: @@ -627,7 +626,7 @@ en: contact_ident: 'Contact ident' results_per_page: 'Results per page' nameserver_hostname: 'Nameserver hostname' - result_count: + result_count: zero: 'No results' other: '%{count} results' one: '1 result' diff --git a/config/locales/notifications.en.yml b/config/locales/notifications.en.yml index b5c1dfd47..3bd65ea7e 100644 --- a/config/locales/notifications.en.yml +++ b/config/locales/notifications.en.yml @@ -6,6 +6,7 @@ en: It was associated with registrant %{old_registrant_code} and contacts %{old_contacts_codes}. contact_update: Contact %{contact} has been updated by registrant + contact_bulk_update: '%{count} contacts have been updated by registrant' csync: CSYNC DNSSEC %{action} for domain %{domain} registrar_locked: Domain %{domain_name} has been locked by registrant registrar_unlocked: Domain %{domain_name} has been unlocked by registrant diff --git a/config/locales/registrar/domains.en.yml b/config/locales/registrar/domains.en.yml index b8605bd42..be2574d92 100644 --- a/config/locales/registrar/domains.en.yml +++ b/config/locales/registrar/domains.en.yml @@ -1,6 +1,8 @@ en: registrar: domains: + email_error_message: 'Check contacts emails' + index: header: Domains new_btn: New domain diff --git a/config/routes.rb b/config/routes.rb index 66debd4b4..4ba44300d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -128,7 +128,11 @@ Rails.application.routes.draw do resources :domains, only: %i[index show], param: :uuid do resource :registry_lock, only: %i[create destroy] end - resources :contacts, only: %i[index show update], param: :uuid + resources :contacts, only: %i[index show update], param: :uuid do + get 'do_need_update_contacts', to: 'contacts#do_need_update_contacts', + as: :do_need_update_contacts + post 'update_contacts', to: 'contacts#update_contacts', as: :update_contacts + end resources :companies, only: %i[index] end @@ -262,6 +266,13 @@ Rails.application.routes.draw do resources :accounts resources :account_activities + resources :auctions, only: [ :index, :create ] do + collection do + post 'upload_spreadsheet', to: 'auctions#upload_spreadsheet', as: :upload_spreadsheet + end + end + # post 'admi/upload_spreadsheet', to: 'customers#upload_spreadsheet', as: :customers_upload_spreadsheet + resources :bank_statements do resources :bank_transactions @@ -331,6 +342,10 @@ Rails.application.routes.draw do member do get 'delete' end + + collection do + post 'release_to_auction', to: 'reserved_domains#release_to_auction', as: 'release_to_auction' + end end resources :disputes do member do diff --git a/db/migrate/20141210085432_add_code_cache_for_domain_contact.rb b/db/migrate/20141210085432_add_code_cache_for_domain_contact.rb index c3c71ff35..d3aac8296 100644 --- a/db/migrate/20141210085432_add_code_cache_for_domain_contact.rb +++ b/db/migrate/20141210085432_add_code_cache_for_domain_contact.rb @@ -1,10 +1,10 @@ class AddCodeCacheForDomainContact < ActiveRecord::Migration[6.0] def change - add_column :domain_contacts, :contact_code_cache, :string - - DomainContact.all.each do |x| - x.contact_code_cache = x.contact.code - x.save - end + # add_column :domain_contacts, :contact_code_cache, :string + # + # DomainContact.all.each do |x| + # x.contact_code_cache = x.contact.code + # x.save + # end end end diff --git a/db/migrate/20220113201642_add_email_history_to_contacts.rb b/db/migrate/20220113201642_add_email_history_to_contacts.rb new file mode 100644 index 000000000..2a5f59953 --- /dev/null +++ b/db/migrate/20220113201642_add_email_history_to_contacts.rb @@ -0,0 +1,9 @@ +class AddEmailHistoryToContacts < ActiveRecord::Migration[6.1] + def change + add_column :contacts, :email_history, :string + + reversible do |dir| + dir.up { Contact.update_all('email_history = email') } + end + end +end diff --git a/db/migrate/20220113220809_add_email_history_to_registrars.rb b/db/migrate/20220113220809_add_email_history_to_registrars.rb new file mode 100644 index 000000000..1f89353a1 --- /dev/null +++ b/db/migrate/20220113220809_add_email_history_to_registrars.rb @@ -0,0 +1,9 @@ +class AddEmailHistoryToRegistrars < ActiveRecord::Migration[6.1] + def change + add_column :registrars, :email_history, :string + + reversible do |dir| + dir.up { Registrar.update_all('email_history = email') } + end + end +end diff --git a/db/migrate/20220228093211_add_failed_validation_reasong_to_dnskey.rb b/db/migrate/20220228093211_add_failed_validation_reasong_to_dnskey.rb new file mode 100644 index 000000000..6d61f385f --- /dev/null +++ b/db/migrate/20220228093211_add_failed_validation_reasong_to_dnskey.rb @@ -0,0 +1,5 @@ +class AddFailedValidationReasongToDnskey < ActiveRecord::Migration[6.1] + def change + add_column :dnskeys, :failed_validation_reason, :string + end +end diff --git a/db/migrate/20220316140727_add_bulk_actions.rb b/db/migrate/20220316140727_add_bulk_actions.rb new file mode 100644 index 000000000..1eae94220 --- /dev/null +++ b/db/migrate/20220316140727_add_bulk_actions.rb @@ -0,0 +1,5 @@ +class AddBulkActions < ActiveRecord::Migration[6.1] + def change + add_column :actions, :bulk_action_id, :integer, default: nil + end +end diff --git a/db/migrate/20220412130856_add_type_to_auction.rb b/db/migrate/20220412130856_add_type_to_auction.rb new file mode 100644 index 000000000..14714e868 --- /dev/null +++ b/db/migrate/20220412130856_add_type_to_auction.rb @@ -0,0 +1,5 @@ +class AddTypeToAuction < ActiveRecord::Migration[6.1] + def change + add_column :auctions, :platform, :integer, null: true + end +end diff --git a/db/migrate/20220413073315_remove_email_address_verifications.rb b/db/migrate/20220413073315_remove_email_address_verifications.rb new file mode 100644 index 000000000..db1f4230c --- /dev/null +++ b/db/migrate/20220413073315_remove_email_address_verifications.rb @@ -0,0 +1,5 @@ +class RemoveEmailAddressVerifications < ActiveRecord::Migration[6.1] + def change + # drop_table :email_address_verifications + end +end diff --git a/db/migrate/20220413084536_remove_email_addresses_validations.rb b/db/migrate/20220413084536_remove_email_addresses_validations.rb new file mode 100644 index 000000000..0084dc08e --- /dev/null +++ b/db/migrate/20220413084536_remove_email_addresses_validations.rb @@ -0,0 +1,5 @@ +class RemoveEmailAddressesValidations < ActiveRecord::Migration[6.1] + def change + # drop_table :email_addresses_validations + end +end diff --git a/db/migrate/20220413084748_remove_email_addresses_verifications.rb b/db/migrate/20220413084748_remove_email_addresses_verifications.rb new file mode 100644 index 000000000..124086d86 --- /dev/null +++ b/db/migrate/20220413084748_remove_email_addresses_verifications.rb @@ -0,0 +1,5 @@ +class RemoveEmailAddressesVerifications < ActiveRecord::Migration[6.1] + def change + # drop_table :email_addresses_verifications + end +end diff --git a/db/migrate/20220504090512_remove_contact_code_cache_from_domain_contacts.rb b/db/migrate/20220504090512_remove_contact_code_cache_from_domain_contacts.rb new file mode 100644 index 000000000..a95d8e193 --- /dev/null +++ b/db/migrate/20220504090512_remove_contact_code_cache_from_domain_contacts.rb @@ -0,0 +1,5 @@ +class RemoveContactCodeCacheFromDomainContacts < ActiveRecord::Migration[6.1] + def change + remove_column :domain_contacts, :contact_code_cache + end +end diff --git a/db/structure.sql b/db/structure.sql index 4403facfb..66152fe27 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -304,7 +304,8 @@ CREATE TABLE public.actions ( user_id integer, operation character varying NOT NULL, created_at timestamp without time zone, - contact_id integer + contact_id integer, + bulk_action_id integer ); @@ -350,7 +351,8 @@ CREATE TABLE public.auctions ( uuid uuid DEFAULT public.gen_random_uuid() NOT NULL, created_at timestamp without time zone NOT NULL, registration_code character varying, - registration_deadline timestamp without time zone + registration_deadline timestamp without time zone, + platform integer ); @@ -667,7 +669,8 @@ CREATE TABLE public.contacts ( upid integer, up_date timestamp without time zone, uuid uuid DEFAULT public.gen_random_uuid() NOT NULL, - disclosed_attributes character varying[] DEFAULT '{}'::character varying[] NOT NULL + disclosed_attributes character varying[] DEFAULT '{}'::character varying[] NOT NULL, + email_history character varying ); @@ -825,7 +828,8 @@ CREATE TABLE public.dnskeys ( updator_str character varying, legacy_domain_id integer, updated_at timestamp without time zone, - validation_datetime timestamp without time zone + validation_datetime timestamp without time zone, + failed_validation_reason character varying ); @@ -858,7 +862,6 @@ CREATE TABLE public.domain_contacts ( domain_id integer, created_at timestamp without time zone, updated_at timestamp without time zone, - contact_code_cache character varying, creator_str character varying, updator_str character varying, type character varying, @@ -1193,6 +1196,7 @@ CREATE TABLE public.invoices ( buyer_vat_no character varying, issue_date date NOT NULL, e_invoice_sent_at timestamp without time zone, + payment_link character varying, CONSTRAINT invoices_due_date_is_not_before_issue_date CHECK ((due_date >= issue_date)) ); @@ -2501,7 +2505,8 @@ CREATE TABLE public.registrars ( iban character varying, settings jsonb DEFAULT '{}'::jsonb NOT NULL, legaldoc_optout boolean DEFAULT false NOT NULL, - legaldoc_optout_comment text + legaldoc_optout_comment text, + email_history character varying ); @@ -5395,7 +5400,17 @@ INSERT INTO "schema_migrations" (version) VALUES ('20211125184334'), ('20211126085139'), ('20211231113934'), -('20220106123143'); - +('20220106123143'), +('20220113201642'), +('20220113220809'), +('20220124105717'), +('20220216113112'), +('20220228093211'), +('20220316140727'), +('20220412130856'), +('20220413073315'), +('20220413084536'), +('20220413084748'), +('20220504090512'); diff --git a/doc/epp_examples.md b/doc/epp_examples.md index 7e0e001ba..62e0f818d 100644 --- a/doc/epp_examples.md +++ b/doc/epp_examples.md @@ -9979,12 +9979,6 @@ RESPONSE: