From 26c7a1e6f12258856a572a20ace02a53bde3da02 Mon Sep 17 00:00:00 2001 From: bsag Date: Sun, 10 Dec 2006 19:30:19 +0000 Subject: [PATCH] Created an admin controller that allows the admin user (only) to view the users on the system and destroy and signup new users. Destroying a user deletes all of their actions, contexts, projects and notes. The link to the admin page only appears when an admin user is logged in, and the signup link is now on the admin page, rather than appearing in the mini-links at the top of the page. The page also lists some statistics associated with each user (the number of actions, contexts, projects etc. each user has). git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@371 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/app/controllers/admin_controller.rb | 51 +++++++++++++++++++ tracks/app/helpers/admin_helper.rb | 2 + tracks/app/models/user.rb | 8 +-- tracks/app/views/admin/create.rhtml | 2 + tracks/app/views/admin/destroy.rjs | 7 +++ tracks/app/views/admin/error.rjs | 1 + tracks/app/views/admin/index.rhtml | 36 +++++++++++++ tracks/app/views/admin/update.rhtml | 2 + tracks/app/views/layouts/standard.rhtml | 6 +-- tracks/config/routes.rb | 5 ++ tracks/db/schema.rb | 26 +++++----- tracks/public/stylesheets/standard.css | 15 +++++- .../test/functional/admin_controller_test.rb | 41 +++++++++++++++ 13 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 tracks/app/controllers/admin_controller.rb create mode 100644 tracks/app/helpers/admin_helper.rb create mode 100644 tracks/app/views/admin/create.rhtml create mode 100644 tracks/app/views/admin/destroy.rjs create mode 100644 tracks/app/views/admin/error.rjs create mode 100644 tracks/app/views/admin/index.rhtml create mode 100644 tracks/app/views/admin/update.rhtml create mode 100644 tracks/test/functional/admin_controller_test.rb diff --git a/tracks/app/controllers/admin_controller.rb b/tracks/app/controllers/admin_controller.rb new file mode 100644 index 00000000..7130060a --- /dev/null +++ b/tracks/app/controllers/admin_controller.rb @@ -0,0 +1,51 @@ +class AdminController < ApplicationController + + before_filter :login_required + before_filter :admin_login_required + layout 'standard' + + def index + @user_pages, @users = paginate :users, :order => 'login ASC', :per_page => 10 + @total_users = User.find(:all).size + # When we call login/signup from the admin page + # we store the URL so that we get returned here when signup is successful + store_location + end + + def destroy + @deleted_user = User.find_by_id(params[:id]) + @saved = @deleted_user.destroy + @total_users = User.find(:all).size + + respond_to do |wants| + + wants.html do + if @saved + notify :notice, "Successfully deleted user #{@deleted_user.login}", 2.0 + redirect_to :action => 'index' + else + notify :error, "Failed to delete user #{@deleted_user.login}", 2.0 + redirect_to :action => 'index' + end + end + + wants.js do + render + end + + wants.xml { render :text => '200 OK. User deleted.', :status => 200 } + + end + end + + protected + + def admin_login_required + unless User.find_by_id_and_is_admin(session['user_id'], true) + notify :error, "Only admin users are allowed access to this function" + redirect_to :controller => 'todo', :action => 'index' + return false + end + end + +end diff --git a/tracks/app/helpers/admin_helper.rb b/tracks/app/helpers/admin_helper.rb new file mode 100644 index 00000000..d5c6d355 --- /dev/null +++ b/tracks/app/helpers/admin_helper.rb @@ -0,0 +1,2 @@ +module AdminHelper +end diff --git a/tracks/app/models/user.rb b/tracks/app/models/user.rb index 9ee55367..96b524ec 100644 --- a/tracks/app/models/user.rb +++ b/tracks/app/models/user.rb @@ -1,10 +1,10 @@ require 'digest/sha1' class User < ActiveRecord::Base - has_many :contexts, :order => "position ASC" - has_many :projects, :order => "position ASC" - has_many :todos, :order => "completed_at DESC, created_at DESC" - has_many :notes, :order => "created_at DESC" + has_many :contexts, :order => "position ASC", :dependent => :delete_all + has_many :projects, :order => "position ASC", :dependent => :delete_all + has_many :todos, :order => "completed_at DESC, created_at DESC", :dependent => :delete_all + has_many :notes, :order => "created_at DESC", :dependent => :delete_all has_one :preference attr_protected :is_admin diff --git a/tracks/app/views/admin/create.rhtml b/tracks/app/views/admin/create.rhtml new file mode 100644 index 00000000..c43706fa --- /dev/null +++ b/tracks/app/views/admin/create.rhtml @@ -0,0 +1,2 @@ +

Admin#create

+

Find me in app/views/admin/create.rhtml

