First version of blocking feature. Allows both time-based (for map protection) and notice-based (for education) blocks on users. Also introduces user roles and a simple UI for displaying and administering these.

This commit is contained in:
Matt Amos 2009-09-28 16:01:00 +00:00
parent 52fa09ecae
commit daa2496024
33 changed files with 766 additions and 23 deletions

View file

@ -78,6 +78,12 @@ class ApplicationController < ActionController::Base
@user = User.authenticate(:username => username, :password => passwd) # basic auth
end
end
# check if the user has been banned
unless @user.nil? or @user.blocks.empty?
# NOTE: need slightly more helpful message than this.
render :text => "You got banned!", :status => :forbidden
end
end
def authorize(realm='Web Password', errormessage="Couldn't authenticate you")

View file

@ -0,0 +1,145 @@
class UserBlocksController < ApplicationController
layout 'site'
before_filter :authorize_web
before_filter :set_locale
before_filter :require_user, :only => [:new, :create, :edit, :delete]
before_filter :require_moderator, :only => [:new, :create, :edit, :delete]
def index
@user_blocks_pages, @user_blocks = paginate(:user_blocks,
:include => [:user, :moderator, :revoker],
:order => "user_blocks.end_at DESC",
:per_page => 20)
end
def show
@user_block = UserBlock.find(params[:id])
if @user and @user.id == @user_block.user_id
@user_block.needs_view = false
@user_block.save!
end
end
def new
@user_block = UserBlock.new
@display_name = params[:display_name]
@this_user = User.find_by_display_name(@display_name, :conditions => {:visible => true})
end
# GET /user_blocks/1/edit
def edit
@user_block = UserBlock.find(params[:id])
params[:user_block_period] = ((@user_block.end_at - Time.now.getutc) / 1.hour).ceil.to_s
end
def create
@display_name = params[:display_name]
@this_user = User.find_by_display_name(@display_name, :conditions => {:visible => true})
block_period = [UserBlock::PERIODS.max, params[:user_block_period].to_i].min
@user_block = UserBlock.new(:user_id => @this_user.id,
:moderator_id => @user.id,
:reason => params[:user_block][:reason],
:end_at => Time.now.getutc() + block_period.hours,
:needs_view => params[:user_block][:needs_view])
if (@this_user and @user.moderator? and
params[:tried_contacting] == "yes" and
params[:tried_waiting] == "yes" and
block_period >= 0)
if @user_block.save
flash[:notice] = t('user_block.create.flash', :name => @display_name)
redirect_to @user_block
else
render :action => "new"
end
else
if !@user.moderator?
flash[:notice] = t('user_block.create.not_a_moderator')
elsif params[:tried_contacting] != "yes"
flash[:notice] = t('user_block.create.try_contacting')
elsif params[:tried_waiting] != "yes"
flash[:notice] = t('user_block.create.try_waiting')
else
flash[:notice] = t('user_block.create.bad_parameters')
end
@display_name = @this_user.nil? ? '' : @this_user.display_name
render :action => "new"
end
end
def update
@user_block = UserBlock.find(params[:id])
block_period = [72, params[:user_block_period].to_i].min
if @user_block.moderator_id != @user.id
flash[:notice] = t('user_block.update.only_creator_can_edit')
redirect_to(@user_block)
elsif !@user_block.active?
flash[:notice] = t('user_block.update.block_expired')
redirect_to(@user_block)
elsif @user_block.update_attributes({ :end_at => Time.now.getutc() + block_period.hours,
:reason => params[:user_block][:reason],
:needs_view => params[:user_block][:needs_view] })
flash[:notice] = t('user_block.update.success')
redirect_to(@user_block)
else
render :action => "edit"
end
end
##
# revokes the block, setting the end_time to now
def revoke
@user_block = UserBlock.find(params[:id])
if !@user.moderator?
flash[:notice] = t('user_block.create.not_a_moderator')
redirect_to @user_block
elsif params[:confirm]
if @user_block.revoke!
flash[:notice] = t'user_block.revoke.flash'
redirect_to(@user_block)
else
flash[:notice] = t'user_block.revoke.error'
render :action => "edit"
end
end
end
##
# shows a list of all the blocks on the given user
def blocks_on
@this_user = User.find_by_display_name(params[:display_name])
@user_blocks_pages, @user_blocks = paginate(:user_blocks,
:include => [:user, :moderator, :revoker],
:conditions => {:user_id => @this_user.id},
:order => "user_blocks.end_at DESC",
:per_page => 20)
end
##
# shows a list of all the blocks by the given user.
def blocks_by
@this_user = User.find_by_display_name(params[:display_name])
@user_blocks_pages, @user_blocks = paginate(:user_blocks,
:include => [:user, :moderator, :revoker],
:conditions => {:moderator_id => @this_user.id},
:order => "user_blocks.end_at DESC",
:per_page => 20)
end
private
def require_moderator
redirect_to "/403.html" unless @user.moderator?
end
end

