diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index a9e44d249..44258bc7a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -79,6 +79,7 @@ //= require send_newsletter_alert //= require managers //= require globalize +//= require send_admin_notification_alert var initialize_modules = function() { App.Comments.initialize(); @@ -124,6 +125,7 @@ var initialize_modules = function() { App.SendNewsletterAlert.initialize(); App.Managers.initialize(); App.Globalize.initialize(); + App.SendAdminNotificationAlert.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/send_admin_notification_alert.js.coffee b/app/assets/javascripts/send_admin_notification_alert.js.coffee new file mode 100644 index 000000000..8c1c928e5 --- /dev/null +++ b/app/assets/javascripts/send_admin_notification_alert.js.coffee @@ -0,0 +1,4 @@ +App.SendAdminNotificationAlert = + initialize: -> + $('#js-send-admin_notification-alert').on 'click', -> + confirm(this.dataset.alert); diff --git a/app/controllers/admin/admin_notifications_controller.rb b/app/controllers/admin/admin_notifications_controller.rb new file mode 100644 index 000000000..1dd03038a --- /dev/null +++ b/app/controllers/admin/admin_notifications_controller.rb @@ -0,0 +1,67 @@ +class Admin::AdminNotificationsController < Admin::BaseController + + def index + @admin_notifications = AdminNotification.all + end + + def show + @admin_notification = AdminNotification.find(params[:id]) + end + + def new + @admin_notification = AdminNotification.new + end + + def create + @admin_notification = AdminNotification.new(admin_notification_params) + + if @admin_notification.save + notice = t("admin.admin_notifications.create_success") + redirect_to [:admin, @admin_notification], notice: notice + else + render :new + end + end + + def edit + @admin_notification = AdminNotification.find(params[:id]) + end + + def update + @admin_notification = AdminNotification.find(params[:id]) + + if @admin_notification.update(admin_notification_params) + notice = t("admin.admin_notifications.update_success") + redirect_to [:admin, @admin_notification], notice: notice + else + render :edit + end + end + + def destroy + @admin_notification = AdminNotification.find(params[:id]) + @admin_notification.destroy + + notice = t("admin.admin_notifications.delete_success") + redirect_to admin_admin_notifications_path, notice: notice + end + + def deliver + @admin_notification = AdminNotification.find(params[:id]) + + if @admin_notification.valid? + @admin_notification.deliver + flash[:notice] = t("admin.admin_notifications.send_success") + else + flash[:error] = t("admin.segment_recipient.invalid_recipients_segment") + end + + redirect_to [:admin, @admin_notification] + end + + private + + def admin_notification_params + params.require(:admin_notification).permit(:title, :body, :link, :segment_recipient) + end +end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 588f2ed6b..ee6878502 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -44,7 +44,11 @@ class NotificationsController < ApplicationController when "Topic" community_topic_path @notification.linkable_resource.community, @notification.linkable_resource else - url_for @notification.linkable_resource + if @notification.linkable_resource.is_a?(AdminNotification) + @notification.linkable_resource.link || notifications_path + else + url_for @notification.linkable_resource + end end end diff --git a/app/models/admin_notification.rb b/app/models/admin_notification.rb new file mode 100644 index 000000000..eccd90910 --- /dev/null +++ b/app/models/admin_notification.rb @@ -0,0 +1,44 @@ +class AdminNotification < ActiveRecord::Base + include Notifiable + + validates :title, presence: true + validates :body, presence: true + validates :segment_recipient, presence: true + validate :validate_segment_recipient + + before_validation :complete_link_url + + def list_of_recipients + UserSegments.send(segment_recipient) if valid_segment_recipient? + end + + def valid_segment_recipient? + segment_recipient && UserSegments.respond_to?(segment_recipient) + end + + def draft? + sent_at.nil? + end + + def list_of_recipients_count + list_of_recipients.try(:count) || 0 + end + + def deliver + list_of_recipients.each { |user| Notification.add(user, self) } + self.update(sent_at: Time.current, recipients_count: list_of_recipients.count) + end + + private + + def validate_segment_recipient + errors.add(:segment_recipient, :invalid) unless valid_segment_recipient? + end + + def complete_link_url + return unless link.present? + unless self.link[/\Ahttp:\/\//] || self.link[/\Ahttps:\/\//] + self.link = "http://#{self.link}" + end + end +end diff --git a/app/models/notification.rb b/app/models/notification.rb index 33bf7701c..dacedb762 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -53,9 +53,19 @@ class Notification < ActiveRecord::Base "proposal_notification" when "Comment" "replies_to" + when "AdminNotification" + nil else "comments_on" end end -end \ No newline at end of file + def link + if notifiable.is_a?(AdminNotification) && notifiable.link.blank? + nil + else + self + end + end + +end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index b830acb46..e5af32cc4 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -79,18 +79,21 @@ <% end %> - <% messages_sections = %w(newsletters emails_download) %> - <% messages_menu_active = messages_sections.include?(controller_name) %> -
  • > + <% newsletters_notifications_sections = %w(newsletters emails_download admin_notifications) %> + <% newsletters_menu_active = newsletters_notifications_sections.include?(controller_name) %> +
  • > - <%= t("admin.menu.emails") %> + <%= t("admin.menu.newsletters_and_notifications") %> -
  • diff --git a/app/views/admin/admin_notifications/_form.html.erb b/app/views/admin/admin_notifications/_form.html.erb new file mode 100644 index 000000000..4053bb66d --- /dev/null +++ b/app/views/admin/admin_notifications/_form.html.erb @@ -0,0 +1,13 @@ +<%= form_for [:admin, @admin_notification] do |f| %> + <%= render 'shared/errors', resource: @admin_notification %> + + <%= f.select :segment_recipient, options_for_select(user_segments_options, + @admin_notification[:segment_recipient]) %> + <%= f.text_field :title %> + <%= f.text_field :link %> + <%= f.text_area :body %> + +
    + <%= f.submit class: "button success" %> +
    +<% end %> diff --git a/app/views/admin/admin_notifications/edit.html.erb b/app/views/admin/admin_notifications/edit.html.erb new file mode 100644 index 000000000..cb14f0a0b --- /dev/null +++ b/app/views/admin/admin_notifications/edit.html.erb @@ -0,0 +1,4 @@ +<%= back_link_to %> +

    <%= t("admin.admin_notifications.edit.section_title") %>

    + +<%= render "form" %> diff --git a/app/views/admin/admin_notifications/index.html.erb b/app/views/admin/admin_notifications/index.html.erb new file mode 100644 index 000000000..d16ee476e --- /dev/null +++ b/app/views/admin/admin_notifications/index.html.erb @@ -0,0 +1,56 @@ +

    <%= t("admin.admin_notifications.index.section_title") %>

    +<%= link_to t("admin.admin_notifications.index.new_notification"), new_admin_admin_notification_path, + class: "button float-right" %> + +<% if @admin_notifications.any? %> + + + + + + + + + + + <% @admin_notifications.order(created_at: :desc).each do |admin_notification| %> + + + + + + + <% end %> + +
    <%= t("admin.admin_notifications.index.title") %><%= t("admin.admin_notifications.index.segment_recipient") %><%= t("admin.admin_notifications.index.sent") %><%= t("admin.admin_notifications.index.actions") %>
    + <%= admin_notification.title %> + + <%= segment_name(admin_notification.segment_recipient) %> + + <% if admin_notification.draft? %> + <%= t("admin.admin_notifications.index.draft") %> + <% else %> + <%= l admin_notification.sent_at.to_date %> + <% end %> + + <% if admin_notification.draft? %> + <%= link_to t("admin.admin_notifications.index.edit"), + edit_admin_admin_notification_path(admin_notification), + method: :get, class: "button hollow" %> + <%= link_to t("admin.admin_notifications.index.delete"), + admin_admin_notification_path(admin_notification), + method: :delete, class: "button hollow alert" %> + <%= link_to t("admin.admin_notifications.index.preview"), + admin_admin_notification_path(admin_notification), + class: "button" %> + <% else %> + <%= link_to t("admin.admin_notifications.index.view"), + admin_admin_notification_path(admin_notification), + class: "button" %> + <% end %> +
    +<% else %> +
    + <%= t("admin.admin_notifications.index.empty_notifications") %> +
    +<% end %> diff --git a/app/views/admin/admin_notifications/new.html.erb b/app/views/admin/admin_notifications/new.html.erb new file mode 100644 index 000000000..69bf2a80b --- /dev/null +++ b/app/views/admin/admin_notifications/new.html.erb @@ -0,0 +1,4 @@ +<%= back_link_to %> +

    <%= t("admin.admin_notifications.new.section_title") %>

    + +<%= render "form" %> diff --git a/app/views/admin/admin_notifications/show.html.erb b/app/views/admin/admin_notifications/show.html.erb new file mode 100644 index 000000000..92c0e37fc --- /dev/null +++ b/app/views/admin/admin_notifications/show.html.erb @@ -0,0 +1,77 @@ +<%= back_link_to admin_admin_notifications_path %> + +

    <%= t("admin.admin_notifications.show.section_title") %>

    + +
    +
    +
    +
    + <%= t("admin.admin_notifications.show.sent_at") %>
    + <% if @admin_notification.draft? %> + <%= t("admin.admin_notifications.index.draft") %> + <% else %> + <%= l(@admin_notification.sent_at.to_date) %> + <% end %> +
    +
    + <%= t("admin.admin_notifications.show.title") %>
    + <%= @admin_notification.title %> +
    +
    +
    + +
    + <%= t("admin.admin_notifications.show.body") %>
    + <%= @admin_notification.body %> +
    +
    + <%= t("admin.admin_notifications.show.link") %>
    + <%= @admin_notification.link %> +
    +
    +
    +
    + <%= t("admin.admin_notifications.show.segment_recipient") %>
    + <%= segment_name(@admin_notification.segment_recipient) %> + <% if @admin_notification.draft? %> + <%= t("admin.admin_notifications.show.will_get_notified", + n: @admin_notification.list_of_recipients_count) %> + <% else %> + <%= t("admin.admin_notifications.show.got_notified", + n: @admin_notification.recipients_count) %> + <% end %> +
    +
    +
    + +

    + <% if @admin_notification.draft? %> + <%= t("admin.admin_notifications.show.preview_guide") %> + <% else %> + <%= t("admin.admin_notifications.show.sent_guide") %> + <% end %> +

    +
    +
    + +
    +
    +
    +<% if @admin_notification.draft? && @admin_notification.valid_segment_recipient? %> + <%= link_to t("admin.admin_notifications.show.send"), + deliver_admin_admin_notification_path(@admin_notification), + "data-alert": t("admin.admin_notifications.show.send_alert", + n: @admin_notification.list_of_recipients_count), + method: :post, + id: "js-send-admin_notification-alert", + class: "button success" %> +<% end %> diff --git a/app/views/notifications/_notification.html.erb b/app/views/notifications/_notification.html.erb index 2d8b115e5..34f1b96b1 100644 --- a/app/views/notifications/_notification.html.erb +++ b/app/views/notifications/_notification.html.erb @@ -1,21 +1,11 @@ -
  • "> - - <% if notification.notifiable_available? %> - <%= link_to notification do %> -

    - - <%= t("notifications.notification.action.#{notification.notifiable_action}", - count: notification.counter) %> - - - <%= notification.notifiable_title %> - -

    - -

    - <%= l notification.timestamp, format: :datetime %> -

    - <% end %> +
  • + <% if notification.try(:notifiable_available?) %> + <% locals = { notification: notification, + timestamp: notification.timestamp, + title: notification.notifiable_title, + body: notification.notifiable.try(:body) } %> + <% link_text = render partial: '/notifications/notification_body', locals: locals %> + <%= link_to_if notification.link.present?, link_text, notification.link %> <% else %>

    diff --git a/app/views/notifications/_notification_body.html.erb b/app/views/notifications/_notification_body.html.erb new file mode 100644 index 000000000..d6ed86673 --- /dev/null +++ b/app/views/notifications/_notification_body.html.erb @@ -0,0 +1,17 @@ +

    + <% if notification && notification.notifiable_action %> + + <%= t("notifications.notification.action.#{notification.notifiable_action}", + count: notification.counter) %> + + <% end %> + + <%= title %> + + <% if body %> +

    <%= body %>

    + <% end %> +

    +

    + <%= l(timestamp, format: :datetime) %> +

    diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb index eccd3ec6a..e005aad88 100644 --- a/app/views/notifications/index.html.erb +++ b/app/views/notifications/index.html.erb @@ -24,7 +24,9 @@ <% else %> <% end %> diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 8e44f6ea9..d3d19f1ed 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -283,6 +283,10 @@ en: attributes: segment_recipient: invalid: "The user recipients segment is invalid" + admin_notification: + attributes: + segment_recipient: + invalid: "The user recipients segment is invalid" map_location: attributes: map: diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 760741453..54bb5adc6 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -518,8 +518,9 @@ en: administrators: Administrators managers: Managers moderators: Moderators - emails: Sending of emails + newsletters_and_notifications: Newsletters & Notifications newsletters: Newsletters + admin_notifications: Notifications emails_download: Emails download valuators: Valuators poll_officers: Poll officers @@ -613,6 +614,41 @@ en: body: Email content body_help_text: This is how the users will see the email send_alert: Are you sure you want to send this newsletter to %{n} users? + admin_notifications: + create_success: Notification created successfully + update_success: Notification updated successfully + send_success: Notification sent successfully + delete_success: Notification deleted successfully + index: + section_title: Notifications + new_notification: New notification + title: Title + segment_recipient: Recipients + sent: Sent + actions: Actions + draft: Draft + edit: Edit + delete: Delete + preview: Preview + view: View + empty_notifications: There are no notifications to show + new: + section_title: New notification + edit: + section_title: Edit notification + show: + section_title: Notification preview + send: Send + will_get_notified: (%{n} users will be notified) + got_notified: (%{n} users got notified) + sent_at: Sent at + title: Title + body: Text + link: Link + segment_recipient: Recipients + preview_guide: "This is how the users will see the notification:" + sent_guide: "This is how the users see the notification:" + send_alert: Are you sure you want to send this notification to %{n} users? emails_download: index: title: Emails download diff --git a/config/locales/en/seeds.yml b/config/locales/en/seeds.yml index 4e40cfd63..a3587c9e3 100644 --- a/config/locales/en/seeds.yml +++ b/config/locales/en/seeds.yml @@ -53,3 +53,18 @@ en: recounting_poll: "Recounting Poll" expired_poll_without_stats: "Expired Poll without Stats & Results" expired_poll_with_stats: "Expired Poll with Stats & Results" + admin_notifications: + internal_link: + title: 'Do you have a proposal?' + body: 'Remember you can create a proposal with your ideas and people will discuss & support it.' + link: '/proposals' + external_link: + title: Help us translate consul + body: 'If you are proficient in a language, please help us translate consul!.' + link: 'https://crwd.in/consul' + without_link: + title: 'You can now geolocate proposals & investments' + body: 'When you create a proposal or investment you now can specify a point on a map' + not_sent: + title: 'We are closing the Participatory Budget!!' + body: 'Hurry up and create a last proposal before it ends next in few days!' diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 2da027031..52bb51e9f 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -283,6 +283,10 @@ es: attributes: segment_recipient: invalid: "El segmento de usuarios es inválido" + admin_notification: + attributes: + segment_recipient: + invalid: "El segmento de usuarios es inválido" map_location: attributes: map: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 909e2509a..c57b42197 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -519,8 +519,9 @@ es: administrators: Administradores managers: Gestores moderators: Moderadores - emails: Envío de emails + newsletters_and_notifications: Newsletters & Notificaciones newsletters: Newsletters + admin_notifications: Notificaciones emails_download: Descarga de emails valuators: Evaluadores poll_officers: Presidentes de mesa @@ -614,6 +615,41 @@ es: body: Contenido del email body_help_text: Así es como verán el email los usuarios send_alert: ¿Estás seguro/a de que quieres enviar esta newsletter a %{n} usuarios? + admin_notifications: + create_success: Notificación creada correctamente + update_success: Notificación actualizada correctamente + send_success: Notificación enviada correctamente + delete_success: Notificación borrada correctamente + index: + section_title: Envío de notificaciones + new_notification: Crear notificación + title: Título + segment_recipient: Destinatarios + sent: Enviado + actions: Acciones + draft: Borrador + edit: Editar + delete: Borrar + preview: Previsualizar + view: Visualizar + empty_notifications: No hay notificaciones para mostrar + new: + section_title: Nueva notificación + edit: + section_title: Editar notificación + show: + section_title: Vista previa de notificación + send: Enviar + will_get_notified: (%{n} usuarios serán notificados) + got_notified: (%{n} usuarios fueron notificados) + sent_at: Enviado + title: Título + body: Texto + link: Enlace + segment_recipient: Destinatarios + preview_guide: "Así es como los usuarios verán la notificación:" + sent_guide: "Así es como los usuarios ven la notificación:" + send_alert: ¿Estás seguro/a de que quieres enviar esta notificación a %{n} usuarios? emails_download: index: title: Descarga de emails diff --git a/config/locales/es/seeds.yml b/config/locales/es/seeds.yml index 1d6563bef..d8a40471b 100644 --- a/config/locales/es/seeds.yml +++ b/config/locales/es/seeds.yml @@ -53,3 +53,18 @@ es: recounting_poll: "Votación en Recuento" expired_poll_without_stats: "Votación Finalizada (sin Estadísticas o Resultados)" expired_poll_with_stats: "Votación Finalizada (con Estadísticas y Resultado)" + admin_notifications: + internal_link: + title: 'Tienes una propuesta?' + body: 'Recuerda que puedes crear propuestas y los ciudadanos las debatirán y apoyarán.' + link: '/proposals' + external_link: + title: 'Ayúdanos a traducir CONSUL' + body: 'Si dominas un idioma, ayúdanos a completar su traducción en CONSUL.' + link: 'https://crwd.in/consul' + without_link: + title: 'Ahora puedes geolocalizar propuestas y proyectos de inversión' + body: 'Cuando crees una propuesta o proyecto de inversión podrás especificar su localización en el mapa' + not_sent: + title: 'Últimos días para crear proyectos de Presupuestos Participativos' + body: 'Quedan pocos dias para que se cierre el plazo de presentación de proyectos de inversión para los presupuestos participativos!' diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 001456f6e..ec7e4bb5f 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -159,6 +159,12 @@ namespace :admin do get :users, on: :collection end + resources :admin_notifications do + member do + post :deliver + end + end + resources :emails_download, only: :index do get :generate_csv, on: :collection end diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index 966307e10..7b7af9342 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -34,5 +34,6 @@ require_relative 'dev_seeds/legislation_processes' require_relative 'dev_seeds/newsletters' require_relative 'dev_seeds/notifications' require_relative 'dev_seeds/widgets' +require_relative 'dev_seeds/admin_notifications' log "All dev seeds created successfuly 👍" diff --git a/db/dev_seeds/admin_notifications.rb b/db/dev_seeds/admin_notifications.rb new file mode 100644 index 000000000..a07077790 --- /dev/null +++ b/db/dev_seeds/admin_notifications.rb @@ -0,0 +1,28 @@ +section "Creating Admin Notifications & Templates" do + AdminNotification.create!( + title: I18n.t('seeds.admin_notification.internal_link.title'), + body: I18n.t('seeds.admin_notification.internal_link.body'), + link: Setting['url'] + I18n.t('seeds.admin_notification.internal_link.link'), + segment_recipient: 'administrators' + ).deliver + + AdminNotification.create!( + title: I18n.t('seeds.admin_notification.external_link.title'), + body: I18n.t('seeds.admin_notification.external_link.body'), + link: I18n.t('seeds.admin_notification.external_link.link'), + segment_recipient: 'administrators' + ).deliver + + AdminNotification.create!( + title: I18n.t('seeds.admin_notification.without_link.title'), + body: I18n.t('seeds.admin_notification.without_link.body'), + segment_recipient: 'administrators' + ).deliver + + AdminNotification.create!( + title: I18n.t('seeds.admin_notification.not_sent.title'), + body: I18n.t('seeds.admin_notification.not_sent.body'), + segment_recipient: 'administrators', + sent_at: nil + ) +end diff --git a/db/migrate/20180221002503_create_admin_notifications.rb b/db/migrate/20180221002503_create_admin_notifications.rb new file mode 100644 index 000000000..041931495 --- /dev/null +++ b/db/migrate/20180221002503_create_admin_notifications.rb @@ -0,0 +1,14 @@ +class CreateAdminNotifications < ActiveRecord::Migration + def change + create_table :admin_notifications do |t| + t.string :title + t.text :body + t.string :link + t.string :segment_recipient + t.integer :recipients_count + t.date :sent_at, default: nil + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 45871b884..878c16869 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -30,6 +30,17 @@ ActiveRecord::Schema.define(version: 20180711224810) do add_index "activities", ["actionable_id", "actionable_type"], name: "index_activities_on_actionable_id_and_actionable_type", using: :btree add_index "activities", ["user_id"], name: "index_activities_on_user_id", using: :btree + create_table "admin_notifications", force: :cascade do |t| + t.string "title" + t.text "body" + t.string "link" + t.string "segment_recipient" + t.integer "recipients_count" + t.date "sent_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "administrators", force: :cascade do |t| t.integer "user_id" end diff --git a/spec/factories.rb b/spec/factories.rb index 77deec79a..beb64aa2d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1003,6 +1003,20 @@ LOREM_IPSUM sequence(:body) { |n| "Body #{n}" } end + factory :admin_notification do + title { |n| "Admin Notification title #{n}" } + body { |n| "Admin Notification body #{n}" } + link nil + segment_recipient UserSegments::SEGMENTS.sample + recipients_count nil + sent_at nil + + trait :sent do + recipients_count 1 + sent_at Time.current + end + end + factory :widget_card, class: 'Widget::Card' do sequence(:title) { |n| "Title #{n}" } sequence(:description) { |n| "Description #{n}" } diff --git a/spec/features/admin/admin_notifications_spec.rb b/spec/features/admin/admin_notifications_spec.rb new file mode 100644 index 000000000..a97170e7c --- /dev/null +++ b/spec/features/admin/admin_notifications_spec.rb @@ -0,0 +1,236 @@ +require 'rails_helper' + +feature "Admin Notifications" do + + background do + admin = create(:administrator) + login_as(admin.user) + create(:budget) + end + + context "Show" do + scenario "Valid Admin Notification" do + notification = create(:admin_notification, title: 'Notification title', + body: 'Notification body', + link: 'https://www.decide.madrid.es/vota', + segment_recipient: :all_users) + + visit admin_admin_notification_path(notification) + + expect(page).to have_content('Notification title') + expect(page).to have_content('Notification body') + expect(page).to have_content('https://www.decide.madrid.es/vota') + expect(page).to have_content('All users') + end + + scenario "Notification with invalid segment recipient" do + invalid_notification = create(:admin_notification) + invalid_notification.update_attribute(:segment_recipient, 'invalid_segment') + + visit admin_admin_notification_path(invalid_notification) + + expect(page).to have_content("Recipients user segment is invalid") + end + end + + context "Index" do + scenario "Valid Admin Notifications" do + draft = create(:admin_notification, segment_recipient: :all_users, title: 'Not yet sent') + sent = create(:admin_notification, :sent, segment_recipient: :administrators, + title: 'Sent one') + + visit admin_admin_notifications_path + + expect(page).to have_css(".admin_notification", count: 2) + + within("#admin_notification_#{draft.id}") do + expect(page).to have_content('Not yet sent') + expect(page).to have_content('All users') + expect(page).to have_content('Draft') + end + + within("#admin_notification_#{sent.id}") do + expect(page).to have_content('Sent one') + expect(page).to have_content('Administrators') + expect(page).to have_content(I18n.l(Date.current)) + end + end + + scenario "Notifications with invalid segment recipient" do + invalid_notification = create(:admin_notification) + invalid_notification.update_attribute(:segment_recipient, 'invalid_segment') + + visit admin_admin_notifications_path + + expect(page).to have_content("Recipients user segment is invalid") + end + end + + scenario "Create" do + visit admin_admin_notifications_path + click_link "New notification" + + fill_in_admin_notification_form(segment_recipient: 'Proposal authors', + title: 'This is a title', + body: 'This is a body', + link: 'http://www.dummylink.dev') + + click_button "Create Admin notification" + + expect(page).to have_content "Notification created successfully" + expect(page).to have_content "Proposal authors" + expect(page).to have_content "This is a title" + expect(page).to have_content "This is a body" + expect(page).to have_content "http://www.dummylink.dev" + end + + context "Update" do + scenario "A draft notification can be updated" do + notification = create(:admin_notification) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + click_link "Edit" + end + + + fill_in_admin_notification_form(segment_recipient: 'All users', + title: 'Other title', + body: 'Other body', + link: '') + + click_button "Update Admin notification" + + expect(page).to have_content "Notification updated successfully" + expect(page).to have_content "All users" + expect(page).to have_content "Other title" + expect(page).to have_content "Other body" + expect(page).not_to have_content "http://www.dummylink.dev" + end + + scenario "Sent notification can not be updated" do + notification = create(:admin_notification, :sent) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + expect(page).not_to have_link("Edit") + end + end + end + + context "Destroy" do + scenario "A draft notification can be destroyed" do + notification = create(:admin_notification) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + click_link "Delete" + end + + expect(page).to have_content "Notification deleted successfully" + expect(page).to have_css(".notification", count: 0) + end + + scenario "Sent notification can not be destroyed" do + notification = create(:admin_notification, :sent) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + expect(page).not_to have_link("Delete") + end + end + end + + context "Visualize" do + scenario "A draft notification can be previewed" do + notification = create(:admin_notification, segment_recipient: :administrators) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + click_link "Preview" + end + + expect(page).to have_content "This is how the users will see the notification:" + expect(page).to have_content "Administrators (1 users will be notified)" + end + + scenario "A sent notification can be viewed" do + notification = create(:admin_notification, :sent, recipients_count: 7, + segment_recipient: :administrators) + + visit admin_admin_notifications_path + within("#admin_notification_#{notification.id}") do + click_link "View" + end + + expect(page).to have_content "This is how the users see the notification:" + expect(page).to have_content "Administrators (7 users got notified)" + end + end + + scenario 'Errors on create' do + visit new_admin_admin_notification_path + + click_button "Create Admin notification" + + expect(page).to have_content error_message + end + + scenario "Errors on update" do + notification = create(:admin_notification) + visit edit_admin_admin_notification_path(notification) + + fill_in :admin_notification_title, with: '' + click_button "Update Admin notification" + + expect(page).to have_content error_message + end + + context "Send notification", :js do + scenario "A draft Admin notification can be sent", :js do + 2.times { create(:user) } + notification = create(:admin_notification, segment_recipient: :all_users) + total_users = notification.list_of_recipients.count + confirm_message = "Are you sure you want to send this notification to #{total_users} users?" + + visit admin_admin_notification_path(notification) + + accept_confirm { click_link "Send" } + + expect(page).to have_content "Notification sent successfully" + + User.all.each do |user| + expect(user.notifications.count).to eq(1) + end + end + + scenario "A sent Admin notification can not be sent", :js do + notification = create(:admin_notification, :sent) + + visit admin_admin_notification_path(notification) + + expect(page).not_to have_link("Send") + end + + scenario "Admin notification with invalid segment recipient cannot be sent", :js do + invalid_notification = create(:admin_notification) + invalid_notification.update_attribute(:segment_recipient, 'invalid_segment') + visit admin_admin_notification_path(invalid_notification) + + expect(page).not_to have_link("Send") + end + end + + scenario "Select list of users to send notification" do + UserSegments::SEGMENTS.each do |user_segment| + segment_recipient = I18n.t("admin.segment_recipient.#{user_segment}") + + visit new_admin_admin_notification_path + + fill_in_admin_notification_form(segment_recipient: segment_recipient) + click_button "Create Admin notification" + + expect(page).to have_content(I18n.t("admin.segment_recipient.#{user_segment}")) + end + end +end diff --git a/spec/features/notifications_spec.rb b/spec/features/notifications_spec.rb index 58aebb472..f8e77ac0d 100644 --- a/spec/features/notifications_spec.rb +++ b/spec/features/notifications_spec.rb @@ -128,4 +128,50 @@ feature "Notifications" do expect(page).to_not have_css("#notifications") end + context "Admin Notifications" do + let(:admin_notification) do + create(:admin_notification, title: 'Notification title', + body: 'Notification body', + link: 'https://www.external.link.dev/', + segment_recipient: 'all_users') + end + + let!(:notification) do + create(:notification, user: user, notifiable: admin_notification) + end + + before do + login_as user + end + + scenario "With external link" do + visit notifications_path + expect(page).to have_content('Notification title') + expect(page).to have_content('Notification body') + + first("#notification_#{notification.id} a").click + expect(page.current_url).to eq('https://www.external.link.dev/') + end + + scenario "With internal link" do + admin_notification.update_attributes(link: '/stats') + + visit notifications_path + expect(page).to have_content('Notification title') + expect(page).to have_content('Notification body') + + first("#notification_#{notification.id} a").click + expect(page).to have_current_path('/stats') + end + + scenario "Without a link" do + admin_notification.update_attributes(link: '/stats') + + visit notifications_path + expect(page).to have_content('Notification title') + expect(page).to have_content('Notification body') + expect(page).not_to have_link(notification_path(notification), visible: false) + end + end + end diff --git a/spec/models/admin_notification_spec.rb b/spec/models/admin_notification_spec.rb new file mode 100644 index 000000000..eeb974e83 --- /dev/null +++ b/spec/models/admin_notification_spec.rb @@ -0,0 +1,91 @@ +require 'rails_helper' + +describe AdminNotification do + let(:admin_notification) { build(:admin_notification) } + + it "is valid" do + expect(admin_notification).to be_valid + end + + it 'is not valid without a title' do + admin_notification.title = nil + expect(admin_notification).not_to be_valid + end + + it 'is not valid without a body' do + admin_notification.body = nil + expect(admin_notification).not_to be_valid + end + + it 'is not valid without a segment_recipient' do + admin_notification.segment_recipient = nil + expect(admin_notification).not_to be_valid + end + + describe '#complete_link_url' do + it 'does not change link if there is no value' do + expect(admin_notification.link).to be_nil + end + + it 'fixes a link without http://' do + admin_notification.link = 'lol.consul.dev' + + expect(admin_notification).to be_valid + expect(admin_notification.link).to eq('http://lol.consul.dev') + end + + it 'fixes a link with wwww. but without http://' do + admin_notification.link = 'www.lol.consul.dev' + + expect(admin_notification).to be_valid + expect(admin_notification.link).to eq('http://www.lol.consul.dev') + end + + it 'does not modify a link with http://' do + admin_notification.link = 'http://lol.consul.dev' + + expect(admin_notification).to be_valid + expect(admin_notification.link).to eq('http://lol.consul.dev') + end + + it 'does not modify a link with https://' do + admin_notification.link = 'https://lol.consul.dev' + + expect(admin_notification).to be_valid + expect(admin_notification.link).to eq('https://lol.consul.dev') + end + + it 'does not modify a link with http://wwww.' do + admin_notification.link = 'http://www.lol.consul.dev' + + expect(admin_notification).to be_valid + expect(admin_notification.link).to eq('http://www.lol.consul.dev') + end + end + + describe '#valid_segment_recipient?' do + it 'is false when segment_recipient value is invalid' do + admin_notification.update(segment_recipient: 'invalid_segment_name') + error = 'The user recipients segment is invalid' + + expect(admin_notification).not_to be_valid + expect(admin_notification.errors.messages[:segment_recipient]).to include(error) + end + end + + describe '#list_of_recipients' do + let(:erased_user) { create(:user, username: 'erased_user') } + + before do + 2.times { create(:user) } + erased_user.erase + admin_notification.update(segment_recipient: 'all_users') + end + + it 'returns list of all active users' do + expect(admin_notification.list_of_recipients.count).to eq(2) + expect(admin_notification.list_of_recipients).not_to include(erased_user) + end + end + +end diff --git a/spec/support/common_actions/notifications.rb b/spec/support/common_actions/notifications.rb index 4ae98c3d0..e387ce1fd 100644 --- a/spec/support/common_actions/notifications.rb +++ b/spec/support/common_actions/notifications.rb @@ -53,4 +53,11 @@ module Notifications field_check_message = 'Please check the marked fields to know how to correct them:' /\d errors? prevented this #{resource_model} from being saved. #{field_check_message}/ end + + def fill_in_admin_notification_form(options = {}) + select (options[:segment_recipient] || 'All users'), from: :admin_notification_segment_recipient + fill_in :admin_notification_title, with: (options[:title] || 'This is the notification title') + fill_in :admin_notification_body, with: (options[:body] || 'This is the notification body') + fill_in :admin_notification_link, with: (options[:link] || 'https://www.decide.madrid.es/vota') + end end