diff --git a/app/assets/images/view-only.png b/app/assets/images/view-only.png new file mode 100644 index 00000000..a4cc262f Binary files /dev/null and b/app/assets/images/view-only.png differ diff --git a/app/assets/stylesheets/request_access.scss.erb b/app/assets/stylesheets/request_access.scss.erb new file mode 100644 index 00000000..19ae2792 --- /dev/null +++ b/app/assets/stylesheets/request_access.scss.erb @@ -0,0 +1,98 @@ +.viewOnly { + float: left; + margin-left: 16px; + display: none; + height: 32px; + border: 1px solid #BDBDBD; + border-radius: 2px; + background-color: #424242; + color: #FFF; + font-size: 14px; + line-height: 32px; + + &.isViewOnly { + display: block; + } + + .eyeball { + background: url('<%= asset_path('view-only.png') %>') no-repeat 4px 0; + padding-left: 40px; + border-right: #747474; + padding-right: 10px; + display: inline-block; + } + + .requestNotice { + display: none; + padding: 0 8px; + } + + .requestAccess { + background-color: #a354cd; + &:hover { + background-color: #9150bc; + } + cursor: pointer; + } + + .requestPending { + background-color: #4fc059; + } + + .requestNotAccepted { + background-color: #c04f4f; + } + + &.sendRequest .requestAccess { + display: inline-block; + } + &.sentRequest .requestPending { + display: inline-block; + } + &.requestDenied .requestNotAccepted { + display: inline-block; + } +} + +.request_access { + position: absolute; + width: 90%; + margin: 0 5%; + + .monkey { + width: 250px; + height: 250px; + border: 6px solid #424242; + border-radius: 125px; + background: url(https://s3.amazonaws.com/metamaps-assets/site/monkeyselfie.jpg) no-repeat; + background-position: 50% 20%; + background-size: 100%; + margin: 80px auto 20px auto; + } + + .explainer_text { + padding: 0 20% 0 20%; + font-size: 24px; + line-height: 30px; + margin-bottom: 20px; + text-align: center; + } + + .make_request { + background-color: #a354cd; + display: block; + width: 220px; + height: 14px; + padding: 16px 0; + margin-bottom: 16px; + text-align: center; + border-radius: 2px; + font-size: 14px; + box-shadow: 0px 1px 1.5px rgba(0,0,0,0.12), 0 1px 1px rgba(0,0,0,0.24); + margin: 0 auto 20px auto; + text-decoration: none; + color: #FFFFFF !important; + cursor: pointer; + } + +} diff --git a/app/controllers/access_controller.rb b/app/controllers/access_controller.rb new file mode 100644 index 00000000..302e9385 --- /dev/null +++ b/app/controllers/access_controller.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true +class AccessController < ApplicationController + before_action :require_user, only: [:access, :access_request, :approve_access, :approve_access_post, + :deny_access, :deny_access_post, :request_access] + before_action :set_map, only: [:access, :access_request, :approve_access, :approve_access_post, + :deny_access, :deny_access_post, :request_access] + after_action :verify_authorized + + + # GET maps/:id/request_access + def request_access + @map = nil + respond_to do |format| + format.html do + render 'maps/request_access' + end + end + end + + # POST maps/:id/access_request + 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 + + respond_to do |format| + format.json do + head :ok + end + end + end + + # POST maps/:id/access + def access + user_ids = params[:access] || [] + + @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 + end + @map.remove_old_collaborators(user_ids) + + respond_to do |format| + format.json do + head :ok + end + end + end + + # GET maps/:id/approve_access/:request_id + def approve_access + request = AccessRequest.find(params[:request_id]) + request.approve() + respond_to do |format| + format.html { redirect_to map_path(@map), notice: 'Request was approved' } + end + end + + # GET maps/:id/deny_access/:request_id + def deny_access + request = AccessRequest.find(params[:request_id]) + request.deny() + respond_to do |format| + format.html { redirect_to map_path(@map), notice: 'Request was turned down' } + end + end + + # POST maps/:id/approve_access/:request_id + def approve_access_post + request = AccessRequest.find(params[:request_id]) + request.approve() + respond_to do |format| + format.json do + head :ok + end + end + end + + # POST maps/:id/deny_access/:request_id + def deny_access_post + request = AccessRequest.find(params[:request_id]) + request.deny() + respond_to do |format| + format.json do + head :ok + end + end + end + + private + + def set_map + @map = Map.find(params[:id]) + authorize @map + end + +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index eddf510d..6138fa31 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,21 +22,26 @@ class ApplicationController < ActionController::Base helper_method :admin? def after_sign_in_path_for(resource) - sign_in_url = url_for(action: 'new', controller: 'sessions', only_path: false) + sign_in_url = new_user_session_url + sign_up_url = new_user_registration_url + stored = stored_location_for(User) - if request.referer == sign_in_url + if stored + stored + elsif request.referer.include?(sign_in_url) || request.referer.include?(sign_up_url) super - elsif params[:uv_login] == '1' - 'http://support.metamaps.cc/login_success?sso=' + current_sso_token else - stored_location_for(resource) || request.referer || root_path + request.referer || root_path end end def handle_unauthorized - if authenticated? + if authenticated? and params[:controller] == 'maps' and params[:action] == 'show' + redirect_to request_access_map_path(params[:id]) + elsif authenticated? redirect_to root_path, notice: "You don't have permission to see that page." else + store_location_for(resource, request.fullpath) redirect_to new_user_session_path, notice: 'Try signing in to do that.' end end diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index cdbbd900..7044d424 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true class MapsController < ApplicationController - before_action :require_user, only: [:create, :update, :destroy, :access, :events] - before_action :set_map, only: [:show, :update, :destroy, :access, :contains, - :events, :export] + before_action :require_user, only: [:create, :update, :destroy, :events] + before_action :set_map, only: [:show, :update, :destroy, :contains, :events, :export] after_action :verify_authorized # GET maps/:id @@ -16,6 +15,7 @@ class MapsController < ApplicationController @allmappings = policy_scope(@map.mappings) @allmessages = @map.messages.sort_by(&:created_at) @allstars = @map.stars + @allrequests = @map.access_requests end format.json { render json: @map } format.csv { redirect_to action: :export, format: :csv } @@ -80,24 +80,6 @@ class MapsController < ApplicationController end end - # POST maps/:id/access - def access - user_ids = params[:access] || [] - - @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 - end - @map.remove_old_collaborators(user_ids) - - respond_to do |format| - format.json do - render json: { message: 'Successfully altered edit permissions' } - end - end - end - # GET maps/:id/contains def contains respond_to do |format| diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 21cd9666..e472152e 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -2,19 +2,22 @@ class Users::RegistrationsController < Devise::RegistrationsController before_action :configure_sign_up_params, only: [:create] before_action :configure_account_update_params, only: [:update] + after_action :store_location, only: [:new] protected - def after_sign_up_path_for(resource) - signed_in_root_path(resource) - end - def after_update_path_for(resource) signed_in_root_path(resource) end private + def store_location + if params[:redirect_to] + store_location_for(User, params[:redirect_to]) + end + end + def configure_sign_up_params devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode]) end diff --git a/app/mailers/map_mailer.rb b/app/mailers/map_mailer.rb index e70d0b82..f6865ecd 100644 --- a/app/mailers/map_mailer.rb +++ b/app/mailers/map_mailer.rb @@ -2,10 +2,17 @@ class MapMailer < ApplicationMailer default from: 'team@metamaps.cc' + def access_request_email(request, map) + @request = request + @map = map + subject = @map.name + ' - request to edit' + mail(to: @map.user.email, subject: subject) + end + def invite_to_edit_email(map, inviter, invitee) @inviter = inviter @map = map - subject = @map.name + ' - Invitation to edit' + subject = @map.name + ' - invitation to edit' mail(to: invitee.email, subject: subject) end end diff --git a/app/models/access_request.rb b/app/models/access_request.rb new file mode 100644 index 00000000..185a04f0 --- /dev/null +++ b/app/models/access_request.rb @@ -0,0 +1,18 @@ +class AccessRequest < ApplicationRecord + belongs_to :user + belongs_to :map + + def approve + self.approved = true + self.answered = true + self.save + UserMap.create(user: self.user, map: self.map) + MapMailer.invite_to_edit_email(self.map, self.map.user, self.user).deliver_later + end + + def deny + self.approved = false + self.answered = true + self.save + end +end diff --git a/app/models/map.rb b/app/models/map.rb index a8e9c866..cdd6b333 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -9,6 +9,7 @@ class Map < ApplicationRecord has_many :messages, as: :resource, dependent: :destroy has_many :stars + has_many :access_requests, dependent: :destroy has_many :user_maps, dependent: :destroy has_many :collaborators, through: :user_maps, source: :user @@ -102,7 +103,8 @@ class Map < ApplicationRecord mappers: contributors, collaborators: editors, messages: messages.sort_by(&:created_at), - stars: stars + stars: stars, + requests: access_requests } end @@ -122,6 +124,7 @@ class Map < ApplicationRecord removed = current_collaborators.map(&:id).map do |old_user_id| next nil if user_ids.include?(old_user_id) user_maps.where(user_id: old_user_id).find_each(&:destroy) + access_requests.where(user_id: old_user_id).find_each(&:destroy) old_user_id end removed.compact diff --git a/app/policies/map_policy.rb b/app/policies/map_policy.rb index 9999a055..f670f59e 100644 --- a/app/policies/map_policy.rb +++ b/app/policies/map_policy.rb @@ -37,10 +37,36 @@ class MapPolicy < ApplicationPolicy end def access? - # note that this is to edit who can access the map + # this is for the map creator to bulk change who can access the map user.present? && record.user == user end + def request_access? + # this is to access the page where you can request access to a map + user.present? + end + + def access_request? + # this is to actually request access + user.present? + end + + def approve_access? + record.user == user + end + + def deny_access? + approve_access? + end + + def approve_access_post? + approve_access? + end + + def deny_access_post? + approve_access? + end + def contains? show? end diff --git a/app/views/layouts/_upperelements.html.erb b/app/views/layouts/_upperelements.html.erb index c2344643..1d26ced9 100644 --- a/app/views/layouts/_upperelements.html.erb +++ b/app/views/layouts/_upperelements.html.erb @@ -14,6 +14,23 @@
+ + <% request = current_user && @map && @allrequests.find{|a| a.user == current_user} + className = (@map and not policy(@map).update?) ? 'isViewOnly ' : '' + if @map + className += 'sendRequest' if not request + className += 'sentRequest' if request and not request.answered + className += 'requestDenied' if request and request.answered and not request.approved + end %> + +<%= @request.user.name %> is requesting access to collaboratively edit the following metamap:
+ +<%= @map.name %>
+ +<%= link_to "Grant", approve_access_map_url(id: @map.id, request_id: @request.id), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %> +
<%= link_to "Deny", 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 %> + +Make sense with Metamaps
+<%= @inviter.name %> has invited you to collaboratively edit the following metamap:
+<%= @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 %>
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 62bd2c90..80eecfed 100644 --- a/app/views/map_mailer/invite_to_edit_email.text.erb +++ b/app/views/map_mailer/invite_to_edit_email.text.erb @@ -1,4 +1,4 @@ -<%= @inviter.name %> has invited you to collaboratively edit the following metamap: +<%= @inviter.name %> has invited you to collaboratively edit the following map: <%= @map.name %> [<%= map_url(@map) %>] diff --git a/app/views/maps/request_access.html.erb b/app/views/maps/request_access.html.erb new file mode 100644 index 00000000..cf8aadb4 --- /dev/null +++ b/app/views/maps/request_access.html.erb @@ -0,0 +1,37 @@ +<%# +# @file +# Code to request access to a map +# /maps/:id/request_access +#%> + +<% content_for :title, 'Request Access | Metamaps' %> +<% content_for :mobile_title, 'Request Access' %> + +