View file

@ -142,9 +142,28 @@ class UserController < ApplicationController
end
def login
if params[:user] and session[:user].nil?
email_or_display_name = params[:user][:email]
pass = params[:user][:password]
user = User.authenticate(:username => email_or_display_name, :password => pass)
if user
session[:user] = user.id
elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true)
@notice = t 'user.login.account not active'
else
@notice = t 'user.login.auth failure'
end
end
if session[:user]
# The user is logged in already, if the referer param exists, redirect them to that
if params[:referer]
# The user is logged in, if the referer param exists, redirect them to that
# unless they've also got a block on them, in which case redirect them to
# the block so they can clear it.
user = User.find(session[:user])
block = user.blocked_on_view
if block
redirect_to block, :referrer => params[:referrer]
elsif params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'site', :action => 'index'
@ -153,25 +172,6 @@ class UserController < ApplicationController
end
@title = t 'user.login.title'
if params[:user]
email_or_display_name = params[:user][:email]
pass = params[:user][:password]
user = User.authenticate(:username => email_or_display_name, :password => pass)
if user
session[:user] = user.id
if params[:referer]
redirect_to params[:referer]
else
redirect_to :controller => 'site', :action => 'index'
end
return
elsif User.authenticate(:username => email_or_display_name, :password => pass, :inactive => true)
@notice = t 'user.login.account not active'
else
@notice = t 'user.login.auth failure'
end
end
end
def logout

View file

@ -0,0 +1,33 @@
class UserRolesController < ApplicationController
layout 'site'
before_filter :authorize_web
before_filter :require_user
before_filter :require_administrator
def grant
this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if this_user and UserRole::ALL_ROLES.include? params[:role]
this_user.roles.create(:role => params[:role])
else
flash[:notice] = t('user_role.grant.fail', :role => params[:role], :name => params[:display_name])
end
redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name]
end
def revoke
this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if this_user and UserRole::ALL_ROLES.include? params[:role]
UserRole.delete_all({:user_id => this_user.id, :role => params[:role]})
else
flash[:notice] = t('user_role.revoke.fail', :role => params[:role], :name => params[:display_name])
end
redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name]
end
private
def require_administrator
redirect_to "/403.html" unless @user.administrator?
end
end

View file

@ -14,6 +14,9 @@ class User < ActiveRecord::Base
has_many :client_applications
has_many :oauth_tokens, :class_name => "OauthToken", :order => "authorized_at desc", :include => [:client_application]
has_many :blocks, :class_name => "UserBlock", :conditions => ["user_blocks.end_at > now() or user_blocks.needs_view"]
has_many :roles, :class_name => "UserRole"
validates_presence_of :email, :display_name
validates_confirmation_of :email#, :message => ' addresses must match'
validates_confirmation_of :pass_crypt#, :message => ' must match the confirmation password'
@ -125,6 +128,31 @@ class User < ActiveRecord::Base
return false
end
##
# returns true if the user has the moderator role, false otherwise
def moderator?
has_role? 'moderator'
end
##
# returns true if the user has the moderator role, false otherwise
def administrator?
has_role? 'administrator'
end
##
# returns true if the user has the requested role
def has_role?(role)
roles.inject(false) { |x, r| x or r.role == role }
end
##
# returns the first active block which would require users to view
# a message, or nil if there are none.
def blocked_on_view
blocks.inject(nil) { |s,x| s || (x.needs_view? ? x : nil) }
end
def delete
self.active = false
self.display_name = "user_#{self.id}"

36
app/models/user_block.rb Normal file
View file

