New migration to add better auditing to user_roles and better column names there and on user_blocks. Added a helper for displaying block status messages.

This commit is contained in:
Matt Amos 2009-09-29 16:44:03 +00:00
parent 09c5740b5b
commit ca06b3c7b1
15 changed files with 93 additions and 60 deletions

View file

@ -8,8 +8,8 @@ class UserBlocksController < ApplicationController
def index
@user_blocks_pages, @user_blocks = paginate(:user_blocks,
:include => [:user, :moderator, :revoker],
:order => "user_blocks.end_at DESC",
:include => [:user, :creator, :revoker],
:order => "user_blocks.ends_at DESC",
:per_page => 20)
end
@ -31,7 +31,7 @@ class UserBlocksController < ApplicationController
# 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
params[:user_block_period] = ((@user_block.ends_at - Time.now.getutc) / 1.hour).ceil.to_s
end
def create
@ -40,9 +40,9 @@ class UserBlocksController < ApplicationController
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,
:creator_id => @user.id,
:reason => params[:user_block][:reason],
:end_at => Time.now.getutc() + block_period.hours,
:ends_at => Time.now.getutc() + block_period.hours,
:needs_view => params[:user_block][:needs_view])
if (@this_user and @user.moderator? and
@ -75,7 +75,7 @@ class UserBlocksController < ApplicationController
@user_block = UserBlock.find(params[:id])
block_period = [72, params[:user_block_period].to_i].min
if @user_block.moderator_id != @user.id
if @user_block.creator_id != @user.id
flash[:notice] = t('user_block.update.only_creator_can_edit')
redirect_to(@user_block)
@ -83,7 +83,7 @@ class UserBlocksController < ApplicationController
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,
elsif @user_block.update_attributes({ :ends_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')
@ -119,9 +119,9 @@ class UserBlocksController < ApplicationController
@this_user = User.find_by_display_name(params[:display_name])
@user_blocks_pages, @user_blocks = paginate(:user_blocks,
:include => [:user, :moderator, :revoker],
:include => [:user, :creator, :revoker],
:conditions => {:user_id => @this_user.id},
:order => "user_blocks.end_at DESC",
:order => "user_blocks.ends_at DESC",
:per_page => 20)
end
@ -131,9 +131,9 @@ class UserBlocksController < ApplicationController
@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",
:include => [:user, :creator, :revoker],
:conditions => {:creator_id => @this_user.id},
:order => "user_blocks.ends_at DESC",
:per_page => 20)
end

View file

@ -10,7 +10,7 @@ class UserRolesController < ApplicationController
if params[:nonce] and params[:nonce] == session[:nonce]
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])
this_user.roles.create(:role => params[:role], :granter_id => @user.id)
redirect_to :controller => 'user', :action => 'view', :display_name => params[:display_name]
else
flash[:notice] = t('user_role.grant.fail', :role => params[:role], :name => params[:display_name])

View file

@ -0,0 +1,20 @@
module UserBlocksHelper
##
# returns a translated string representing the status of the
# user block (i.e: whether it's active, what the expiry time is)
def block_status(block)
if block.active?
if block.needs_view?
I18n.t('user_block.helper.until_login')
else
I18n.t('user_block.helper.time_future', :time => distance_of_time_in_words_to_now(block.ends_at))
end
else
# the max of the last update time or the ends_at time is when this block finished
# either because the user viewed the block (updated_at) or it expired or was
# revoked (ends_at)
last_time = [block.ends_at, block.updated_at].max
I18n.t('user_block.helper.time_past', :time => distance_of_time_in_words_to_now(last_time))
end
end
end

View file

@ -14,7 +14,7 @@ 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 :blocks, :class_name => "UserBlock", :conditions => ["user_blocks.ends_at > now() or user_blocks.needs_view"]
has_many :roles, :class_name => "UserRole"
validates_presence_of :email, :display_name

View file

@ -2,7 +2,7 @@ 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 :creator, :class_name => "User", :foreign_key => :creator_id
belongs_to :revoker, :class_name => "User", :foreign_key => :revoker_id
PERIODS = [0, 1, 3, 6, 12, 24, 48, 96]
@ -11,14 +11,14 @@ class UserBlock < ActiveRecord::Base
# 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
needs_view or ends_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(),
attrs = { :ends_at => Time.now.getutc(),
:revoker_id => @user.id,
:needs_view => false }
revoker.moderator? and update_attributes(attrs)
@ -30,7 +30,7 @@ class UserBlock < ActiveRecord::Base
# 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 create or update a block.") if creator_id_changed? and !creator.moderator?
errors.add_to_base("Must be a moderator to revoke a block.") unless revoker_id.nil? or revoker.moderator?
end
end

View file