diff --git a/tracks/app/views/admin/destroy.rjs b/tracks/app/views/admin/destroy.rjs new file mode 100644 index 00000000..a3dd1121 --- /dev/null +++ b/tracks/app/views/admin/destroy.rjs @@ -0,0 +1,7 @@ +if @saved + page["user-#{@deleted_user.id}"].remove + page['user_count'].replace_html @total_users.to_s + page.notify :notice, "User #{@deleted_user.login} was successfully destroyed", 2.0 +else + page.notify :error, "There was an error deleting the user #{@deleted_user.login}", 8.0 +end \ No newline at end of file diff --git a/tracks/app/views/admin/error.rjs b/tracks/app/views/admin/error.rjs new file mode 100644 index 00000000..4f82b3e4 --- /dev/null +++ b/tracks/app/views/admin/error.rjs @@ -0,0 +1 @@ +page.notify :error, @error_message || "An error occurred on the server.", 8.0 \ No newline at end of file diff --git a/tracks/app/views/admin/index.rhtml b/tracks/app/views/admin/index.rhtml new file mode 100644 index 00000000..6054aac4 --- /dev/null +++ b/tracks/app/views/admin/index.rhtml @@ -0,0 +1,36 @@ +

Manage users

+ +

You have a total of <%= @total_users %> users

+ + + + + + + + + + + + + + <% for user in @users %> + id="user-<%= user.id %>"> + + + + + + + + + + + <% end %> +
LoginFull nameAuthorization typeOpen ID URLTotal actionsTotal contextsTotal projectsTotal notes 
<%=h user.login %><%=h user.last_name? ? user.display_name : '-' %><%= h user.auth_type %><%= h user.open_id_url || '-' %><%= h user.todos.size %><%= h user.contexts.size %><%= h user.projects.size %><%= h user.notes.size %><%= !user.is_admin? ? link_to_remote( image_tag("blank.png", :title =>"Destroy user", :class=>"delete_item"), {:url => { :controller => 'admin', :action => 'destroy', :id => user.id }, :confirm => "Warning: this will delete user \'#{user.login}\', all their actions, contexts, project and notes. Are you sure that you want to continue?" }, { :class => "icon" } ) : " " %>
+

+ <%= link_to "« Previous page", { :page => @user_pages.current.previous } if @user_pages.current.previous %>   + <%= link_to "Next page »", { :page => @user_pages.current.next } if @user_pages.current.next %> +

+ +

<%= link_to 'Signup new user', :controller => 'login', :action => 'signup' %>

\ No newline at end of file diff --git a/tracks/app/views/admin/update.rhtml b/tracks/app/views/admin/update.rhtml new file mode 100644 index 00000000..a84656bc --- /dev/null +++ b/tracks/app/views/admin/update.rhtml @@ -0,0 +1,2 @@ +

Admin#update

+

Find me in app/views/admin/update.rhtml

diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml index 9b329728..5abc5afe 100644 --- a/tracks/app/views/layouts/standard.rhtml +++ b/tracks/app/views/layouts/standard.rhtml @@ -35,9 +35,6 @@ page.select('.notes').each { |e| e.toggle } end -%> |  - <% if @user.is_admin? -%> - <%= link_to "Add users", :controller => "login", :action => "signup" %> |  - <% end -%> <%= link_to "Logout (#{@user.display_name}) ยป", :controller => "login", :action=>"logout"%> @@ -50,6 +47,9 @@
  • <%= navigation_link( "Done", {:controller => "todo", :action => "completed"}, {:accesskey=>"d", :title=>"Completed"} ) %>
  • <%= navigation_link( "Notes", {:controller => "note", :action => "index"}, {:accesskey => "o", :title => "Show all notes"} ) %>
  • <%= navigation_link( "Preferences", {:controller => "user", :action => "preferences"}, {:accesskey => "u", :title => "Show my preferences"} ) %>
  • + <% if @user.is_admin? -%> +
  • <%= navigation_link("Admin", {:controller => "admin", :action => "index"}, {:accesskey => "a", :title => "Add or delete users"} ) %>
  • + <% end -%>
  • <%= navigation_link(image_tag("feed-icon.png", :size => "16X16", :border => 0), {:controller => "feed", :action => "index"}, :title => "See a list of available feeds" ) %>
  • diff --git a/tracks/config/routes.rb b/tracks/config/routes.rb index 809e438f..d5b5011e 100644 --- a/tracks/config/routes.rb +++ b/tracks/config/routes.rb @@ -16,6 +16,11 @@ ActionController::Routing::Routes.draw do |map| # Index Route map.connect '', :controller => 'todo', :action => 'index' + + # Admin Routes + map.connect 'admin', :controller => 'admin', :action => 'index' + # map.connect 'admin/signup', :controller => 'admin', :action => 'create' + map.connect 'admin/destroy/:id', :controller => 'admin', :action => 'destroy', :requirements => {:id => /\d+/} # Mobile/lite version map.connect 'mobile', :controller => 'mobile', :action => 'index' diff --git a/tracks/db/schema.rb b/tracks/db/schema.rb index e16f95d1..d2906f34 100644 --- a/tracks/db/schema.rb +++ b/tracks/db/schema.rb @@ -5,10 +5,10 @@ ActiveRecord::Schema.define(:version => 20) do create_table "contexts", :force => true do |t| - t.column "name", :string, :default => "", :null => false - t.column "hide", :integer, :limit => 4, :default => 0, :null => false - t.column "position", :integer, :default => 0, :null => false - t.column "user_id", :integer, :default => 0, :null => false + t.column "name", :string, :default => "", :null => false + t.column "position", :integer, :default => 0, :null => false + t.column "hide", :boolean, :default => false + t.column "user_id", :integer, :default => 1 end create_table "notes", :force => true do |t| @@ -56,7 +56,7 @@ ActiveRecord::Schema.define(:version => 20) do create_table "projects", :force => true do |t| t.column "name", :string, :default => "", :null => false t.column "position", :integer, :default => 0, :null => false - t.column "user_id", :integer, :default => 0, :null => false + t.column "user_id", :integer, :default => 1 t.column "description", :text t.column "state", :string, :limit => 20, :default => "active", :null => false end @@ -70,23 +70,23 @@ ActiveRecord::Schema.define(:version => 20) do add_index "sessions", ["session_id"], :name => "sessions_session_id_index" create_table "todos", :force => true do |t| - t.column "context_id", :integer, :default => 0, :null => false - t.column "description", :string, :limit => 100, :default => "", :null => false + t.column "context_id", :integer, :default => 0, :null => false + t.column "project_id", :integer + t.column "description", :string, :default => "", :null => false t.column "notes", :text t.column "created_at", :datetime t.column "due", :date t.column "completed_at", :datetime - t.column "project_id", :integer - t.column "user_id", :integer, :default => 0, :null => false + t.column "user_id", :integer, :default => 1 t.column "show_from", :date - t.column "state", :string, :limit => 20, :default => "immediate", :null => false + t.column "state", :string, :limit => 20, :default => "immediate", :null => false end create_table "users", :force => true do |t| - t.column "login", :string, :limit => 80 - t.column "password", :string, :limit => 40 + t.column "login", :string, :limit => 80, :default => "", :null => false + t.column "password", :string, :limit => 40, :default => "", :null => false t.column "word", :string - t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false + t.column "is_admin", :boolean, :default => false, :null => false t.column "first_name", :string t.column "last_name", :string t.column "auth_type", :string, :default => "database", :null => false diff --git a/tracks/public/stylesheets/standard.css b/tracks/public/stylesheets/standard.css index 2fbcaf31..8492537a 100644 --- a/tracks/public/stylesheets/standard.css +++ b/tracks/public/stylesheets/standard.css @@ -206,7 +206,7 @@ h2 a:hover { .item-container-drop-target { border:2px inset black; } -.container a.icon { +a.icon { float: left; vertical-align: middle; background-color: transparent; @@ -728,4 +728,15 @@ div.page_name_auto_complete ul strong.highlight { color: #800; margin: 0; padding: 0; -} \ No newline at end of file +} + +table.users_table { + width: 100%; + text-align: center; + border: 1px solid #666; + background-color: #fff; + border-spacing: 0px; +} + +.users_table th {color: #fff; background-color: #000;} +.users_table td {border: none;} \ No newline at end of file diff --git a/tracks/test/functional/admin_controller_test.rb b/tracks/test/functional/admin_controller_test.rb new file mode 100644 index 00000000..ff33052a --- /dev/null +++ b/tracks/test/functional/admin_controller_test.rb @@ -0,0 +1,41 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'admin_controller' + +# Re-raise errors caught by the controller. +class AdminController; def rescue_action(e) raise e end; end + +class AdminControllerTest < Test::Unit::TestCase + fixtures :users, :preferences, :projects, :contexts, :todos + + def setup + @controller = AdminController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_get_index_when_not_logged_in + get :index + assert_redirected_to :controller => 'login', :action => 'login' + end + + def test_get_index_by_nonadmin + @request.session['user_id'] = users(:other_user).id + get :index + assert_redirected_to :controller => 'todo', :action => 'index' + end + + def test_get_index_by_admin + @request.session['user_id'] = users(:admin_user).id + get :index + assert_response :success + end + + def test_destroy_user + @no_users_before = User.find(:all).size + @request.session['user_id'] = users(:admin_user).id + xhr :post, :destroy, :id => 3 + assert_rjs :page, "user-3", :remove + assert_equal @no_users_before-1, User.find(:all).size + end + +end