@ -0,0 +1,36 @@
class UserBlock < ActiveRecord::Base
validate :moderator_permissions
belongs_to :user, :class_name => "User", :foreign_key => :user_id
belongs_to :moderator, :class_name => "User", :foreign_key => :moderator_id
belongs_to :revoker, :class_name => "User", :foreign_key => :revoker_id
PERIODS = [0, 1, 3, 6, 12, 24, 48, 96]
##
# returns true if the block is currently active (i.e: the user can't
# use the API).
def active?
needs_view or end_at > Time.now.getutc
end
##
# revokes the block, allowing the user to use the API again. the argument
# is the user object who is revoking the ban.
def revoke!(revoker)
attrs = { :end_at => Time.now.getutc(),
:revoker_id => @user.id,
:needs_view => false }
revoker.moderator? and update_attributes(attrs)
end
private
##
# validate that only moderators are allowed to change the
# block. this should be caught and dealt with in the controller,
# but i've also included it here just in case.
def moderator_permissions
errors.add_to_base("Must be a moderator to create or update a block.") if moderator_id_changed? and !moderator.moderator?
errors.add_to_base("Must be a moderator to revoke a block.") unless revoker_id.nil? or revoker.moderator?
end
end

8
app/models/user_role.rb Normal file
View file

@ -0,0 +1,8 @@
class UserRole < ActiveRecord::Base
ALL_ROLES = ['administrator', 'moderator']
validates_inclusion_of :role, :in => ALL_ROLES
belongs_to :user
end

View file

@ -1,4 +1,15 @@
<h2><%= h(@this_user.display_name) %></h2>
<h2><%= h(@this_user.display_name) %>
<% UserRole::ALL_ROLES.each do |role| %>
<% if @user and @user.administrator? %>
<% if @this_user.has_role? role %>
<%= link_to(image_tag("roles/#{role}.png", :size => "20x20", :border => 0, :alt => t("user.view.role.#{role}")), :controller => 'user_roles', :action => 'revoke', :display_name => @this_user.display_name, :role => role) %>
<% else %>
<%= link_to(image_tag("roles/blank_#{role}.png", :size => "20x20", :border => 0, :alt => t("user.view.role.#{role}")), :controller => 'user_roles', :action => 'grant', :display_name => @this_user.display_name, :role => role) %>
<% end %>
<% elsif @this_user.has_role? role %>
<%= image_tag("roles/#{role}.png", :size => "20x20", :border => 0, :alt => t("user.view.role.#{role}")) %>
<% end %>
<% end %></h2>
<div id="userinformation">
<% if @user and @this_user.id == @user.id %>
<!-- Displaying user's own profile page -->
@ -18,6 +29,13 @@
<% else %>
<%= link_to t('user.view.add as friend'), :controller => 'user', :action => 'make_friend', :display_name => @this_user.display_name %>
<% end %>
| <%= link_to t('user.view.block_history'), :controller => 'user_blocks', :action => 'blocks_on', :display_name => @this_user.display_name %>
<% if @this_user.moderator? %>
| <%= link_to t('user.view.moderator_history'), :controller => 'user_blocks', :action => 'blocks_by', :display_name => @this_user.display_name %>
<% end %>
<% if @user and @user.moderator? %>
| <%= link_to t('user.view.create_block'), :controller => 'user_blocks', :action => 'new', :display_name => @this_user.display_name %>
<% end %>
<% end %>
</div>

View file

@ -0,0 +1,34 @@
<tr>
<% c1 = cycle('table0', 'table1') %>
<% if show_user_name %>
<td class="<%= c1 %>"><%= link_to h(block.user.display_name), :controller => 'user', :action => 'view', :display_name => block.user.display_name %></td>
<% end %>
<% if show_moderator_name %>
<td class="<%= c1 %>"><%= link_to h(block.moderator.display_name), :controller => 'user', :action => 'view', :display_name => block.moderator.display_name %></td>
<% end %>
<td class="<%= c1 %>"><%=h block.reason %></td>
<td class="<%= c1 %>">
<% if block.active? %>
<% if block.needs_view? %>
<%= t'user_block.partial.until_login' %>
<% else %>
<%= t('user_block.partial.time_future', :time => distance_of_time_in_words_to_now(block.end_at)) %>
<% end %>
<% else %>
<%= t'user_block.partial.not_active' %>
<% end %>
</td>
<td class="<%= c1 %>">
<% if block.revoker_id.nil? %>
<%= t('user_block.partial.not_revoked') %>
<% else %>
<%= link_to h(block.revoker.display_name), :controller => 'user', :action => 'view', :display_name => block.revoker.display_name %>
<% end %>
</td>
<td class="<%= c1 %>"><%= link_to t('user_block.partial.show'), block %></td>
<td class="<%= c1 %>"><% if @user and @user.id == block.moderator_id and block.active? %><%= link_to t('user_block.partial.edit'), edit_user_block_path(block) %><% end %></td>
<% if show_revoke_link %>
<td class="<%= c1 %>"><% if block.active? %><%= link_to t('user_block.partial.revoke'), block, :confirm => t('user_block.partial.confirm'), :action => :revoke %><% end %></td>
<% end %>
</tr>