@ -4,21 +4,11 @@
<% 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>
<% if show_creator_name %>
<td class="<%= c1 %>"><%= link_to h(block.creator.display_name), :controller => 'user', :action => 'view', :display_name => block.creator.display_name %></td>
<% end %>
<td class="<%= c1 %>"><%=h truncate(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 %>"><%=h block_status(block) %></td>
<td class="<%= c1 %>">
<% if block.revoker_id.nil? %>
<%= t('user_block.partial.not_revoked') %>
@ -27,7 +17,7 @@
<% 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>
<td class="<%= c1 %>"><% if @user and @user.id == block.creator_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 %>

View file

@ -3,8 +3,8 @@
<% 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>
<% if show_creator_name %>
<th><%= t'user_block.partial.creator_name' %></th>
<% end %>
<th><%= t'user_block.partial.reason' %></th>
<th><%= t'user_block.partial.status' %></th>
@ -15,5 +15,5 @@
<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? %>
<%= render :partial => 'block', :locals => {:show_revoke_link => show_revoke_link, :show_user_name => show_user_name, :show_creator_name => show_creator_name }, :collection => @user_blocks unless @user_blocks.nil? %>
</table>

View file

@ -1,3 +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 } %>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => true, :show_creator_name => false } %>

View file

@ -1,3 +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 } %>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => false, :show_creator_name => true } %>

View file

@ -1,3 +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 } %>
<%= render :partial => 'blocks', :locals => { :show_revoke_link => (@user and @user.moderator?), :show_user_name => true, :show_creator_name => true } %>

View file

@ -1,10 +1,10 @@
<h1><%= t('user_block.revoke.heading',
:block_on => @user_block.user.display_name,
:block_by => @user_block.moderator.display_name) %></h1>
:block_by => @user_block.creator.display_name) %></h1>
<% if @user_block.end_at > Time.now %>
<% if @user_block.ends_at > Time.now %>
<p><b>
<%= t('user_block.revoke.time_future', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
<%= t('user_block.revoke.time_future', :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %>
</b></p>
<% form_for :revoke, :url => { :action => "revoke" } do |f| %>
@ -20,6 +20,6 @@
<% else %>
<p>
<%= t('user_block.revoke.past', :time => distance_of_time_in_words_to_now(@user_block.end_at)) %>
<%= t('user_block.revoke.past', :time => distance_of_time_in_words_to_now(@user_block.ends_at)) %>
</p>
<% end %>

View file

@ -1,6 +1,6 @@
<h1><%= t('user_block.show.heading',
:block_on => @user_block.user.display_name,
:block_by => @user_block.moderator.display_name) %></h1>
:block_by => @user_block.creator.display_name) %></h1>
<% if @user_block.revoker %>
<p>
@ -9,17 +9,7 @@
</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.status' %></b>: <%= block_status(@user_block) %></p>
<p>
<b><%= t'user_block.show.reason' %></b>
@ -27,8 +17,8 @@
</p>
<% if @user_block.end_at > Time.now.getutc %>
<% if @user and @user.id == @user_block.moderator_id %>
<% if @user_block.ends_at > Time.now.getutc %>
<% if @user and @user.id == @user_block.creator_id %>
<%= link_to t('user_block.show.edit'), edit_user_block_path(@user_block) %> |
<% end %>
<% if @user and @user.moderator? %>

View file

@ -1053,14 +1053,15 @@ en:
revoke: "Revoke!"
confirm: "Are you sure?"
display_name: "Blocked User"
moderator_name: "Moderator"
creator_name: "Creator"
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)"
helper:
time_future: "Ends in {{time}}."
until_login: "Active until the user logs in."
time_past: "Ended {{time}} ago."
blocks_on:
heading: "List blocks on {{name}}"
blocks_by:
@ -1069,6 +1070,7 @@ en:
heading: "Block on {{block_on}} by {{block_by}}"
time_future: "Ends in {{time}}"
time_past: "Ended {{time}} ago"
status: "Status"
show: "Show"
edit: "Edit"
revoke: "Revoke!"

View file

@ -0,0 +1,29 @@
require 'lib/migrate'
class AlterUserRolesAndBlocks < ActiveRecord::Migration
def self.up
# the initial granter IDs can be "self" - there are none of these
# in the current live DB, but there may be some in people's own local
# copies.
add_column :user_roles, :granter_id, :bigint
UserRole.update_all("granter_id = user_id")
change_column :user_roles, :granter_id, :bigint, :null => false
add_foreign_key :user_roles, [:granter_id], :users, [:id]
# make sure that [user_id, role] is unique
add_index :user_roles, [:user_id, :role], :name => "user_roles_id_role_unique", :unique => true
# change the user_blocks to have a creator_id rather than moderator_id
rename_column :user_blocks, :moderator_id, :creator_id
# change the "end_at" column to the more grammatically correct "ends_at"
rename_column :user_blocks, :end_at, :ends_at
end
def self.down
remove_column :user_roles, :granter_id
remove_index :user_roles, :name => "user_roles_id_role_unique"
rename_column :user_blocks, :creator_id, :moderator_id
rename_column :user_blocks, :ends_at, :end_at
end
end

View file

@ -3,7 +3,9 @@
administrator:
user_id: 6
role: administrator
granter_id: 6
moderator:
user_id: 5
role: moderator
granter_id: 6