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