View file

@ -0,0 +1,19 @@
<table id="block_list" cellpadding="3">
<tr>
<% if show_user_name %>
<th><%= t'user_block.partial.display_name' %></th>
<% end %>
<% if show_moderator_name %>
<th><%= t'user_block.partial.moderator_name' %></th>
<% end %>
<th><%= t'user_block.partial.reason' %></th>
<th><%= t'user_block.partial.status' %></th>
<th><%= t'user_block.partial.revoker_name' %></th>
<th></th>
<th></th>
<% if show_revoke_link %>
<th></th>
<% end %>
</tr>
<%= render :partial => 'block', :locals => {:show_revoke_link => show_revoke_link, :show_user_name => show_user_name, :show_moderator_name => show_moderator_name }, :collection => @user_blocks unless @user_blocks.nil? %>
</table>

View file

@ -0,0 +1,3 @@
<h1><%= t('user_block.blocks_by.heading', :name => @this_user.display_name) %></h1>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => true, :show_moderator_name => false } %>

View file

@ -0,0 +1,3 @@
<h1><%= t('user_block.blocks_on.heading', :name => @this_user.display_name) %></h1>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => false, :show_moderator_name => true } %>

View file

@ -0,0 +1,25 @@
<h1><%= t('user_block.edit.title', :name => @user_block.user.display_name) %></h1>
<% form_for(@user_block) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :reason, t('user_block.edit.reason', :name => @user_block.user.display_name) %><br />
<%= f.text_area :reason %>
</p>
<p>
<%= label_tag 'user_block_period', t('user_block.edit.period') %><br />
<%= hidden_field_tag 'what is the period', params[:user_block_period] %>
<%= select_tag('user_block_period', options_for_select(UserBlock::PERIODS.collect { |h| [t('user_block.period', :count => h), h.to_s] }, params[:user_block_period])) %>
</p>
<p>
<%= f.check_box :needs_view %>
<%= f.label :needs_view, t('user_block.edit.needs_view') %>
</p>
<p>
<%= f.submit t('user_block.edit.submit') %>
</p>
<% end %>
<%= link_to t('user_block.edit.show'), @user_block %> |
<%= link_to t('user_block.edit.back'), user_blocks_path %>

View file

@ -0,0 +1,3 @@
<h1><%= t('user_block.index.heading') %></h1>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => true, :show_moderator_name => true } %>

View file

@ -0,0 +1,32 @@
<h1><%= t('user_block.new.title', :name => @display_name) %></h1>
<% form_for(@user_block) do |f| %>
<%= f.error_messages %>
<p>
<%= check_box_tag 'tried_contacting', 'yes', (params[:tried_contacting] == "yes") %>
<%= label_tag 'tried_contacting', t('user_block.new.tried_contacting') %>
</p>
<p>
<%= check_box_tag 'tried_waiting', 'yes', (params[:tried_waiting] == "yes") %>
<%= label_tag 'tried_waiting', t('user_block.new.tried_waiting') %>
</p>
<p>
<%= f.label :reason, t('user_block.new.reason', :name => @display_name) %><br />
<%= f.text_area :reason %>
</p>
<p>
<%= label_tag 'user_block_period', t('user_block.new.period') %><br />
<%= select_tag('user_block_period', options_for_select(UserBlock::PERIODS.collect { |h| [t('user_block.period', :count => h), h.to_s] }, params[:user_block_period] )) %>
</p>
<p>
<%= f.check_box :needs_view %>
<%= f.label :needs_view, t('user_block.new.needs_view') %>
</p>
<p>
<%= hidden_field_tag 'display_name', @display_name %>
<%= f.submit t('user_block.new.submit') %>
</p>
<% end %>
<%= link_to 'Back', user_blocks_path %>

