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
+
+
+
+ | Login |
+ Full name |
+ Authorization type |
+ Open ID URL |
+ Total actions |
+ Total contexts |
+ Total projects |
+ Total notes |
+ |
+
+ <% for user in @users %>
+ id="user-<%= user.id %>">
+ | <%=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" } ) : " " %> |
+
+ <% end %>
+
+
+ <%= 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