From 85408a14d3267c1233a67cde5a0d5c1b33f88044 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Tue, 1 Nov 2016 11:18:27 +0800 Subject: [PATCH] Initial notification centre using mailboxer --- .codeclimate.yml | 2 + Gemfile | 1 + Gemfile.lock | 10 ++ app/assets/images/.DS_Store | Bin 16388 -> 0 bytes app/assets/images/topright_sprite.png | Bin 1646 -> 3445 bytes app/assets/images/user_sprite.png | Bin 1936 -> 2706 bytes app/assets/stylesheets/application.scss.erb | 6 +- app/assets/stylesheets/apps.css.erb | 8 ++ app/assets/stylesheets/clean.css.erb | 14 ++- app/assets/stylesheets/mobile.scss.erb | 28 ++++- app/assets/stylesheets/notifications.scss.erb | 71 +++++++++++++ app/controllers/access_controller.rb | 16 +-- app/controllers/notifications_controller.rb | 97 ++++++++++++++++++ app/controllers/users/sessions_controller.rb | 29 ++++-- app/controllers/users_controller.rb | 7 +- app/helpers/application_helper.rb | 16 +++ app/mailers/application_mailer.rb | 4 + app/models/user.rb | 17 +++ app/views/layouts/_account.html.erb | 8 +- app/views/layouts/_mobilemenu.html.erb | 12 ++- app/views/layouts/_upperelements.html.erb | 11 ++ app/views/layouts/application.html.erb | 6 +- app/views/layouts/doorkeeper.html.erb | 1 + .../message_mailer/new_message_email.html.erb | 20 ++++ .../message_mailer/new_message_email.text.erb | 10 ++ .../reply_message_email.html.erb | 20 ++++ .../reply_message_email.text.erb | 10 ++ .../new_notification_email.html.erb | 10 ++ .../new_notification_email.text.erb | 2 + .../map_mailer/access_request_email.html.erb | 27 ++--- .../map_mailer/access_request_email.text.erb | 2 +- .../map_mailer/invite_to_edit_email.html.erb | 29 ++---- .../map_mailer/invite_to_edit_email.text.erb | 2 +- app/views/notifications/_header.html.erb | 18 ++++ app/views/notifications/index.html.erb | 33 ++++++ app/views/notifications/mark_read.js.erb | 6 ++ app/views/notifications/mark_unread.js.erb | 6 ++ app/views/notifications/show.html.erb | 15 +++ app/views/shared/_back_to_mapping.html.erb | 3 + .../shared/_mailer_unsubscribe_link.html.erb | 3 + .../shared/_mailer_unsubscribe_link.text.erb | 5 + app/views/users/edit.html.erb | 38 ++++--- config/application.rb | 11 +- config/environments/development.rb | 14 +-- config/initializers/mailboxer.rb | 21 ++++ config/routes.rb | 35 +++++-- ...31231_create_mailboxer.mailboxer_engine.rb | 65 ++++++++++++ ...dd_conversation_optout.mailboxer_engine.rb | 15 +++ ...33_add_missing_indices.mailboxer_engine.rb | 20 ++++ ..._to_mailboxer_receipts.mailboxer_engine.rb | 8 ++ ...61125175229_add_emails_allowed_to_users.rb | 5 + db/schema.rb | 63 +++++++++++- doc/metamaps-qa-steps.md | 7 ++ 53 files changed, 778 insertions(+), 109 deletions(-) delete mode 100644 app/assets/images/.DS_Store mode change 100755 => 100644 app/assets/images/user_sprite.png create mode 100644 app/assets/stylesheets/notifications.scss.erb create mode 100644 app/controllers/notifications_controller.rb create mode 100644 app/views/mailboxer/message_mailer/new_message_email.html.erb create mode 100644 app/views/mailboxer/message_mailer/new_message_email.text.erb create mode 100644 app/views/mailboxer/message_mailer/reply_message_email.html.erb create mode 100644 app/views/mailboxer/message_mailer/reply_message_email.text.erb create mode 100644 app/views/mailboxer/notification_mailer/new_notification_email.html.erb create mode 100644 app/views/mailboxer/notification_mailer/new_notification_email.text.erb create mode 100644 app/views/notifications/_header.html.erb create mode 100644 app/views/notifications/index.html.erb create mode 100644 app/views/notifications/mark_read.js.erb create mode 100644 app/views/notifications/mark_unread.js.erb create mode 100644 app/views/notifications/show.html.erb create mode 100644 app/views/shared/_back_to_mapping.html.erb create mode 100644 app/views/shared/_mailer_unsubscribe_link.html.erb create mode 100644 app/views/shared/_mailer_unsubscribe_link.text.erb create mode 100644 config/initializers/mailboxer.rb create mode 100644 db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb create mode 100644 db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb create mode 100644 db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb create mode 100644 db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb create mode 100644 db/migrate/20161125175229_add_emails_allowed_to_users.rb diff --git a/.codeclimate.yml b/.codeclimate.yml index 719b2807..a187069d 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -20,6 +20,8 @@ engines: enabled: true rubocop: enabled: true + exclude_fingerprints: + - 74f18007b920e8d81148d2f6a2756534 ratings: paths: - 'Gemfile.lock' diff --git a/Gemfile b/Gemfile index 9fd59b62..0d8e8d7a 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem 'exception_notification' gem 'httparty' gem 'json' gem 'kaminari' +gem 'mailboxer' gem 'paperclip' gem 'pg' gem 'pundit' diff --git a/Gemfile.lock b/Gemfile.lock index 92215068..d104cb51 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,6 +65,12 @@ GEM brakeman (3.4.0) builder (3.2.2) byebug (9.0.5) + carrierwave (0.11.2) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + json (>= 1.7) + mime-types (>= 1.16) + mimemagic (>= 0.3.0) climate_control (0.0.3) activesupport (>= 3.0) cocaine (0.5.8) @@ -125,6 +131,9 @@ GEM nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) + mailboxer (0.14.0) + carrierwave (>= 0.5.8) + rails (>= 4.2.0) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) @@ -284,6 +293,7 @@ DEPENDENCIES json json-schema kaminari + mailboxer paperclip pg pry-byebug diff --git a/app/assets/images/.DS_Store b/app/assets/images/.DS_Store deleted file mode 100644 index 2bbe5f6a30a17587916373a8968facc58339b0db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16388 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 diff --git a/app/assets/images/topright_sprite.png b/app/assets/images/topright_sprite.png index 163dd6f7330b3323bf0e6d498a0c270a28cafc5c..4c969887dd42f30ac81eab16de5969d56c4dd85e 100644 GIT binary patch literal 3445 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 X&6~pKtb@=s00000NkvXXu0mjfF#c`> literal 1646 zcmV-!29f!RP)B&0pP<309a}OunIn42_+1B$_$qpbd%>w2k)i-B~J#( z`n%+SfacYW_m`B{0l+aJb^#z48+?-fuL8h7(tl@Z`WAp~iU@_E-{0T&lFbg_^6y;; z5R^U=%BfgB9!q7$chdiaz@y*Z-hPqC{*(SIhyN7-NVOnTL%&ahPe8Db0aOVM@N-?) zEd>C&d~YTFr(Cpex<-t{xqPonX%Z?5_^#3wFD_aga&?}S@9Nisk1pR^PYnQA{Xg0! zTfYB?(g&%l=#bN)ng|7eCY*YpQ(9$&IxvF=0K^ag?(XiIh;&lrpnbHDK9em!_nY(| z5*c8hkpcEG3}8~vSY<>6zju|e7%-rF69AMJ5R>J$o12@@zLPFL{!6L?&H(~AM+o{M zXKTJ606{zCv>6q%R{>OSp#Kd5L%wcLqQun#UZf040YPZ+Efm`8Io&46Pz0(jWgU6l zS`Ri{Qyyy(2mlH^zbY9!_&hj#u6W5@8UQIE$Ps)B0=lX(xR!Z&@fCfJrWUOJ)&vd+ zKB*2i11Y0y-((B`#DE}Y@aa7m(m`8r%(YASseZwBNHh)sXm$Vy0YQo26Jfg2#a*du zVKv|qeYUCrQ^;8Wzy|~+gHP!_L=P|kMgqYY!6)PHL+krc1L{`*SUPkt=pdAr}wXO%~)^jEScnqwqRD`|&3PB41#MuErZE8#gfFV7AAW)k@JDIGB*?6SfDlRf~HeF7E#P;l{cXSwQ65dew_ zHXdzYuWq1iBZyFaJIZe&a&N}u6pE(=0Bj%o?guyku(64`QGX1>0EntiI@?a5t1awk z>a=5{ngI~mHr6zNw-|IVz?uOt0{~zZ5V;E|EZe{r|341js9i%d=wy^Hl!ytx2Q*ay zOO84l8yg!N8ylN6Aw+$3b+wbemw)r~0WQt1udiF_2Xvm1%VU5BgAXh~jxXi0PQGrW zS3FNb=yNpJLA3H^O^#KH1t8dObuy#{1O|YB0q_cd*|H?$dP?;$Alv5w=?sAJ0Ptai z0W38DG>*@&0R+-}$_$qpbd%>w2gm0FC}b6VJ^;|Xy7B&!^14u#8vwc3;P`w1buxZF zz_)2=n<7Gixu}*=rS2-=`<3|L8k#aC|<1Be!IEK0wCcTzYf;9uNfj=d|zW0RQ5Q z@57_*yFCj4_<*2f@F~5A=m7@6NFW#^_(bIOr6(C%xHb`K&;q93!TIfony9#Ft~2Cb zLz%Xr_m^RDwYG&ts{vE%xO9q|8USKS09f?E2FpN|8breRHzok69>Gp;E)Aq6u{cLN z1prW**2_aJMRXw=L2m-#?;5ng8#{uz+rA8dfR*Y|nKY5(nx++;dow1dP&_37(0uK) z9}o+s(Zw7CAgXcG*~&3jTbQ(v50j6N0g%)-?qkH5g8|kIfEfUQQ~@!&fI~fwe!=Ff s!^H{R-zxF9BNiYcXrh^PTzo~Ef^P(DV) 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 delta 1921 zcmV-{2Y&dH6_5{*B!3BTNLh0L01FcU01FcV0GgZ_0000PbVXQnQ*UN;cVTj606}DL zVr3vnZDD6+Qe|Oed2z{QJOBU(9Z5t%RCwC#T|I6iHxwQVDpEzdfpr7fC$Iz`z}_;| zj!&?1<1UGebgl$!`p73(r49^_Zy?=3?zRX52}l{hdC&f&P=7Rs!+#+wK7f!%qv1z> zeDCp*L()etFE7O*ORG3=$pmBq4qV!GH*Rijy7W;|n%~{sm5Tc(^f7I?&(DwO{?xW$ z=jZ2J{D+5!QgMHauetwPL#cFZoFw?!1T^>Nl!kjOTN3f`1Fr#dXR^aR(pQ$w!0#3n!PIpa%h)h^j7%n!))@9q%_u68xk5ax|MjCLj}# z3CILw0x|)IDm!BV8s+f_i>H%jsRuYLG|JoJ`wVbw5U%tbpGf=}Sl zRHq@IXQ*oh|FH__6Rh!V7E&~h24NChhzpCd^DuTC!c<@qPWUFKV$uzku*n}N-9q&i z4bSlEtuL56^c81(%pIC{gEhkH^eC~rLSeq6^p(>0Ad|bWEl4tD&iFc?U_2v)cY@Qxej@UT2p9zL1X5gX$O#w<>w=@Vrff* zzCdt{R`{|n@LC^$5#m@NWQsw|XTeE*m4cdJ1Ym?X76`G%4>lo!1=b&7Z+{8>@}E*r z6KfRdgdcK7h;8#8>bOzKNwK?LXA*HL!3jUgt1;3B&h(zQ($L?mqCao06HThT;(zm2 z8v1*Sui9GEj!^lo!r86SLMbZWRXDp^{NMzjuNT3Wp<)0-$Ed^)76^T7lHUtt0x|)a zfJ{IpAQO-Y$OL2pG69)@gOs}S19(!g9~kSIhQvL?F?|$#8WQ(RHJ~4X>|@Lunn)CU z%o`dJa9V`j2gG@uTC60Ag0EUhQh!GPGc^cQh&A%E*-V)Xzm9<4inlp5wwZ+o_yT#1 zkf*66!`ICTZdPeAdzFJ-F7T{s#g~aB!EYH9Vv3AxHbCEdRz4ZN$*k}NT3{fxj1GL{ z*lNk~O#`7mGUSY}HNU$JT>0quEz3g8k@=P4{uR_UbmgPs+lIo&0N5OJwSPvs!gupX zm5+w+G8X8d5d0R1x6O8imgyAnWs6UN4R4$E!f$&N;*mnz%_@!5TWDYLDYW&*_kB0G z&%MJz@bi3tCVmbr>qx)#$}_(m&L)rv$OL2pG69)@Oh6_e6OakW1RSJz%?Aj3i_4fh z%IiefTU@&CDEHYl)+=_;_J16vJbzemw&>91{9zvx=oM+^0Hfl!4O)7$7nfyea*_m| z;rV&~t=D#XtpDHQo17$pXSVpdt+OT?z&7@XR2r6>ZIgFt@C(Tbgt&?~J_xZVfC|nm zUN{t5JnphzI3B4-1&@y3YEa{`^tGvz;ev~Ea2K{`382-2;n$rdpnubVQgSl4rd}R? zqEzH(Bk%A{y*&Jc5q=W^8(}uBw!9%Oc&brLQv#gv5f?P#>j)6vvAKG3(e^qPi=^J+ zJN}269ekTbQnI)nQzLg-uDwAt{FWxlYbwC10n9hbOM>6hI5ANMir;Xdeis~MFai|8 z&Oq^-9emfLwseu;T7U4uCpAPhIkroWR%*njN_*q$d_X1j<2}p4QdcU#v!#|)Qa|3a zG^2O;DlO3wFw=Nc(_rM-^1SAYQknQ3`jEfnNX00000NkvXX Hu0mjfD); + background-image: url(<%= asset_path('user_sprite.png') %>); } .accountSettings .accountIcon { background-position: 0 0; @@ -3076,3 +3076,7 @@ script.data-gratipay-username { display: inline; float: left; } + +.inline { + display: inline-block; +} diff --git a/app/assets/stylesheets/apps.css.erb b/app/assets/stylesheets/apps.css.erb index e6d75dd7..a88199af 100644 --- a/app/assets/stylesheets/apps.css.erb +++ b/app/assets/stylesheets/apps.css.erb @@ -129,3 +129,11 @@ box-sizing: border-box; border-radius: 2px; } + +.back-to-mapping { + margin: 1em; + width: auto; + max-width: 100%; + box-sizing: border-box; +} + diff --git a/app/assets/stylesheets/clean.css.erb b/app/assets/stylesheets/clean.css.erb index e4da394b..b25816f0 100644 --- a/app/assets/stylesheets/clean.css.erb +++ b/app/assets/stylesheets/clean.css.erb @@ -210,7 +210,10 @@ } .addMap { background-position: -96px 0; - margin-right:10px; +} +.notificationsIcon { + background-position: -128px 0; + margin-right: 10px; // make it look more natural next to the account menu icon } .importDialog:hover { background-position: 0 -32px; @@ -758,7 +761,7 @@ } .exploreMapsCenter .authedApps .exploreMapsIcon { - background-image: url(<%= asset_data_uri('user_sprite.png') %>); + background-image: url(<%= asset_path('user_sprite.png') %>); background-position: 0 -32px; } .exploreMapsCenter .myMaps .exploreMapsIcon { @@ -781,6 +784,10 @@ background-image: url(<%= asset_path 'exploremaps_sprite.png' %>); background-position: -96px 0; } +.exploreMapsCenter .notificationsLink .exploreMapsIcon { + background-image: url(<%= asset_path 'user_sprite.png' %>); + background-position: 0 -128px; +} .authedApps:hover .exploreMapsIcon, .authedApps.active .exploreMapsIcon { background-position-x: -32px; } @@ -799,6 +806,9 @@ .sharedMaps:hover .exploreMapsIcon, .sharedMaps.active .exploreMapsIcon { background-position: -128px -32px; } +.notificationsLink:hover .exploreMapsIcon, .notificationsLink.active .exploreMapsIcon { + background-position-x: -32px; +} .mapsWrapper { /*overflow-y: auto; */ diff --git a/app/assets/stylesheets/mobile.scss.erb b/app/assets/stylesheets/mobile.scss.erb index 01fa5f61..e7eb9a7d 100644 --- a/app/assets/stylesheets/mobile.scss.erb +++ b/app/assets/stylesheets/mobile.scss.erb @@ -213,8 +213,17 @@ line-height: 50px; } +#mobile_header #menu_icon .unread-notifications-dot { + top: 5px; + left: 29px; + width: 12px; + height: 12px; + border: 3px solid #eee; + border-radius: 9px; +} + #mobile_menu { - display: none; + display: none; background: #EEE; position: fixed; top: 50px; @@ -222,11 +231,20 @@ padding: 10px; width: 200px; box-shadow: 3px 3px 3px rgba(0,0,0,0.23), 3px 3px 3px rgba(0,0,0,0.16); -} -#mobile_menu li { - padding: 10px; - list-style: none; + li { + padding: 10px; + list-style: none; + + &.notifications { + position: relative; + + .unread-notifications-dot { + top: 17px; + left: 0px; + } + } + } } /* diff --git a/app/assets/stylesheets/notifications.scss.erb b/app/assets/stylesheets/notifications.scss.erb new file mode 100644 index 00000000..b25bf4e8 --- /dev/null +++ b/app/assets/stylesheets/notifications.scss.erb @@ -0,0 +1,71 @@ +$unread_notifications_dot_size: 8px; +.unread-notifications-dot { + width: $unread_notifications_dot_size; + height: $unread_notifications_dot_size; + background-color: #e22; + border-radius: $unread_notifications_dot_size / 2; + position: absolute; + top: 0; + right: 0; +} + +.upperRightUI { + .notificationsIcon { + position: relative; + } +} + +.controller-notifications { + ul.notifications { + list-style: none; + } + + $menu_bar_height: 6em; + .notificationPage, + .notificationsPage { + width: auto; + max-width: 100%; + box-sizing: border-box; + margin: 1em; + margin-top: 1em + $menu_bar_height; + + & > .title { + border-bottom: 1px solid #eee; + padding-bottom: 0.25em; + margin-bottom: 0.5em; + } + + .back { + margin-top: 1em; + } + } + + + .notification { + .notification-subject { + width: 25%; + } + .notification-body { + width: 50%; + } + .notification-read-unread { + width: 10%; + } + + .notification-body, + .notification-subject, + .notification-read-unread { + display: inline-block; + vertical-align: top; + font-weight: 300; + } + + &.unread { + .notification-body, + .notification-subject, + .notification-read-unread { + font-weight: bold; + } + } + } +} diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb index c48ac418..a0271981 100644 --- a/app/controllers/access_controller.rb +++ b/app/controllers/access_controller.rb @@ -21,7 +21,8 @@ class AccessController < ApplicationController def access_request request = AccessRequest.create(user: current_user, map: @map) # what about push notification to map owner? - MapMailer.access_request_email(request, @map).deliver_later + mail = MapMailer.access_request_email(request, @map) + @map.user.notify(mail.subject, mail.body) respond_to do |format| format.json do @@ -37,7 +38,9 @@ class AccessController < ApplicationController @map.add_new_collaborators(user_ids).each do |user_id| # add_new_collaborators returns array of added users, # who we then send an email to - MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)).deliver_later + user = User.find(user_id) + mail = MapMailer.invite_to_edit_email(@map, current_user, User.find(user_id)) + user.notify(mail.subject, mail.body) end @map.remove_old_collaborators(user_ids) @@ -51,7 +54,7 @@ class AccessController < ApplicationController # GET maps/:id/approve_access/:request_id def approve_access request = AccessRequest.find(params[:request_id]) - request.approve() + request.approve respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was approved' } end @@ -60,7 +63,7 @@ class AccessController < ApplicationController # GET maps/:id/deny_access/:request_id def deny_access request = AccessRequest.find(params[:request_id]) - request.deny() + request.deny respond_to do |format| format.html { redirect_to map_path(@map), notice: 'Request was turned down' } end @@ -69,7 +72,7 @@ class AccessController < ApplicationController # POST maps/:id/approve_access/:request_id def approve_access_post request = AccessRequest.find(params[:request_id]) - request.approve() + request.approve respond_to do |format| format.json do head :ok @@ -80,7 +83,7 @@ class AccessController < ApplicationController # POST maps/:id/deny_access/:request_id def deny_access_post request = AccessRequest.find(params[:request_id]) - request.deny() + request.deny respond_to do |format| format.json do head :ok @@ -94,5 +97,4 @@ class AccessController < ApplicationController @map = Map.find(params[:id]) authorize @map end - end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb new file mode 100644 index 00000000..4759ef20 --- /dev/null +++ b/app/controllers/notifications_controller.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true +class NotificationsController < ApplicationController + before_action :set_receipts, only: [:index, :show, :mark_read, :mark_unread] + before_action :set_notification, only: [:show, :mark_read, :mark_unread] + before_action :set_receipt, only: [:show, :mark_read, :mark_unread] + + def index + @notifications = current_user.mailbox.notifications + + respond_to do |format| + format.html + format.json do + render json: @notifications.map do |notification| + receipt = @receipts.find_by(notification_id: notification.id) + notification.as_json.merge(is_read: receipt.is_read) + end + end + end + end + + def show + @receipt.update(is_read: true) + respond_to do |format| + format.html + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def mark_read + @receipt.update(is_read: true) + respond_to do |format| + format.js + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def mark_unread + @receipt.update(is_read: false) + respond_to do |format| + format.js + format.json do + render json: @notification.as_json.merge( + is_read: @receipt.is_read + ) + end + end + end + + def unsubscribe + unsubscribe_redirect_if_logged_out! + check_if_already_unsubscribed! + return if performed? # if one of these checks already redirected, we're done + + if current_user.update(emails_allowed: false) + redirect_to edit_user_path(current_user), + notice: 'You will no longer receive emails from Metamaps.' + else + flash[:alert] = 'Sorry, something went wrong. You have not been unsubscribed from emails.' + redirect_to edit_user_path(current_user) + end + end + + private + + def unsubscribe_redirect_if_logged_out! + return if current_user.present? + + flash[:notice] = 'Continue to unsubscribe from emails by logging in.' + redirect_to "#{sign_in_path}?redirect_to=#{unsubscribe_notifications_path}" + end + + def check_if_already_unsubscribed! + return if current_user.emails_allowed + + redirect_to edit_user_path(current_user), notice: 'You were already unsubscribed from emails.' + end + + def set_receipts + @receipts = current_user.mailboxer_notification_receipts + end + + def set_notification + @notification = current_user.mailbox.notifications.find_by(id: params[:id]) + end + + def set_receipt + @receipt = @receipts.find_by(notification_id: params[:id]) + end +end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index fed670ae..1c9c0a1e 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,14 +1,25 @@ -class Users::SessionsController < Devise::SessionsController - protected +# frozen_string_literal: true +module Users + class SessionsController < Devise::SessionsController + after_action :store_location, only: [:new] - def after_sign_in_path_for(resource) - stored = stored_location_for(User) - return stored if stored + protected - if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url) - super - else - request.referer || root_path + def after_sign_in_path_for(resource) + stored = stored_location_for(User) + return stored if stored + + if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url) + super + else + request.referer || root_path + end + end + + private + + def store_location + store_location_for(User, params[:redirect_to]) if params[:redirect_to] end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index a9fff9de..6b771d6f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -13,13 +13,12 @@ class UsersController < ApplicationController # GET /users/:id/edit def edit - @user = current_user - respond_with(@user) + @user = User.find(current_user.id) end # PUT /users/:id def update - @user = current_user + @user = User.find(current_user.id) if user_params[:password] == '' && user_params[:password_confirmation] == '' # not trying to change the password @@ -96,6 +95,6 @@ class UsersController < ApplicationController private def user_params - params.require(:user).permit(:name, :email, :image, :password, :password_confirmation) + params.require(:user).permit(:name, :email, :image, :password, :password_confirmation, :emails_allowed) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3221aa34..cc121cbe 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -37,4 +37,20 @@ module ApplicationHelper def invite_link "#{request.base_url}/join" + (current_user ? "?code=#{current_user.code}" : '') end + + def user_has_unread_notifications? + return @user_has_unread_notifications unless @user_has_unread_notifications.nil? + return (@user_has_unread_notifications = false) if current_user.nil? + current_user.mailboxer_notification_receipts.each do |receipt| + return (@user_has_unread_notifications = true) if receipt.is_read == false + end + @user_has_unread_notifications = false + end + + def user_unread_notification_count + return 0 if current_user.nil? + current_user.mailboxer_notification_receipts.reduce(0) do |total, receipt| + receipt.is_read ? total : total + 1 + end + end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 59a2175a..10961836 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -2,4 +2,8 @@ class ApplicationMailer < ActionMailer::Base default from: 'team@metamaps.cc' layout 'mailer' + + def deliver + raise NotImplementedError('Please use Mailboxer to send your emails.') + end end diff --git a/app/models/user.rb b/app/models/user.rb index 23ef6440..f6fcb60e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,8 @@ require 'open-uri' class User < ApplicationRecord + acts_as_messageable # mailboxer notifications + has_many :topics has_many :synapses has_many :maps @@ -108,4 +110,19 @@ class User < ApplicationRecord def settings=(val) self[:settings] = val end + + # Mailboxer hooks and helper functions + + def mailboxer_email(_message) + return email if emails_allowed + # else return nil, which sends no email + end + + def mailboxer_notifications + mailbox.notifications + end + + def mailboxer_notification_receipts + mailbox.receipts.includes(:notification).where(mailbox_type: nil) + end end diff --git a/app/views/layouts/_account.html.erb b/app/views/layouts/_account.html.erb index 748e5f1b..3d66f687 100644 --- a/app/views/layouts/_account.html.erb +++ b/app/views/layouts/_account.html.erb @@ -18,14 +18,14 @@ <%= link_to "Admin", metacodes_path %> <% end %> -
  • -
    - Share Invite -
  • <%= link_to "Apps", oauth_authorized_applications_path %>
  • +
  • +
    + Share Invite +
  • <%= link_to "Sign Out", "/logout", id: "Logout" %> diff --git a/app/views/layouts/_mobilemenu.html.erb b/app/views/layouts/_mobilemenu.html.erb index e012a808..c557f516 100644 --- a/app/views/layouts/_mobilemenu.html.erb +++ b/app/views/layouts/_mobilemenu.html.erb @@ -2,7 +2,11 @@
    <%= yield(:mobile_title) %>
    - +
      @@ -49,6 +53,12 @@
    • <%= link_to "Account", edit_user_url(current_user) %>
    • +
    • + <%= link_to "Notifications", notifications_path %> + <% if user_has_unread_notifications? %> +
      + <% end %> +
    • <%= link_to "Sign Out", "/logout", id: "Logout" %>
    • diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index 63de15fd..cc484272 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -71,6 +71,17 @@ <% end %> + <% if current_user.present? %> + <%= link_to notifications_path, target: '_blank', class: "notificationsIcon upperRightEl upperRightIcon #{user_has_unread_notifications? ? 'unread' : 'read'}" do %> +
      + Notifications +
      + <% if user_has_unread_notifications? %> +
      + <% end %> + <% end %> + <% end %> + <% if !(controller_name == "sessions" && action_name == "new") %>
      diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 231c76e3..d48ec0a1 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -64,9 +64,13 @@

      <% if devise_error_messages? %> <%= devise_error_messages! %> - <% elsif notice %> + <% end %> + <% if notice %> <%= notice %> <% end %> + <% if alert %> + <%= alert %> + <% end %>

      diff --git a/app/views/layouts/doorkeeper.html.erb b/app/views/layouts/doorkeeper.html.erb index 500a0f0e..a6a74f41 100644 --- a/app/views/layouts/doorkeeper.html.erb +++ b/app/views/layouts/doorkeeper.html.erb @@ -50,6 +50,7 @@ <% end %>

      + <%= render partial: 'shared/back_to_mapping' %>
    <% end %> diff --git a/app/views/mailboxer/message_mailer/new_message_email.html.erb b/app/views/mailboxer/message_mailer/new_message_email.html.erb new file mode 100644 index 00000000..c779e888 --- /dev/null +++ b/app/views/mailboxer/message_mailer/new_message_email.html.erb @@ -0,0 +1,20 @@ + + + + + + +

    You have a new message: <%= @subject %>

    +

    + You have received a new message: +

    +
    +

    + <%= raw @message.body %> +

    +
    +

    + Visit <%= link_to root_url, root_url %> and go to your inbox for more info. +

    + + diff --git a/app/views/mailboxer/message_mailer/new_message_email.text.erb b/app/views/mailboxer/message_mailer/new_message_email.text.erb new file mode 100644 index 00000000..228ca58a --- /dev/null +++ b/app/views/mailboxer/message_mailer/new_message_email.text.erb @@ -0,0 +1,10 @@ +You have a new message: <%= @subject %> +=============================================== + +You have received a new message: + +----------------------------------------------- +<%= @message.body.html_safe? ? @message.body : strip_tags(@message.body) %> +----------------------------------------------- + +Visit <%= root_url %> and go to your inbox for more info. diff --git a/app/views/mailboxer/message_mailer/reply_message_email.html.erb b/app/views/mailboxer/message_mailer/reply_message_email.html.erb new file mode 100644 index 00000000..fd1286c5 --- /dev/null +++ b/app/views/mailboxer/message_mailer/reply_message_email.html.erb @@ -0,0 +1,20 @@ + + + + + + +

    You have a new reply: <%= @subject %>

    +

    + You have received a new reply: +

    +
    +

    + <%= raw @message.body %> +

    +
    +

    + Visit <%= link_to root_url, root_url %> and go to your inbox for more info. +

    + + diff --git a/app/views/mailboxer/message_mailer/reply_message_email.text.erb b/app/views/mailboxer/message_mailer/reply_message_email.text.erb new file mode 100644 index 00000000..c56bfb5e --- /dev/null +++ b/app/views/mailboxer/message_mailer/reply_message_email.text.erb @@ -0,0 +1,10 @@ +You have a new reply: <%= @subject %> +=============================================== + +You have received a new reply: + +----------------------------------------------- +<%= @message.body.html_safe? ? @message.body : strip_tags(@message.body) %> +----------------------------------------------- + +Visit <%= root_url %> and go to your inbox for more info. diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.html.erb b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb new file mode 100644 index 00000000..23ee4087 --- /dev/null +++ b/app/views/mailboxer/notification_mailer/new_notification_email.html.erb @@ -0,0 +1,10 @@ + + + + + + + <% binding.pry %> + <%= raw @notification.body.parts[1].encoded %> + + diff --git a/app/views/mailboxer/notification_mailer/new_notification_email.text.erb b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb new file mode 100644 index 00000000..fa39d477 --- /dev/null +++ b/app/views/mailboxer/notification_mailer/new_notification_email.text.erb @@ -0,0 +1,2 @@ +<% body = @notification.body.parts[0].encoded %> +<%= body.html_safe? ? body : strip_tags(body) %> diff --git a/app/views/map_mailer/access_request_email.html.erb b/app/views/map_mailer/access_request_email.html.erb index ae4f4018..2e89eb6e 100644 --- a/app/views/map_mailer/access_request_email.html.erb +++ b/app/views/map_mailer/access_request_email.html.erb @@ -1,23 +1,16 @@ - - - - - - +
    + <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    -

    <%= @request.user.name %> is requesting access to collaboratively edit the following map:

    +

    <%= @map.name %>

    -

    <%= @map.name %>

    +

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> +

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    -

    <%= link_to "Allow", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> -

    <%= link_to "Decline", deny_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #DB5D5D;" %>

    + <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> +

    Make sense with Metamaps

    -

    Make sense with Metamaps

    -
    - - + <%= render partial: 'shared/mailer_unsubscribe_link' %> +
    diff --git a/app/views/map_mailer/access_request_email.text.erb b/app/views/map_mailer/access_request_email.text.erb index ad698d9e..25ccb2bd 100644 --- a/app/views/map_mailer/access_request_email.text.erb +++ b/app/views/map_mailer/access_request_email.text.erb @@ -7,4 +7,4 @@ Decline [<%= deny_access_map_url(id: @map.id, request_id: @request.id) %>] Make sense with Metamaps - +<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/map_mailer/invite_to_edit_email.html.erb b/app/views/map_mailer/invite_to_edit_email.html.erb index 73067c48..cd2b7b2e 100644 --- a/app/views/map_mailer/invite_to_edit_email.html.erb +++ b/app/views/map_mailer/invite_to_edit_email.html.erb @@ -1,22 +1,15 @@ - - - - - - +
    + <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> -
    - <% button_style = "background-color:#4fc059;border-radius:2px;color:white;display:inline-block;font-family:Roboto,Arial,Helvetica,sans-serif;font-size:12px;font-weight:bold;min-height:29px;line-height:29px;min-width:54px;outline:0px;padding:0 8px;text-align:center;text-decoration:none" %> +

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    +

    <%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    + <% if @map.desc %> +

    <%= @map.desc %>

    + <% end %> -

    <%= @inviter.name %> has invited you to collaboratively edit the following map:

    -

    <%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %>

    - <% if @map.desc %> -

    <%= @map.desc %>

    - <% end %> + <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> - <%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %> +

    Make sense with Metamaps

    -

    Make sense with Metamaps

    -
    - - + <%= render partial: 'shared/mailer_unsubscribe_link' %> +
    diff --git a/app/views/map_mailer/invite_to_edit_email.text.erb b/app/views/map_mailer/invite_to_edit_email.text.erb index 80eecfed..7d3bf397 100644 --- a/app/views/map_mailer/invite_to_edit_email.text.erb +++ b/app/views/map_mailer/invite_to_edit_email.text.erb @@ -4,4 +4,4 @@ Make sense with Metamaps - +<%= render partial: 'shared/mailer_unsubscribe_link' %> diff --git a/app/views/notifications/_header.html.erb b/app/views/notifications/_header.html.erb new file mode 100644 index 00000000..f93f46a6 --- /dev/null +++ b/app/views/notifications/_header.html.erb @@ -0,0 +1,18 @@ +
    + +
    +

    + <% if devise_error_messages? %> + <%= devise_error_messages! %> + <% elsif notice %> + <%= notice %> + <% end %> +

    diff --git a/app/views/notifications/index.html.erb b/app/views/notifications/index.html.erb new file mode 100644 index 00000000..59ac08a3 --- /dev/null +++ b/app/views/notifications/index.html.erb @@ -0,0 +1,33 @@ +<% content_for :title, 'Notifications | Metamaps' %> +<% content_for :mobile_title, 'Notifications' %> + +
    +
    +

    My Notifications

    +
      + <% @notifications.each do |notification| %> + <% receipt = @receipts.find_by(notification_id: notification.id) %> +
    • + <%= link_to notification_path(notification.id) do %> +
      + <%= notification.subject %> +
      +
      + <%= notification.body.truncate(70) %> +
      + <% end %> +
      + <% if receipt.is_read? %> + <%= link_to 'mark as unread', mark_unread_notification_path(notification.id), remote: true, method: :put %> + <% else %> + <%= link_to 'mark as read', mark_read_notification_path(notification.id), remote: true, method: :put %> + <% end %> +
      +
    • + <% end %> +
    +
    + <%= render partial: 'shared/back_to_mapping' %> +
    + +<%= render partial: 'notifications/header' %> diff --git a/app/views/notifications/mark_read.js.erb b/app/views/notifications/mark_read.js.erb new file mode 100644 index 00000000..5b28f453 --- /dev/null +++ b/app/views/notifications/mark_read.js.erb @@ -0,0 +1,6 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as unread') + .attr('href', '<%= mark_unread_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('unread') + .addClass('read') diff --git a/app/views/notifications/mark_unread.js.erb b/app/views/notifications/mark_unread.js.erb new file mode 100644 index 00000000..46744388 --- /dev/null +++ b/app/views/notifications/mark_unread.js.erb @@ -0,0 +1,6 @@ +$('#notification-<%= @notification.id %> .notification-read-unread > a') + .text('mark as read') + .attr('href', '<%= mark_read_notification_path(@notification.id) %>') +$('#notification-<%= @notification.id %>') + .removeClass('read') + .addClass('unread') diff --git a/app/views/notifications/show.html.erb b/app/views/notifications/show.html.erb new file mode 100644 index 00000000..be641f66 --- /dev/null +++ b/app/views/notifications/show.html.erb @@ -0,0 +1,15 @@ +<% content_for :title, 'Notifications | Metamaps' %> +<% content_for :mobile_title, 'Notifications' %> + +
    +
    +

    <%= @notification.subject %>

    + <%= @notification.body %> +
    + <%= link_to 'Back', notifications_path %> +
    +
    + <%= render partial: 'shared/back_to_mapping' %> +
    + +<%= render partial: 'notifications/header' %> diff --git a/app/views/shared/_back_to_mapping.html.erb b/app/views/shared/_back_to_mapping.html.erb new file mode 100644 index 00000000..682a71e6 --- /dev/null +++ b/app/views/shared/_back_to_mapping.html.erb @@ -0,0 +1,3 @@ + diff --git a/app/views/shared/_mailer_unsubscribe_link.html.erb b/app/views/shared/_mailer_unsubscribe_link.html.erb new file mode 100644 index 00000000..2bbe7a89 --- /dev/null +++ b/app/views/shared/_mailer_unsubscribe_link.html.erb @@ -0,0 +1,3 @@ + diff --git a/app/views/shared/_mailer_unsubscribe_link.text.erb b/app/views/shared/_mailer_unsubscribe_link.text.erb new file mode 100644 index 00000000..3a2f7d0d --- /dev/null +++ b/app/views/shared/_mailer_unsubscribe_link.text.erb @@ -0,0 +1,5 @@ + + +You can unsubscribe from all Metamaps emails by visiting the following link: + +<%= unsubscribe_notifications_url %> diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 92890a92..8427582a 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -33,23 +33,35 @@
    <%= @user.name %>
    - <%= form.label :name, "Name:", :class => "firstFieldText" %> - <%= form.text_field :name %> + <%= form.label :name, "Name:", class: 'firstFieldText' %> + <%= form.text_field :name %> +
    +
    + <%= form.label :email, "Email:", class: 'firstFieldText' %> + <%= form.email_field :email %> +
    +
    + <%= form.label :emails_allowed, class: 'firstFieldText' do %> + <%= form.check_box :emails_allowed, class: 'inline' %> + Send Metamaps notifications to my email. + <% end %>
    -
    <%= form.label :email, "Email:", :class => "firstFieldText" %> - <%= form.email_field :email %>
    Change Password
    -
    - <%= form.label :current_password, "Current Password:", :class => "firstFieldText" %> - <%= password_field_tag :current_password, params[:current_password] %> +
    + <%= form.label :current_password, "Current Password:", :class => "firstFieldText" %> + <%= password_field_tag :current_password, params[:current_password] %> +
    +
    + <%= form.label :password, "New Password:", :class => "firstFieldText" %> + <%= form.password_field :password, :autocomplete => :off%> +
    +
    + <%= form.label :password_confirmation, "Confirm New Password:", :class => "firstFieldText" %> + <%= form.password_field :password_confirmation, :autocomplete => :off%> +
    +
    Oops, don't change password
    -
    <%= form.label :password, "New Password:", :class => "firstFieldText" %> - <%= form.password_field :password, :autocomplete => :off%>
    -
    <%= form.label :password_confirmation, "Confirm New Password:", :class => "firstFieldText" %> - <%= form.password_field :password_confirmation, :autocomplete => :off%>
    -
    Oops, don't change password
    -
    <%= form.submit "Update", class: "update", onclick: "Metamaps.Account.showLoading()" %>
    diff --git a/config/application.rb b/config/application.rb index 0b98bfe8..ff5a621c 100644 --- a/config/application.rb +++ b/config/application.rb @@ -8,14 +8,15 @@ Bundler.require(*Rails.groups) module Metamaps class Application < Rails::Application - config.active_job.queue_adapter = :delayed_job - if ENV['ACTIVE_JOB_FRAMEWORK'] == 'sucker_punch' - config.active_job.queue_adapter = :sucker_punch - end - # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + # + config.active_job.queue_adapter = if ENV['ACTIVE_JOB_FRAMEWORK'] == 'sucker_punch' + :sucker_punch + else + :delayed_job + end # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths << Rails.root.join('app', 'services') diff --git a/config/environments/development.rb b/config/environments/development.rb index 38741a18..8fef2145 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -14,19 +14,11 @@ Rails.application.configure do config.consider_all_requests_local = true config.action_controller.perform_caching = false - config.action_mailer.delivery_method = :smtp - config.action_mailer.smtp_settings = { - address: ENV['SMTP_SERVER'], - port: ENV['SMTP_PORT'], - user_name: ENV['SMTP_USERNAME'], - password: ENV['SMTP_PASSWORD'], - domain: ENV['SMTP_DOMAIN'], - authentication: 'plain', - enable_starttls_auto: true, - openssl_verify_mode: 'none' + config.action_mailer.delivery_method = :file + config.action_mailer.file_settings = { + location: 'tmp/mails' } config.action_mailer.default_url_options = { host: 'localhost:3000' } - # Don't care if the mailer can't send config.action_mailer.raise_delivery_errors = true # Print deprecation notices to the Rails logger diff --git a/config/initializers/mailboxer.rb b/config/initializers/mailboxer.rb new file mode 100644 index 00000000..9e1efe66 --- /dev/null +++ b/config/initializers/mailboxer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +Mailboxer.setup do |config| + # Configures if your application uses or not email sending for Notifications and Messages + config.uses_emails = true + + # Configures the default from for emails sent for Messages and Notifications + config.default_from = 'no-reply@metamaps.cc' + + # Configures the methods needed by mailboxer + config.email_method = :mailboxer_email + config.name_method = :name + + # Configures if you use or not a search engine and which one you are using + # Supported engines: [:solr,:sphinx] + config.search_enabled = false + config.search_engine = :solr + + # Configures maximum length of the message subject and body + config.subject_max_length = 255 + config.body_max_length = 32_000 +end diff --git a/config/routes.rb b/config/routes.rb index 8ba116a1..000784f6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,12 +20,25 @@ Metamaps::Application.routes.draw do post 'events/:event', action: :events get :contains - get :request_access, to: 'access#request_access' - get 'approve_access/:request_id', to: 'access#approve_access', as: :approve_access - get 'deny_access/:request_id', to: 'access#deny_access', as: :deny_access - post :access_request, to: 'access#access_request', default: { format: :json } - post 'approve_access/:request_id', to: 'access#approve_access_post', default: { format: :json } - post 'deny_access/:request_id', to: 'access#deny_access_post', default: { format: :json } + get :request_access, + to: 'access#request_access' + get 'approve_access/:request_id', + to: 'access#approve_access', + as: :approve_access + get 'deny_access/:request_id', + to: 'access#deny_access', + as: :deny_access + + post :access_request, + to: 'access#access_request', + default: { format: :json } + post 'approve_access/:request_id', + to: 'access#approve_access_post', + default: { format: :json } + post 'deny_access/:request_id', + to: 'access#deny_access_post', + default: { format: :json } + post :access, to: 'access#access', default: { format: :json } post :star, to: 'stars#create', default: { format: :json } @@ -36,6 +49,15 @@ Metamaps::Application.routes.draw do resources :mappings, except: [:index, :new, :edit] resources :messages, only: [:show, :create, :update, :destroy] + resources :notifications, only: [:index, :show] do + collection do + get :unsubscribe + end + member do + put :mark_read + put :mark_unread + end + end resources :metacode_sets, except: [:show] @@ -109,3 +131,4 @@ Metamaps::Application.routes.draw do get 'load_url_title' end end +# rubocop:enable Rubocop/Metrics/BlockLength diff --git a/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb b/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb new file mode 100644 index 00000000..99ed59b1 --- /dev/null +++ b/db/migrate/20161101031231_create_mailboxer.mailboxer_engine.rb @@ -0,0 +1,65 @@ +# This migration comes from mailboxer_engine (originally 20110511145103) +class CreateMailboxer < ActiveRecord::Migration + def self.up + #Tables + #Conversations + create_table :mailboxer_conversations do |t| + t.column :subject, :string, :default => "" + t.column :created_at, :datetime, :null => false + t.column :updated_at, :datetime, :null => false + end + #Receipts + create_table :mailboxer_receipts do |t| + t.references :receiver, :polymorphic => true + t.column :notification_id, :integer, :null => false + t.column :is_read, :boolean, :default => false + t.column :trashed, :boolean, :default => false + t.column :deleted, :boolean, :default => false + t.column :mailbox_type, :string, :limit => 25 + t.column :created_at, :datetime, :null => false + t.column :updated_at, :datetime, :null => false + end + #Notifications and Messages + create_table :mailboxer_notifications do |t| + t.column :type, :string + t.column :body, :text + t.column :subject, :string, :default => "" + t.references :sender, :polymorphic => true + t.column :conversation_id, :integer + t.column :draft, :boolean, :default => false + t.string :notification_code, :default => nil + t.references :notified_object, :polymorphic => true + t.column :attachment, :string + t.column :updated_at, :datetime, :null => false + t.column :created_at, :datetime, :null => false + t.boolean :global, default: false + t.datetime :expires + end + + #Indexes + #Conversations + #Receipts + add_index "mailboxer_receipts","notification_id" + + #Messages + add_index "mailboxer_notifications","conversation_id" + + #Foreign keys + #Conversations + #Receipts + add_foreign_key "mailboxer_receipts", "mailboxer_notifications", :name => "receipts_on_notification_id", :column => "notification_id" + #Messages + add_foreign_key "mailboxer_notifications", "mailboxer_conversations", :name => "notifications_on_conversation_id", :column => "conversation_id" + end + + def self.down + #Tables + remove_foreign_key "mailboxer_receipts", :name => "receipts_on_notification_id" + remove_foreign_key "mailboxer_notifications", :name => "notifications_on_conversation_id" + + #Indexes + drop_table :mailboxer_receipts + drop_table :mailboxer_conversations + drop_table :mailboxer_notifications + end +end diff --git a/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb b/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb new file mode 100644 index 00000000..c4f4555a --- /dev/null +++ b/db/migrate/20161101031232_add_conversation_optout.mailboxer_engine.rb @@ -0,0 +1,15 @@ +# This migration comes from mailboxer_engine (originally 20131206080416) +class AddConversationOptout < ActiveRecord::Migration + def self.up + create_table :mailboxer_conversation_opt_outs do |t| + t.references :unsubscriber, :polymorphic => true + t.references :conversation + end + add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", :name => "mb_opt_outs_on_conversations_id", :column => "conversation_id" + end + + def self.down + remove_foreign_key "mailboxer_conversation_opt_outs", :name => "mb_opt_outs_on_conversations_id" + drop_table :mailboxer_conversation_opt_outs + end +end diff --git a/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb b/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb new file mode 100644 index 00000000..fde96718 --- /dev/null +++ b/db/migrate/20161101031233_add_missing_indices.mailboxer_engine.rb @@ -0,0 +1,20 @@ +# This migration comes from mailboxer_engine (originally 20131206080417) +class AddMissingIndices < ActiveRecord::Migration + def change + # We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63 + # characters limitation. + add_index :mailboxer_conversation_opt_outs, [:unsubscriber_id, :unsubscriber_type], + name: 'index_mailboxer_conversation_opt_outs_on_unsubscriber_id_type' + add_index :mailboxer_conversation_opt_outs, :conversation_id + + add_index :mailboxer_notifications, :type + add_index :mailboxer_notifications, [:sender_id, :sender_type] + + # We'll explicitly specify its name, as the auto-generated name is too long and exceeds 63 + # characters limitation. + add_index :mailboxer_notifications, [:notified_object_id, :notified_object_type], + name: 'index_mailboxer_notifications_on_notified_object_id_and_type' + + add_index :mailboxer_receipts, [:receiver_id, :receiver_type] + end +end diff --git a/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb b/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb new file mode 100644 index 00000000..a820919e --- /dev/null +++ b/db/migrate/20161101031234_add_delivery_tracking_info_to_mailboxer_receipts.mailboxer_engine.rb @@ -0,0 +1,8 @@ +# This migration comes from mailboxer_engine (originally 20151103080417) +class AddDeliveryTrackingInfoToMailboxerReceipts < ActiveRecord::Migration + def change + add_column :mailboxer_receipts, :is_delivered, :boolean, default: false + add_column :mailboxer_receipts, :delivery_method, :string + add_column :mailboxer_receipts, :message_id, :string + end +end diff --git a/db/migrate/20161125175229_add_emails_allowed_to_users.rb b/db/migrate/20161125175229_add_emails_allowed_to_users.rb new file mode 100644 index 00000000..609e4309 --- /dev/null +++ b/db/migrate/20161125175229_add_emails_allowed_to_users.rb @@ -0,0 +1,5 @@ +class AddEmailsAllowedToUsers < ActiveRecord::Migration[5.0] + def change + add_column :users, :emails_allowed, :boolean, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index d16d4fb9..5839929c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161105160340) do +ActiveRecord::Schema.define(version: 20161125175229) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -63,6 +63,59 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.index ["metacode_set_id"], name: "index_in_metacode_sets_on_metacode_set_id", using: :btree end + create_table "mailboxer_conversation_opt_outs", force: :cascade do |t| + t.string "unsubscriber_type" + t.integer "unsubscriber_id" + t.integer "conversation_id" + t.index ["conversation_id"], name: "index_mailboxer_conversation_opt_outs_on_conversation_id", using: :btree + t.index ["unsubscriber_id", "unsubscriber_type"], name: "index_mailboxer_conversation_opt_outs_on_unsubscriber_id_type", using: :btree + end + + create_table "mailboxer_conversations", force: :cascade do |t| + t.string "subject", default: "" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "mailboxer_notifications", force: :cascade do |t| + t.string "type" + t.text "body" + t.string "subject", default: "" + t.string "sender_type" + t.integer "sender_id" + t.integer "conversation_id" + t.boolean "draft", default: false + t.string "notification_code" + t.string "notified_object_type" + t.integer "notified_object_id" + t.string "attachment" + t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.boolean "global", default: false + t.datetime "expires" + t.index ["conversation_id"], name: "index_mailboxer_notifications_on_conversation_id", using: :btree + t.index ["notified_object_id", "notified_object_type"], name: "index_mailboxer_notifications_on_notified_object_id_and_type", using: :btree + t.index ["sender_id", "sender_type"], name: "index_mailboxer_notifications_on_sender_id_and_sender_type", using: :btree + t.index ["type"], name: "index_mailboxer_notifications_on_type", using: :btree + end + + create_table "mailboxer_receipts", force: :cascade do |t| + t.string "receiver_type" + t.integer "receiver_id" + t.integer "notification_id", null: false + t.boolean "is_read", default: false + t.boolean "trashed", default: false + t.boolean "deleted", default: false + t.string "mailbox_type", limit: 25 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "is_delivered", default: false + t.string "delivery_method" + t.string "message_id" + t.index ["notification_id"], name: "index_mailboxer_receipts_on_notification_id", using: :btree + t.index ["receiver_id", "receiver_type"], name: "index_mailboxer_receipts_on_receiver_id_and_receiver_type", using: :btree + end + create_table "mappings", force: :cascade do |t| t.text "category" t.integer "xloc" @@ -243,8 +296,8 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.string "password_salt", limit: 255 t.string "persistence_token", limit: 255 t.string "perishable_token", limit: 255 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.string "code", limit: 8 t.string "joinedwithcode", limit: 8 t.text "settings" @@ -264,6 +317,7 @@ ActiveRecord::Schema.define(version: 20161105160340) do t.integer "image_file_size" t.datetime "image_updated_at" t.integer "generation" + t.boolean "emails_allowed", default: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree end @@ -279,5 +333,8 @@ ActiveRecord::Schema.define(version: 20161105160340) do add_foreign_key "access_requests", "maps" add_foreign_key "access_requests", "users" + add_foreign_key "mailboxer_conversation_opt_outs", "mailboxer_conversations", column: "conversation_id", name: "mb_opt_outs_on_conversations_id" + add_foreign_key "mailboxer_notifications", "mailboxer_conversations", column: "conversation_id", name: "notifications_on_conversation_id" + add_foreign_key "mailboxer_receipts", "mailboxer_notifications", column: "notification_id", name: "receipts_on_notification_id" add_foreign_key "tokens", "users" end diff --git a/doc/metamaps-qa-steps.md b/doc/metamaps-qa-steps.md index a3f136ca..7c5c8480 100644 --- a/doc/metamaps-qa-steps.md +++ b/doc/metamaps-qa-steps.md @@ -32,6 +32,13 @@ Run these tests to be reasonably sure that your code changes haven't broken anyt - Add a number of synapses to one of your maps. Reload to see if they are still there. - Rearrange one of your maps and save the layout. Reload to see if the layout is preserved. +### Unsubscribing from Notifications + + - Log out + - Visit /notifications/unsubscribe. It should redirect you to the login page. + - Log in. + - It should redirect you to the user edit page, and you should be unsubscribed. + ### Misc - Login as admin. Change metacode sets.