View file

@ -0,0 +1,25 @@
<h1><%= t('user_block.revoke.heading',
:block_on => @user_block.user.display_name,
:block_by => @user_block.moderator.display_name) %></h1>
<% if @user_block.end_at > Time.now %>
<p><b>
<%= t('user_block.revoke.time_future', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
</b></p>
<% form_for :revoke, :url => { :action => "revoke" } do |f| %>
<%= f.error_messages %>
<p>
<%= check_box_tag 'confirm', 'yes' %>
<%= label_tag 'confirm', t('user_block.revoke.confirm') %>
</p>
<p>
<%= submit_tag t('user_block.revoke.revoke') %>
</p>
<% end %>
<% else %>
<p>
<%= t('user_block.revoke.past', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
</p>
<% end %>

View file

@ -0,0 +1,38 @@
<h1><%= t('user_block.show.heading',
:block_on => @user_block.user.display_name,
:block_by => @user_block.moderator.display_name) %></h1>
<% if @user_block.revoker %>
<p>
<b><%= t'user_block.show.revoker' %></b>
<%= link_to h(@user_block.revoker.display_name), :controller => 'user', :action => 'view', :display_name => @user_block.revoker.display_name %>
</p>
<% end %>
<p>
<% if @user_block.end_at > Time.now %>
<%= t('user_block.show.time_future', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
<% else %>
<%= t('user_block.show.time_past', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
<% end %>
</p>
<% if @user_block.needs_view %>
<p><%= t'user_block.show.needs_view' %></p>
<% end %>
<p>
<b><%= t'user_block.show.reason' %></b>
<%=h @user_block.reason %>
</p>
<% if @user_block.end_at > Time.now.getutc %>
<% if @user and @user.id == @user_block.moderator_id %>
<%= link_to t('user_block.show.edit'), edit_user_block_path(@user_block) %> |
<% end %>
<% if @user and @user.moderator? %>
<%= link_to(t('user_block.show.revoke'),{:controller => 'user_blocks', :action => 'revoke', :id => @user_block.id}) %> |
<% end %>
<% end %>
<%= link_to t('user_block.show.back'), user_blocks_path %>

View file

@ -0,0 +1,20 @@
<h1>Editing user_role</h1>
<% form_for(@user_role) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :user_id %><br />
<%= f.text_field :user_id %>
</p>
<p>
<%= f.label :role %><br />
<%= f.text_field :role %>
</p>
<p>
<%= f.submit 'Update' %>
</p>
<% end %>
<%= link_to 'Show', @user_role %> |
<%= link_to 'Back', user_roles_path %>

View file

@ -0,0 +1,22 @@
<h1>Listing user_roles</h1>
<table>
<tr>
<th>User</th>
<th>Role</th>
</tr>
<% @user_roles.each do |user_role| %>
<tr>
<td><%=h user_role.user_id %></td>
<td><%=h user_role.role %></td>
<td><%= link_to 'Show', user_role %></td>
<td><%= link_to 'Edit', edit_user_role_path(user_role) %></td>
<td><%= link_to 'Destroy', user_role, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New user_role', new_user_role_path %>

View file

@ -0,0 +1,19 @@
<h1>New user_role</h1>
<% form_for(@user_role) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :user_id %><br />
<%= f.text_field :user_id %>
</p>
<p>
<%= f.label :role %><br />
<%= f.text_field :role %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', user_roles_path %>

View file

@ -0,0 +1,13 @@
<p>
<b>User:</b>
<%=h @user_role.user_id %>
</p>
<p>
<b>Role:</b>
<%=h @user_role.role %>
</p>
<%= link_to 'Edit', edit_user_role_path(@user_role) %> |
<%= link_to 'Back', user_roles_path %>

View file

@ -944,6 +944,12 @@ en:
no nearby users: "There are no users who admit to mapping nearby yet."
change your settings: change your settings
my_oauth_details: "View my OAuth details"
role:
administrator: "Administrator"
moderator: "Moderator"
block_history: "view blocks received"
moderator_history: "view blocks given"
create_block: "block this user"
friend_map:
your location: Your location
nearby mapper: "Nearby mapper: [[nearby_user]]"
@ -993,3 +999,78 @@ en:
remove_friend:
success: "{{name}} was removed from your friends."
not_a_friend: "{{name}} is not one of your friends."
user_role:
grant:
fail: "Couldn't grant role `{{role}}' to user `{{name}}'. Please check that the user and role are both valid."
revoke:
fail: "Couldn't revoke role `{{role}}' from user `{{name}}'. Please check that the user and role are both valid."
user_block:
new:
reason: "The reason why {{name}} is being blocked. Please be as calm and as reasonable as possible, giving as much detail as you can about the situation. Bear in mind that not all users understand the community jargon, so please try to use laymans terms."
period: "How long, starting now, the user will be blocked from the API for."
submit: "Create block"
tried_contacting: "I have contacted the user and asked them to stop."
tried_waiting: "I have given a reasonable amount of time for the user to respond to those communications."
title: "Creating block on {{name}}"
needs_view: "Does the user need to log in before this block will be cleared?"
edit:
reason: "The reason why {{name}} is being blocked. Please be as calm and as reasonable as possible, giving as much detail as you can about the situation. Bear in mind that not all users understand the community jargon, so please try to use laymans terms."
period: "How long, starting now, the user will be blocked from the API for."
submit: "Update block"
show: "Show"
back: "Back"
title: "Editing block on {{name}}"
needs_view: "Does the user need to log in before this block will be cleared?"
create:
not_a_moderator: "User block could not be created: you are not a moderator."
try_contacting: "Please try contacting the user before blocking them and giving them a reasonable time to respond."
try_waiting: "Please try giving the user a reasonable time to respond before blocking them."
bad_parameters: "Could not create a new block due to bad parameters. Maybe the blocking period is not valid?"
flash: "Created a block on user {{name}}."
update:
only_creator_can_edit: "Only the moderator who created this block can edit it."
block_expired: "The block has already expired and cannot be edited."
success: "Block updated."
index:
heading: "Listing User Blocks"
revoke:
heading: "Revoking block on {{block_on}} by {{block_by}}"
time_future: "This block will end in {{time}}."
past: "This block ended {{time}} ago and cannot be revoked now."
confirm: "Are you sure you wish to revoke this block?"
revoke: "Revoke!"
flash: "This block has been revoked."
period:
one: "1 hour"
other: "{{count}} hours"
partial:
show: "Show"
edit: "Edit"
revoke: "Revoke!"
confirm: "Are you sure?"
display_name: "Blocked User"
moderator_name: "Moderator"
reason: "Reason for block"
status: "Status"
revoker_name: "Revoked by"
not_revoked: "(not revoked)"
time_future: "Ends in {{time}}"
until_login: "Until the user logs in"
not_active: "(not active)"
blocks_on:
heading: "List blocks on {{name}}"
blocks_by:
heading: "List blocks by {{name}}"
show:
heading: "Block on {{block_on}} by {{block_by}}"
time_future: "Ends in {{time}}"
time_past: "Ended {{time}} ago"
show: "Show"
edit: "Edit"
revoke: "Revoke!"
confirm: "Are you sure?"
reason: "Reason for block:"
back: "Back"
revoker: "Revoker:"
needs_view: "The user needs to log in before this block will be cleared."

View file

@ -1,5 +1,4 @@
ActionController::Routing::Routes.draw do |map|
# API
map.connect "api/capabilities", :controller => 'api', :action => 'capabilities'
map.connect "api/#{API_VERSION}/capabilities", :controller => 'api', :action => 'capabilities'
@ -202,6 +201,14 @@ ActionController::Routing::Routes.draw do |map|
map.access_token '/oauth/access_token', :controller => 'oauth', :action => 'access_token'
map.test_request '/oauth/test_request', :controller => 'oauth', :action => 'test_request'
# roles and banning pages
map.connect '/user/:display_name/role/:role/grant', :controller => 'user_roles', :action => 'grant'
map.connect '/user/:display_name/role/:role/revoke', :controller => 'user_roles', :action => 'revoke'
map.connect '/user/:display_name/blocks', :controller => 'user_blocks', :action => 'blocks_on'
map.connect '/user/:display_name/blocks_by', :controller => 'user_blocks', :action => 'blocks_by'
map.resources :user_blocks, :as => 'blocks'
map.connect '/blocks/:id/revoke', :controller => 'user_blocks', :action => 'revoke'
# fall through
map.connect ':controller/:id/:action'
map.connect ':controller/:action'

View file

@ -0,0 +1,33 @@
require 'lib/migrate'
class CreateUserRoles < ActiveRecord::Migration
def self.up
create_enumeration :user_role_enum, ["administrator", "moderator"]
create_table :user_roles do |t|
t.column :user_id, :bigint, :null => false
t.timestamps
end
add_column :user_roles, :role, :user_role_enum, :null => false
User.all(:conditions => ['administrator = ?', true]).each do |user|
UserRole.create(:user_id => user.id, :role => "administrator")
end
remove_column :users, :administrator
add_foreign_key :user_roles, [:user_id], :users, [:id]
add_index :user_roles, [:user_id]
end
def self.down
add_column :users, :administrator, :boolean, :default => false, :null => false
UserRole.all(:conditions => ['role = ?', "administrator"]).each do |role|
user = User.find(role.user_id)
user.administrator = true
user.save!
end
drop_table :user_roles
drop_enumeration :user_role_enum
end
end

View file

@ -0,0 +1,26 @@
require 'lib/migrate'
class CreateUserBlocks < ActiveRecord::Migration
def self.up
create_table :user_blocks do |t|
t.column :user_id, :bigint, :null => false
t.column :moderator_id, :bigint, :null => false
t.column :reason, :text, :null => false
t.column :end_at, :datetime, :null => false
t.column :needs_view, :boolean, :null => false, :default => false
t.column :revoker_id, :bigint
t.timestamps
end
add_foreign_key :user_blocks, [:user_id], :users, [:id]
add_foreign_key :user_blocks, [:moderator_id], :users, [:id]
add_foreign_key :user_blocks, [:revoker_id], :users, [:id]
add_index :user_blocks, [:user_id]
end
def self.down
drop_table :user_blocks
end
end

12
public/403.html Normal file
View file

@ -0,0 +1,12 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<img src="http://www.openstreetmap.org/images/osm_logo.png" style="float:left; margin:10px">
<div style="float:left;">
<h1>Forbidden</h1>
<p>The operation you requested on the OpenStreetMap server is only available to administrators (HTTP 403)</p>
<p>Feel free to <a href="http://wiki.openstreetmap.org/wiki/Contact" title="Various contact channels explained">contact</a> the OpenStreetMap community if you have found a broken link / bug. Make a note of the exact URL of your request.</p>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 824 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

9
test/fixtures/user_roles.yml vendored Normal file
View file

@ -0,0 +1,9 @@
# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
administrator:
user_id: 6
role: administrator
moderator:
user_id: 5
role: moderator

View file

@ -51,3 +51,20 @@ second_public_user:
home_lon: 87
home_zoom: 12
moderator_user:
id: 5
email: moderator@example.com
active: true
pass_crypt: <%= Digest::MD5.hexdigest('test') %>
creation_time: "2008-05-01 01:23:45"
display_name: moderator
data_public: true
administrator_user:
id: 6
email: administrator@example.com
active: true
pass_crypt: <%= Digest::MD5.hexdigest('test') %>
creation_time: "2008-05-01 01:23:45"
display_name: administrator
data_public: true

View file

@ -0,0 +1,28 @@
require File.dirname(__FILE__) + '/../test_helper'
class UserRolesControllerTest < ActionController::TestCase
fixtures :users, :user_roles
test "grant" do
check_redirect(:grant, :public_user, "/403.html")
check_redirect(:grant, :moderator_user, "/403.html")
check_redirect(:grant, :administrator_user, {:controller => :user, :action => :view})
end
test "revoke" do
check_redirect(:revoke, :public_user, "/403.html")
check_redirect(:revoke, :moderator_user, "/403.html")
check_redirect(:revoke, :administrator_user, {:controller => :user, :action => :view})
end
def check_redirect(action, user, redirect)
UserRole::ALL_ROLES.each do |role|
u = users(user)
basic_authorization(u.email, "test")
get(action, {:display_name => users(:second_public_user).display_name, :role => role}, {'user' => u.id})
assert_response :redirect
assert_redirected_to redirect
end
end
end