From 864954114b33ee873cb12cc373b3abb6c394e5cd Mon Sep 17 00:00:00 2001 From: msdundar Date: Sat, 23 Mar 2019 00:55:17 +0300 Subject: [PATCH 01/15] Implement Nexmo::Message API --- Gemfile | 4 + Gemfile.lock | 6 ++ app/errors/concatenation_error.rb | 7 ++ app/errors/encoding_mismatch_error.rb | 7 ++ app/errors/id_number_error.rb | 7 -- app/errors/invalid_phone_number_error.rb | 7 ++ app/errors/unicode_support_error.rb | 7 ++ app/services/sms/nexmo/api/error_handler.rb | 48 ++++++++++++ app/services/sms/nexmo/message.rb | 76 +++++++++++++++++++ app/views/location/countries/_form.html.erb | 68 ++++++++--------- config/application.rb | 1 + config/initializers/nexmo.rb | 6 ++ config/locales/errors/en.yml | 5 +- config/locales/errors/tr.yml | 5 +- plugins/tenant/omu/config/tenant.yml | 1 + .../omu/test/dummy/config/credentials.yml.enc | 2 +- test/errors/id_number_error_test.rb | 12 --- 17 files changed, 213 insertions(+), 56 deletions(-) create mode 100644 app/errors/concatenation_error.rb create mode 100644 app/errors/encoding_mismatch_error.rb delete mode 100644 app/errors/id_number_error.rb create mode 100644 app/errors/invalid_phone_number_error.rb create mode 100644 app/errors/unicode_support_error.rb create mode 100644 app/services/sms/nexmo/api/error_handler.rb create mode 100644 app/services/sms/nexmo/message.rb create mode 100644 config/initializers/nexmo.rb delete mode 100644 test/errors/id_number_error_test.rb diff --git a/Gemfile b/Gemfile index d062f24cb..817c2a6fa 100644 --- a/Gemfile +++ b/Gemfile @@ -62,6 +62,10 @@ gem 'rollbar', github: 'rollbar/rollbar-gem' # permalinks gem 'friendly_id', '~> 5.2.0' +# sms +gem 'nexmo' +gem 'smstools' + group :development, :test do gem 'brakeman', require: false gem 'bullet', github: 'flyerhzm/bullet' diff --git a/Gemfile.lock b/Gemfile.lock index 2768aba95..560cd9c65 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -244,6 +244,7 @@ GEM multi_json (>= 1.2) jmespath (1.4.0) json (2.2.0) + jwt (2.1.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.7.0) @@ -272,6 +273,8 @@ GEM msgpack (1.2.9) multi_json (1.13.1) netaddr (2.0.3) + nexmo (5.6.0) + jwt (~> 2) nio4r (2.3.1) nokogiri (1.10.1) mini_portile2 (~> 2.4.0) @@ -366,6 +369,7 @@ GEM simpleidn (0.1.1) unf (~> 0.1.4) smart_properties (1.13.1) + smstools (0.2.0) spring (2.0.2) activesupport (>= 4.2) spring-watcher-listen (2.0.1) @@ -442,6 +446,7 @@ DEPENDENCIES letter_opener listen (>= 3.0.5, < 3.2) lol_dba + nexmo nokul-support! nokul-tenant! nokul-tenant-omu! @@ -465,6 +470,7 @@ DEPENDENCIES sidekiq simple_form simplecov + smstools spring spring-watcher-listen (~> 2.0.0) telephone_number diff --git a/app/errors/concatenation_error.rb b/app/errors/concatenation_error.rb new file mode 100644 index 000000000..93e381856 --- /dev/null +++ b/app/errors/concatenation_error.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class ConcatenationError < StandardError + def message + I18n.t('errors.can_not_be_concatenated') + end +end diff --git a/app/errors/encoding_mismatch_error.rb b/app/errors/encoding_mismatch_error.rb new file mode 100644 index 000000000..171776b48 --- /dev/null +++ b/app/errors/encoding_mismatch_error.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class EncodingMismatchError < StandardError + def message + I18n.t('errors.encoding_mismatch') + end +end diff --git a/app/errors/id_number_error.rb b/app/errors/id_number_error.rb deleted file mode 100644 index b7b9f4fb6..000000000 --- a/app/errors/id_number_error.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class IdNumberError < StandardError - def message - I18n.t('errors.invalid_id') - end -end diff --git a/app/errors/invalid_phone_number_error.rb b/app/errors/invalid_phone_number_error.rb new file mode 100644 index 000000000..a9e1be762 --- /dev/null +++ b/app/errors/invalid_phone_number_error.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class InvalidPhoneNumberError < StandardError + def message + I18n.t('errors.invalid_phone_number') + end +end diff --git a/app/errors/unicode_support_error.rb b/app/errors/unicode_support_error.rb new file mode 100644 index 000000000..ff84126e6 --- /dev/null +++ b/app/errors/unicode_support_error.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class UnicodeSupportError < StandardError + def message + I18n.t('errors.unicode_not_supported') + end +end diff --git a/app/services/sms/nexmo/api/error_handler.rb b/app/services/sms/nexmo/api/error_handler.rb new file mode 100644 index 000000000..31b5db961 --- /dev/null +++ b/app/services/sms/nexmo/api/error_handler.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Sms + module Nexmo + module Api + module ErrorHandler + SOFT_FAIL_CODES = { + '2' => 'Missing Parameters', + '3' => 'Invalid Parameters', + '6' => 'Invalid Message', + '12' => 'Message Too Long', + '22' => 'Invalid Network Code', + '33' => 'Number De-activated' + }.freeze + + HARD_FAIL_CODES = { + '1' => 'Throttled', + '4' => 'Invalid Credentials', + '5' => 'Internal Error', + '7' => 'Number Barred', + '8' => 'Partner Account Barred', + '9' => 'Partner Quota Violation', + '10' => 'Too Many Existing Binds', + '11' => 'Account Not Enabled For HTTP', + '14' => 'Invalid Signature', + '15' => 'Invalid Sender Address', + '23' => 'Invalid Callback Url', + '32' => 'Signature And API Secret Disallowed' + }.freeze + + def notify_admin(response) + status = response.status + message_id = response.message_id + + if status == '0' + Rails.logger.info "Sent message id=#{message_id}" + elsif SOFT_FAIL_CODES.key?(status) + # TODO: What to do? Notify developer? How? + Rails.logger.error "An error occured: id=#{message_id} error_code=#{status}" + elsif HARD_FAIL_CODES.key?(status) + # TODO: What to do? Notify admin? How? + Rails.logger.fatal "An error occured: id=#{message_id} error_code=#{status}" + end + end + end + end + end +end diff --git a/app/services/sms/nexmo/message.rb b/app/services/sms/nexmo/message.rb new file mode 100644 index 000000000..508f6ab76 --- /dev/null +++ b/app/services/sms/nexmo/message.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require_relative 'api/error_handler' + +module Sms + module Nexmo + class Message + include Api::ErrorHandler + + # Some countries and operators do not support concatenation and unicode. + # Enable multipart and unicode type thoughtfully! + # Nexmo supports all the standard GSM characters + characters from the GSM extended table. + # Test GSM-7 encoding: http://chadselph.github.io/smssplit/ + def initialize(to_number, message, country = 'tr', multipart = false, type = 'text') + @to_number = to_number.to_s + @message = type.eql?('text') ? message.asciified.squish : message + @country = country + @multipart = multipart + @type = type + + process_message + end + + def process_message + check_country + check_destination_number + check_multipart + check_encoding + send_message + end + + private + + def check_country + @country = Country.find_by(alpha_2_code: @country.upcase) + + raise ActiveRecord::RecordNotFound, 'Country can not be found!' unless @country + end + + def check_destination_number + valid_number = TelephoneNumber.valid?(@to_number, @country.alpha_2_code.to_sym, [:mobile]) + + raise InvalidPhoneNumberError unless valid_number + + parser = TelephoneNumber.parse(@to_number, @country.alpha_2_code.to_sym) + @to_number = parser.country.country_code + parser.formatter.normalized_number + end + + def check_multipart + encoding = SmsTools::EncodingDetection.new(@message) + + raise ConcatenationError if @multipart.eql?(false) && encoding.concatenated? + raise ConcatenationError if @multipart.eql?(false) && !@country.sms_concatenation + end + + def check_encoding + encoding = SmsTools::EncodingDetection.new(@message).encoding.to_s + encoding = 'text' if encoding.eql?('gsm') || encoding.eql?('ascii') + + raise EncodingMismatchError unless encoding == @type + raise UnicodeSupportError if encoding.eql?('unicode') && !@country.sms_unicode + end + + def send_message + response = NEXMO_CLIENT.send( + from: Tenant.credentials.dig(:nexmo, :from), + to: @to_number, + type: @type, + text: @message + ).messages.first + + log_and_notify_admin(response) + end + end + end +end diff --git a/app/views/location/countries/_form.html.erb b/app/views/location/countries/_form.html.erb index 9b206e486..482b19428 100644 --- a/app/views/location/countries/_form.html.erb +++ b/app/views/location/countries/_form.html.erb @@ -31,57 +31,57 @@ width: 4 }, { - field: 'un_locode', - width: 4 + field: 'un_locode', + width: 4 }, { - field: 'continent', - width: 6, - collection: continents, - required: true, - selected: @country.continent + field: 'continent', + width: 6, + collection: continents, + required: true, + selected: @country.continent }, { - field: 'region', - width: 6, - collection: regions, - selected: @country.region + field: 'region', + width: 6, + collection: regions, + selected: @country.region }, { - field: 'subregion', - width: 6, - collection: subregions, - selected: @country.subregion + field: 'subregion', + width: 6, + collection: subregions, + selected: @country.subregion }, { - field: 'world_region', - width: 6, - collection: %w[APAC EMEA AMER], - selected: @country.world_region + field: 'world_region', + width: 6, + collection: %w[APAC EMEA AMER], + selected: @country.world_region }, { - field: 'currency_code', - width: 6, - required: true + field: 'currency_code', + width: 6, + required: true }, { - field: 'phone_code', - width: 6 + field: 'phone_code', + width: 6 }, { - field: 'latitude', - width: 4, - required: true + field: 'latitude', + width: 4, + required: true }, { - field: 'longitude', - width: 4, - required: true + field: 'longitude', + width: 4, + required: true }, { - field: 'start_of_week', - width: 4, - collection: %w[Cumartesi Pazar Pazartesi], - selected: @country.start_of_week + field: 'start_of_week', + width: 4, + collection: %w[Cumartesi Pazar Pazartesi], + selected: @country.start_of_week } ] %> diff --git a/config/application.rb b/config/application.rb index 555ab00e9..abf55c69a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -9,6 +9,7 @@ module Nokul class Application < Rails::Application + # multi-tenancy Nokul::Tenant.load # Initialize configuration defaults for originally generated Rails version. diff --git a/config/initializers/nexmo.rb b/config/initializers/nexmo.rb new file mode 100644 index 000000000..6c70fb9d0 --- /dev/null +++ b/config/initializers/nexmo.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +NEXMO_CLIENT = Nexmo::Client.new( + api_key: Tenant.credentials.dig(:nexmo, :api_key), + api_secret: Tenant.credentials.dig(:nexmo, :api_secret) +).sms diff --git a/config/locales/errors/en.yml b/config/locales/errors/en.yml index 0b897c148..d73260da9 100644 --- a/config/locales/errors/en.yml +++ b/config/locales/errors/en.yml @@ -1,3 +1,6 @@ en: errors: - invalid_id: Invalid ID Number + invalid_phone_number: This is not a valid phone number! + can_not_be_concatenated: This text cannot be fit into a single SMS, or the sreceiver doesn't support concatenation! + encoding_mismatch: Requested encoding does not match with the encoding of the text! + unicode_not_supported: Receiver does not support unicode encoding! diff --git a/config/locales/errors/tr.yml b/config/locales/errors/tr.yml index 14e0547df..3bcb676d1 100644 --- a/config/locales/errors/tr.yml +++ b/config/locales/errors/tr.yml @@ -1,3 +1,6 @@ tr: errors: - invalid_id: Geçersiz T.C. Kimlik Numarası + invalid_phone_number: Geçerli bir telefon numarası değil! + can_not_be_concatenated: Bu mesaj tek bir SMS için fazla uzun veya alıcı ülke mesaj birleştirmeyi desteklemiyor! + encoding_mismatch: Mesaj metin kodlaması talep edilen kodlama ile eşleşmiyor! + unicode_not_supported: Alıcı numara unicode desteklemiyor! diff --git a/plugins/tenant/omu/config/tenant.yml b/plugins/tenant/omu/config/tenant.yml index 9e4deafe7..42b56e688 100644 --- a/plugins/tenant/omu/config/tenant.yml +++ b/plugins/tenant/omu/config/tenant.yml @@ -10,6 +10,7 @@ default: &default domain: 'omu.edu.tr' default_from: 'noreply@baum.omu.edu.tr' support: 'hotline@baum.omu.edu.tr' + admin: 'sistem@baum.omu.edu.tr' network: subnet: '10.0.0.0/8' branding: diff --git a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc index 3103430cb..0657dd57d 100644 --- a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc +++ b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc @@ -1 +1 @@ -LFd8AWWgiSD/kSGc+xOQO9VoOIvEJxKuf2FZd/Gg5DeNaX+LIJ/usrfGSkdY2/bGrQon5/AyJrTq4GKiHywiKRusyLbucfMjZPxnwQ47n75u86zTZh19aSIA/eOZibSHECdgrmnkbLYnw6tdqGNI0+8Q7ZQblV6I3k5PZDR48oIetDy5RtG9Po5EHjUqzbiEQERevGsYlb6KFQPoQ4V0SkE6W4oU647582WlIs+taFdd5VyMJ4+jnQ3kuDR1MCEXIugnH09dkGY7jhq9bT8v8so8Z5LoGnX/CRqJkby1H0IW41RtQIEmZaWQv83cefXTN3g1IotRBZhg63tBGGIHNCgTZmRA4eVG1oAw0ZX+ExqbKXjg+GxnP4d7stMktyPaTU4HqQumWARH4+OGVc9C1BvdWPm2cCwpakN30VTPz0WkpaMcbe6qphAxedTd3z8y3vEml+rE83r6NIxxQwMNyn6ilDidHZpLlauWNia1DKZYTlVZPJri/AEt0I/AIEi4PI2nVVwejFasFVZNfA4SAfz7M6AhCMzpvIU3JdfArIsTzhMTFQ4rSZmyGnsjw8GGX9k=--DozmC759xwANS9er--GMdT5mY/g8kNoiMlXT3FDA== \ No newline at end of file +MW2QmdWia8kzmxH7MoUSpbQNgH5+IsjU2IBzWzPyzT/BCPI/pyZBxk0950ol3ZmzccP2JVqe2lYNQYUJiVBj0qoppxldGzOzYjQDWM/4lFPP7smbB+0PQ4aeqvGByQkO6xGDyoUWjoMakm6z9XPH/z/w8aMX7bY8QBIUQvNfk7Ye9BnmrUjL9+/jcMrdrTr/qifoh7lvCi0ormOoguVOy4FnbiLG2/J1NDwfSvuUPpRS3ghVMwPSIin0VBrOdt1FAx/P4UqZBOEoEXaU4WJxTXVo2b9er5oBKcWaZNz3ZlWJeVtObwNHssjbz3Js8rPkjgGHaMGmzWQ7kvn+0JshFis8S+NwZeMfJV7lW3rNsqS8GodjMQUOoyu8mFKbbq81LiW35WRpc0J1tdyYMwzw20nfJDm1XHwZQqVMjPEmOSATCm7pAqhlxGmGSRZyljKvTN8JI1x8TAScvXUeeWuskuCFtDQ3Q3g+5aE6EasrypwnhLD4svgIN4TIqcN5Cy9rHlzBBzhTgwz3O+JrhcQ/zkvBPGP1GwJVpyFTiCJx5BzKS8B1oPD17BV2irs9SsI0UXvN6a5ff2wN08dBaBUr8VsFATAh6Gcjy/BRbSg5Gs7YKtlqzp1eRIaAkVegSaugdAoBZziSnKHpqZUF3v+ic96yBgWGDJ23O2vijg==--/6mmWPKJQxRkQ1rc--dk33EoWV8MX9d5JpOugUVg== \ No newline at end of file diff --git a/test/errors/id_number_error_test.rb b/test/errors/id_number_error_test.rb deleted file mode 100644 index 9365e0116..000000000 --- a/test/errors/id_number_error_test.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class IdNumberErrorTest < ActiveSupport::TestCase - test 'ID number throws an error message when raised' do - assert_equal IdNumberError.new.message, 'Geçersiz T.C. Kimlik Numarası' - I18n.locale = 'en' - assert_equal IdNumberError.new.message, 'Invalid ID Number' - I18n.locale = I18n.default_locale - end -end From c98d86173d431042116ebc9f7f19d40eda926170 Mon Sep 17 00:00:00 2001 From: msdundar Date: Sat, 23 Mar 2019 01:37:29 +0300 Subject: [PATCH 02/15] Add Slack notifier for SMS API errors --- Gemfile | 1 + Gemfile.lock | 2 ++ app/services/sms/nexmo/api/error_handler.rb | 14 +++++--------- app/services/sms/nexmo/message.rb | 2 +- .../omu/test/dummy/config/credentials.yml.enc | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Gemfile b/Gemfile index 817c2a6fa..7139e7e91 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,7 @@ gem 'telephone_number' # error tracking gem 'rollbar', github: 'rollbar/rollbar-gem' +gem 'slack-notifier' # permalinks gem 'friendly_id', '~> 5.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 560cd9c65..1f821fda9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -368,6 +368,7 @@ GEM simplecov-html (0.10.2) simpleidn (0.1.1) unf (~> 0.1.4) + slack-notifier (2.3.2) smart_properties (1.13.1) smstools (0.2.0) spring (2.0.2) @@ -470,6 +471,7 @@ DEPENDENCIES sidekiq simple_form simplecov + slack-notifier smstools spring spring-watcher-listen (~> 2.0.0) diff --git a/app/services/sms/nexmo/api/error_handler.rb b/app/services/sms/nexmo/api/error_handler.rb index 31b5db961..21fe6a4da 100644 --- a/app/services/sms/nexmo/api/error_handler.rb +++ b/app/services/sms/nexmo/api/error_handler.rb @@ -28,18 +28,14 @@ module ErrorHandler '32' => 'Signature And API Secret Disallowed' }.freeze - def notify_admin(response) + def log_or_notify_admin(response) status = response.status - message_id = response.message_id + notifier = Slack::Notifier.new Tenant.credentials.slack[:panik_hook] if status == '0' - Rails.logger.info "Sent message id=#{message_id}" - elsif SOFT_FAIL_CODES.key?(status) - # TODO: What to do? Notify developer? How? - Rails.logger.error "An error occured: id=#{message_id} error_code=#{status}" - elsif HARD_FAIL_CODES.key?(status) - # TODO: What to do? Notify admin? How? - Rails.logger.fatal "An error occured: id=#{message_id} error_code=#{status}" + Rails.logger.info "Sent message id=#{response.message_id}" + elsif SOFT_FAIL_CODES.key?(status) || HARD_FAIL_CODES.key?(status) + notifier.ping "An error occured: error_code=#{status} error_text=#{response.error_text}" end end end diff --git a/app/services/sms/nexmo/message.rb b/app/services/sms/nexmo/message.rb index 508f6ab76..b00684b4d 100644 --- a/app/services/sms/nexmo/message.rb +++ b/app/services/sms/nexmo/message.rb @@ -69,7 +69,7 @@ def send_message text: @message ).messages.first - log_and_notify_admin(response) + log_or_notify_admin(response) end end end diff --git a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc index 0657dd57d..ab191c0c2 100644 --- a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc +++ b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc @@ -1 +1 @@ -MW2QmdWia8kzmxH7MoUSpbQNgH5+IsjU2IBzWzPyzT/BCPI/pyZBxk0950ol3ZmzccP2JVqe2lYNQYUJiVBj0qoppxldGzOzYjQDWM/4lFPP7smbB+0PQ4aeqvGByQkO6xGDyoUWjoMakm6z9XPH/z/w8aMX7bY8QBIUQvNfk7Ye9BnmrUjL9+/jcMrdrTr/qifoh7lvCi0ormOoguVOy4FnbiLG2/J1NDwfSvuUPpRS3ghVMwPSIin0VBrOdt1FAx/P4UqZBOEoEXaU4WJxTXVo2b9er5oBKcWaZNz3ZlWJeVtObwNHssjbz3Js8rPkjgGHaMGmzWQ7kvn+0JshFis8S+NwZeMfJV7lW3rNsqS8GodjMQUOoyu8mFKbbq81LiW35WRpc0J1tdyYMwzw20nfJDm1XHwZQqVMjPEmOSATCm7pAqhlxGmGSRZyljKvTN8JI1x8TAScvXUeeWuskuCFtDQ3Q3g+5aE6EasrypwnhLD4svgIN4TIqcN5Cy9rHlzBBzhTgwz3O+JrhcQ/zkvBPGP1GwJVpyFTiCJx5BzKS8B1oPD17BV2irs9SsI0UXvN6a5ff2wN08dBaBUr8VsFATAh6Gcjy/BRbSg5Gs7YKtlqzp1eRIaAkVegSaugdAoBZziSnKHpqZUF3v+ic96yBgWGDJ23O2vijg==--/6mmWPKJQxRkQ1rc--dk33EoWV8MX9d5JpOugUVg== \ No newline at end of file +vDWYeprzrZbc8nYyQVEwxDeX8iamw2NRXKYyx4pgBD4W4zl6wXCoYTU6WkYikY9sNSl8fR+ayplZLM4+6uV93XrVfx+4YEApO8tEp5R9uJcbMfWUw2W8d2Rqsi7WyP399RQ+ccm7VAFpuVyUUtaODP1lbTL2FvYA+O1crzwvtsSlN4CrUV2P2k4gfeDZvlUOuXLOjgHdTuBqnCmlvDIb3zEHFB6J9U8IWJHE39TWMj6p8gJn7vlWegpSBgkgqvTlFVPyZ6E67bZPy8X9cWj/96jEOQePevZg0BVbDWbT6yEgBAt3upF2z7/uGLjNg8t9zP04D6Oo5ukzV6iUtShJKDjRhooo0eAVcZz4PHsQ2xcD3ecLlBpFBB06kIMcwh9cpcLWud5If9GwvLpIt27X176pf5rJ5WJ3xy/8McugM+XWch8fBVkRpEUHrFaontPf2EFfyBtNq9zggc+9uf5bxlAGhqFVdeVW+Et7T0Gk12ZKyLWsRi1bQV9vCn943g8Ln3j69b7CrSdoShuo92bG9pYC9/axo181WP1WNu4NHGYgfiHIb3qTXaE7K3CY6fRX4tBdr28HAFodvk5OtkmoSPQ0r2hFk3hSrZteEWn0r70wMeZdU+rWgBf497ocYWxYdfxmuui3lW3GRP9eATv+uCLJVMzIqocJA4xUINBEHA3f0xX8fCCQ32clplEOq8G49FSgPeBoyKBMde9JrbygM/GFhhKDPhwvDBwQz2RurIM1tpEORZai71NkOSrBhzXYfsAeG8S5iIQQ5Z3t5y9Bpkyru/XctC35UZpLs0W0iXaO--lYr3AzSbAbCJF8Ao--w9F8YjpXBgaAe5+1z3PQZQ== \ No newline at end of file From 65cee4635761c17d0f3809772228af70bb995407 Mon Sep 17 00:00:00 2001 From: msdundar Date: Sat, 23 Mar 2019 01:40:51 +0300 Subject: [PATCH 03/15] Use a better notification message --- app/services/sms/nexmo/api/error_handler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/sms/nexmo/api/error_handler.rb b/app/services/sms/nexmo/api/error_handler.rb index 21fe6a4da..ba12454f4 100644 --- a/app/services/sms/nexmo/api/error_handler.rb +++ b/app/services/sms/nexmo/api/error_handler.rb @@ -35,7 +35,7 @@ def log_or_notify_admin(response) if status == '0' Rails.logger.info "Sent message id=#{response.message_id}" elsif SOFT_FAIL_CODES.key?(status) || HARD_FAIL_CODES.key?(status) - notifier.ping "An error occured: error_code=#{status} error_text=#{response.error_text}" + notifier.ping "Nexmo SMS Error (code: #{status}, text: #{response.error_text})" end end end From f33595ed8fcb5b4dca26a7fdbb4e33dfdf5d26ef Mon Sep 17 00:00:00 2001 From: msdundar Date: Sat, 23 Mar 2019 05:44:31 +0300 Subject: [PATCH 04/15] Upgrade dependencies --- Gemfile.lock | 6 +++--- app/services/sms/nexmo/message.rb | 6 +++--- config/initializers/nexmo.rb | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1f821fda9..40bad3941 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GIT GIT remote: https://github.com/rails/rails.git - revision: efb706daad0e2e1039c6abb4879c837ef8bf4d10 + revision: 5851ac69e11fadcd3b4bf052f1d963a5803a2449 specs: actioncable (6.0.0.beta3) actionpack (= 6.0.0.beta3) @@ -133,7 +133,7 @@ GEM io-like (~> 0.3.0) ast (2.4.0) aws-eventstream (1.0.2) - aws-partitions (1.145.0) + aws-partitions (1.146.0) aws-sdk-core (3.48.2) aws-eventstream (~> 1.0, >= 1.0.2) aws-partitions (~> 1.0) @@ -142,7 +142,7 @@ GEM aws-sdk-kms (1.16.0) aws-sdk-core (~> 3, >= 3.48.2) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.34.0) + aws-sdk-s3 (1.35.0) aws-sdk-core (~> 3, >= 3.48.2) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.0) diff --git a/app/services/sms/nexmo/message.rb b/app/services/sms/nexmo/message.rb index b00684b4d..01ab060ec 100644 --- a/app/services/sms/nexmo/message.rb +++ b/app/services/sms/nexmo/message.rb @@ -21,6 +21,8 @@ def initialize(to_number, message, country = 'tr', multipart = false, type = 'te process_message end + private + def process_message check_country check_destination_number @@ -29,8 +31,6 @@ def process_message send_message end - private - def check_country @country = Country.find_by(alpha_2_code: @country.upcase) @@ -62,7 +62,7 @@ def check_encoding end def send_message - response = NEXMO_CLIENT.send( + response = NEXMO_CLIENT.sms.send( from: Tenant.credentials.dig(:nexmo, :from), to: @to_number, type: @type, diff --git a/config/initializers/nexmo.rb b/config/initializers/nexmo.rb index 6c70fb9d0..6b1b6267b 100644 --- a/config/initializers/nexmo.rb +++ b/config/initializers/nexmo.rb @@ -3,4 +3,4 @@ NEXMO_CLIENT = Nexmo::Client.new( api_key: Tenant.credentials.dig(:nexmo, :api_key), api_secret: Tenant.credentials.dig(:nexmo, :api_secret) -).sms +) From c3d66b4494d8505861a4e2cdf211f2b1446bd723 Mon Sep 17 00:00:00 2001 From: msdundar Date: Sat, 23 Mar 2019 20:36:02 +0300 Subject: [PATCH 05/15] Integrate with Twilio API --- Gemfile | 2 + Gemfile.lock | 12 +++ app/services/nexmo/error_handler.rb | 40 +++++++++ app/services/nexmo/sms.rb | 74 +++++++++++++++ app/services/sms/nexmo/api/error_handler.rb | 44 --------- app/services/sms/nexmo/message.rb | 76 ---------------- app/services/twilio/lookup.rb | 11 +++ app/services/twilio/sms.rb | 15 ++++ app/services/twilio/verify.rb | 90 +++++++++++++++++++ config/initializers/authy.rb | 4 + .../initializers/filter_parameter_logging.rb | 3 + config/initializers/twilio.rb | 6 ++ .../omu/test/dummy/config/credentials.yml.enc | 2 +- 13 files changed, 258 insertions(+), 121 deletions(-) create mode 100644 app/services/nexmo/error_handler.rb create mode 100644 app/services/nexmo/sms.rb delete mode 100644 app/services/sms/nexmo/api/error_handler.rb delete mode 100644 app/services/sms/nexmo/message.rb create mode 100644 app/services/twilio/lookup.rb create mode 100644 app/services/twilio/sms.rb create mode 100644 app/services/twilio/verify.rb create mode 100644 config/initializers/authy.rb create mode 100644 config/initializers/twilio.rb diff --git a/Gemfile b/Gemfile index 7139e7e91..44e6d3563 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ gem 'aws-sdk-s3', require: false gem 'image_processing', '~> 1.2' # authentication +gem 'authy' gem 'devise' # assets: core asset dependencies @@ -66,6 +67,7 @@ gem 'friendly_id', '~> 5.2.0' # sms gem 'nexmo' gem 'smstools' +gem 'twilio-ruby', '~> 5.21.2' group :development, :test do gem 'brakeman', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 40bad3941..b8cd394d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -132,6 +132,8 @@ GEM archive-zip (0.12.0) io-like (~> 0.3.0) ast (2.4.0) + authy (2.7.5) + httpclient (>= 2.5.3.3) aws-eventstream (1.0.2) aws-partitions (1.146.0) aws-sdk-core (3.48.2) @@ -219,6 +221,8 @@ GEM smart_properties erubi (1.8.0) execjs (2.7.0) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) ffi (1.10.0) fit-commit (3.8.1) swearjar (~> 1.3) @@ -232,6 +236,7 @@ GEM activesupport (>= 4.2) hashdiff (0.3.8) html_tokenizer (0.0.7) + httpclient (2.8.3) i18n (1.6.0) concurrent-ruby (~> 1.0) image_processing (1.8.0) @@ -272,6 +277,7 @@ GEM minitest (5.11.3) msgpack (1.2.9) multi_json (1.13.1) + multipart-post (2.0.0) netaddr (2.0.3) nexmo (5.6.0) jwt (~> 2) @@ -388,6 +394,10 @@ GEM thor (0.20.3) thread_safe (0.3.6) tilt (2.0.9) + twilio-ruby (5.21.2) + faraday (~> 0.9) + jwt (>= 1.5, <= 2.5) + nokogiri (>= 1.6, < 2.0) tzinfo (1.2.5) thread_safe (~> 0.1) uglifier (4.1.20) @@ -421,6 +431,7 @@ PLATFORMS DEPENDENCIES ancestry + authy aws-sdk-s3 bcrypt (~> 3.1.7) bootsnap (>= 1.4.0) @@ -476,6 +487,7 @@ DEPENDENCIES spring spring-watcher-listen (~> 2.0.0) telephone_number + twilio-ruby (~> 5.21.2) uglifier (>= 1.3.0) web-console! webmock diff --git a/app/services/nexmo/error_handler.rb b/app/services/nexmo/error_handler.rb new file mode 100644 index 000000000..0f5466091 --- /dev/null +++ b/app/services/nexmo/error_handler.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Nexmo + module ErrorHandler + SOFT_FAIL_CODES = { + '2' => 'Missing Parameters', + '3' => 'Invalid Parameters', + '6' => 'Invalid Message', + '12' => 'Message Too Long', + '22' => 'Invalid Network Code', + '33' => 'Number De-activated' + }.freeze + + HARD_FAIL_CODES = { + '1' => 'Throttled', + '4' => 'Invalid Credentials', + '5' => 'Internal Error', + '7' => 'Number Barred', + '8' => 'Partner Account Barred', + '9' => 'Partner Quota Violation', + '10' => 'Too Many Existing Binds', + '11' => 'Account Not Enabled For HTTP', + '14' => 'Invalid Signature', + '15' => 'Invalid Sender Address', + '23' => 'Invalid Callback Url', + '32' => 'Signature And API Secret Disallowed' + }.freeze + + def log_or_notify_admin(response) + status = response.status + notifier = Slack::Notifier.new Tenant.credentials.slack[:panik_hook] + + if status == '0' + Rails.logger.info "Sent message id=#{response.message_id}" + elsif SOFT_FAIL_CODES.key?(status) || HARD_FAIL_CODES.key?(status) + notifier.ping "Nexmo SMS Error (code: #{status}, text: #{response.error_text})" + end + end + end +end diff --git a/app/services/nexmo/sms.rb b/app/services/nexmo/sms.rb new file mode 100644 index 000000000..e9ea0e1e0 --- /dev/null +++ b/app/services/nexmo/sms.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require_relative 'error_handler' + +module Nexmo + class Sms + include ErrorHandler + + # Some countries and operators do not support concatenation and unicode. + # Enable multipart and unicode type thoughtfully! + # Nexmo supports all the standard GSM characters + characters from the GSM extended table. + # Test GSM-7 encoding: http://chadselph.github.io/smssplit/ + def initialize(to_number, message, country = 'tr', multipart = false, type = 'text') + @to_number = to_number.to_s + @message = type.eql?('text') ? message.asciified.squish : message + @country = country + @multipart = multipart + @type = type + + process_message + end + + private + + def process_message + check_country + check_destination_number + check_multipart + check_encoding + send_message + end + + def check_country + @country = Country.find_by(alpha_2_code: @country.upcase) + + raise ActiveRecord::RecordNotFound, 'Country can not be found!' unless @country + end + + def check_destination_number + valid_number = TelephoneNumber.valid?(@to_number, @country.alpha_2_code.to_sym, [:mobile]) + + raise InvalidPhoneNumberError unless valid_number + + parser = TelephoneNumber.parse(@to_number, @country.alpha_2_code.to_sym) + @to_number = parser.country.country_code + parser.formatter.normalized_number + end + + def check_multipart + encoding = SmsTools::EncodingDetection.new(@message) + + raise ConcatenationError if @multipart.eql?(false) && encoding.concatenated? + raise ConcatenationError if @multipart.eql?(false) && !@country.sms_concatenation + end + + def check_encoding + encoding = SmsTools::EncodingDetection.new(@message).encoding.to_s + encoding = 'text' if encoding.eql?('gsm') || encoding.eql?('ascii') + + raise EncodingMismatchError unless encoding == @type + raise UnicodeSupportError if encoding.eql?('unicode') && !@country.sms_unicode + end + + def send_message + response = NEXMO_CLIENT.sms.send( + from: Tenant.credentials.dig(:nexmo, :from), + to: @to_number, + type: @type, + text: @message + ).messages.first + + log_or_notify_admin(response) + end + end +end diff --git a/app/services/sms/nexmo/api/error_handler.rb b/app/services/sms/nexmo/api/error_handler.rb deleted file mode 100644 index ba12454f4..000000000 --- a/app/services/sms/nexmo/api/error_handler.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Sms - module Nexmo - module Api - module ErrorHandler - SOFT_FAIL_CODES = { - '2' => 'Missing Parameters', - '3' => 'Invalid Parameters', - '6' => 'Invalid Message', - '12' => 'Message Too Long', - '22' => 'Invalid Network Code', - '33' => 'Number De-activated' - }.freeze - - HARD_FAIL_CODES = { - '1' => 'Throttled', - '4' => 'Invalid Credentials', - '5' => 'Internal Error', - '7' => 'Number Barred', - '8' => 'Partner Account Barred', - '9' => 'Partner Quota Violation', - '10' => 'Too Many Existing Binds', - '11' => 'Account Not Enabled For HTTP', - '14' => 'Invalid Signature', - '15' => 'Invalid Sender Address', - '23' => 'Invalid Callback Url', - '32' => 'Signature And API Secret Disallowed' - }.freeze - - def log_or_notify_admin(response) - status = response.status - notifier = Slack::Notifier.new Tenant.credentials.slack[:panik_hook] - - if status == '0' - Rails.logger.info "Sent message id=#{response.message_id}" - elsif SOFT_FAIL_CODES.key?(status) || HARD_FAIL_CODES.key?(status) - notifier.ping "Nexmo SMS Error (code: #{status}, text: #{response.error_text})" - end - end - end - end - end -end diff --git a/app/services/sms/nexmo/message.rb b/app/services/sms/nexmo/message.rb deleted file mode 100644 index 01ab060ec..000000000 --- a/app/services/sms/nexmo/message.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require_relative 'api/error_handler' - -module Sms - module Nexmo - class Message - include Api::ErrorHandler - - # Some countries and operators do not support concatenation and unicode. - # Enable multipart and unicode type thoughtfully! - # Nexmo supports all the standard GSM characters + characters from the GSM extended table. - # Test GSM-7 encoding: http://chadselph.github.io/smssplit/ - def initialize(to_number, message, country = 'tr', multipart = false, type = 'text') - @to_number = to_number.to_s - @message = type.eql?('text') ? message.asciified.squish : message - @country = country - @multipart = multipart - @type = type - - process_message - end - - private - - def process_message - check_country - check_destination_number - check_multipart - check_encoding - send_message - end - - def check_country - @country = Country.find_by(alpha_2_code: @country.upcase) - - raise ActiveRecord::RecordNotFound, 'Country can not be found!' unless @country - end - - def check_destination_number - valid_number = TelephoneNumber.valid?(@to_number, @country.alpha_2_code.to_sym, [:mobile]) - - raise InvalidPhoneNumberError unless valid_number - - parser = TelephoneNumber.parse(@to_number, @country.alpha_2_code.to_sym) - @to_number = parser.country.country_code + parser.formatter.normalized_number - end - - def check_multipart - encoding = SmsTools::EncodingDetection.new(@message) - - raise ConcatenationError if @multipart.eql?(false) && encoding.concatenated? - raise ConcatenationError if @multipart.eql?(false) && !@country.sms_concatenation - end - - def check_encoding - encoding = SmsTools::EncodingDetection.new(@message).encoding.to_s - encoding = 'text' if encoding.eql?('gsm') || encoding.eql?('ascii') - - raise EncodingMismatchError unless encoding == @type - raise UnicodeSupportError if encoding.eql?('unicode') && !@country.sms_unicode - end - - def send_message - response = NEXMO_CLIENT.sms.send( - from: Tenant.credentials.dig(:nexmo, :from), - to: @to_number, - type: @type, - text: @message - ).messages.first - - log_or_notify_admin(response) - end - end - end -end diff --git a/app/services/twilio/lookup.rb b/app/services/twilio/lookup.rb new file mode 100644 index 000000000..22270784e --- /dev/null +++ b/app/services/twilio/lookup.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# See https://www.twilio.com/docs/lookup/api for options + +module Twilio + class Lookup + def initialize(number, **options) + @response = TWILIO_CLIENT.lookups.phone_numbers(number.to_s).fetch(options) + end + end +end diff --git a/app/services/twilio/sms.rb b/app/services/twilio/sms.rb new file mode 100644 index 000000000..267d31312 --- /dev/null +++ b/app/services/twilio/sms.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Twilio + class SMS + def initialize(to, body) + response = TWILIO_CLIENT.messages.create( + from: Tenant.credentials.twilio[:sender], + to: to, + body: body + ) + + response.sid + end + end +end diff --git a/app/services/twilio/verify.rb b/app/services/twilio/verify.rb new file mode 100644 index 000000000..cdfbb7034 --- /dev/null +++ b/app/services/twilio/verify.rb @@ -0,0 +1,90 @@ +# frozen_string_literal: true + +# See https://www.twilio.com/verify and authy documentation for details. + +module Twilio + class Verify + # register user to authy! + def register_user(email, phone, country_code) + authy = Authy::API.register_user( + email: email, + cellphone: phone, + country_code: country_code + ) + + if authy.ok? + # store authy_id in database + user.authy_id = authy.id + else + authy.errors + end + end + + # verify authy token + def verify_token(user, token) + response = Authy::API.verify( + id: user.authy_id, + token: token + ) + + response.ok? ? 'ok' : 'invalid' + end + + # request authy code as SMS + def request_sms(user) + response = Authy::API.request_sms( + id: user.authy_id, + locale: user.preferred_language || 'tr' + ) + + response.ok? ? 'ok' : response.errors + end + + # request QR code for Google Authenticator etc. + def request_qr_code(user) + response = Authy::API.request_qr_code( + id: user.authy_id, + qr_size: 500, + label: Tenant.configuration.name + ) + + if response.ok? + response.qr_code # link of the QR image + else + response.errors + end + end + + def send_phone_verification_code(phone_number, locale = 'tr') + response = Authy::PhoneVerification.start( + via: 'sms', + country_code: parse_phone_number(phone_number)[:country_code], + phone_number: parse_phone_number(phone_number)[:normalized_number], + locale: locale + ) + + 'ok' if response.ok? + end + + def check_verification_code(phone_number, verification_code) + response = Authy::PhoneVerification.check( + verification_code: verification_code, + country_code: parse_phone_number(phone_number)[:country_code], + phone_number: parse_phone_number(phone_number)[:normalized_number] + ) + + 'ok' if response.ok? + end + + private + + def parse_phone_number(phone_number) + parsed_number = TelephoneNumber.parse(phone_number) + + { + country_code: parsed_number.country.country_code, + normalized_number: parsed_number.normalized_number + } + end + end +end diff --git a/config/initializers/authy.rb b/config/initializers/authy.rb new file mode 100644 index 000000000..8607a6018 --- /dev/null +++ b/config/initializers/authy.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +Authy.api_key = Tenant.credentials.authy[:api_secret] +Authy.api_uri = 'https://api.authy.com' diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 56bf1ce9d..b4432b378 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -2,6 +2,9 @@ Rails.application.config.filter_parameters += %i[ access_token + account_sid + api_secret + auth_token bearer_token client_id credit_card diff --git a/config/initializers/twilio.rb b/config/initializers/twilio.rb new file mode 100644 index 000000000..fef34414b --- /dev/null +++ b/config/initializers/twilio.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +TWILIO_CLIENT = Twilio::REST::Client.new( + Tenant.credentials.dig(:twilio, :account_sid), + Tenant.credentials.dig(:twilio, :auth_token) +) diff --git a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc index ab191c0c2..8a6292910 100644 --- a/plugins/tenant/omu/test/dummy/config/credentials.yml.enc +++ b/plugins/tenant/omu/test/dummy/config/credentials.yml.enc @@ -1 +1 @@ -vDWYeprzrZbc8nYyQVEwxDeX8iamw2NRXKYyx4pgBD4W4zl6wXCoYTU6WkYikY9sNSl8fR+ayplZLM4+6uV93XrVfx+4YEApO8tEp5R9uJcbMfWUw2W8d2Rqsi7WyP399RQ+ccm7VAFpuVyUUtaODP1lbTL2FvYA+O1crzwvtsSlN4CrUV2P2k4gfeDZvlUOuXLOjgHdTuBqnCmlvDIb3zEHFB6J9U8IWJHE39TWMj6p8gJn7vlWegpSBgkgqvTlFVPyZ6E67bZPy8X9cWj/96jEOQePevZg0BVbDWbT6yEgBAt3upF2z7/uGLjNg8t9zP04D6Oo5ukzV6iUtShJKDjRhooo0eAVcZz4PHsQ2xcD3ecLlBpFBB06kIMcwh9cpcLWud5If9GwvLpIt27X176pf5rJ5WJ3xy/8McugM+XWch8fBVkRpEUHrFaontPf2EFfyBtNq9zggc+9uf5bxlAGhqFVdeVW+Et7T0Gk12ZKyLWsRi1bQV9vCn943g8Ln3j69b7CrSdoShuo92bG9pYC9/axo181WP1WNu4NHGYgfiHIb3qTXaE7K3CY6fRX4tBdr28HAFodvk5OtkmoSPQ0r2hFk3hSrZteEWn0r70wMeZdU+rWgBf497ocYWxYdfxmuui3lW3GRP9eATv+uCLJVMzIqocJA4xUINBEHA3f0xX8fCCQ32clplEOq8G49FSgPeBoyKBMde9JrbygM/GFhhKDPhwvDBwQz2RurIM1tpEORZai71NkOSrBhzXYfsAeG8S5iIQQ5Z3t5y9Bpkyru/XctC35UZpLs0W0iXaO--lYr3AzSbAbCJF8Ao--w9F8YjpXBgaAe5+1z3PQZQ== \ No newline at end of file +nHUu9aczGYJnqdTOugL9+FOSsEICzzg/IsdO6a7A8baI/iaCK9XY2uW8v7zR8f25Ll5bp/lS3KckyGGBxRlyOK/J44lwbVCN0CPhf+gDSXK1oCm5PNLUQI7XZKHB2Hq/abAYiUf8ro+fBqMOIVviQYq02fkdVOQFsI+Uhz0v1TrXUzR3EGDysGl98YXKJOgBzxmjtE6y4gdiZzJ6BV3lFoZmC12uW6Flu6qEdZAMGnZxwDDt/kg2wBx5KhNa9fP4uv9dhVjSrap5LZfB+ciCNW6foibqWQsqxux7o/qNpKnSWQcBFDF79iGxTghvBTJOl9x2W4jdeH8RvzizM3p6MATilkhd5XcdfrOZ0TFAL1HWOXyB/nbSzOJdNu2nWPMnWg1JtRvri7YvrkYblq29OogFjov4xyqF2yTZjS4JX5om86ZsYCFh3QcJADedN/a4tR9PeWt+R3hW0pn2UG0khnMhL5+fEoOfuEHipXLZk66vWBnvjU3zhkR12UpVYmXcJfNJX19bDbQEPYndWikm8Id4l2hdktxzRMAR6XOyoTdpmk/napUuWjoSpLj4nTFODyJ8+VXjTQaEmGqvMnKI4qyMVRbtPdrJOthOCT/GDoOJ1AA/kZmFyHhCDiC9QpXY6iphuv78i/z5MOSAFDWo5esOtPVNUpSC8ZU4inRNRF3SFnaK1bVXvcGKk0whd0GdWcQZaKDH6J7kOnHgXLyz4YvIf33T3Kt603AZ/PIFxoRFGulK6Aw21wi86mDHdyoZuSaY2OJ2z3qIky8qwHVcZxp6A6sOUVLiO50sO6ZlvN+TaZsuBVOYVLdbjlPKZlYHm/hgs6co7mrH5uOjOjZGRZZkDByOTuzbelYJr1FJzDkOPBmSEfb2kvt+0ZEbUPVx6IprVFYe3LyvQ5vdrhPk69tOj/inzBLEauwxZc/Cl1hXP0oMifUuLS60F8shn3jnq7q88rR8ghblbnXSpPN6y9aMhRHEWePljY3N7R+uYLqAQ+p7fP/H7L6vWw/8qwiLDR7sjdDl7Diyz9h9LMWPkyZd/lGd4xAUKoHSPZ5Veklz1O0RMgT3gkTcCCszUq0zIKI=--vUY1DDCrir9O0Fes--pyU0mID+RxvRkknxbbddjw== \ No newline at end of file From d42513f3063d5a51da696471edd3f6a8e39839dd Mon Sep 17 00:00:00 2001 From: msdundar Date: Sun, 24 Mar 2019 00:17:47 +0300 Subject: [PATCH 06/15] Show full name of units Fixes #876 --- app/views/course_management/available_courses/index.html.erb | 2 +- app/views/course_management/course_groups/index.html.erb | 2 +- app/views/course_management/courses/index.html.erb | 2 +- app/views/course_management/curriculums/index.html.erb | 2 +- .../first_registration/prospective_students/_search.html.erb | 2 +- .../first_registration/prospective_students/index.html.erb | 2 +- .../first_registration/registration_documents/index.html.erb | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/course_management/available_courses/index.html.erb b/app/views/course_management/available_courses/index.html.erb index c2d7c920f..b4640e186 100644 --- a/app/views/course_management/available_courses/index.html.erb +++ b/app/views/course_management/available_courses/index.html.erb @@ -26,7 +26,7 @@ <% @available_courses.each do |available_course| %> - <%= available_course.unit.name %> + <%= available_course.unit.names_depth_cache %> <%= full_name(available_course.academic_term) %> <%= available_course.curriculum.name %> <%= available_course.course.code %> diff --git a/app/views/course_management/course_groups/index.html.erb b/app/views/course_management/course_groups/index.html.erb index 70e0be59f..1aebdcffa 100644 --- a/app/views/course_management/course_groups/index.html.erb +++ b/app/views/course_management/course_groups/index.html.erb @@ -26,7 +26,7 @@ <%= course_group.name %> <%= course_group.total_ects_condition %> - <%= course_group.unit.try(:name) %> + <%= course_group.unit.try(:names_depth_cache) %> <%= course_group.course_group_type.try(:name) %> <%= link_to_actions(course_group) %> diff --git a/app/views/course_management/courses/index.html.erb b/app/views/course_management/courses/index.html.erb index 988de8e6d..2b26d81db 100644 --- a/app/views/course_management/courses/index.html.erb +++ b/app/views/course_management/courses/index.html.erb @@ -35,7 +35,7 @@ <%= course.name %> <%= course.code %> - <%= course.unit.try(:name) %> + <%= course.unit.try(:names_depth_cache) %> <%= course.course_type.try(:name) %> T: <%= course.theoric %> diff --git a/app/views/course_management/curriculums/index.html.erb b/app/views/course_management/curriculums/index.html.erb index 4e84f0874..0015d163d 100644 --- a/app/views/course_management/curriculums/index.html.erb +++ b/app/views/course_management/curriculums/index.html.erb @@ -23,7 +23,7 @@ <% @curriculums.each do |curriculum| %> <%= link_to curriculum.name, curriculum %> - <%= curriculum.unit.try(:name) %> + <%= curriculum.unit.try(:names_depth_cache) %> <%= curriculum.semesters_count %> <%= enum_t(curriculum, :status) %> <%= link_to_actions(curriculum) %> diff --git a/app/views/first_registration/prospective_students/_search.html.erb b/app/views/first_registration/prospective_students/_search.html.erb index 23ee2a095..ea73b4945 100644 --- a/app/views/first_registration/prospective_students/_search.html.erb +++ b/app/views/first_registration/prospective_students/_search.html.erb @@ -81,7 +81,7 @@
<%= label_tag :root_unit_id, t('.root_unit') %> <%= select_tag(:root_unit_id, - options_from_collection_for_select(Unit.active.academic.without_programs.order(:name), :id, :name, params[:root_unit_id]), + options_from_collection_for_select(Unit.active.academic.without_programs.order(:name), :id, :names_depth_cache, params[:root_unit_id]), include_blank: true, class: 'form-control', style: 'width: 100%') %> diff --git a/app/views/first_registration/prospective_students/index.html.erb b/app/views/first_registration/prospective_students/index.html.erb index 9045c424c..2d8bd6af7 100644 --- a/app/views/first_registration/prospective_students/index.html.erb +++ b/app/views/first_registration/prospective_students/index.html.erb @@ -29,7 +29,7 @@ <%= prospective_student.id_number %> <%= prospective_student.first_name %> <%= prospective_student.last_name %> - <%= prospective_student.unit.name %> + <%= prospective_student.unit.names_depth_cache %> <%= prospective_student.meb_status ? t('.graduated') : t('.not_graduated_or_unknown') %> <%= prospective_student.military_status ? t('.unproblematic') : t('.must_see_recruiting_office') %> <%= prospective_student.obs_status ? t('.unproblematic') : t('.student_in_a_different_unit') %> diff --git a/app/views/first_registration/registration_documents/index.html.erb b/app/views/first_registration/registration_documents/index.html.erb index 2ca878a74..19bfff49a 100644 --- a/app/views/first_registration/registration_documents/index.html.erb +++ b/app/views/first_registration/registration_documents/index.html.erb @@ -25,7 +25,7 @@ <% @registration_documents.each do |registration_document| %> <%= full_name(registration_document.academic_term) %> - <%= registration_document.unit.name %> + <%= registration_document.unit.names_depth_cache %> <%= registration_document.document_type.name %> <%= registration_document.description %> <%= link_to_actions(registration_document, except: :show) %> From e47a17c8afdb35d950e2f85505727241def82d80 Mon Sep 17 00:00:00 2001 From: msdundar Date: Sun, 24 Mar 2019 00:26:02 +0300 Subject: [PATCH 07/15] Standardize index page views --- .../committee/agenda_types/index.html.erb | 7 ++-- .../available_courses/index.html.erb | 3 +- .../course_group_types/index.html.erb | 7 ++-- .../course_groups/index.html.erb | 10 +++--- .../course_types/index.html.erb | 7 ++-- .../course_management/courses/index.html.erb | 32 +++++++++---------- .../curriculums/index.html.erb | 16 +++++----- app/views/units/index.html.erb | 7 ++-- 8 files changed, 40 insertions(+), 49 deletions(-) diff --git a/app/views/committee/agenda_types/index.html.erb b/app/views/committee/agenda_types/index.html.erb index bb685a2ab..da1efa3da 100644 --- a/app/views/committee/agenda_types/index.html.erb +++ b/app/views/committee/agenda_types/index.html.erb @@ -1,12 +1,11 @@ -
- <%= link_to_new t('.new_agenda_type_link'), new_agenda_type_path %> -
-
<%= fa_icon 'tags', text: t('.card_header') %> +
+ <%= link_to_new t('.new_agenda_type_link'), new_agenda_type_path %> +
<%= render 'layouts/shared/smart_search_form', diff --git a/app/views/course_management/available_courses/index.html.erb b/app/views/course_management/available_courses/index.html.erb index b4640e186..cb586fda1 100644 --- a/app/views/course_management/available_courses/index.html.erb +++ b/app/views/course_management/available_courses/index.html.erb @@ -1,5 +1,3 @@ -<%= render 'search' %> -
@@ -10,6 +8,7 @@
+ <%= render 'search' %> diff --git a/app/views/course_management/course_group_types/index.html.erb b/app/views/course_management/course_group_types/index.html.erb index 8abc975ef..afb68e58a 100644 --- a/app/views/course_management/course_group_types/index.html.erb +++ b/app/views/course_management/course_group_types/index.html.erb @@ -1,12 +1,11 @@ -
- <%= link_to_new t('.new_course_group_type_link'), new_course_group_type_path %> -
-
<%= fa_icon 'tags', text: t('.card_header') %> +
+ <%= link_to_new t('.new_course_group_type_link'), new_course_group_type_path %> +
<%= render 'layouts/shared/smart_search_form', diff --git a/app/views/course_management/course_groups/index.html.erb b/app/views/course_management/course_groups/index.html.erb index 1aebdcffa..d3f35c35c 100644 --- a/app/views/course_management/course_groups/index.html.erb +++ b/app/views/course_management/course_groups/index.html.erb @@ -1,16 +1,14 @@ -
- <%= link_to_new t('.new_course_group_link'), new_course_group_path %> -
- -<%= render 'search' %> -
<%= fa_icon 'tags', text: t('.card_header') %> +
+ <%= link_to_new t('.new_course_group_link'), new_course_group_path %> +
+ <%= render 'search' %>
diff --git a/app/views/course_management/course_types/index.html.erb b/app/views/course_management/course_types/index.html.erb index 90583d2ae..476eab3f1 100644 --- a/app/views/course_management/course_types/index.html.erb +++ b/app/views/course_management/course_types/index.html.erb @@ -1,12 +1,11 @@ -
- <%= link_to_new t('.new_course_type_link'), new_course_type_path %> -
-
<%= fa_icon 'tags', text: t('.card_header') %> +
+ <%= link_to_new t('.new_course_type_link'), new_course_type_path %> +
<%= render 'layouts/shared/smart_search_form', diff --git a/app/views/course_management/courses/index.html.erb b/app/views/course_management/courses/index.html.erb index 2b26d81db..9edf45c39 100644 --- a/app/views/course_management/courses/index.html.erb +++ b/app/views/course_management/courses/index.html.erb @@ -1,18 +1,16 @@ -
- <%= link_to_new t('.add_new_course'), new_course_path %> -
- -<%= render 'search' %> -
-
-
+
+
<%= fa_icon 'align-justify', text: t('.courses') %> +
+ <%= link_to_new t('.add_new_course'), new_course_path %> +
-
+
+ <%= render 'search' %>
- + @@ -38,10 +36,10 @@ @@ -52,9 +50,9 @@
<%= t('.name') %> <%= t('.code') %><%= course.unit.try(:names_depth_cache) %> <%= course.course_type.try(:name) %> - T: <%= course.theoric %> - P: <%= course.practice %> - L: <%= course.laboratory %> - K: <%= course.credit %> + T: <%= course.theoric %> + P: <%= course.practice %> + L: <%= course.laboratory %> + K: <%= course.credit %> <%= enum_t(course, :program_type) %> <%= course.language.try(:name) %>
-