diff --git a/app/controllers/application.rb b/app/controllers/application.rb
--- a/app/controllers/application.rb
+++ b/app/controllers/application.rb
@@ -5,6 +5,18 @@
# Pick a unique cookie name to distinguish our session data from others'
session :session_key => '_grader_session_id'
+ SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
+
+ def authorization_by_roles(allowed_roles)
+ return false unless authenticate
+ user = User.find(session[:user_id])
+ unless user.roles.detect { |role| allowed_roles.member?(role.name) }
+ flash[:notice] = 'You are not authorized to view the page you requested'
+ redirect_to :controller => 'main', :action => 'login'
+ return false
+ end
+ end
+
protected
def authenticate
unless session[:user_id]
@@ -13,7 +25,7 @@
end
# check if run in single user mode
- if defined?(SINGLE_USER_MODE) and (SINGLE_USER_MODE)
+ if (Configuration[SINGLE_USER_MODE_CONF_KEY])
user = User.find(session[:user_id])
if user==nil or user.login != 'root'
redirect_to :controller => 'main', :action => 'login'
@@ -30,7 +42,7 @@
unless user.roles.detect { |role|
role.rights.detect{ |right|
right.controller == self.class.controller_name and
- (right.action == 'all' or right.action == action_name)
+ (right.action == 'all' or right.action == action_name)
}
}
flash[:notice] = 'You are not authorized to view the page you requested'
diff --git a/app/controllers/configurations_controller.rb b/app/controllers/configurations_controller.rb
new file mode 100644
--- /dev/null
+++ b/app/controllers/configurations_controller.rb
@@ -0,0 +1,20 @@
+class ConfigurationsController < ApplicationController
+
+ before_filter :authenticate
+ before_filter { |controller| controller.authorization_by_roles(['admin'])}
+
+ in_place_edit_for :configuration, :key
+ in_place_edit_for :configuration, :type
+ in_place_edit_for :configuration, :value
+
+ def index
+ @configurations = Configuration.find(:all,
+ :order => '`key`')
+ end
+
+ def reload
+ Configuration.reload
+ redirect_to :action => 'index'
+ end
+
+end
diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb
--- a/app/controllers/main_controller.rb
+++ b/app/controllers/main_controller.rb
@@ -11,7 +11,13 @@
end
def login
+ saved_notice = flash[:notice]
reset_session
+ flash[:notice] = saved_notice
+
+ @title = Configuration['ui.front.title']
+ @welcome = Configuration['ui.front.welcome_message']
+
render :action => 'login', :layout => 'empty'
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -19,6 +19,11 @@
# general options
append_to menu_items, '[Settings]', 'users', 'index'
+
+ if (user!=nil) and (user.admin?)
+ append_to menu_items, '[Site config]', 'configurations', 'index'
+ end
+
append_to menu_items, '[Log out]', 'main', 'login'
menu_items
diff --git a/app/helpers/configurations_helper.rb b/app/helpers/configurations_helper.rb
new file mode 100644
--- /dev/null
+++ b/app/helpers/configurations_helper.rb
@@ -0,0 +1,2 @@
+module ConfigurationsHelper
+end
diff --git a/app/models/configuration.rb b/app/models/configuration.rb
new file mode 100644
--- /dev/null
+++ b/app/models/configuration.rb
@@ -0,0 +1,43 @@
+class Configuration < ActiveRecord::Base
+
+ @@configurations = nil
+
+ def self.get(key)
+ if @@configurations == nil
+ self.read_config
+ end
+ return @@configurations[key]
+ end
+
+ def self.[](key)
+ self.get(key)
+ end
+
+ def self.reload
+ self.read_config
+ end
+
+ def self.clear
+ @@configurations = nil
+ end
+
+ protected
+ def self.read_config
+ @@configurations = {}
+ Configuration.find(:all).each do |conf|
+ key = conf.key
+ val = conf.value
+ case conf.value_type
+ when 'string'
+ @@configurations[key] = val
+
+ when 'integer'
+ @@configurations[key] = val.to_i
+
+ when 'boolean'
+ @@configurations[key] = (val=='true')
+ end
+ end
+ end
+
+end
diff --git a/app/views/configurations/index.html.haml b/app/views/configurations/index.html.haml
new file mode 100644
--- /dev/null
+++ b/app/views/configurations/index.html.haml
@@ -0,0 +1,26 @@
+- content_for :head do
+ = javascript_include_tag :defaults
+
+%h1 Grader configuration
+
+%table
+ %tr
+ %th Key
+ %th Type
+ %th Value
+
+ - @configurations.each do |conf|
+ - @configuration = conf
+ %tr
+ %td
+ = in_place_editor_field :configuration, :key, {}, :rows=>1
+ %td
+ = in_place_editor_field :configuration, :value_type, {}, :rows=>1
+ %td
+ = in_place_editor_field :configuration, :value, {}, :rows=>1
+
+%br/
+= link_to '[Reload configuration]', :action => 'reload'
+%br/
+Your config is saved, but it does not automatically take effect.
+You must reload.
diff --git a/app/views/main/login.rhtml b/app/views/main/login.rhtml
--- a/app/views/main/login.rhtml
+++ b/app/views/main/login.rhtml
@@ -1,6 +1,6 @@
-
Grader
+<%= @title %>
-Welcome back!
+<%= @welcome %>
Please login to see the problem list.
<% if flash[:notice] %>
diff --git a/config/environment.rb.SAMPLE b/config/environment.rb.SAMPLE
--- a/config/environment.rb.SAMPLE
+++ b/config/environment.rb.SAMPLE
@@ -62,6 +62,3 @@
# These are where inputs and outputs of test requests are stored
TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
TEST_REQUEST_OUTPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/output'
-
-# Uncomment this for single user mode (only root is allowed to log in)
-# SINGLE_USER_MODE = true
diff --git a/db/migrate/022_create_configurations.rb b/db/migrate/022_create_configurations.rb
new file mode 100644
--- /dev/null
+++ b/db/migrate/022_create_configurations.rb
@@ -0,0 +1,37 @@
+class CreateConfigurations < ActiveRecord::Migration
+ def self.up
+ create_table :configurations do |t|
+ t.column :key, :string
+ t.column :value_type, :string
+ t.column :value, :string
+ t.timestamps
+ end
+
+ Configuration.reset_column_information
+
+ Configuration.create(:key => 'system.single_user_mode',
+ :value_type => 'boolean',
+ :value => 'false')
+
+ Configuration.create(:key => 'ui.front.title',
+ :value_type => 'string',
+ :value => 'Grader')
+
+ Configuration.create(:key => 'ui.front.welcome_message',
+ :value_type => 'string',
+ :value => 'Welcome!')
+
+ Configuration.create(:key => 'ui.show_score',
+ :value_type => 'boolean',
+ :value => 'true')
+
+ Configuration.create(:key => 'contest.time_limit',
+ :value_type => 'string',
+ :value => 'unlimited')
+
+ end
+
+ def self.down
+ drop_table :configurations
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -9,7 +9,15 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 21) do
+ActiveRecord::Schema.define(:version => 22) do
+
+ create_table "configurations", :force => true do |t|
+ t.string "key"
+ t.string "value_type"
+ t.string "value"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
create_table "grader_processes", :force => true do |t|
t.string "host", :limit => 20
diff --git a/spec/controllers/main_controller_spec.rb b/spec/controllers/main_controller_spec.rb
--- a/spec/controllers/main_controller_spec.rb
+++ b/spec/controllers/main_controller_spec.rb
@@ -14,27 +14,27 @@
:source => 'sample source',
:compiler_message => 'none')
@user = mock(User, :id => 1, :login => 'john')
- Submission.should_receive(:find).with(@user.id.to_s).and_return(@submission)
+ Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
end
it "should let user sees her own source" do
- get 'source', {:id => 1}, {:user_id => 1}
+ get 'source', {:id => @submission.id}, {:user_id => 1}
response.should be_success
end
it "should let user sees her own compiler message" do
- get 'compiler_msg', {:id => 1}, {:user_id => 1}
+ get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
response.should be_success
end
it "should not let user sees other user's source" do
- get 'source', {:id => 1}, {:user_id => 2}
+ get 'source', {:id => @submission.id}, {:user_id => 2}
flash[:notice].should =~ /[Ee]rror/
response.should redirect_to(:action => 'list')
end
it "should not let user sees other user's compiler message" do
- get 'compiler_msg', {:id => 1}, {:user_id => 2}
+ get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
flash[:notice].should =~ /[Ee]rror/
response.should redirect_to(:action => 'list')
end
diff --git a/spec/models/configuration_spec.rb b/spec/models/configuration_spec.rb
new file mode 100644
--- /dev/null
+++ b/spec/models/configuration_spec.rb
@@ -0,0 +1,90 @@
+
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Configuration do
+
+ before(:each) do
+ @int_config = mock(Configuration,
+ :id => 1,
+ :key => 'mode',
+ :value_type => 'integer',
+ :value => '30')
+
+ @string_config = mock(Configuration,
+ :id => 2,
+ :key => 'title',
+ :value_type => 'string',
+ :value => 'Hello')
+
+ @boolean_config = mock(Configuration,
+ :id => 3,
+ :key => 'single_user_mode',
+ :value_type => 'boolean',
+ :value => 'true')
+ end
+
+ it "should retrieve int config" do
+ Configuration.should_receive(:find).once.with(:all).
+ and_return([@int_config, @string_config, @boolean_config])
+
+ Configuration.clear
+ val = Configuration.get('mode')
+ val.should == 30
+ end
+
+ it "should retrieve boolean config" do
+ Configuration.should_receive(:find).once.with(:all).
+ and_return([@int_config, @string_config, @boolean_config])
+
+ Configuration.clear
+ val = Configuration.get('single_user_mode')
+ val.should == true
+ end
+
+ it "should retrieve string config" do
+ Configuration.should_receive(:find).once.with(:all).
+ and_return([@int_config, @string_config, @boolean_config])
+
+ Configuration.clear
+ val = Configuration.get('title')
+ val.should == "Hello"
+ end
+
+ it "should retrieve config with []" do
+ Configuration.should_receive(:find).once.with(:all).
+ and_return([@int_config, @string_config, @boolean_config])
+
+ Configuration.clear
+ val = Configuration['title']
+ val.should == "Hello"
+ end
+
+ it "should read config table once" do
+ Configuration.should_receive(:find).once.with(:all).
+ and_return([@int_config, @string_config, @boolean_config])
+
+ Configuration.clear
+ val = Configuration.get('title')
+ val.should == "Hello"
+ val = Configuration.get('single_user_mode')
+ val.should == true
+ val = Configuration.get('mode')
+ val.should == 30
+ end
+
+ it "should be able to reload config" do
+ Configuration.should_receive(:find).twice.with(:all).
+ and_return([@int_config, @string_config, @boolean_config],
+ [mock(Configuration,
+ :key => 'title', :value_type => 'string',
+ :value => 'Goodbye')])
+
+ Configuration.clear
+ val = Configuration.get('title')
+ val.should == "Hello"
+ Configuration.reload
+ val = Configuration.get('title')
+ val.should == "Goodbye"
+ end
+
+end
diff --git a/test/fixtures/configurations.yml b/test/fixtures/configurations.yml
new file mode 100644
--- /dev/null
+++ b/test/fixtures/configurations.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+# one:
+# column: value
+#
+# two:
+# column: value
diff --git a/test/functional/configurations_controller_test.rb b/test/functional/configurations_controller_test.rb
new file mode 100644
--- /dev/null
+++ b/test/functional/configurations_controller_test.rb
@@ -0,0 +1,8 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ConfigurationsControllerTest < ActionController::TestCase
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end
diff --git a/test/unit/configuration_test.rb b/test/unit/configuration_test.rb
new file mode 100644
--- /dev/null
+++ b/test/unit/configuration_test.rb
@@ -0,0 +1,8 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ConfigurationTest < ActiveSupport::TestCase
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end