diff --git a/app/admin/category.rb b/app/admin/category.rb index 3c4182e3b..dd39a86fb 100644 --- a/app/admin/category.rb +++ b/app/admin/category.rb @@ -7,7 +7,7 @@ end form do |f| - f.inputs "Admin Details" do + f.inputs do f.input :name end f.actions @@ -18,17 +18,12 @@ row :created_at row :updated_at row :name_translations do - cat.disable_fallback - content_tag :div do - I18n.available_locales.map do |loc| - next unless cat.send("name_#{loc}") - content_tag(:strong, "#{loc}: ") + - content_tag(:span, cat.send("name_#{loc}")) - end.compact.sum - end + cat.name_translations.map do |locale, translation| + tag.strong("#{I18n.t("locales.#{locale}", locale: locale)}: ") + + tag.span(translation) + end.join(" | ").html_safe end end - active_admin_comments end permit_params :name diff --git a/app/admin/user.rb b/app/admin/user.rb index 728c90e03..77435bdad 100644 --- a/app/admin/user.rb +++ b/app/admin/user.rb @@ -8,14 +8,13 @@ end collection_action :import_csv, method: :post do - errors = CsvDb.convert_save(params[:dump][:organization_id], - params[:dump][:file]) + errors = UserImporter.call(params[:dump][:organization_id], params[:dump][:file]) flash[:error] = errors.join("
").html_safe if errors.present? + redirect_to action: :index end index do - # selectable_column column do |user| link_to image_tag(avatar_url(user, 24)), admin_user_path(user) end diff --git a/app/assets/stylesheets/active_admin.scss b/app/assets/stylesheets/active_admin.scss index 90ba1d475..fb70e0687 100644 --- a/app/assets/stylesheets/active_admin.scss +++ b/app/assets/stylesheets/active_admin.scss @@ -1,17 +1,6 @@ -// SASS variable overrides must be declared before loading up Active Admin's styles. -// -// To view the variables that Active Admin provides, take a look at -// `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the -// Active Admin source. -// -// For example, to change the sidebar width: -// $sidebar-width: 242px; +$primary-color: #165e6d; +$link-color: $primary-color; +$table-stripe-color: #f5f5f5; -// Active Admin's got SASS! @import "active_admin/mixins"; @import "active_admin/base"; - -// Overriding any non-variable SASS must be done after the fact. -// For example, to change the default status-tag color: -// -// .status_tag { background: #6090DB; } diff --git a/app/models/csv_db.rb b/app/models/csv_db.rb deleted file mode 100644 index efc3e3d81..000000000 --- a/app/models/csv_db.rb +++ /dev/null @@ -1,45 +0,0 @@ -require "csv" - -class CsvDb - class << self - def user_from_row(row) - User.new(username: row[2..4].join(" "), date_of_birth: row[6], - email: row[9], phone: row[7], alt_phone: row[8], - gender: User::GENDERS[row[5].to_i - 1]) - end - - def member_from_row(row, user, organization, errors) - member = organization.members.create(member_uid: row[0], - entry_date: row[1], - user_id: user.id) - errors.push(member_id: row[0], email: row[9], - errors: member.errors.full_messages - ) if member.errors.present? - end - - def process_row(row, organization, errors) - user = user_from_row(row) - user.skip_confirmation! # auto-confirm, not sending confirmation email - - if user.save - member_from_row(row, user, organization, errors) - else - errors.push(member_id: row[0], email: row[9], - errors: user.errors.full_messages) - end - end - - def convert_save(organization_id, csv_data) - data = csv_data.read - # data.force_encoding Encoding::ISO_8859_1 - # normalized_data = data.encode(Encoding::UTF_8)[1..-1] - organization = Organization.find(organization_id) - errors = [] - CSV.parse(data, headers: false) do |row| - process_row(row, organization, errors) - end - organization.ensure_reg_number_seq! - errors - end - end -end diff --git a/app/services/user_importer.rb b/app/services/user_importer.rb new file mode 100644 index 000000000..e8825dc0a --- /dev/null +++ b/app/services/user_importer.rb @@ -0,0 +1,75 @@ +# Used in the Admin section to import users and members +# to a specific organization, from a CSV file. + +require "csv" + +class UserImporter + Row = Struct.new( + :username, + :member_id, + :entry_date, + :date_of_birth, + :email, + :phone, + :alt_phone, + :gender + ) do + def user_from_row + User.new( + username: username, + date_of_birth: date_of_birth, + email: email, + phone: phone, + alt_phone: alt_phone, + gender: gender + ) + end + end + + class << self + def call(organization_id, csv_data) + data = csv_data.read + organization = Organization.find(organization_id) + errors = [] + + CSV.parse(data, headers: false) do |data_row| + row = Row.new( + data_row[2..4].join(" "), + data_row[0], + data_row[1], + data_row[6], + data_row[9], + data_row[7], + data_row[8], + User::GENDERS[data_row[5].to_i - 1] + ) + process_row(row, organization, errors) + end + + organization.ensure_reg_number_seq! + errors + end + + def process_row(row, organization, errors) + user = row.user_from_row + user.skip_confirmation! # auto-confirm, not sending confirmation email + + if user.save + member_from_row(row, user, organization, errors) + else + errors.push(member_id: row.member_id, email: row.email, + errors: user.errors.full_messages) + end + end + + def member_from_row(row, user, organization, errors) + member = organization.members.create(member_uid: row.member_id, + entry_date: row.entry_date, + user_id: user.id) + + errors.push(member_id: row.member_id, email: row.email, + errors: member.errors.full_messages + ) if member.errors.present? + end + end +end diff --git a/app/views/admin/csv/upload_csv.html.erb b/app/views/admin/csv/upload_csv.html.erb index 619fab914..3458a3d6b 100644 --- a/app/views/admin/csv/upload_csv.html.erb +++ b/app/views/admin/csv/upload_csv.html.erb @@ -1,15 +1,15 @@ -<%= form_for :dump, url: { action: "import_csv" }, html: { multipart: true, class: "formtastic user" } do |f| %> +<%= form_for :dump, url: { action: "import_csv" }, html: { multipart: true } do |f| %>
  1. <%= label_tag I18n.t("active_admin.users.upload_csv") %> - <%= f.file_field :file %> + <%= f.file_field :file, required: true %>
  1. <%= label_tag I18n.t("active_admin.users.organization") %> - <%= f.select :organization_id, options_for_select(Organization.all.map{ |org| [org.name, org.id] }), include_blank: true %> + <%= f.select :organization_id, options_for_select(Organization.pluck(:name, :id)), { include_blank: true }, { required: true } %>
diff --git a/config/initializers/active_admin.rb b/config/initializers/active_admin.rb index 83b8198f6..f7b99c3bc 100644 --- a/config/initializers/active_admin.rb +++ b/config/initializers/active_admin.rb @@ -1,246 +1,20 @@ ActiveAdmin.setup do |config| - # == Site Title - # - # Set the title that is displayed on the main layout - # for each of the active admin pages. - # config.site_title = "TimeOverflow" - - # Set the link url for the title. For example, to take - # users to your main site. Defaults to no link. - # config.site_title_link = "/" - - # Set an optional image to be displayed for the header - # instead of a string (overrides :site_title) - # - # Note: Recommended image height is 21px to properly fit in the header - # - # config.site_title_image = "/images/logo.png" - - # == Default Namespace - # - # Set the default namespace each administration resource - # will be added to. - # - # eg: - # config.default_namespace = :hello_world - # - # This will create resources in the HelloWorld module and - # will namespace routes to /hello_world/* - # - # To set no namespace by default, use: - # config.default_namespace = false - # - # Default: - # config.default_namespace = :admin - # - # You can customize the settings for each namespace by using - # a namespace block. For example, to change the site title - # within a namespace: - # - # config.namespace :admin do |admin| - # admin.site_title = "Custom Admin Title" - # end - # - # This will ONLY change the title for the admin section. Other - # namespaces will continue to use the main "site_title" configuration. - - # == User Authentication - # - # Active Admin will automatically call an authentication - # method in a before filter of all controller actions to - # ensure that there is a currently logged in admin user. - # - # This setting changes the method which Active Admin calls - # within the controller. config.authentication_method = :authenticate_superuser! - - # == User Authorization - # - # Active Admin will automatically call an authorization - # method in a before filter of all controller actions to - # ensure that there is a user with proper rights. You can use - # CanCanAdapter or make your own. Please refer to documentation. - # config.authorization_adapter = ActiveAdmin::CanCanAdapter - - # You can customize your CanCan Ability class name here. - # config.cancan_ability_class = "Ability" - - # You can specify a method to be called on unauthorized access. - # This is necessary in order to prevent a redirect loop which happens - # because, by default, user gets redirected to Dashboard. If user - # doesn't have access to Dashboard, he'll end up in a redirect loop. - # Method provided here should be defined in application_controller.rb. - # config.on_unauthorized_access = :access_denied - - # == Current User - # - # Active Admin will associate actions with the current - # user performing them. - # - # This setting changes the method which Active Admin calls - # to return the currently logged in user. config.current_user_method = :current_user - - - # == Logging Out - # - # Active Admin displays a logout link on each screen. These - # settings configure the location and method used for the link. - # - # This setting changes the path where the link points to. If it's - # a string, the strings is used as the path. If it's a Symbol, we - # will call the method to return the path. - # - # Default: config.logout_link_path = :destroy_user_session_path - - # This setting changes the http method used when rendering the - # link. For example :get, :delete, :put, etc.. - # - # Default: config.logout_link_method = :delete - - - # == Root - # - # Set the action to call for the root path. You can set different - # roots for each namespace. - # - # Default: - # config.root_to = 'dashboard#index' - - - # == Admin Comments - # - # This allows your users to comment on any resource registered with Active Admin. - # - # You can completely disable comments: - # config.allow_comments = false - # - # You can disable the menu item for the comments index page: - # config.show_comments_in_menu = false - # - # You can change the name under which comments are registered: - # config.comments_registration_name = 'AdminComment' - - - # == Batch Actions - # - # Enable and disable Batch Actions - # - config.batch_actions = true - - - # == Controller Filters - # - # You can add before, after and around filters to all of your - # Active Admin resources and pages from here. - # - # config.before_filter :do_something_awesome - - - # == Setting a Favicon - # - # config.favicon = '/assets/favicon.ico' - - - # == Register Stylesheets & Javascripts - # - # We recommend using the built in Active Admin layout and loading - # up your own stylesheets / javascripts to customize the look - # and feel. - # - # To load a stylesheet: - # config.register_stylesheet 'my_stylesheet.css' - # - # You can provide an options hash for more control, which is passed along to stylesheet_link_tag(): - # config.register_stylesheet 'my_print_stylesheet.css', :media => :print - # - # To load a javascript file: - # config.register_javascript 'my_javascript.js' - - - # == CSV options - # - # Set the CSV builder separator - # config.csv_options = { :col_sep => ';' } - # - # Force the use of quotes - # config.csv_options = { :force_quotes => true } - - - # == Menu System - # - # You can add a navigation menu to be used in your application, or configure a provided menu - # - # To change the default utility navigation to show a link to your website & a logout btn - # - # config.namespace :admin do |admin| - # admin.build_menu :utility_navigation do |menu| - # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank } - # admin.add_logout_button_to_menu menu - # end - # end - # - # If you wanted to add a static menu item to the default menu provided: - # - # config.namespace :admin do |admin| - # admin.build_menu :default do |menu| - # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank } - # end - # end + config.comments = false config.namespace :admin do |admin| admin.build_menu :utility_navigation do |menu| menu.add label: "Languages" do |lang| - lang.add label: "Español", url: ->{ url_for(:locale => 'es') }, id: 'i18n-es', priority: 1 - lang.add label: "Català", url: ->{ url_for(:locale => 'ca') }, id: 'i18n-ca', priority: 2 + I18n.available_locales.each do |locale| + lang.add label: I18n.t("locales.#{locale}", locale: locale), url: ->{ url_for(locale: locale) } + end end - menu.add label: ->{ display_name current_active_admin_user}, - url: '#', - id: 'current_user', - if: ->{ current_active_admin_user? } + admin.add_current_user_to_menu menu admin.add_logout_button_to_menu menu end end - - - # == Download Links - # - # You can disable download links on resource listing pages, - # or customize the formats shown per namespace/globally - # - # To disable/customize for the :admin namespace: - # - # config.namespace :admin do |admin| - # - # # Disable the links entirely - # admin.download_links = false - # - # # Only show XML & PDF options - # admin.download_links = [:xml, :pdf] - # - # # Enable/disable the links based on block - # # (for example, with cancan) - # admin.download_links = proc { can?(:view_download_links) } - # - # end - - - # == Pagination - # - # Pagination is enabled by default for all resources. - # You can control the default per page count for all resources here. - # - # config.default_per_page = 30 - - - # == Filters - # - # By default the index screen includes a “Filters” sidebar on the right - # hand side with a filter for each attribute of the registered model. - # You can enable or disable them for all resources here. - # - # config.filters = true end diff --git a/config/locales/eu.yml b/config/locales/eu.yml index fd2ed50db..8f60c09ce 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -287,7 +287,7 @@ eu: title: Ez da topatu global: add_new: Berria sortu - all: Dena. + all: Dena amount: Zenbat announcements: Iragarkia back: Atzera diff --git a/spec/services/user_importer_spec.rb b/spec/services/user_importer_spec.rb new file mode 100644 index 000000000..b8b9ad17e --- /dev/null +++ b/spec/services/user_importer_spec.rb @@ -0,0 +1,50 @@ +RSpec.describe UserImporter do + describe '#call' do + let(:organization) { Fabricate(:organization) } + let(:user) do + User.new( + username: 'Hermione Cadena Muñoz', + date_of_birth: '1989-03-16', + email: 'user@example.com', + phone: '622743103', + alt_phone: '691777984', + gender: 'female' + ) + end + + # member_id, entry_date, username, gender, date_of_birth, phone, alt_phone, email + let(:csv_data) { StringIO.new('1,2018-01-30,Hermione,Cadena,Muñoz,1,1989-03-16,622743103,691777984,user@example.com') } + + before do + allow(Organization) + .to receive(:find).with(organization.id).and_return(organization) + + allow(User).to receive(:new).with( + username: 'Hermione Cadena Muñoz', + date_of_birth: '1989-03-16', + email: 'user@example.com', + phone: '622743103', + alt_phone: '691777984', + gender: 'female' + ).and_return(user) + end + + it 'creates a user out of a CSV row' do + expect(User).to receive(:new).with( + username: 'Hermione Cadena Muñoz', + date_of_birth: '1989-03-16', + email: 'user@example.com', + phone: '622743103', + alt_phone: '691777984', + gender: 'female' + ).and_return(user) + + described_class.call(organization.id, csv_data) + end + + it 'persists the user' do + expect(user).to receive(:save) + described_class.call(organization.id, csv_data) + end + end +end