Permalink
Browse files

This is TrackRecord v2.0, for Rails v3.2.

  • Loading branch information...
1 parent bee793c commit e9f824ac36fce0cd8ab72126477713015b50be82 @pond committed Mar 27, 2013
Showing 607 changed files with 50,279 additions and 40,475 deletions.
View
318 CHANGELOG

Large diffs are not rendered by default.

Oops, something went wrong.
View
67 Gemfile
@@ -0,0 +1,67 @@
+source 'http://rubygems.org'
+
+# The following line requires Bundler v1.2 or later; see:
+# http://gembundler.com/v1.2/whats_new.html
+ruby '1.9.3'
+
+gem 'rails', '3.2.13'
+
+# Bundle edge Rails instead:
+# gem 'rails', :git => 'git://github.com/rails/rails.git'
+
+gem 'pg'
+gem 'json'
+
+# Gems used only for assets and not required
+# in production environments by default.
+# group :assets do
+# gem 'sass-rails', " ~> 3.1.0"
+# gem 'coffee-rails', "~> 3.1.0"
+# gem 'uglifier'
+# end
+
+gem 'prototype-rails'
+
+# Uncomment to use thin as the 'rails server' web server.
+# gem 'thin'
+
+# Deploy with Capistrano
+# gem 'capistrano'
+
+# To use debugger
+# gem 'ruby-debug'
+
+# https://github.com/timcharper/calendar_date_select (original, but not Rails 3 compatible)
+# http://github.com/paneq/calendar_date_select (Rails 3 fork)
+# https://github.com/openid/ruby-openid
+# https://github.com/rails/open_id_authentication
+# https://github.com/mislav/will_paginate/
+# https://github.com/collectiveidea/audited
+# https://github.com/swanandp/acts_as_list
+
+gem 'calendar_date_select', '~> 1.6', :git => 'http://github.com/paneq/calendar_date_select'
+gem 'ruby-openid', '~> 2.2'
+gem 'open_id_authentication'
+gem 'will_paginate', '~> 3.0'
+gem 'audited-activerecord', '~> 3.0'
+gem 'acts_as_list'
+
+# If you want the charting stuff for some reason... Note that
+# this brings in awkward dependencies such as ImageMagick via
+# rmagick.
+#
+# https://github.com/topfunky/gruff
+#
+# gem 'gruff'
+# gem 'rmagick', :require => false
+
+# https://github.com/tenderlove/rails_autolink
+# https://github.com/joelmoss/dynamic_form
+
+gem 'rails_autolink'
+gem 'dynamic_form'
+
+# Visualisation aid - for more information, see:
+# http://railroady.prestonlee.com/
+
+gem 'railroady'
View
@@ -1,4 +1,4 @@
-Copyright (c) 2008, 2009 Andrew Hodgkinson
+Copyright (c) 2007-2013 Andrew Hodgkinson
All rights reserved.
Redistribution and use in source and binary forms, with or without
View
Binary file not shown.
View
@@ -1,4 +1,4 @@
-== Welcome to TrackRecord v1.54
+== Welcome to TrackRecord v2.0
TrackRecord is a timesheet system written for the Ruby On Rails web
development framework. More information can be found at:
@@ -12,3 +12,6 @@ for details along with:
See the CHANGELOG file for information on changes and how to upgrade
from an earlier version of the application.
+
+Technical documentation can be found by opening 'doc/app/index.html'
+in your preferred web browser.
View
@@ -1,10 +1,7 @@
+#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-require(File.join(File.dirname(__FILE__), 'config', 'boot'))
+require File.expand_path('../config/application', __FILE__)
-require 'rake'
-require 'rake/testtask'
-require 'rake/rdoctask'
-
-require 'tasks/rails'
+Trackrecord::Application.load_tasks
@@ -1,6 +1,6 @@
########################################################################
# File:: application_controller.rb
-# (C):: Hipposoft 2008, 2009
+# (C):: Hipposoft 2007
#
# Purpose:: Standard Rails application controller.
# ----------------------------------------------------------------------
@@ -71,18 +71,15 @@ def appctrl_show( model )
def appctrl_new( model )
return appctrl_not_permitted() if ( @current_user.restricted? )
-
- @record = model.constantize.new
- @record.assign_defaults( @current_user )
+ @record = model.constantize.new( nil, @current_user )
end
# Create a new object following submission of a 'create' view form.
# Restricted users can't do this. Pass the model name as a string.
def appctrl_create( model )
return appctrl_not_permitted() if ( @current_user.restricted? )
-
- @record = model.constantize.new( params[ model.downcase ] )
+ @record = model.constantize.new( params[ model.downcase ], @current_user )
if ( @record.save )
flash[ :notice ] = "New #{ model.downcase } added"
@@ -156,19 +153,48 @@ def appctrl_delete_confirm( model )
# method has been given, for sorting purposes.
def appctrl_index_assist( model )
+
+ # Set up some default sort and pagination data.
+
+ default_sort = -1 # "-1" => "unknown"
default_direction = model::DEFAULT_SORT_DIRECTION.downcase
default_entries = 10
default_page = 1
- params[ :sort ] = "#{ -1 }" if ( params[ :sort ].nil? )
- params[ :page ] = "#{ default_page }" if ( params[ :page ].nil? )
- params[ :entries ] = "#{ default_entries }" if ( params[ :entries ].nil? )
- params[ :direction ] = "#{ default_direction }" if ( params[ :direction ].nil? )
+ # Attempt to read user preferences for sorting and pagination in index
+ # views for the given model. Note the heavy use of "try()" to tolerate
+ # 'nil' values propagated through, e.g. due to no logged in user, or
+ # a user with no control panel (not that this ought to ever happen).
+
+ prefs_prefix = "sorting.#{ model.name.downcase }."
+ cp = @current_user.try( :control_panel )
+ cp_sort = cp.try( :get_preference, "#{ prefs_prefix }sort" )
+ cp_direction = cp.try( :get_preference, "#{ prefs_prefix }direction" )
+ cp_entries = cp.try( :get_preference, "#{ prefs_prefix }entries" )
- sort = params[ :sort ].to_i
- page = params[ :page ].to_i
- entries = params[ :entries ].to_i
+ # For each one, try to read from the parameters; or fall back to the user
+ # settings; or fall back to the defaults. If the value so determined is
+ # different from the user's current setting, then update that setting.
+
+ sort = params[ :sort ].try( :to_i ) || cp_sort || default_sort
+ cp.try( :set_preference, "#{ prefs_prefix }sort", sort ) unless ( cp_sort == sort )
+
+ direction = params[ :direction ] || cp_direction || default_direction
+ cp.try( :set_preference, "#{ prefs_prefix }direction", direction ) unless ( cp_direction == direction )
+
+ entries = params[ :entries ].try( :to_i ) || cp_entries || default_entries
entries = default_entries if ( entries <= 0 or entries > 500 )
+ cp.try( :set_preference, "#{ prefs_prefix }entries", entries ) unless ( cp_entries == entries )
+
+ # Establish a page number, then write the final determined values back into
+ # the parameters hash as views or plugins may refer to these directly.
+
+ page = params[ :page ].try( :to_i ) || default_page
+
+ params[ :sort ] = sort.to_s
+ params[ :direction ] = direction
+ params[ :entries ] = entries.to_s
+ params[ :page ] = page.to_s
if ( 0..@columns.length ).include?( sort )
@@ -197,7 +223,7 @@ def appctrl_index_assist( model )
end
end
- if ( params[ :direction ] == 'desc' )
+ if ( direction == 'desc' )
order << ' DESC'
else
order << ' ASC'
@@ -206,6 +232,88 @@ def appctrl_index_assist( model )
return { :page => page, :per_page => entries, :order => order }
end
+ # Given a key for the params hash, construct a date from the value
+ # associated with the key. If the key is not present or has an emtpy
+ # value, or if any exception occurs trying to parse the date, the
+ # function returns 'nil'; else it returns a Date object instance.
+ #
+ def appctrl_date_from_params( key )
+ unless ( params[ key ].blank? )
+ begin
+ return Date.parse( params[ key ] )
+ rescue
+ # Do nothing - drop through to have-no-date case
+ end
+ end
+
+ nil
+ end
+
+ # Return an array giving a start date and end date based on search
+ # form submission data in the params hash. Pass a default start and
+ # end date for the case where none has been provided in the params,
+ # or the provided value is invalid.
+ #
+ def appctrl_dates_from_search( default_start, default_end )
+ a = appctrl_date_from_params( :search_range_start ) || default_start
+ b = appctrl_date_from_params( :search_range_end ) || default_end
+
+ a, b = b, a if ( a > b )
+
+ return [ a, b ]
+ end
+
+ # Return an SQL fragment of the form "date-field >= :range_start AND
+ # date-field <= bar :range_end" where the ranges are dates obtained
+ # from "appctrl_dates_from_search" and 'date-field' is given as an
+ # optional second input parameter. The return value includes the
+ # ranges which you pass in using named parameter substitution when
+ # the SQL fragment is included in a wider query.
+ #
+ # Pass the model being searched; it must support a 'used_range'
+ # class method that returns a Range of years for all existant
+ # instances in the database at the time of calling, for the field
+ # that is to be searched. The second parameter is that field, or
+ # if omitted, the model's USED_RANGE_COLUMN by default.
+ #
+ # Returns an array (anticipating parallel assignment by the caller)
+ # with the SQl data, the start Date and the end Date of the range,
+ # or an array of 'nil' if there is no usable search data in the
+ # parameters hash (or it is being explicitly cleared).
+ #
+ # Intended side-effect: Makes sure that search parameters are cleared
+ # out if an explicit 'search_cancel' params key has a value.
+ #
+ def appctrl_search_range_sql( model, field = nil )
+ sql = range_start = range_end = nil
+
+ field = model::USED_RANGE_COLUMN if ( field.nil? )
+
+ unless ( params[ :search ].nil? )
+ if ( ( params[ :search ].blank? and params[ :search_range_start ].blank? and params[ :search_range_end ].blank? ) or params[ :search_cancel ] )
+ params.delete( :search )
+ params.delete( :search_range_start )
+ params.delete( :search_range_end )
+ else
+ range = model.used_range()
+ range_start, range_end = appctrl_dates_from_search(
+ Date.new( range.first ), # I.e. start year
+ Date.new( range.last + 1 ) - 1 # I.e. start of year after end year, minus one day; that is, the last day of end year
+ )
+
+ # Since SQL date-only queries work on a 'start of the day' basis,
+ # we do a ">=" start and a "<" end comparison, setting the end to
+ # the beginning of the *next* day.
+
+ range_end += 1
+
+ sql = "#{ model.table_name }.#{ field } >= :range_start AND #{ model.table_name }.#{ field } < :range_end AND"
+ end
+ end
+
+ [ sql, range_start, range_end ]
+ end
+
# YUI tree form submission will present selected task IDs as a single string
# in a comma separated list; the non-JS code does it properly as an array of
# IDs. Sort this out by patching the params hash. Pass the item to patch
@@ -250,7 +358,7 @@ def appctrl_confirm_user
# redirect back to that form.
def appctrl_ensure_user_name
- if ( ( not @current_user.nil? ) and @current_user.name.empty? )
+ if ( ( not @current_user.nil? ) and @current_user.name.blank? )
redirect_to( edit_user_path( @current_user) )
end
end
@@ -1,6 +1,6 @@
########################################################################
# File:: audits_controller.rb
-# (C):: Hipposoft 2008, 2009
+# (C):: Hipposoft 2008
#
# Purpose:: Manage Acts As Audited tables.
# ----------------------------------------------------------------------
@@ -9,6 +9,8 @@
class AuditsController < ApplicationController
+ uses_prototype( :only => :index )
+
# Security.
before_filter( :permitted? )
@@ -32,24 +34,21 @@ def index
# Get the basic options hash from ApplicationController, then handle
# search forms.
- options = appctrl_index_assist( Audit )
+ options = appctrl_index_assist( Audited::Adapters::ActiveRecord::Audit )
conditions_vars = {}
- unless ( params[ :search ].nil? )
- if ( params[ :search ].empty? or params[ :search_cancel ] )
- params.delete( :search )
- else
- search_num = params[ :search ].to_i
- search_str = "%#{ params[ :search ] }%" # SQL wildcards either side of the search string
- conditions_sql = 'WHERE ( action ILIKE :search_str OR auditable_type ILIKE :search_str OR users.name ILIKE :search_str OR version ILIKE :search_num )'
- conditions_vars = { :search_num => search_num, :search_str => search_str }
- end
- end
+ search_num = params[ :search ].to_i
+ search_str = "%#{ params[ :search ] }%" # SQL wildcards either side of the search string
+
+ range_sql, range_start, range_end = appctrl_search_range_sql( Audited::Adapters::ActiveRecord::Audit )
+
+ conditions_sql = "WHERE #{ range_sql } ( action ILIKE :search_str OR auditable_type ILIKE :search_str OR users.name ILIKE :search_str OR version = :search_num )"
+ conditions_vars = { :search_num => search_num, :search_str => search_str, :range_start => range_start, :range_end => range_end }
# Sort order is already partially compiled in 'options' from the earlier
# call to 'appctrl_index_assist'.
- order_sql = "ORDER BY #{ options[ :order ] }"
+ order_sql = "ORDER BY #{ options[ :order ] }, created_at DESC"
options.delete( :order )
# Construct the query.
@@ -61,7 +60,7 @@ def index
# Now paginate using this SQL query.
- @audits = Audit.paginate_by_sql( [ finder_sql, conditions_vars ], options )
+ @audits = Audited::Adapters::ActiveRecord::Audit.paginate_by_sql( [ finder_sql, conditions_vars ], options )
end
private
@@ -1,6 +1,6 @@
########################################################################
# File:: charts_controller.rb
-# (C):: Hipposoft 2008, 2009
+# (C):: Hipposoft 2008
#
# Purpose:: Use the Gruff library to generate charts in PNG format for
# inclusion in HTML reports.
@@ -40,7 +40,7 @@ def show
g = Gruff::Mini::Pie.new( "#{ width }x#{ width * 0.75 }" )
g.title = 'Chart'
- g.font = "#{ RAILS_ROOT }/#{ GRAPH_FONT }"
+ g.font = "#{ Rails.root }/#{ GRAPH_FONT }"
g.hide_legend = true
g.hide_title = true
g.marker_font_size = 70
@@ -1,6 +1,6 @@
########################################################################
# File:: control_panels_controller.rb
-# (C):: Hipposoft 2008, 2009
+# (C):: Hipposoft 2008
#
# Purpose:: Manage User settings through a ControlPanel object.
# ----------------------------------------------------------------------
Oops, something went wrong.

0 comments on commit e9f824a

Please sign in to comment.