access requests (#762)
* start on access requests * set up access requests further * set default values for approved and answered
This commit is contained in:
parent
14ea18a967
commit
4602ded8a4
23 changed files with 448 additions and 54 deletions
BIN
app/assets/images/view-only.png
Normal file
BIN
app/assets/images/view-only.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 421 B |
98
app/assets/stylesheets/request_access.scss.erb
Normal file
98
app/assets/stylesheets/request_access.scss.erb
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
app/controllers/access_controller.rb
Normal file
98
app/controllers/access_controller.rb
Normal file
|
@ -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
|
|
@ -22,21 +22,26 @@ class ApplicationController < ActionController::Base
|
||||||
helper_method :admin?
|
helper_method :admin?
|
||||||
|
|
||||||
def after_sign_in_path_for(resource)
|
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
|
super
|
||||||
elsif params[:uv_login] == '1'
|
|
||||||
'http://support.metamaps.cc/login_success?sso=' + current_sso_token
|
|
||||||
else
|
else
|
||||||
stored_location_for(resource) || request.referer || root_path
|
request.referer || root_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_unauthorized
|
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."
|
redirect_to root_path, notice: "You don't have permission to see that page."
|
||||||
else
|
else
|
||||||
|
store_location_for(resource, request.fullpath)
|
||||||
redirect_to new_user_session_path, notice: 'Try signing in to do that.'
|
redirect_to new_user_session_path, notice: 'Try signing in to do that.'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class MapsController < ApplicationController
|
class MapsController < ApplicationController
|
||||||
before_action :require_user, only: [:create, :update, :destroy, :access, :events]
|
before_action :require_user, only: [:create, :update, :destroy, :events]
|
||||||
before_action :set_map, only: [:show, :update, :destroy, :access, :contains,
|
before_action :set_map, only: [:show, :update, :destroy, :contains, :events, :export]
|
||||||
:events, :export]
|
|
||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
# GET maps/:id
|
# GET maps/:id
|
||||||
|
@ -16,6 +15,7 @@ class MapsController < ApplicationController
|
||||||
@allmappings = policy_scope(@map.mappings)
|
@allmappings = policy_scope(@map.mappings)
|
||||||
@allmessages = @map.messages.sort_by(&:created_at)
|
@allmessages = @map.messages.sort_by(&:created_at)
|
||||||
@allstars = @map.stars
|
@allstars = @map.stars
|
||||||
|
@allrequests = @map.access_requests
|
||||||
end
|
end
|
||||||
format.json { render json: @map }
|
format.json { render json: @map }
|
||||||
format.csv { redirect_to action: :export, format: :csv }
|
format.csv { redirect_to action: :export, format: :csv }
|
||||||
|
@ -80,24 +80,6 @@ class MapsController < ApplicationController
|
||||||
end
|
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
|
|
||||||
render json: { message: 'Successfully altered edit permissions' }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET maps/:id/contains
|
# GET maps/:id/contains
|
||||||
def contains
|
def contains
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -2,19 +2,22 @@
|
||||||
class Users::RegistrationsController < Devise::RegistrationsController
|
class Users::RegistrationsController < Devise::RegistrationsController
|
||||||
before_action :configure_sign_up_params, only: [:create]
|
before_action :configure_sign_up_params, only: [:create]
|
||||||
before_action :configure_account_update_params, only: [:update]
|
before_action :configure_account_update_params, only: [:update]
|
||||||
|
after_action :store_location, only: [:new]
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def after_sign_up_path_for(resource)
|
|
||||||
signed_in_root_path(resource)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update_path_for(resource)
|
def after_update_path_for(resource)
|
||||||
signed_in_root_path(resource)
|
signed_in_root_path(resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def store_location
|
||||||
|
if params[:redirect_to]
|
||||||
|
store_location_for(User, params[:redirect_to])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def configure_sign_up_params
|
def configure_sign_up_params
|
||||||
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
|
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,10 +2,17 @@
|
||||||
class MapMailer < ApplicationMailer
|
class MapMailer < ApplicationMailer
|
||||||
default from: 'team@metamaps.cc'
|
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)
|
def invite_to_edit_email(map, inviter, invitee)
|
||||||
@inviter = inviter
|
@inviter = inviter
|
||||||
@map = map
|
@map = map
|
||||||
subject = @map.name + ' - Invitation to edit'
|
subject = @map.name + ' - invitation to edit'
|
||||||
mail(to: invitee.email, subject: subject)
|
mail(to: invitee.email, subject: subject)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
18
app/models/access_request.rb
Normal file
18
app/models/access_request.rb
Normal file
|
@ -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
|
|
@ -9,6 +9,7 @@ class Map < ApplicationRecord
|
||||||
has_many :messages, as: :resource, dependent: :destroy
|
has_many :messages, as: :resource, dependent: :destroy
|
||||||
has_many :stars
|
has_many :stars
|
||||||
|
|
||||||
|
has_many :access_requests, dependent: :destroy
|
||||||
has_many :user_maps, dependent: :destroy
|
has_many :user_maps, dependent: :destroy
|
||||||
has_many :collaborators, through: :user_maps, source: :user
|
has_many :collaborators, through: :user_maps, source: :user
|
||||||
|
|
||||||
|
@ -102,7 +103,8 @@ class Map < ApplicationRecord
|
||||||
mappers: contributors,
|
mappers: contributors,
|
||||||
collaborators: editors,
|
collaborators: editors,
|
||||||
messages: messages.sort_by(&:created_at),
|
messages: messages.sort_by(&:created_at),
|
||||||
stars: stars
|
stars: stars,
|
||||||
|
requests: access_requests
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -122,6 +124,7 @@ class Map < ApplicationRecord
|
||||||
removed = current_collaborators.map(&:id).map do |old_user_id|
|
removed = current_collaborators.map(&:id).map do |old_user_id|
|
||||||
next nil if user_ids.include?(old_user_id)
|
next nil if user_ids.include?(old_user_id)
|
||||||
user_maps.where(user_id: old_user_id).find_each(&:destroy)
|
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
|
old_user_id
|
||||||
end
|
end
|
||||||
removed.compact
|
removed.compact
|
||||||
|
|
|
@ -37,10 +37,36 @@ class MapPolicy < ApplicationPolicy
|
||||||
end
|
end
|
||||||
|
|
||||||
def access?
|
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
|
user.present? && record.user == user
|
||||||
end
|
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?
|
def contains?
|
||||||
show?
|
show?
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,23 @@
|
||||||
<div class="sidebarSearchIcon"></div>
|
<div class="sidebarSearchIcon"></div>
|
||||||
<div class="clearfloat"></div>
|
<div class="clearfloat"></div>
|
||||||
</div> <!-- end sidebarSearch -->
|
</div> <!-- end sidebarSearch -->
|
||||||
|
|
||||||
|
<% 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 %>
|
||||||
|
|
||||||
|
<div class="viewOnly <%= className %>">
|
||||||
|
<div class="eyeball">View Only</div>
|
||||||
|
<% if current_user %>
|
||||||
|
<div class="requestAccess requestNotice">Request Access</div>
|
||||||
|
<div class="requestPending requestNotice">Request Pending</div>
|
||||||
|
<div class="requestNotAccepted requestNotice">Request Not Accepted</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
<div class="clearfloat"></div>
|
<div class="clearfloat"></div>
|
||||||
</div><!-- end upperLeftUI -->
|
</div><!-- end upperLeftUI -->
|
||||||
|
|
||||||
|
|
23
app/views/map_mailer/access_request_email.html.erb
Normal file
23
app/views/map_mailer/access_request_email.html.erb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
|
||||||
|
</head>
|
||||||
|
<body style="font-family: sans-serif; width: 100%; padding: 24px 16px 16px 16px; background-color: #f5f5f5; text-align: center;">
|
||||||
|
|
||||||
|
<div style="padding: 16px; background: white; text-align: left;">
|
||||||
|
<% 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" %>
|
||||||
|
|
||||||
|
<p><span style="font-weight: bold;"><%= @request.user.name %></span> is requesting access to <span style="font-weight: bold">collaboratively edit</span> the following metamap:</p>
|
||||||
|
|
||||||
|
<p><%= @map.name %></p>
|
||||||
|
|
||||||
|
<p><%= 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;" %>
|
||||||
|
<p><%= 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;" %></p>
|
||||||
|
|
||||||
|
<%= link_to 'Open in Metamaps', map_url(@map), target: "_blank", style: button_style %>
|
||||||
|
|
||||||
|
<p style="font-size: 12px;">Make sense with Metamaps</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
app/views/map_mailer/access_request_email.text.erb
Normal file
10
app/views/map_mailer/access_request_email.text.erb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<%= @request.user.name %> has requested to collaboratively edit the following metamap:
|
||||||
|
|
||||||
|
<%= @map.name %> [<%= map_url(@map) %>]
|
||||||
|
|
||||||
|
Approve Request [<%= approve_access_map_url(id: @map.id, request_id: @request.id) %>]
|
||||||
|
Deny Request [<%= deny_access_map_url(id: @map.id, request_id: @request.id) %>]
|
||||||
|
|
||||||
|
Make sense with Metamaps
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div style="padding: 16px; background: white; text-align: left;">
|
<div style="padding: 16px; background: white; text-align: left;">
|
||||||
<% 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" %>
|
||||||
|
|
||||||
<p><span style="font-weight: bold;"><%= @inviter.name %></span> has invited you to <span style="font-weight: bold">collaboratively edit</span> the following metamap:</p>
|
<p><span style="font-weight: bold;"><%= @inviter.name %></span> has invited you to <span style="font-weight: bold">collaboratively edit</span> the following map:</p>
|
||||||
<p><%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %></p>
|
<p><%= link_to @map.name, map_url(@map), target: "_blank", style: "font-size: 18px; text-decoration: none; color: #4fc059;" %></p>
|
||||||
<% if @map.desc %>
|
<% if @map.desc %>
|
||||||
<p style="font-size: 12px;"><%= @map.desc %></p>
|
<p style="font-size: 12px;"><%= @map.desc %></p>
|
||||||
|
|
|
@ -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) %>]
|
<%= @map.name %> [<%= map_url(@map) %>]
|
||||||
|
|
||||||
|
|
37
app/views/maps/request_access.html.erb
Normal file
37
app/views/maps/request_access.html.erb
Normal file
|
@ -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' %>
|
||||||
|
|
||||||
|
<div id="yield">
|
||||||
|
<div class='request_access'>
|
||||||
|
<div class='monkey'></div>
|
||||||
|
<div class='explainer_text'>
|
||||||
|
Hmmm. This map is private, but you can request to edit it from the map creator.
|
||||||
|
</div>
|
||||||
|
<div class='make_request'>REQUEST ACCESS</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.make_request').click(function() {
|
||||||
|
var that = $(this)
|
||||||
|
that.off('click')
|
||||||
|
that.text('requesting...')
|
||||||
|
$.ajax({
|
||||||
|
url: '/maps/<%= params[:id] %>/access_request',
|
||||||
|
type: 'POST',
|
||||||
|
contentType: 'application/json',
|
||||||
|
statusCode: {
|
||||||
|
200: function () { that.text('Request Sent'); setTimeout(function () {window.location.href = '/'}, 2000) },
|
||||||
|
400: function () { that.text('An error occurred') }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -32,7 +32,7 @@ Rails.application.configure do
|
||||||
# Print deprecation notices to the Rails logger
|
# Print deprecation notices to the Rails logger
|
||||||
config.active_support.deprecation = :log
|
config.active_support.deprecation = :log
|
||||||
|
|
||||||
config.action_mailer.preview_path = '/vagrant/spec/mailers/previews'
|
config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews"
|
||||||
|
|
||||||
# Expands the lines which load the assets
|
# Expands the lines which load the assets
|
||||||
config.assets.debug = false
|
config.assets.debug = false
|
||||||
|
|
|
@ -19,9 +19,17 @@ Metamaps::Application.routes.draw do
|
||||||
get :export
|
get :export
|
||||||
post 'events/:event', action: :events
|
post 'events/:event', action: :events
|
||||||
get :contains
|
get :contains
|
||||||
post :access, default: { format: :json }
|
|
||||||
post :star, to: 'stars#create', defaults: { format: :json }
|
get :request_access, to: 'access#request_access'
|
||||||
post :unstar, to: 'stars#destroy', defaults: { format: :json }
|
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 }
|
||||||
|
post :unstar, to: 'stars#destroy', default: { format: :json }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -54,6 +62,19 @@ Metamaps::Application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
devise_for :users, skip: :sessions, controllers: {
|
||||||
|
registrations: 'users/registrations',
|
||||||
|
passwords: 'users/passwords',
|
||||||
|
sessions: 'devise/sessions'
|
||||||
|
}
|
||||||
|
|
||||||
|
devise_scope :user do
|
||||||
|
get 'login' => 'devise/sessions#new', :as => :new_user_session
|
||||||
|
post 'login' => 'devise/sessions#create', :as => :user_session
|
||||||
|
get 'logout' => 'devise/sessions#destroy', :as => :destroy_user_session
|
||||||
|
get 'join' => 'devise/registrations#new', :as => :new_user_registration_path
|
||||||
|
end
|
||||||
|
|
||||||
resources :users, except: [:index, :destroy] do
|
resources :users, except: [:index, :destroy] do
|
||||||
member do
|
member do
|
||||||
get :details
|
get :details
|
||||||
|
@ -84,19 +105,6 @@ Metamaps::Application.routes.draw do
|
||||||
match '*path', to: 'v2/restful#catch_404', via: :all
|
match '*path', to: 'v2/restful#catch_404', via: :all
|
||||||
end
|
end
|
||||||
|
|
||||||
devise_for :users, skip: :sessions, controllers: {
|
|
||||||
registrations: 'users/registrations',
|
|
||||||
passwords: 'users/passwords',
|
|
||||||
sessions: 'devise/sessions'
|
|
||||||
}
|
|
||||||
|
|
||||||
devise_scope :user do
|
|
||||||
get 'login' => 'devise/sessions#new', :as => :new_user_session
|
|
||||||
post 'login' => 'devise/sessions#create', :as => :user_session
|
|
||||||
get 'logout' => 'devise/sessions#destroy', :as => :destroy_user_session
|
|
||||||
get 'join' => 'devise/registrations#new', :as => :new_user_registration_path
|
|
||||||
end
|
|
||||||
|
|
||||||
namespace :hacks do
|
namespace :hacks do
|
||||||
get 'load_url_title'
|
get 'load_url_title'
|
||||||
end
|
end
|
||||||
|
|
12
db/migrate/20161013162214_create_access_requests.rb
Normal file
12
db/migrate/20161013162214_create_access_requests.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
class CreateAccessRequests < ActiveRecord::Migration[5.0]
|
||||||
|
def change
|
||||||
|
create_table :access_requests do |t|
|
||||||
|
t.references :user, foreign_key: true
|
||||||
|
t.boolean :approved, default: false
|
||||||
|
t.boolean :answered, default: false
|
||||||
|
t.references :map, foreign_key: true
|
||||||
|
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
db/schema.rb
15
db/schema.rb
|
@ -10,11 +10,22 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20160928022635) do
|
ActiveRecord::Schema.define(version: 20161013162214) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
|
create_table "access_requests", force: :cascade do |t|
|
||||||
|
t.integer "user_id"
|
||||||
|
t.boolean "approved"
|
||||||
|
t.boolean "answered"
|
||||||
|
t.integer "map_id"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
|
t.index ["map_id"], name: "index_access_requests_on_map_id", using: :btree
|
||||||
|
t.index ["user_id"], name: "index_access_requests_on_user_id", using: :btree
|
||||||
|
end
|
||||||
|
|
||||||
create_table "delayed_jobs", force: :cascade do |t|
|
create_table "delayed_jobs", force: :cascade do |t|
|
||||||
t.integer "priority", default: 0, null: false
|
t.integer "priority", default: 0, null: false
|
||||||
t.integer "attempts", default: 0, null: false
|
t.integer "attempts", default: 0, null: false
|
||||||
|
@ -268,5 +279,7 @@ ActiveRecord::Schema.define(version: 20160928022635) do
|
||||||
t.index ["hookable_type", "hookable_id"], name: "index_webhooks_on_hookable_type_and_hookable_id", using: :btree
|
t.index ["hookable_type", "hookable_id"], name: "index_webhooks_on_hookable_type_and_hookable_id", using: :btree
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_foreign_key "access_requests", "maps"
|
||||||
|
add_foreign_key "access_requests", "users"
|
||||||
add_foreign_key "tokens", "users"
|
add_foreign_key "tokens", "users"
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,8 +60,28 @@ const Map = {
|
||||||
InfoBox.init()
|
InfoBox.init()
|
||||||
CheatSheet.init()
|
CheatSheet.init()
|
||||||
|
|
||||||
|
$('.viewOnly .requestAccess').click(self.requestAccess)
|
||||||
|
|
||||||
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
|
$(document).on(Map.events.editedByActiveMapper, self.editedByActiveMapper)
|
||||||
},
|
},
|
||||||
|
requestAccess: function () {
|
||||||
|
$('.viewOnly').removeClass('sendRequest').addClass('sentRequest')
|
||||||
|
const mapId = Active.Map.id
|
||||||
|
$.post({
|
||||||
|
url: `/maps/${mapId}/access_request`
|
||||||
|
})
|
||||||
|
GlobalUI.notifyUser('Map creator will be notified of your request')
|
||||||
|
},
|
||||||
|
setAccessRequest: function (requests, activeMapper) {
|
||||||
|
let className = 'isViewOnly '
|
||||||
|
if (activeMapper) {
|
||||||
|
const request = _.find(requests, r => r.user_id === activeMapper.id)
|
||||||
|
if (!request) className += 'sendRequest'
|
||||||
|
else if (request && !request.answered) className += 'sentRequest'
|
||||||
|
else if (request && request.answered && !request.approved) className += 'requestDenied'
|
||||||
|
}
|
||||||
|
$('.viewOnly').removeClass('sendRequest sentRequest requestDenied').addClass(className)
|
||||||
|
},
|
||||||
launch: function (id) {
|
launch: function (id) {
|
||||||
var bb = Metamaps.Backbone
|
var bb = Metamaps.Backbone
|
||||||
var start = function (data) {
|
var start = function (data) {
|
||||||
|
@ -84,6 +104,9 @@ const Map = {
|
||||||
if (map.authorizeToEdit(mapper)) {
|
if (map.authorizeToEdit(mapper)) {
|
||||||
$('.wrapper').addClass('canEditMap')
|
$('.wrapper').addClass('canEditMap')
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Map.setAccessRequest(data.requests, mapper)
|
||||||
|
}
|
||||||
|
|
||||||
// add class to .wrapper for specifying if the map can
|
// add class to .wrapper for specifying if the map can
|
||||||
// be collaborated on
|
// be collaborated on
|
||||||
|
@ -139,6 +162,7 @@ const Map = {
|
||||||
Filter.close()
|
Filter.close()
|
||||||
InfoBox.close()
|
InfoBox.close()
|
||||||
Realtime.endActiveMap()
|
Realtime.endActiveMap()
|
||||||
|
$('.viewOnly').removeClass('isViewOnly')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateStar: function () {
|
updateStar: function () {
|
||||||
|
|
|
@ -4,4 +4,9 @@ class MapMailerPreview < ActionMailer::Preview
|
||||||
def invite_to_edit_email
|
def invite_to_edit_email
|
||||||
MapMailer.invite_to_edit_email(Map.first, User.first, User.second)
|
MapMailer.invite_to_edit_email(Map.first, User.first, User.second)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def access_request_email
|
||||||
|
request = AccessRequest.first
|
||||||
|
MapMailer.access_request_email(request, request.map)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
5
spec/models/access_request_spec.rb
Normal file
5
spec/models/access_request_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe AccessRequest, type: :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
Loading…
Reference in a new issue