From ec3ee77797cd293716b2e62f2576db4f1c1ea846 Mon Sep 17 00:00:00 2001 From: bsag Date: Sun, 9 Jan 2005 11:59:57 +0000 Subject: [PATCH] Initial import git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@1 a4c988fc-2ded-0310-b66e-134b36920a42 --- tracks/README_FIRST.txt | 13 + tracks/README_LOGIN | 29 + tracks/Rakefile | 112 ++ tracks/app/controllers/application.rb | 18 + tracks/app/controllers/context_controller.rb | 94 ++ tracks/app/controllers/feed_controller.rb | 33 + tracks/app/controllers/login_controller.rb | 49 + tracks/app/controllers/project_controller.rb | 107 ++ tracks/app/controllers/todo_controller.rb | 111 ++ tracks/app/helpers/application_helper.rb | 51 + tracks/app/helpers/context_helper.rb | 2 + tracks/app/helpers/feed_helper.rb | 34 + tracks/app/helpers/login_helper.rb | 18 + tracks/app/helpers/project_helper.rb | 2 + tracks/app/helpers/todo_helper.rb | 36 + tracks/app/models/context.rb | 3 + tracks/app/models/project.rb | 3 + tracks/app/models/todo.rb | 16 + tracks/app/models/user.rb | 30 + tracks/app/views/context/list.rhtml | 32 + tracks/app/views/context/show.rhtml | 61 + tracks/app/views/feed/na_feed.rxml | 14 + tracks/app/views/feed/na_text.rhtml | 1 + tracks/app/views/layouts/scaffold.rhtml | 10 + tracks/app/views/layouts/standard.rhtml | 38 + tracks/app/views/login/login.rhtml | 22 + tracks/app/views/login/logout.rhtml | 10 + tracks/app/views/login/signup.rhtml | 17 + tracks/app/views/login/welcome.rhtml | 13 + tracks/app/views/project/list.rhtml | 32 + tracks/app/views/project/show.rhtml | 65 ++ tracks/app/views/todo/_done.rhtml | 29 + tracks/app/views/todo/completed.rhtml | 10 + tracks/app/views/todo/edit.rhtml | 48 + tracks/app/views/todo/list.rhtml | 89 ++ tracks/config/database.yml.tmpl | 20 + tracks/config/environment.rb | 63 + tracks/config/environments/development.rb | 3 + tracks/config/environments/production.rb | 2 + tracks/config/environments/test.rb | 3 + tracks/config/settings.yml.tmpl | 2 + tracks/db/tracks_dump 03.01.2005.sql | 88 ++ tracks/doc/CHANGENOTES.txt | 34 + tracks/doc/README.txt | 40 + tracks/lib/login_system.rb | 87 ++ tracks/lib/redcloth.rb | 1018 +++++++++++++++++ tracks/log.tmpl/apache.log | 0 tracks/log.tmpl/development.log | 0 tracks/log.tmpl/production.log | 0 tracks/log.tmpl/test.log | 0 tracks/public/.htaccess | 29 + tracks/public/404.html | 6 + tracks/public/500.html | 6 + tracks/public/_doc/index.html | 70 ++ tracks/public/dispatch.cgi | 10 + tracks/public/dispatch.fcgi | 7 + tracks/public/dispatch.rb | 10 + tracks/public/images/delete.png | Bin 0 -> 3128 bytes tracks/public/images/done.png | Bin 0 -> 905 bytes tracks/public/images/edit.png | Bin 0 -> 3055 bytes tracks/public/images/notes.png | Bin 0 -> 2963 bytes tracks/public/index.html | 1 + tracks/public/javascripts/toggle_notes.js | 13 + tracks/public/stylesheets/scaffold.css | 51 + tracks/public/stylesheets/standard.css | 251 ++++ tracks/script/breakpointer | 4 + tracks/script/console | 30 + tracks/script/generate | 69 ++ tracks/script/server | 49 + tracks/test/fixtures/contexts.yml | 1 + tracks/test/fixtures/projects.yml | 1 + tracks/test/fixtures/todos.yml | 1 + tracks/test/fixtures/users.yml | 1 + .../functional/context_controller_test.rb | 17 + .../test/functional/feed_controller_test.rb | 17 + .../test/functional/login_controller_test.rb | 17 + .../functional/project_controller_test.rb | 17 + .../test/functional/todo_controller_test.rb | 17 + tracks/test/test_helper.rb | 14 + tracks/test/unit/context_test.rb | 10 + tracks/test/unit/project_test.rb | 10 + tracks/test/unit/todo_test.rb | 10 + tracks/test/unit/user_test.rb | 10 + 83 files changed, 3361 insertions(+) create mode 100644 tracks/README_FIRST.txt create mode 100644 tracks/README_LOGIN create mode 100644 tracks/Rakefile create mode 100644 tracks/app/controllers/application.rb create mode 100644 tracks/app/controllers/context_controller.rb create mode 100644 tracks/app/controllers/feed_controller.rb create mode 100644 tracks/app/controllers/login_controller.rb create mode 100644 tracks/app/controllers/project_controller.rb create mode 100644 tracks/app/controllers/todo_controller.rb create mode 100644 tracks/app/helpers/application_helper.rb create mode 100644 tracks/app/helpers/context_helper.rb create mode 100644 tracks/app/helpers/feed_helper.rb create mode 100644 tracks/app/helpers/login_helper.rb create mode 100644 tracks/app/helpers/project_helper.rb create mode 100644 tracks/app/helpers/todo_helper.rb create mode 100644 tracks/app/models/context.rb create mode 100644 tracks/app/models/project.rb create mode 100644 tracks/app/models/todo.rb create mode 100644 tracks/app/models/user.rb create mode 100644 tracks/app/views/context/list.rhtml create mode 100644 tracks/app/views/context/show.rhtml create mode 100644 tracks/app/views/feed/na_feed.rxml create mode 100644 tracks/app/views/feed/na_text.rhtml create mode 100644 tracks/app/views/layouts/scaffold.rhtml create mode 100644 tracks/app/views/layouts/standard.rhtml create mode 100644 tracks/app/views/login/login.rhtml create mode 100644 tracks/app/views/login/logout.rhtml create mode 100644 tracks/app/views/login/signup.rhtml create mode 100644 tracks/app/views/login/welcome.rhtml create mode 100644 tracks/app/views/project/list.rhtml create mode 100644 tracks/app/views/project/show.rhtml create mode 100644 tracks/app/views/todo/_done.rhtml create mode 100644 tracks/app/views/todo/completed.rhtml create mode 100644 tracks/app/views/todo/edit.rhtml create mode 100644 tracks/app/views/todo/list.rhtml create mode 100644 tracks/config/database.yml.tmpl create mode 100644 tracks/config/environment.rb create mode 100644 tracks/config/environments/development.rb create mode 100644 tracks/config/environments/production.rb create mode 100644 tracks/config/environments/test.rb create mode 100644 tracks/config/settings.yml.tmpl create mode 100644 tracks/db/tracks_dump 03.01.2005.sql create mode 100644 tracks/doc/CHANGENOTES.txt create mode 100644 tracks/doc/README.txt create mode 100644 tracks/lib/login_system.rb create mode 100644 tracks/lib/redcloth.rb create mode 100755 tracks/log.tmpl/apache.log create mode 100755 tracks/log.tmpl/development.log create mode 100755 tracks/log.tmpl/production.log create mode 100755 tracks/log.tmpl/test.log create mode 100644 tracks/public/.htaccess create mode 100644 tracks/public/404.html create mode 100644 tracks/public/500.html create mode 100644 tracks/public/_doc/index.html create mode 100755 tracks/public/dispatch.cgi create mode 100755 tracks/public/dispatch.fcgi create mode 100755 tracks/public/dispatch.rb create mode 100644 tracks/public/images/delete.png create mode 100644 tracks/public/images/done.png create mode 100644 tracks/public/images/edit.png create mode 100644 tracks/public/images/notes.png create mode 100644 tracks/public/index.html create mode 100644 tracks/public/javascripts/toggle_notes.js create mode 100644 tracks/public/stylesheets/scaffold.css create mode 100644 tracks/public/stylesheets/standard.css create mode 100755 tracks/script/breakpointer create mode 100755 tracks/script/console create mode 100755 tracks/script/generate create mode 100755 tracks/script/server create mode 100644 tracks/test/fixtures/contexts.yml create mode 100644 tracks/test/fixtures/projects.yml create mode 100644 tracks/test/fixtures/todos.yml create mode 100644 tracks/test/fixtures/users.yml create mode 100644 tracks/test/functional/context_controller_test.rb create mode 100644 tracks/test/functional/feed_controller_test.rb create mode 100644 tracks/test/functional/login_controller_test.rb create mode 100644 tracks/test/functional/project_controller_test.rb create mode 100644 tracks/test/functional/todo_controller_test.rb create mode 100644 tracks/test/test_helper.rb create mode 100644 tracks/test/unit/context_test.rb create mode 100644 tracks/test/unit/project_test.rb create mode 100644 tracks/test/unit/todo_test.rb create mode 100644 tracks/test/unit/user_test.rb diff --git a/tracks/README_FIRST.txt b/tracks/README_FIRST.txt new file mode 100644 index 00000000..3f1f3970 --- /dev/null +++ b/tracks/README_FIRST.txt @@ -0,0 +1,13 @@ +The main README.txt file is in tracks/doc/README.txt, and the change log in tracks/doc/CHANGENOTES.txt. + +The database structure dump file (with some test contents) is in tracks/db/tracks_dump 03.01.2005.sql, which you can import into your database. + +** IMPORTANT ** + +Before you do anything else, you need to copy certain files and rename the copy: + + tracks/config/database.yml.tmpl -> tracks/config/database.yml + tracks/config/settings.yml.tmpl -> tracks/config/settings.yml + tracks/log.tmpl -> tracks/log + +You need to put your settings into database.yml and settings.yml. Just leave the .tmpl versions as they are. I'm sorry to impose this extra step, but it's important for the subversion repository not to have your super-seekrit MySQL database user name and password checked in to the repository for all to see! \ No newline at end of file diff --git a/tracks/README_LOGIN b/tracks/README_LOGIN new file mode 100644 index 00000000..a0ac1d59 --- /dev/null +++ b/tracks/README_LOGIN @@ -0,0 +1,29 @@ +== Installation + +Done generating the login system. but there are still a few things you have to do +manually. First open your application.rb and add + + require_dependency "login_system" + +to the top of the file and include the login system with + + include LoginSystem + +The beginning of your ApplicationController. +It should look something like this : + + require_dependency "login_system" + + class ApplicationController < ActionController::Base + include LoginSystem + + +After you have done the modifications the the AbstractController you can +import the user model into the database. This model is meant as an example +and you should extend it. If you just want to get things up and running you +can find some create table syntax in db/user_model.sql. + +== Useage + +Now you can go around and happily add "before_filter :login_required" to the controllers which you would like to protect. +If the user hits a controller with the login_required filter he will be redirected to the login page and redirected back after a successful login. You can find the login_system.rb in the lib/ directory. It comes with some comments which should help explain the general useage. \ No newline at end of file diff --git a/tracks/Rakefile b/tracks/Rakefile new file mode 100644 index 00000000..87263c4b --- /dev/null +++ b/tracks/Rakefile @@ -0,0 +1,112 @@ +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' + +$VERBOSE = nil + +require File.dirname(__FILE__) + '/config/environment' +require 'code_statistics' + +desc "Run all the tests on a fresh test database" +task :default => [ :clone_development_structure_to_test, :test_units, :test_functional ] + +desc "Generate API documentatio, show coding stats" +task :doc => [ :appdoc, :stats ] + + +desc "Run the unit tests in test/unit" +Rake::TestTask.new("test_units") { |t| + t.libs << "test" + t.pattern = 'test/unit/*_test.rb' + t.verbose = true +} + +desc "Run the functional tests in test/functional" +Rake::TestTask.new("test_functional") { |t| + t.libs << "test" + t.pattern = 'test/functional/*_test.rb' + t.verbose = true +} + +desc "Generate documentation for the application" +Rake::RDocTask.new("appdoc") { |rdoc| + rdoc.rdoc_dir = 'doc/app' + rdoc.title = "Rails Application Documentation" + rdoc.options << '--line-numbers --inline-source' + rdoc.rdoc_files.include('doc/README_FOR_APP') + rdoc.rdoc_files.include('app/**/*.rb') +} + +desc "Generate documentation for the Rails framework" +Rake::RDocTask.new("apidoc") { |rdoc| + rdoc.rdoc_dir = 'doc/api' + rdoc.title = "Rails Framework Documentation" + rdoc.options << '--line-numbers --inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('CHANGELOG') + rdoc.rdoc_files.include('vendor/railties/lib/breakpoint.rb') + rdoc.rdoc_files.include('vendor/railties/CHANGELOG') + rdoc.rdoc_files.include('vendor/railties/MIT-LICENSE') + rdoc.rdoc_files.include('vendor/activerecord/README') + rdoc.rdoc_files.include('vendor/activerecord/CHANGELOG') + rdoc.rdoc_files.include('vendor/activerecord/lib/active_record/**/*.rb') + rdoc.rdoc_files.exclude('vendor/activerecord/lib/active_record/vendor/*') + rdoc.rdoc_files.include('vendor/actionpack/README') + rdoc.rdoc_files.include('vendor/actionpack/CHANGELOG') + rdoc.rdoc_files.include('vendor/actionpack/lib/action_controller/**/*.rb') + rdoc.rdoc_files.include('vendor/actionpack/lib/action_view/**/*.rb') + rdoc.rdoc_files.include('vendor/actionmailer/README') + rdoc.rdoc_files.include('vendor/actionmailer/CHANGELOG') + rdoc.rdoc_files.include('vendor/actionmailer/lib/action_mailer/base.rb') +} + +desc "Report code statistics (KLOCs, etc) from the application" +task :stats do + CodeStatistics.new( + ["Helpers", "app/helpers"], + ["Controllers", "app/controllers"], + ["Functionals", "test/functional"], + ["Models", "app/models"], + ["Units", "test/unit"] + ).to_s +end + +desc "Recreate the test databases from the development structure" +task :clone_development_structure_to_test => [ :db_structure_dump, :purge_test_database ] do + if ActiveRecord::Base.configurations["test"]["adapter"] == "mysql" + ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') + IO.readlines("db/development_structure.sql").join.split("\n\n").each do |table| + ActiveRecord::Base.connection.execute(table) + end + elsif ActiveRecord::Base.configurations["test"]["adapter"] == "postgresql" + `psql -U #{ActiveRecord::Base.configurations["test"]["username"]} -f db/development_structure.sql #{ActiveRecord::Base.configurations["test"]["database"]}` + elsif ActiveRecord::Base.configurations["test"]["adapter"] == "sqlite" + `sqlite #{ActiveRecord::Base.configurations["test"]["dbfile"]} < db/development_structure.sql` + end +end + +desc "Dump the database structure to a SQL file" +task :db_structure_dump do + if ActiveRecord::Base.configurations["development"]["adapter"] == "mysql" + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["development"]) + File.open("db/development_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + elsif ActiveRecord::Base.configurations["development"]["adapter"] == "postgresql" + `pg_dump -U #{ActiveRecord::Base.configurations["development"]["username"]} -s -f db/development_structure.sql #{ActiveRecord::Base.configurations["development"]["database"]}` + elsif ActiveRecord::Base.configurations["development"]["adapter"] == "sqlite" + `sqlite #{ActiveRecord::Base.configurations["development"]["dbfile"]} .schema > db/development_structure.sql` + end +end + +desc "Drop the test database and bring it back again" +task :purge_test_database do + if ActiveRecord::Base.configurations["test"]["adapter"] == "mysql" + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["development"]) + ActiveRecord::Base.connection.recreate_database(ActiveRecord::Base.configurations["test"]["database"]) + elsif ActiveRecord::Base.configurations["test"]["adapter"] == "postgresql" + `dropdb -U #{ActiveRecord::Base.configurations["test"]["username"]} #{ActiveRecord::Base.configurations["test"]["database"]}` + `createdb -U #{ActiveRecord::Base.configurations["test"]["username"]} #{ActiveRecord::Base.configurations["test"]["database"]}` + elsif ActiveRecord::Base.configurations["test"]["adapter"] == "sqlite" + File.delete(ActiveRecord::Base.configurations["test"]["dbfile"]) if File.exist?(ActiveRecord::Base.configurations["test"]["dbfile"]) + end +end diff --git a/tracks/app/controllers/application.rb b/tracks/app/controllers/application.rb new file mode 100644 index 00000000..141e688a --- /dev/null +++ b/tracks/app/controllers/application.rb @@ -0,0 +1,18 @@ +# The filters added to this controller will be run for all controllers in the application. +# Likewise will all the methods added be available for all controllers. + +require_dependency "login_system" +require_dependency "redcloth" +require 'date' + +$delete_img = "" +$edit_img = "" +$notes_img = "" +$done_img = "" + +class ApplicationController < ActionController::Base + + helper :application + include LoginSystem + +end \ No newline at end of file diff --git a/tracks/app/controllers/context_controller.rb b/tracks/app/controllers/context_controller.rb new file mode 100644 index 00000000..3397ada9 --- /dev/null +++ b/tracks/app/controllers/context_controller.rb @@ -0,0 +1,94 @@ +class ContextController < ApplicationController + + + helper :context + model :project + scaffold :context + before_filter :login_required + + layout "standard" + + + # Main method for listing contexts + # Set page title, and collect existing contexts in @contexts + # + def list + @page_title = "List Contexts" + @contexts = Context.find_all + end + + + # Called by a form button + # Parameters from form fields should be passed to create new context + # + def add_context + context = Context.new + context.name = @params["new_context"]["name"] + + if context.save + flash["confirmation"] = "Succesfully created context" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't add new context" + redirect_to( :action => "list" ) + end + end + + + # Filter the contexts to show just the one passed in the URL + # e.g. /context/show/ shows just . + # + def show + @context = Context.find(@params["id"]) + @places = Context.find_all + @projects = Project.find_all + @page_title = "Context: #{@context.name.capitalize}" + @not_done = Todo.find_all( "context_id=#{@context.id} AND done=0", "created DESC" ) + end + + + # Called by a form button + # Parameters from form fields are passed to create new action + # in the selected context. + def add_item + item = Todo.new + item.attributes = @params["new_item"] + + # Convert the date format entered (as set in config/settings.yml) + # to the mysql format YYYY-MM-DD + if @params["new_item"]["due"] != "" + date_fmt = app_configurations["formats"]["date"] + formatted_date = DateTime.strptime(@params["new_item"]["due"], "#{date_fmt}") + item.due = formatted_date.strftime("%Y-%m-%d") + else + item.due = "0000-00-00" + end + + back_to = item.context_id + + if item.save + flash["confirmation"] = "Succesfully added action to context" + redirect_to( :controller => "context", :action => "show", :id => "#{back_to}" ) + else + flash["warning"] = "Could not add action to context" + redirect_to( :controller => "context", :action => "show", :id => "#{back_to}" ) + end + end + + + # Fairly self-explanatory; deletes the context + # If the context contains actions, you'll get a warning dialogue. + # If you choose to go ahead, any actions in the context will also be deleted. + def destroy + context = Context.find(@params['id']) + if context.destroy + flash["confirmation"] = "Succesfully deleted context" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't delete context" + redirect_to( :action => "list" ) + end + end + + +end diff --git a/tracks/app/controllers/feed_controller.rb b/tracks/app/controllers/feed_controller.rb new file mode 100644 index 00000000..1368a088 --- /dev/null +++ b/tracks/app/controllers/feed_controller.rb @@ -0,0 +1,33 @@ +# Produces an feeds of the next actions, both RSS and plain text +# +class FeedController < ApplicationController + + helper :feed + model :todo, :context, :project + + def index + end + + # Builds an RSS feed for the latest 15 items + # This is fairly basic: it lists the action description as the title + # and the item context as the description + # + def na_feed + @not_done = Todo.find_all( "done=0", "created DESC" ) + @headers["Content-Type"] = "text/xml; charset=utf-8" + end + + # Builds a plain text page listing all the next actions, + # sorted by context. Showing notes doesn' make much sense here + # so they are omitted. You can use this with GeekTool to get your next actions + # on the desktop: + # curl http://url_for_the_app/feed/na_text + # + def na_text + @places = Context.find_all + @projects = Project.find_all + @not_done = Todo.find_all( "done=0", "context_id ASC" ) + @headers["Content-Type"] = "text/plain; charset=utf-8" + end + +end diff --git a/tracks/app/controllers/login_controller.rb b/tracks/app/controllers/login_controller.rb new file mode 100644 index 00000000..ef9bfbfb --- /dev/null +++ b/tracks/app/controllers/login_controller.rb @@ -0,0 +1,49 @@ +class LoginController < ApplicationController + model :user + layout 'scaffold' + + def login + case @request.method + when :post + if @session['user'] = User.authenticate(@params['user_login'], @params['user_password']) + + flash['notice'] = "Login successful" + redirect_back_or_default :controller => "todo", :action => "list" + else + @login = @params['user_login'] + @message = "Login unsuccessful" + end + end + end + + def signup + case @request.method + when :post + @user = User.new(@params['user']) + + if @user.save + @session['user'] = User.authenticate(@user.login, @params['user']['password']) + flash['notice'] = "Signup successful" + redirect_back_or_default :controller => "todo", :action => "list" + end + when :get + @user = User.new + end + end + + def delete + if @params['id'] + @user = User.find(@params['id']) + @user.destroy + end + redirect_back_or_default :controller => "todo", :action => "list" + end + + def logout + @session['user'] = nil + end + + def welcome + end + +end diff --git a/tracks/app/controllers/project_controller.rb b/tracks/app/controllers/project_controller.rb new file mode 100644 index 00000000..be49725f --- /dev/null +++ b/tracks/app/controllers/project_controller.rb @@ -0,0 +1,107 @@ +class ProjectController < ApplicationController + + helper :project + model :context + model :todo + scaffold :project + + before_filter :login_required + layout "standard" + + # Main method for listing projects + # Set page title, and collect existing projects in @projects + # + def list + @page_title = "List Projects" + @projects = Project.find_all + end + + + # Filter the projects to show just the one passed in the URL + # e.g. /project/show/ shows just . + # + def show + @project = Project.find(@params["id"]) + @places = Context.find_all + @page_title = "Project: #{@project.name}" + @not_done = Todo.find_all( "project_id=#{@project.id} AND done=0", "created DESC" ) + end + + + # Called by a form button + # Parameters from form fields should be passed to create new project + # + def add_project + project = Project.new + project.name = @params["new_project"]["name"] + + if project.save + flash["confirmation"] = "Succesfully added project" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't add project" + redirect_to( :action => "list" ) + end + end + + + # Called by a form button + # Parameters from form fields should be passed to create new item + # + def add_item + item = Todo.new + item.attributes = @params["new_item"] + + # Convert the date format entered (as set in config/settings.yml) + # to the mysql format YYYY-MM-DD + if @params["new_item"]["due"] != "" + date_fmt = app_configurations["formats"]["date"] + formatted_date = DateTime.strptime(@params["new_item"]["due"], "#{date_fmt}") + item.due = formatted_date.strftime("%Y-%m-%d") + else + item.due = "0000-00-00" + end + + back_to = item.project_id + + if item.save + flash["confirmation"] = "Successfully added next action to project" + redirect_to( :controller => "project", :action => "show", :id => "#{back_to}" ) + else + flash["warning"] = "Couldn't add next action to project" + redirect_to( :controller => "project", :action => "show", :id => "#{back_to}" ) + end + end + + + def destroy + project = Project.find( @params['id'] ) + if project.destroy + flash["confirmation"] = "Succesfully deleted project" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't delete project" + redirect_to( :action => "list" ) + end + end + + + # Toggles the 'done' status of the action + def toggle_check + item = Todo.find( @params['id'] ) + + case item.done + when 0: item.done = 1; item.completed = Time.now() + when 1: item.done = 0; item.completed = nil + end + + if item.save + flash["confirmation"] = "Marked next action as completed" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't mark next action as completed" + redirect_to( :action => "list" ) + end + end + +end diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb new file mode 100644 index 00000000..b0e030cc --- /dev/null +++ b/tracks/app/controllers/todo_controller.rb @@ -0,0 +1,111 @@ +class TodoController < ApplicationController + + helper :todo + model :context + model :project + + scaffold :todo + before_filter :login_required + layout "standard" + + # Main method for listing tasks + # Set page title, and fill variables with contexts and done and not-done tasks + # + def list + @page_title = "List tasks" + @places = Context.find_all + @projects = Project.find_all + @not_done = Todo.find_all( "done=0", "completed DESC" ) + @done = Todo.find_all( "done=1", "completed DESC", 5 ) + end + + + # List the completed tasks, sorted by completion date + # + def completed + @page_title = "Completed tasks" + @done = Todo.find_all( "done=1", "completed DESC" ) + end + + + # Called by a form button + # Parameters from form fields should be passed to create new item + # + def add_item + item = Todo.new + item.attributes = @params["new_item"] + + # Convert the date format entered (as set in config/settings.yml) + # to the mysql format YYYY-MM-DD + if @params["new_item"]["due"] != "" + date_fmt = app_configurations["formats"]["date"] + formatted_date = DateTime.strptime(@params["new_item"]["due"], "#{date_fmt}") + item.due = formatted_date.strftime("%Y-%m-%d") + else + item.due = "0000-00-00" + end + + if item.save + flash["confirmation"] = "Next action was successfully added" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't add the action" + redirect_to( :action => "list" ) + end + end + + + def edit + @item = Todo.find(@params['id']) + @belongs = @item.project_id + @page_title = "Edit task: #{@item.description}" + @places = Context.find_all + @projects = Project.find_all + end + + + def update + @item = Todo.find(@params['item']['id']) + @item.attributes = @params['item'] + if @item.save + flash["confirmation"] = 'Next action was successfully updated' + redirect_to :action => 'list' + else + flash["warning"] = 'Next action could not be updated' + redirect_to :action => 'list' + end + end + + + def destroy + item = Todo.find(@params['id']) + if item.destroy + flash["confirmation"] = "Next action was successfully deleted" + redirect_to :action => "list" + else + flash["warning"] = "Couldn't delete next action" + redirect_to :action => "list" + end + end + + # Toggles the 'done' status of the action + # + def toggle_check + item = Todo.find(@params['id']) + + case item.done + when 0: item.done = 1; item.completed = Time.now() + when 1: item.done = 0; item.completed = nil + end + + if item.save + flash["confirmation"] = "Next action marked as completed" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't mark action as completed" + redirect_to( :action => "list" ) + end + end + + +end diff --git a/tracks/app/helpers/application_helper.rb b/tracks/app/helpers/application_helper.rb new file mode 100644 index 00000000..e36d7a81 --- /dev/null +++ b/tracks/app/helpers/application_helper.rb @@ -0,0 +1,51 @@ +# The methods added to this helper will be available to all templates in the application. +module ApplicationHelper + #require_dependency 'date' + + def format_date(date) + # Convert a date object to the format specified + # in config/settings.yml + # + date_fmt = app_configurations["formats"]["date"] + formatted_date = date.strftime("#{date_fmt}") + end + + def markdown(text) + # Uses RedCloth to transform text using either Textile or Markdown + # Need to require redcloth above + # RedCloth 3.0 or greater is needed to use Markdown, otherwise it only handles Textile + # + RedCloth.new(text).to_html + end + + def tag_object(object, tag) + # Wraps object in HTML tags, tag + # + tagged = "<#{tag}>#{object}" + end + + def due_date(due) + # Check due date in comparison to today's date + # Flag up date appropriately with a 'traffic light' colour code + # + if due == nil + return "" + end + + @now = Date.today + @days = due-@now + case @days + # overdue or due very soon! sound the alarm! + when -365..1 + "" + format_date(due) + " " + # due 2-7 days away + when 2..7 + "" + format_date(due) + " " + # more than a week away - relax + else + "" + format_date(due) + " " + end + end + + +end diff --git a/tracks/app/helpers/context_helper.rb b/tracks/app/helpers/context_helper.rb new file mode 100644 index 00000000..bdd0d8ea --- /dev/null +++ b/tracks/app/helpers/context_helper.rb @@ -0,0 +1,2 @@ +module ContextHelper +end diff --git a/tracks/app/helpers/feed_helper.rb b/tracks/app/helpers/feed_helper.rb new file mode 100644 index 00000000..e37902f9 --- /dev/null +++ b/tracks/app/helpers/feed_helper.rb @@ -0,0 +1,34 @@ +module FeedHelper + + # Build a nicely formatted text string for display + # Context forms the heading, then the items are + # indented underneath. If there is a due date + # and the item is in a project, these are also displayed + # + def build_text_page(list,contexts,projects) + result_string = "" + for @place in @places + result_string << "\n" + @place.name.upcase + ":\n" + + list.each do |@item| + if @item.context_id == @place.id + + if @item.due + result_string << " [" + format_date(@item.due) + "] " + result_string << @item.description + " " + else + result_string << " " + @item.description + " " + end + + if @item.project_id + result_string << "(" + @item.project['name'] + ")" + end + result_string << "\n" + end + + end + end + return result_string + end + +end diff --git a/tracks/app/helpers/login_helper.rb b/tracks/app/helpers/login_helper.rb new file mode 100644 index 00000000..be781c97 --- /dev/null +++ b/tracks/app/helpers/login_helper.rb @@ -0,0 +1,18 @@ +module LoginHelper + + + def render_errors(obj) + return "" unless obj + return "" unless @request.post? + tag = String.new + + unless obj.valid? + tag << %{
    } + obj.errors.each_full { |message| tag << %{
  • #{message}
  • } } + tag << %{
} + end + tag + end + + +end \ No newline at end of file diff --git a/tracks/app/helpers/project_helper.rb b/tracks/app/helpers/project_helper.rb new file mode 100644 index 00000000..12c6d3c7 --- /dev/null +++ b/tracks/app/helpers/project_helper.rb @@ -0,0 +1,2 @@ +module ProjectHelper +end diff --git a/tracks/app/helpers/todo_helper.rb b/tracks/app/helpers/todo_helper.rb new file mode 100644 index 00000000..e2c62222 --- /dev/null +++ b/tracks/app/helpers/todo_helper.rb @@ -0,0 +1,36 @@ +module TodoHelper + + + def display_done(ary,context) + result_string = "" + result_string << "
    " + ary.each do |@item| + result_string << "
  • " + @item.description + " " + + # Item should have a completion date if it is done + # This is just a sanity check + # + if @item.completed != nil + result_string << "[completed: " + format_date(@item.completed) + "]" + " " + end + + result_string << "in " + @item.context['name'].capitalize + "
  • " + end + result_string << "
" + return result_string + end + + + def count_items(items, context) + # Count the number of items in the selected context + # + count = 0 + for item in items + if item.context['name'] == context + count += 1 + end + end + return count + end + +end diff --git a/tracks/app/models/context.rb b/tracks/app/models/context.rb new file mode 100644 index 00000000..ca8f849b --- /dev/null +++ b/tracks/app/models/context.rb @@ -0,0 +1,3 @@ +class Context < ActiveRecord::Base + has_many :todo, :dependent => true +end diff --git a/tracks/app/models/project.rb b/tracks/app/models/project.rb new file mode 100644 index 00000000..93a309ea --- /dev/null +++ b/tracks/app/models/project.rb @@ -0,0 +1,3 @@ +class Project < ActiveRecord::Base + has_many :todo, :dependent => true +end diff --git a/tracks/app/models/todo.rb b/tracks/app/models/todo.rb new file mode 100644 index 00000000..89a965f0 --- /dev/null +++ b/tracks/app/models/todo.rb @@ -0,0 +1,16 @@ +class Todo < ActiveRecord::Base + + belongs_to :context + belongs_to :project + + def before_save + # Add a creation date (Ruby object format) to item before it's saved + # if there is no existing creation date (this prevents creation date + # being reset to completion date when item is completed) + # + if self.created == nil + self.created = Time.now() + end + end + +end diff --git a/tracks/app/models/user.rb b/tracks/app/models/user.rb new file mode 100644 index 00000000..fcc0a4ec --- /dev/null +++ b/tracks/app/models/user.rb @@ -0,0 +1,30 @@ +require 'digest/sha1' + +# this model expects a certain database layout and its based on the name/login pattern. +class User < ActiveRecord::Base + + def self.authenticate(login, pass) + find_first(["login = ? AND password = ?", login, sha1(pass)]) + end + + def change_password(pass) + update_attribute "password", self.class.sha1(pass) + end + + protected + + def self.sha1(pass) + Digest::SHA1.hexdigest("change-me--#{pass}--") + end + + before_create :crypt_password + + def crypt_password + write_attribute("password", self.class.sha1(password)) if password == @password_confirmation + end + + validates_length_of :password, :login, :within => 5..40 + validates_presence_of :password, :login + validates_uniqueness_of :login, :on => :create + validates_confirmation_of :password, :on => :create +end diff --git a/tracks/app/views/context/list.rhtml b/tracks/app/views/context/list.rhtml new file mode 100644 index 00000000..d49bcf52 --- /dev/null +++ b/tracks/app/views/context/list.rhtml @@ -0,0 +1,32 @@ +
+ + +<% row = 1 %> +<% for @context in @contexts %> + <% if row % 2 == 0 %> + + <% else %> + + <% end %> + + + + +<% row += 1 %> +<% end %> +
<%= @context.id.to_s %><%= link_to( "#{@context.name.capitalize}", :action => "show", :id => @context.id ) %><%= link_to($edit_img, { :action => "edit", :id => @context.id }, :title => "Edit item" ) + " " + link_to($delete_img, { :action => "destroy", :id => @context.id }, :title => "Delete item", :confirm => "Are you sure you want to delete this context: #{@context.name}. Any todos in this context will be deleted.") + " " %>
+ +
+ +
+

Add context

+
+
+ <%= text_field("new_context", "name") %> +
+ +
+
+ +<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file diff --git a/tracks/app/views/context/show.rhtml b/tracks/app/views/context/show.rhtml new file mode 100644 index 00000000..9b2c4ea7 --- /dev/null +++ b/tracks/app/views/context/show.rhtml @@ -0,0 +1,61 @@ +
+ +
+

<%= @context.name.capitalize %>

+
    +<% for @item in @not_done %> +
  • +
    + <%= check_box( "item", "done", "onclick" => "document.location.href='/todo/toggle_check/#{@item.id}'" ) %> +
    + +
    + <%= due_date(@item.due) %> + <%= @item.description %> + <% if @item.project_id %> + <%= link_to( "[P]", { :controller => "project", :action => "show", :id => @item.project_id }, :title => "View project: #{@item.project['name']}" ) %> + <% end %> +
    + +
    + <%= link_to($edit_img, { :controller => "todo", :action => "edit", :id => @item.id }, :title => "Edit item") + " " + link_to($delete_img, { :controller => "todo", :action => "destroy", :id => @item.id }, :title => "Delete item", :confirm => "Are you sure you want to delete this entry: #{@item.description}") + " " %> + + <% if @item.notes? %> + <%= "" + $notes_img + "" %>
    + <% m_notes = markdown(@item.notes) %> + <%= "
    " + m_notes + "
    " %> + <% else %> +
+ <% end %> + +<% end %> + +
+ + +
+

Add next action to this context

+ <%= form_tag( :controller => "context", :action => "add_item") %> + <%= hidden_field( "new_item", "context_id", "value" => "#{@context.id}") %> +
+ <%= text_field( "new_item", "description" ) %> +
+
+ <%= text_area( "new_item", "notes", "cols" => 40, "rows" => 15 ) %> +
+
+
+
+ <%= text_field( "new_item", "due", {"size" => 10, "maxlength" => 10} ) %> +
+ + +
+ +<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file diff --git a/tracks/app/views/feed/na_feed.rxml b/tracks/app/views/feed/na_feed.rxml new file mode 100644 index 00000000..f7e8fe1b --- /dev/null +++ b/tracks/app/views/feed/na_feed.rxml @@ -0,0 +1,14 @@ +xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do + xml.channel do + xml.title("Tracks - Next Actions") + xml.link("http://#{@request.host}:#{@request.port}/todo/list") + xml.description("Lists the last 15 undone next actions") + @not_done.each { |i| + xml.item do + xml.title(i.description) + xml.link("http://#{@request.host}:#{@request.port}/context/show/#{i.context_id}") + xml.description(i.context['name'].capitalize) + end + } + end +end \ No newline at end of file diff --git a/tracks/app/views/feed/na_text.rhtml b/tracks/app/views/feed/na_text.rhtml new file mode 100644 index 00000000..c9c00007 --- /dev/null +++ b/tracks/app/views/feed/na_text.rhtml @@ -0,0 +1 @@ +<%= build_text_page( @not_done, @places, @projects ) %> diff --git a/tracks/app/views/layouts/scaffold.rhtml b/tracks/app/views/layouts/scaffold.rhtml new file mode 100644 index 00000000..a57887a2 --- /dev/null +++ b/tracks/app/views/layouts/scaffold.rhtml @@ -0,0 +1,10 @@ + + + + Login + + + +<%= @content_for_layout %> + + diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml new file mode 100644 index 00000000..f5f9e2c7 --- /dev/null +++ b/tracks/app/views/layouts/standard.rhtml @@ -0,0 +1,38 @@ + + + + + + + +<%= @page_title %> + + +
+

+ <% if @not_done %> + <%= @not_done.length %> + <% end %> + <%= today = Time.now; datestamp = today.strftime("%A, %e %B %Y") %>

+
+ +<%= @content_for_layout %> + + + diff --git a/tracks/app/views/login/login.rhtml b/tracks/app/views/login/login.rhtml new file mode 100644 index 00000000..0088c883 --- /dev/null +++ b/tracks/app/views/login/login.rhtml @@ -0,0 +1,22 @@ +<%= start_form_tag :action=> "login" %> + +
+

Please log in to use Tracks:

+ + <% if @message %> +
<%= @message %>
+ <% end %> + + +
+ + + + +
+ + +
+ +<%= end_form_tag %> + diff --git a/tracks/app/views/login/logout.rhtml b/tracks/app/views/login/logout.rhtml new file mode 100644 index 00000000..6a3d22c2 --- /dev/null +++ b/tracks/app/views/login/logout.rhtml @@ -0,0 +1,10 @@ + +
+

Logoff

+ +

You are now logged out of the system...

+ + <%= link_to "« login", :action=>"login"%> + +
+ diff --git a/tracks/app/views/login/signup.rhtml b/tracks/app/views/login/signup.rhtml new file mode 100644 index 00000000..eda713d7 --- /dev/null +++ b/tracks/app/views/login/signup.rhtml @@ -0,0 +1,17 @@ +<%= start_form_tag :action=> "signup" %> + +
+

Signup

+ <%= render_errors @user %>
+ +
+ <%= text_field "user", "login", :size => 30 %>
+
+ <%= password_field "user", "password", :size => 30 %>
+
+ <%= password_field "user", "password_confirmation", :size => 30 %>
+ + + +<%= end_form_tag %> + diff --git a/tracks/app/views/login/welcome.rhtml b/tracks/app/views/login/welcome.rhtml new file mode 100644 index 00000000..ff9cc713 --- /dev/null +++ b/tracks/app/views/login/welcome.rhtml @@ -0,0 +1,13 @@ + +
+

Welcome

+ +

You are now logged into the system...

+

+ Since you are here it's safe to assume the application never called store_location, otherwise + you would have been redirected somewhere else after a successful login. +

+ + <%= link_to "« logout", :action=>"logout"%> + +
diff --git a/tracks/app/views/project/list.rhtml b/tracks/app/views/project/list.rhtml new file mode 100644 index 00000000..cb21dadd --- /dev/null +++ b/tracks/app/views/project/list.rhtml @@ -0,0 +1,32 @@ +
+ + +<% row = 1 %> +<% for @project in @projects %> + <% if row % 2 == 0 %> + + <% else %> + + <% end %> + + + + +<% row += 1 %> +<% end %> +
<%= @project.id.to_s %><%= link_to ( "#{@project.name}", :action => "show", :id => @project.id ) %><%= link_to( $edit_img, { :action => "edit", :id => @project.id }, :title => "Edit item" ) + " " + link_to($delete_img, { :action => "destroy", :id => @project.id }, :title => "Delete item", :confirm => "Are you sure you want to delete this context: #{@project.name}. Any todos in this context will be deleted." ) + " " %>
+ +
+ +
+

Add project

+
+
+ <%= text_field( "new_project", "name" ) %> +
+ +
+
+ +<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file diff --git a/tracks/app/views/project/show.rhtml b/tracks/app/views/project/show.rhtml new file mode 100644 index 00000000..b34558a2 --- /dev/null +++ b/tracks/app/views/project/show.rhtml @@ -0,0 +1,65 @@ +
+ +
+

<%= @project.name %>

+ +<% if @not_done == [] %> +

There are no next actions yet in this project

+<% else %> +
    +<% for @item in @not_done %> +
  • +
    + <%= check_box( "item", "done", "onclick" => "document.location.href='/todo/toggle_check/#{@item.id}'" ) %> +
    + +
    + <%= due_date( @item.due ) %> + <%= @item.description %> + <%= link_to( "[C]", { :controller => "context", :action => "show", :id => @item.context_id }, :title => "View context: #{@item.context['name'].capitalize}" ) %> +
    + +
    + <%= link_to( $edit_img, { :controller => "todo", :action => "edit", :id => @item.id }, :title => "Edit item" ) + " " + link_to($delete_img, { :controller => "todo", :action => "destroy", :id => @item.id }, :title => "Delete item", :confirm => "Are you sure you want to delete this entry: #{@item.description}" ) + " " %> + + <% if @item.notes? %> + <%= "" + $notes_img + "" %>
    + <% m_notes = markdown(@item.notes) %> + <%= "
    " + m_notes + "
    " %> + <% else %> +
+ <% end %> + +<% end %> + +<% end %> +
+ +
+ +
+

Add next action to this project

+ <%= form_tag( :controller => "project", :action => "add_item") %> + <%= hidden_field( "new_item", "project_id", "value" => "#{@project.id}") %> +
+ <%= text_field( "new_item", "description" ) %> +
+
+ <%= text_area( "new_item", "notes", "cols" => 40, "rows" => 15 ) %> +
+
+ +
+
+ <%= text_field( "new_item", "due", {"size" => 10, "maxlength" => 10} ) %> +
+ + +
+ +<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file diff --git a/tracks/app/views/todo/_done.rhtml b/tracks/app/views/todo/_done.rhtml new file mode 100644 index 00000000..9c4ef424 --- /dev/null +++ b/tracks/app/views/todo/_done.rhtml @@ -0,0 +1,29 @@ +<% @item = done %> +
  • +<% if @item.completed %> +
    + <%= $done_img %> +
    + +
    + <%= format_date( @item.completed ) %> + <%= " " + @item.description + " "%> + + <% if @item.project_id %> + <%= "(" + @item.context['name'].capitalize + ", " + @item.project['name'] + ")" %> + <% else %> + <%= "(" + @item.context['name'].capitalize + ")" %> + <% end %> + + <% if @item.due %> + <%= " - was due on " + format_date( @item.due ) %> + <% end %> + + <% if @item.notes? %> + <%= "" + $notes_img + "" %> + <% m_notes = markdown( @item.notes ) %> + <%= "
    " + m_notes + "
    " %> + <% end %> +
    +<% end %> +
  • \ No newline at end of file diff --git a/tracks/app/views/todo/completed.rhtml b/tracks/app/views/todo/completed.rhtml new file mode 100644 index 00000000..0a2d4ca1 --- /dev/null +++ b/tracks/app/views/todo/completed.rhtml @@ -0,0 +1,10 @@ +
    + +
    +

    Completed items

    +
      + <%= render_collection_of_partials "done", @done %> +
    +
    + +
    diff --git a/tracks/app/views/todo/edit.rhtml b/tracks/app/views/todo/edit.rhtml new file mode 100644 index 00000000..3ecb129e --- /dev/null +++ b/tracks/app/views/todo/edit.rhtml @@ -0,0 +1,48 @@ +
    +

    Update task

    +
    + <%= hidden_field( "item", "id" ) %> + +


    + <%= text_field( "item", "description" ) %>

    + +


    + <%= text_area( "item", "notes" ) %>

    + +


    +

    + +


    +

    + +


    + <%= text_field( "item", "due" ) %>

    + +

    +
    + + <%= link_to 'Cancel', :action => 'list' %> +
    + +<% if @flash["confirmation"] %>
    <%= @flash["confirmation"] %>
    <% end %> +<% if @flash["warning"] %>
    <%= @flash["warning"] %>
    <% end %> \ No newline at end of file diff --git a/tracks/app/views/todo/list.rhtml b/tracks/app/views/todo/list.rhtml new file mode 100644 index 00000000..2c51a32f --- /dev/null +++ b/tracks/app/views/todo/list.rhtml @@ -0,0 +1,89 @@ +
    + +<% for @place in @places %> + <% heading = false %> + + <% for @item in @not_done %> + <% if @place.name == @item.context['name'] %> + + <% if ( Todo.find_all( "context_id=#{@item.context['id']}" ) && heading == false ) %> +
    +

    <%= link_to( "#{@item.context['name'].capitalize}", :controller => "context", :action => "show", :id => @item.context_id ) %>

    +
      + <% heading = true %> + <% end %> + +
    • +
      + <%= check_box( "item", "done", "onclick" => "document.location.href='/todo/toggle_check/#{@item.id}'" ) %> +
      + +
      + <%= due_date( @item.due ) %> + <%= @item.description %> + <% if @item.project_id %> + <%= link_to( "[P]", { :controller => "project", :action => "show", :id => @item.project_id }, :title => "View project: #{@item.project['name']}" ) %> + <% end %> +
      + +
      + <%= link_to( $edit_img, { :action => "edit", :id => @item.id }, :title => "Edit item" ) + " " + link_to($delete_img, { :action => "destroy", :id => @item.id }, :title => "Delete item", :confirm => "Are you sure you want to delete this entry: #{@item.description}" ) + " " %> + + <% if @item.notes? %> + <%= "" + $notes_img + "" %>
      + <% m_notes = markdown( @item.notes ) %> + <%= "
      " + m_notes + "
      " %> + <% else %> +
    + <% end %> + + <% end %> + <% end %> + + <% if heading == true %> + +
    + <% end %> +<% end %> + +
    +

    Last 5 completed actions

    +
      + <%= render_collection_of_partials "done", @done %> +
    +
    + + +
    +

    Add next action

    +
    +
    + <%= text_field( "new_item", "description" ) %> +
    +
    + <%= text_area( "new_item", "notes", "cols" => 40, "rows" => 15 ) %> +
    +
    + +
    +
    + +
    +
    + <%= text_field( "new_item", "due", {"size" => 10, "maxlength" => 10} ) %> +
    + +
    +
    + +<% if @flash["confirmation"] %>
    <%= @flash["confirmation"] %>
    <% end %> +<% if @flash["warning"] %>
    <%= @flash["warning"] %>
    <% end %> diff --git a/tracks/config/database.yml.tmpl b/tracks/config/database.yml.tmpl new file mode 100644 index 00000000..41446cc0 --- /dev/null +++ b/tracks/config/database.yml.tmpl @@ -0,0 +1,20 @@ +development: + adapter: mysql + database: tracks + host: localhost + username: root + password: + +test: + adapter: mysql + database: tracks_test + host: localhost + username: root + password: + +production: + adapter: mysql + database: tracks + host: localhost + username: root + password: diff --git a/tracks/config/environment.rb b/tracks/config/environment.rb new file mode 100644 index 00000000..554b4ea5 --- /dev/null +++ b/tracks/config/environment.rb @@ -0,0 +1,63 @@ +RAILS_ROOT = File.dirname(__FILE__) + "/../" +RAILS_ENV = ENV['RAILS_ENV'] || 'development' + + +# Mocks first. +ADDITIONAL_LOAD_PATHS = ["#{RAILS_ROOT}/test/mocks/#{RAILS_ENV}"] + +# Then model subdirectories. +ADDITIONAL_LOAD_PATHS.concat(Dir["#{RAILS_ROOT}/app/models/[_a-z]*"]) + +# Followed by the standard includes. +ADDITIONAL_LOAD_PATHS.concat %w( + app + app/models + app/controllers + app/helpers + config + lib + vendor +).map { |dir| "#{RAILS_ROOT}/#{dir}" } + +# Prepend to $LOAD_PATH +ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } + + +# Require Rails gems. +require 'rubygems' +require_gem 'activerecord' +require_gem 'actionpack' +require_gem 'actionmailer' +require_gem 'rails' + + +# Environment-specific configuration. +require_dependency "environments/#{RAILS_ENV}" +ActiveRecord::Base.configurations = YAML::load(File.open("#{RAILS_ROOT}/config/database.yml")) +ActiveRecord::Base.establish_connection + + +# Configure defaults if the included environment did not. +begin + RAILS_DEFAULT_LOGGER = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}.log") +rescue StandardError + RAILS_DEFAULT_LOGGER = Logger.new(STDERR) + RAILS_DEFAULT_LOGGER.level = Logger::WARN + RAILS_DEFAULT_LOGGER.warn( + "Rails Error: Unable to access log file. Please ensure that log/#{RAILS_ENV}.log exists and is chmod 0777. " + + "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed." + ) +end + +[ActiveRecord::Base, ActionController::Base, ActionMailer::Base].each do |klass| + klass.logger ||= RAILS_DEFAULT_LOGGER +end +[ActionController::Base, ActionMailer::Base].each do |klass| + klass.template_root ||= "#{RAILS_ROOT}/app/views/" +end + + +# Include your app's configuration here: +def app_configurations + YAML::load(File.open("#{RAILS_ROOT}/config/settings.yml")) +end \ No newline at end of file diff --git a/tracks/config/environments/development.rb b/tracks/config/environments/development.rb new file mode 100644 index 00000000..42a43d59 --- /dev/null +++ b/tracks/config/environments/development.rb @@ -0,0 +1,3 @@ +Dependencies.mechanism = :load +ActionController::Base.consider_all_requests_local = true +BREAKPOINT_SERVER_PORT = 42531 \ No newline at end of file diff --git a/tracks/config/environments/production.rb b/tracks/config/environments/production.rb new file mode 100644 index 00000000..2069ebcf --- /dev/null +++ b/tracks/config/environments/production.rb @@ -0,0 +1,2 @@ +Dependencies.mechanism = :require +ActionController::Base.consider_all_requests_local = false diff --git a/tracks/config/environments/test.rb b/tracks/config/environments/test.rb new file mode 100644 index 00000000..d4db0e9c --- /dev/null +++ b/tracks/config/environments/test.rb @@ -0,0 +1,3 @@ +Dependencies.mechanism = :require +ActionController::Base.consider_all_requests_local = true +ActionMailer::Base.delivery_method = :test \ No newline at end of file diff --git a/tracks/config/settings.yml.tmpl b/tracks/config/settings.yml.tmpl new file mode 100644 index 00000000..659a2b42 --- /dev/null +++ b/tracks/config/settings.yml.tmpl @@ -0,0 +1,2 @@ +formats: + date: %d/%m/%Y diff --git a/tracks/db/tracks_dump 03.01.2005.sql b/tracks/db/tracks_dump 03.01.2005.sql new file mode 100644 index 00000000..c3c6f4e2 --- /dev/null +++ b/tracks/db/tracks_dump 03.01.2005.sql @@ -0,0 +1,88 @@ +# CocoaMySQL dump +# Version 0.5 +# http://cocoamysql.sourceforge.net +# +# Host: localhost (MySQL 4.0.20-max) +# Database: todo +# Generation Time: 2005-01-03 14:14:18 +0000 +# ************************************************************ + +# Dump of table contexts +# ------------------------------------------------------------ + +CREATE TABLE `contexts` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(255) NOT NULL default '', + PRIMARY KEY (`id`) +) TYPE=MyISAM; + +INSERT INTO `contexts` (`id`,`name`) VALUES ("1","agenda"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("2","call"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("3","email"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("4","errand"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("5","lab"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("6","library"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("7","freetime"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("8","office"); +INSERT INTO `contexts` (`id`,`name`) VALUES ("11","waiting-for"); + + +# Dump of table projects +# ------------------------------------------------------------ + +CREATE TABLE `projects` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(255) NOT NULL default '', + PRIMARY KEY (`id`) +) TYPE=MyISAM; + +INSERT INTO `projects` (`id`,`name`) VALUES ("1","Build a working time machine"); +INSERT INTO `projects` (`id`,`name`) VALUES ("2","Make more money than Billy Gates"); +INSERT INTO `projects` (`id`,`name`) VALUES ("3","Evict dinosaurs from the garden"); + + +# Dump of table todos +# ------------------------------------------------------------ + +CREATE TABLE `todos` ( + `id` int(11) NOT NULL auto_increment, + `context_id` int(11) NOT NULL default '0', + `description` varchar(100) NOT NULL default '', + `notes` text, + `done` tinyint(4) NOT NULL default '0', + `created` datetime NOT NULL default '0000-00-00 00:00:00', + `due` date default NULL, + `completed` datetime default NULL, + `project_id` int(11) default NULL, + PRIMARY KEY (`id`) +) TYPE=MyISAM; + +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("1","1","Call Bill Gates to find out how much he makes per day","","0","2004-11-28 16:01:00","2004-10-30",NULL,"2"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("52","2","Call dinosaur exterminator","Ask him if I need to hire a skip for the corpses.","0","2004-11-28 16:06:08","2004-11-30",NULL,"3"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("53","4","Buy milk","","1","2004-11-28 16:06:31","0000-00-00","2004-11-28 16:06:42",NULL); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("54","4","Buy bread","","1","2004-11-28 16:06:58","0000-00-00","2004-11-30 13:41:09",NULL); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("55","5","Construct time dilation device","","0","2004-11-28 16:07:33","0000-00-00",NULL,"1"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("56","2","Phone Grandfather to ask about the paradox","Added some _notes_.","0","2004-11-28 16:08:33","2004-12-30",NULL,"1"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("61","6","Get a book out of the library","Dinosaurs\'R\'Us","0","2004-12-22 14:07:06","0000-00-00",NULL,"3"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("60","4","Upgrade to Rails 0.9.1","","1","2004-12-20 17:02:52","2004-12-21","2004-12-20 17:06:48",NULL); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("65","1","This should be due today","","0","2004-12-31 17:23:06","2004-12-31",NULL,NULL); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("75","1","foo","","1","2004-12-31 18:38:34","2005-01-05","2005-01-02 12:27:10",NULL); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("81","1","Buy shares","","0","2005-01-01 12:40:26","2005-02-01",NULL,"2"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("85","1","Buy stegosaurus bait","","1","2005-01-01 12:53:12","2005-01-02","2005-01-01 12:53:43","3"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("92","1","New action in context","Some notes","1","2005-01-02 14:52:49","2005-03-01","2005-01-02 15:44:19","3"); +INSERT INTO `todos` (`id`,`context_id`,`description`,`notes`,`done`,`created`,`due`,`completed`,`project_id`) VALUES ("97","2","Call stock broker","tel: 12345","0","2005-01-03 11:38:25","0000-00-00",NULL,"2"); + + +# Dump of table users +# ------------------------------------------------------------ + +CREATE TABLE `users` ( + `id` int(11) NOT NULL auto_increment, + `login` varchar(80) default NULL, + `password` varchar(40) default NULL, + PRIMARY KEY (`id`) +) TYPE=MyISAM; + +INSERT INTO `users` (`id`,`login`,`password`) VALUES ("1","test","9a91e1d8d95b6315991a88121bb0aa9f03ba0dfc"); + + diff --git a/tracks/doc/CHANGENOTES.txt b/tracks/doc/CHANGENOTES.txt new file mode 100644 index 00000000..e5626f27 --- /dev/null +++ b/tracks/doc/CHANGENOTES.txt @@ -0,0 +1,34 @@ +## Tracks :: GTD web application +## $LastChangedDate$ +## $HeadURL$ + +Main project site: +Project wiki: + +## Version 1.01 + +A small increment to fix a few bugs and typographical errors in the README: + +1. README.txt now lists the correct location for the dump file +2. Fixed the redirection from the login page. Once you have signed up or logged in, you should be taken automatically to the main todo/list page. This also works once you have logged out and clicked the "<< login" page to get back to login/login. (Thanks, Max Prophet!) +3. Properly updated for Rails 0.9.3: this probably means that it won't work for a lower version of Rails. If you have the gems version of Rails installed, you can update it with: + sudo gem update rails +4. Made some minor changes to the appearance of the listed items, so that the description wraps properly, and the tool buttons (edit, delete and notes) are always in the same place relative to the checkbox. + +## Version 1.0 + +1. Updated to run on Rails 0.9.1. This should work with any 0.9.x branch. +2. Added a proper edit page for todos. This also allows you to add a todo to a project, or re-assign it to a new project. +3. Improved the efficiency of the Projects page: it now only finds the Project you've selected, rather than all of them (thanks, Johan !) +4. You now get a warning if you try to delete a project or context with undone items. If you choose to continue, the dependent items will be deleted, so that the list page should no longer break. +5. Using Redcloth for markup now that it allows you to use either Textile or Markdown format transparently. This is now included in the distribution, so that you don't need to install it. +6. Added links to context/list page so that you can go directly to todo/show/blah if you click the link for context 'blah'. +7. You can now set the date format you want to use in the file config/settings.yml. Use the strftime formats, with whatever separator you like. e.g. If you want 2004-12-31, the correct format is %Y-%m-%d. 31/12/2004 would be %d/%m/%Y. The same format is used for entry of the date in the due date box, and for display of the date in the list. +8. You can now add a new item to a particular project when you are viewing it at /project/show/[id]. This makes it easy to add a block of next actions to a project. Edit and delete buttons work on this page, but redirect you to /todo/list. I need to fix this. +9. Added a login system by using the login_generator: + +The first time you access the system, you need to visit to enter a username and password. Then when you visit, you will be greeted with a login page. This stores a temporary cookie for your session. The whole app is protected by the login. +10. You can now add a new item to a particular context when you are viewing it at /context/show/[id]. This makes it easy to add a block of next actions to a project. Edit and delete buttons work on this page, but redirect you to /todo/list. I need to fix this. +11. Feeds! There's an RSS feed available at which shows the last 15 next actions, and a text feed at which gives you a plain text list of all your next actions, sorted by context. You can use the latter to display your next actions on the desktop with GeekTool. Simply set up a shell command containing the following: + curl http://[tracks_url]/feed/na_text +Obviously, you need to replace [tracks_url] with the actual URL to the Tracks application. \ No newline at end of file diff --git a/tracks/doc/README.txt b/tracks/doc/README.txt new file mode 100644 index 00000000..f3ffa4e7 --- /dev/null +++ b/tracks/doc/README.txt @@ -0,0 +1,40 @@ +Tracks: a GTD web application, built with Ruby on Rails +------------------------------------------------------------- + +## $LastChangedDate$ +## $HeadURL$ + +# Homepage:: http://www.rousette.org.uk/projects/ +# Author:: bsag (http://www.rousette.org.uk/) +# Version:: 1.01 +# Copyright:: (cc) 2004-2005 rousette.org.uk +# License:: GNU GPL + +This is a still a work in progress, and there's almost no error checking or validation of the data yet. Use caution when you run it, and make sure that you back up any important data. Full changenotes can be found in tracks/doc/CHANGENOTES.txt. Full API documentation can be found at tracks/doc/app/index.html. + +**IF THIS CRASHES YOUR MACHINE AND LOSES YOUR DATA, IT'S NOT MY FAULT!** + +## Installation + +Before you start, you need to make sure that you have Ruby 1.8.x, mySQL and Rails 0.9.x. Note particularly the last requirement: this will probably not work with Rails 0.8.x. I have now included RedCloth 3.0 (which allows both Textile and Markdown format in the notes field) in the distribution. Find out more about RedCloth here: + +It's licensed under a BSD license. + +1. Unzip tracks.zip somewhere in your home folder ( e.g. /Users/yourusername/Sites). +2. Make a mySQL database called tracks for which you have full access rights. +3. Import the tables and contents using the tracks_dump 03.01.2005.sql file (in tracks/db). If you have previously used the application, export the contents of the tables from your old todo database and import them into the tracks database. +4. Open the tracks/config/database.yml file, and enter your username and password details. +5. Open the tracks/config/setting.yml file, and enter your desired format for dates (see CHANGENOTES.txt for details). +6. Open Terminal and navigate to the todo folder in your Sites folder. +7. Run the command: ruby script/server --environment=production +If you already have the previous GTD application running, make sure that you stop the server, or run Tracks on a different port with ruby script/server --environment=production --port=3030 +8. *IMPORTANT* Tracks now has password protection on all the browser accessible files. The first time that you access it, you need to visit . Choose your username and password, and you will be directed back to the main listing page (). +9. Have fun! + +## Contacting me + +I'd love any suggestions you have for improvements, bug-fixes etc. Email me on: + butshesagirl@rousette.org.uk + +You can also leave bug reports, feature requests, and comments at: + \ No newline at end of file diff --git a/tracks/lib/login_system.rb b/tracks/lib/login_system.rb new file mode 100644 index 00000000..b02c8f77 --- /dev/null +++ b/tracks/lib/login_system.rb @@ -0,0 +1,87 @@ +require_dependency "user" + +module LoginSystem + + protected + + # overwrite this if you want to restrict access to only a few actions + # or if you want to check if the user has the correct rights + # example: + # + # # only allow nonbobs + # def authorize?(user) + # user.login != "bob" + # end + def authorize?(user) + true + end + + # overwrite this method if you only want to protect certain actions of the controller + # example: + # + # # don't protect the login and the about method + # def protect?(action) + # if ['action', 'about'].include?(action) + # return false + # else + # return true + # end + # end + def protect?(action) + true + end + + # login_required filter. add + # + # before_filter :login_required + # + # if the controller should be under any rights management. + # for finer access control you can overwrite + # + # def authorize?(user) + # + def login_required + + if not protect?(action_name) + return true + end + + if @session['user'] and authorize?(@session['user']) + return true + end + + # store current location so that we can + # come back after the user logged in + store_location + + # call overwriteable reaction to unauthorized access + access_denied + return false + end + + # overwrite if you want to have special behavior in case the user is not authorized + # to access the current operation. + # the default action is to redirect to the login screen + # example use : + # a popup window might just close itself for instance + def access_denied + redirect_to :controller=>"login", :action =>"login" + end + + # store current uri in the session. + # we can return to this location by calling return_location + def store_location + @session['return-to'] = @request.request_uri + end + + # move to the last store_location call or to the passed default one + def redirect_back_or_default(default) + if @session['return-to'].nil? + redirect_to default + else + redirect_to_url @session['return-to'] + @session['return-to'] = nil + end + end + +end \ No newline at end of file diff --git a/tracks/lib/redcloth.rb b/tracks/lib/redcloth.rb new file mode 100644 index 00000000..ddb49447 --- /dev/null +++ b/tracks/lib/redcloth.rb @@ -0,0 +1,1018 @@ +# vim:ts=4:sw=4: +# = RedCloth - Textile and Markdown Hybrid for Ruby +# +# Homepage:: http://whytheluckystiff.net/ruby/redcloth/ +# Author:: why the lucky stiff (http://whytheluckystiff.net/) +# Copyright:: (cc) 2004 why the lucky stiff (and his puppet organizations.) +# License:: BSD +# +# (see http://hobix.com/textile/ for a Textile Reference.) +# +# Based on (and also inspired by) both: +# +# PyTextile: http://diveintomark.org/projects/textile/textile.py.txt +# Textism for PHP: http://www.textism.com/tools/textile/ +# +# +require 'uri' + +# = RedCloth +# +# RedCloth is a Ruby library for converting Textile and/or Markdown +# into HTML. You can use either format, intermingled or separately. +# You can also extend RedCloth to honor your own custom text stylings. +# +# RedCloth users are encouraged to use Textile if they are generating +# HTML and to use Markdown if others will be viewing the plain text. +# +# == What is Textile? +# +# Textile is a simple formatting style for text +# documents, loosely based on some HTML conventions. +# +# == Sample Textile Text +# +# h2. This is a title +# +# h3. This is a subhead +# +# This is a bit of paragraph. +# +# bq. This is a blockquote. +# +# = Writing Textile +# +# A Textile document consists of paragraphs. Paragraphs +# can be specially formatted by adding a small instruction +# to the beginning of the paragraph. +# +# h[n]. Header of size [n]. +# bq. Blockquote. +# # Numeric list. +# * Bulleted list. +# +# == Quick Phrase Modifiers +# +# Quick phrase modifiers are also included, to allow formatting +# of small portions of text within a paragraph. +# +# \_emphasis\_ +# \_\_italicized\_\_ +# \*strong\* +# \*\*bold\*\* +# ??citation?? +# -deleted text- +# +inserted text+ +# ^superscript^ +# ~subscript~ +# @code@ +# %(classname)span% +# +# ==notextile== (leave text alone) +# +# == Links +# +# To make a hypertext link, put the link text in "quotation +# marks" followed immediately by a colon and the URL of the link. +# +# Optional: text in (parentheses) following the link text, +# but before the closing quotation mark, will become a Title +# attribute for the link, visible as a tool tip when a cursor is above it. +# +# Example: +# +# "This is a link (This is a title) ":http://www.textism.com +# +# Will become: +# +# This is a link +# +# == Images +# +# To insert an image, put the URL for the image inside exclamation marks. +# +# Optional: text that immediately follows the URL in (parentheses) will +# be used as the Alt text for the image. Images on the web should always +# have descriptive Alt text for the benefit of readers using non-graphical +# browsers. +# +# Optional: place a colon followed by a URL immediately after the +# closing ! to make the image into a link. +# +# Example: +# +# !http://www.textism.com/common/textist.gif(Textist)! +# +# Will become: +# +# Textist +# +# With a link: +# +# !/common/textist.gif(Textist)!:http://textism.com +# +# Will become: +# +# Textist +# +# == Defining Acronyms +# +# HTML allows authors to define acronyms via the tag. The definition appears as a +# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing, +# this should be used at least once for each acronym in documents where they appear. +# +# To quickly define an acronym in Textile, place the full text in (parentheses) +# immediately following the acronym. +# +# Example: +# +# ACLU(American Civil Liberties Union) +# +# Will become: +# +# ACLU +# +# == Adding Tables +# +# In Textile, simple tables can be added by seperating each column by +# a pipe. +# +# |a|simple|table|row| +# |And|Another|table|row| +# +# Attributes are defined by style definitions in parentheses. +# +# table(border:1px solid black). +# (background:#ddd;color:red). |{}| | | | +# +# == Using RedCloth +# +# RedCloth is simply an extension of the String class, which can handle +# Textile formatting. Use it like a String and output HTML with its +# RedCloth#to_html method. +# +# doc = RedCloth.new " +# +# h2. Test document +# +# Just a simple test." +# +# puts doc.to_html +# +# By default, RedCloth uses both Textile and Markdown formatting, with +# Textile formatting taking precedence. If you want to turn off Markdown +# formatting, to boost speed and limit the processor: +# +# class RedCloth::Textile.new( str ) + +class RedCloth < String + + VERSION = '3.0.0' + + # + # Two accessor for setting security restrictions. + # + # This is a nice thing if you're using RedCloth for + # formatting in public places (e.g. Wikis) where you + # don't want users to abuse HTML for bad things. + # + # If +:filter_html+ is set, HTML which wasn't + # created by the Textile processor will be escaped. + # + # If +:filter_styles+ is set, it will also disable + # the style markup specifier. ('{color: red}') + # + attr_accessor :filter_html, :filter_styles + + # + # Accessor for toggling hard breaks. + # + # If +:hard_breaks+ is set, single newlines will + # be converted to HTML break tags. This is the + # default behavior for traditional RedCloth. + # + attr_accessor :hard_breaks + + # + # Establishes the markup predence. Available rules include: + # + # == Textile Rules + # + # The following textile rules can be set individually. Or add the complete + # set of rules with the single :textile rule, which supplies the rule set in + # the following precedence: + # + # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/) + # block_textile_table:: Textile table block structures + # block_textile_lists:: Textile list structures + # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.) + # inline_textile_image:: Textile inline images + # inline_textile_link:: Textile inline links + # inline_textile_span:: Textile inline spans + # inline_textile_glyphs:: Textile entities (such as em-dashes and smart quotes) + # + # == Markdown + # + # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/) + # block_markdown_setext:: Markdown setext headers + # block_markdown_atx:: Markdown atx headers + # block_markdown_rule:: Markdown horizontal rules + # block_markdown_bq:: Markdown blockquotes + # block_markdown_lists:: Markdown lists + # inline_markdown_link:: Markdown links + attr_accessor :rules + + # Returns a new RedCloth object, based on _string_ and + # enforcing all the included _restrictions_. + # + # r = RedCloth.new( "h1. A bold man", [:filter_html] ) + # r.to_html + # #=>"

    A <b>bold</b> man

    " + # + def initialize( string, restrictions = [] ) + restrictions.each { |r| method( "#{ r }=" ).call( true ) } + @rules = [:textile, :markdown] + super( string ) + end + + # + # Generates HTML from the Textile contents. + # + # r = RedCloth.new( "And then? She *fell*!" ) + # r.to_html( true ) + # #=>"And then? She fell!" + # + def to_html( *rules ) + rules = @rules if rules.empty? + # make our working copy + text = self.dup + + @urlrefs = {} + @shelf = [] + textile_rules = [:refs_textile, :block_textile_table, :block_textile_lists, + :block_textile_prefix, :inline_textile_image, :inline_textile_link, + :inline_textile_code, :inline_textile_span, :inline_textile_glyphs] + markdown_rules = [:refs_markdown, :block_markdown_setext, :block_markdown_atx, :block_markdown_rule, + :block_markdown_bq, :block_markdown_lists, + :inline_markdown_reflink, :inline_markdown_link] + @rules = rules.collect do |rule| + case rule + when :markdown + markdown_rules + when :textile + textile_rules + else + rule + end + end.flatten + + # standard clean up + incoming_entities text + clean_white_space text + + # start processor + pre_list = rip_offtags text + refs text + blocks text + inline text + smooth_offtags text, pre_list + + retrieve text + + text.gsub!( /<\/?notextile>/, '' ) + text.gsub!( /x%x%/, '&' ) + text.strip! + text + + end + + ####### + private + ####### + # + # Mapping of 8-bit ASCII codes to HTML numerical entity equivalents. + # (from PyTextile) + # + TEXTILE_TAGS = + + [[128, 8364], [129, 0], [130, 8218], [131, 402], [132, 8222], [133, 8230], + [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249], + [140, 338], [141, 0], [142, 0], [143, 0], [144, 0], [145, 8216], [146, 8217], + [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732], + [153, 8482], [154, 353], [155, 8250], [156, 339], [157, 0], [158, 0], [159, 376]]. + + collect! do |a, b| + [a.chr, ( b.zero? and "" or "&#{ b };" )] + end + + # + # Regular expressions to convert to HTML. + # + A_HLGN = /(?:(?:<>|<|>|\=|[()]+)+)/ + A_VLGN = /[\-^~]/ + C_CLAS = '(?:\([^)]+\))' + C_LNGE = '(?:\[[^\]]+\])' + C_STYL = '(?:\{[^}]+\})' + S_CSPN = '(?:\\\\\d+)' + S_RSPN = '(?:/\d+)' + A = "(?:#{A_HLGN}?#{A_VLGN}?|#{A_VLGN}?#{A_HLGN}?)" + S = "(?:#{S_CSPN}?#{S_RSPN}|#{S_RSPN}?#{S_CSPN}?)" + C = "(?:#{C_CLAS}?#{C_STYL}?#{C_LNGE}?|#{C_STYL}?#{C_LNGE}?#{C_CLAS}?|#{C_LNGE}?#{C_STYL}?#{C_CLAS}?)" + # PUNCT = Regexp::quote( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ) + PUNCT = Regexp::quote( '!"#$%&\'*+,-./:;=?@\\^_`|~' ) + HYPERLINK = '(\S+?)([^\w\s/;=\?]*?)(?=\s|<|$)' + + # Text markup tags, don't conflict with block tags + SIMPLE_HTML_TAGS = [ + 'tt', 'b', 'i', 'big', 'small', 'em', 'strong', 'dfn', 'code', + 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'a', 'img', 'br', + 'br', 'map', 'q', 'sub', 'sup', 'span', 'bdo' + ] + + # Elements to handle + GLYPHS = [ + # [ /([^\s\[{(>])?\'([dmst]\b|ll\b|ve\b|\s|:|$)/, '\1’\2' ], # single closing + [ /([^\s\[{(>])\'/, '\1’' ], # single closing + [ /\'(?=\s|s\b|[#{PUNCT}])/, '’' ], # single closing + [ /\'/, '‘' ], # single opening + # [ /([^\s\[{(])?"(\s|:|$)/, '\1”\2' ], # double closing + [ /([^\s\[{(>])"/, '\1”' ], # double closing + [ /"(?=\s|[#{PUNCT}])/, '”' ], # double closing + [ /"/, '“' ], # double opening + [ /\b( )?\.{3}/, '\1…' ], # ellipsis + [ /\b([A-Z][A-Z0-9]{2,})\b(?:[(]([^)]*)[)])/, '\1' ], # 3+ uppercase acronym + [ /(^|[^"][>\s])([A-Z][A-Z0-9 ]{2,})([^\2\3' ], # 3+ uppercase caps + [ /(\.\s)?\s?--\s?/, '\1—' ], # em dash + [ /\s->\s/, ' → ' ], # en dash + [ /\s-\s/, ' – ' ], # en dash + [ /(\d+) ?x ?(\d+)/, '\1×\2' ], # dimension sign + [ /\b ?[(\[]TM[\])]/i, '™' ], # trademark + [ /\b ?[(\[]R[\])]/i, '®' ], # registered + [ /\b ?[(\[]C[\])]/i, '©' ] # copyright + ] + + H_ALGN_VALS = { + '<' => 'left', + '=' => 'center', + '>' => 'right', + '<>' => 'justify' + } + + V_ALGN_VALS = { + '^' => 'top', + '-' => 'middle', + '~' => 'bottom' + } + + QTAGS = [ + ['**', 'b'], + ['*', 'strong'], + ['??', 'cite'], + ['-', 'del'], + ['__', 'i'], + ['_', 'em'], + ['%', 'span'], + ['+', 'ins'], + ['^', 'sup'], + ['~', 'sub'] + ].collect do |rc, ht| + ttr = Regexp.quote(rc) + punct = PUNCT.sub( Regexp::quote(rc), '' ) + re = /(^|[\s\>#{punct}{(\[]) + #{ttr} + (#{C}) + (?::(\S+?))? + ([^\s#{ttr}]+?(?:[^\n]|\n(?!\n))*?) + ([#{punct}]*?) + #{ttr} + (?=[\s\])}<#{punct}]|$)/xm + [re, ht] + end + + # + # Flexible HTML escaping + # + def htmlesc( str, mode ) + str.gsub!( '&', '&' ) + str.gsub!( '"', '"' ) if mode != :NoQuotes + str.gsub!( "'", ''' ) if mode == :Quotes + str.gsub!( '<', '<') + str.gsub!( '>', '>') + end + + # Search and replace for Textile glyphs (quotes, dashes, other symbols) + def pgl( text ) + GLYPHS.each do |re, resub| + text.gsub! re, resub + end + end + + # Parses Textile attribute lists and builds an HTML attribute string + def pba( text_in, element = "" ) + + return '' unless text_in + + style = [] + text = text_in.dup + if element == 'td' + colspan = $1 if text =~ /\\(\d+)/ + rowspan = $1 if text =~ /\/(\d+)/ + style << "vertical-align:#{ v_align( $& ) };" if text =~ A_VLGN + end + + style << "#{ $1 };" if not @filter_styles and + text.sub!( /\{([^}]*)\}/, '' ) + + lang = $1 if + text.sub!( /\[([^)]+?)\]/, '' ) + + cls = $1 if + text.sub!( /\(([^()]+?)\)/, '' ) + + style << "padding-left:#{ $1.length }em;" if + text.sub!( /([(]+)/, '' ) + + style << "padding-right:#{ $1.length }em;" if text.sub!( /([)]+)/, '' ) + + style << "text-align:#{ h_align( $& ) };" if text =~ A_HLGN + + cls, id = $1, $2 if cls =~ /^(.*?)#(.*)$/ + + atts = '' + atts << " style=\"#{ style.join }\"" unless style.empty? + atts << " class=\"#{ cls }\"" unless cls.to_s.empty? + atts << " lang=\"#{ lang }\"" if lang + atts << " id=\"#{ id }\"" if id + atts << " colspan=\"#{ colspan }\"" if colspan + atts << " rowspan=\"#{ rowspan }\"" if rowspan + + atts + end + + TABLE_RE = /^(?:table(_?#{S}#{A}#{C})\. ?\n)?^(#{A}#{C}\.? ?\|.*?\|)(\n\n|\Z)/m + + # Parses a Textile table block, building HTML from the result. + def block_textile_table( text ) + text.gsub!( TABLE_RE ) do |matches| + + tatts, fullrow = $~[1..2] + tatts = pba( tatts, 'table' ) + rows = [] + + fullrow. + split( /\|$/m ). + delete_if { |x| x.empty? }. + each do |row| + + ratts, row = pba( $1, 'tr' ), $2 if row =~ /^(#{A}#{C}\. )(.*)/m + + cells = [] + row.split( '|' ).each do |cell| + ctyp = 'd' + ctyp = 'h' if cell =~ /^_/ + + catts = '' + catts, cell = pba( $1, 'td' ), $2 if cell =~ /^(_?#{S}#{A}#{C}\. ?)(.*)/ + + unless cell.strip.empty? + cells << "\t\t\t#{ cell }" + end + end + rows << "\t\t\n#{ cells.join( "\n" ) }\n\t\t" + end + "\t\n#{ rows.join( "\n" ) }\n\t\n\n" + end + end + + LISTS_RE = /^([#*]+?#{C} .*?)$(?![^#*])/m + LISTS_CONTENT_RE = /^([#*]+)(#{A}#{C}) (.*)$/m + + # Parses Textile lists and generates HTML + def block_textile_lists( text ) + text.gsub!( LISTS_RE ) do |match| + lines = match.split( /\n/ ) + last_line = -1 + depth = [] + lines.each_with_index do |line, line_id| + if line =~ LISTS_CONTENT_RE + tl,atts,content = $~[1..3] + if depth.last + if depth.last.length > tl.length + (depth.length - 1).downto(0) do |i| + break if depth[i].length == tl.length + lines[line_id - 1] << "\n\t\n\t" + depth.pop + end + end + if depth.last.length == tl.length + lines[line_id - 1] << '' + end + end + unless depth.last == tl + depth << tl + atts = pba( atts ) + lines[line_id] = "\t<#{ lT(tl) }l#{ atts }>\n\t
  • #{ content }" + else + lines[line_id] = "\t\t
  • #{ content }" + end + last_line = line_id + + else + last_line = line_id + end + if line_id - last_line > 1 or line_id == lines.length - 1 + depth.delete_if do |v| + lines[last_line] << "
  • \n\t" + end + end + end + lines.join( "\n" ) + end + end + + CODE_RE = / + (^|[\s>#{PUNCT}{(\[]) # 1 open bracket? + @ # opening + (?:\|(\w+?)\|)? # 2 language + (\S(?:[^\n]|\n(?!\n))*?) # 3 code + @ # closing + (?=[\s\]}\)<#{PUNCT}]|$) # 4 closing bracket? + /x + + def inline_textile_code( text ) + text.gsub!( CODE_RE ) do |m| + before,lang,code,after = $~[1..4] + lang = " lang=\"#{ lang }\"" if lang + "#{ before }#{ code }#{ after }" + end + end + + def lT( text ) + text =~ /\#$/ ? 'o' : 'u' + end + + def hard_break( text ) + text.gsub!( /(.)\n(?! *[#*\s|])/, "\\1
    " ) if @hard_breaks + end + + BLOCKS_GROUP_RE = /(#{ + ['#', '*', '>'].collect do |sym| + sym = Regexp::quote( sym ) + '(?:\n*[' + sym + ' ](?:[^\n]|\n+[' + sym + ' ]|\n(?!\n|\Z))+)' + end.join '|' + })|((?:[^\n]+|\n+ +|\n(?![#*\n]|\Z))+)/m + + def blocks( text, deep_code = false ) + text.gsub!( BLOCKS_GROUP_RE ) do |blk| + plain = $2 ? true : false + + # skip blocks that are complex HTML + if blk =~ /^<\/?(\w+).*>/ and not SIMPLE_HTML_TAGS.include? $1 + blk + else + # search for indentation levels + blk.strip! + if blk.empty? + blk + else + code_blk = nil + blk.gsub!( /((?:\n(?:\n^ +[^\n]*)+)+)/m ) do |iblk| + flush_left iblk + blocks iblk, plain + iblk.gsub( /^(\S)/, "\t\\1" ) + if plain + code_blk = iblk; "" + else + iblk + end + end + + block_applied = nil + @rules.each do |rule_name| + break if block_applied = ( rule_name.to_s.match /^block_/ and method( rule_name ).call( blk ) ) + end + unless block_applied + if deep_code + blk = "\t
    #{ blk }
    " + else + blk = "\t

    #{ blk }

    " + end + end + # hard_break blk + blk + "\n#{ code_blk }" + end + end + + end + end + + def textile_bq( tag, atts, cite, content ) + cite, cite_title = check_refs( cite ) + cite = " cite=\"#{ cite }\"" if cite + "\t\n\t\t#{ content }

    \n\t" + end + + def textile_p( tag, atts, cite, content ) + "\t<#{ tag }#{ atts }>#{ content }" + end + + alias textile_h1 textile_p + alias textile_h2 textile_p + alias textile_h3 textile_p + alias textile_h4 textile_p + alias textile_h5 textile_p + alias textile_h6 textile_p + + def textile_fn_( tag, num, atts, cite, content ) + atts << " id=\"fn#{ num }\"" + content = "#{ num } #{ content }" + "\t#{ content }

    " + end + + BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m + + def block_textile_prefix( text ) + if text =~ BLOCK_RE + tag,tagpre,num,atts,cite,content = $~[1..6] + atts = pba( atts ) + + # pass to prefix handler + if respond_to? "textile_#{ tag }", true + text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) ) + elsif respond_to? "textile_#{ tagpre }_", true + text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) ) + end + end + end + + SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m + def block_markdown_setext( text ) + if text =~ SETEXT_RE + tag = if $2 == "="; "h1"; else; "h2"; end + blk, cont = "<#{ tag }>#{ $1 }", $' + blocks cont + text.replace( blk + cont ) + end + end + + ATX_RE = /\A(\#{1,6}) # $1 = string of #'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #'s (not counted) + $/x + def block_markdown_atx( text ) + if text =~ ATX_RE + tag = "h#{ $1.length }" + blk, cont = "<#{ tag }>#{ $2 }\n\n", $' + blocks cont + text.replace( blk + cont ) + end + end + + MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m + + def block_markdown_bq( text ) + text.gsub!( MARKDOWN_BQ_RE ) do |blk| + blk.gsub!( /^ *> ?/, '' ) + flush_left blk + blocks blk + blk.gsub!( /^(\S)/, "\t\\1" ) + "
    \n#{ blk }\n
    \n\n" + end + end + + MARKDOWN_RULE_RE = /^#{ + ['*', '-', '_'].collect { |ch| '( ?' + Regexp::quote( ch ) + ' ?){3,}' }.join( '|' ) + }$/ + + def block_markdown_rule( text ) + text.gsub!( MARKDOWN_RULE_RE ) do |blk| + "
    " + end + end + + # XXX TODO XXX + def block_markdown_lists( text ) + end + + def inline_markdown_link( text ) + end + + def inline_textile_span( text ) + QTAGS.each do |ttr, ht| + text.gsub!(ttr) do |m| + + start,atts,cite,content,tend = $~[1..5] + atts = pba( atts ) + atts << " cite=\"#{ cite }\"" if cite + + "#{ start }<#{ ht }#{ atts }>#{ content }#{ tend }" + + end + end + end + + LINK_RE = / + ([\s\[{(]|[#{PUNCT}])? # $pre + " # start + (#{C}) # $atts + ([^"]+?) # $text + \s? + (?:\(([^)]+?)\)(?="))? # $title + ": + (\S+?) # $url + (\/)? # $slash + ([^\w\/;]*?) # $post + (?=<|\s|$) + /x + + def inline_textile_link( text ) + text.gsub!( LINK_RE ) do |m| + pre,atts,text,title,url,slash,post = $~[1..7] + + url, url_title = check_refs( url ) + title ||= url_title + + atts = pba( atts ) + atts = " href=\"#{ url }#{ slash }\"#{ atts }" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) if atts + + "#{ pre }#{ text }#{ post }" + end + end + + MARKDOWN_REFLINK_RE = / + \[([^\[\]]+)\] # $text + [ ]? # opt. space + (?:\n[ ]*)? # one optional newline followed by spaces + \[(.*?)\] # $id + /x + + def inline_markdown_reflink( text ) + text.gsub!( MARKDOWN_REFLINK_RE ) do |m| + text, id = $~[1..2] + + if id.empty? + url, title = check_refs( text ) + else + url, title = check_refs( id ) + end + + atts = " href=\"#{ url }\"" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) + + "#{ text }" + end + end + + MARKDOWN_LINK_RE = / + \[([^\[\]]+)\] # $text + \( # open paren + [ \t]* # opt space + ? # $href + [ \t]* # opt space + (?: # whole title + (['"]) # $quote + (.*?) # $title + \3 # matching quote + )? # title is optional + \) + /x + + def inline_markdown_link( text ) + text.gsub!( MARKDOWN_LINK_RE ) do |m| + text, url, quote, title = $~[1..4] + + atts = " href=\"#{ url }\"" + atts << " title=\"#{ title }\"" if title + atts = shelve( atts ) + + "#{ text }" + end + end + + TEXTILE_REFS_RE = /(^ *)\[([^\n]+?)\](#{HYPERLINK})(?=\s|$)/ + MARKDOWN_REFS_RE = /(^ *)\[([^\n]+?)\]:\s+?(?:\s+"((?:[^"]|\\")+)")?(?=\s|$)/m + + def refs( text ) + @rules.each do |rule_name| + method( rule_name ).call( text ) if rule_name.to_s.match /^refs_/ + end + end + + def refs_textile( text ) + text.gsub!( TEXTILE_REFS_RE ) do |m| + flag, url = $~[2..3] + @urlrefs[flag.downcase] = [url, nil] + nil + end + end + + def refs_markdown( text ) + text.gsub!( MARKDOWN_REFS_RE ) do |m| + flag, url = $~[2..3] + title = $~[6] + @urlrefs[flag.downcase] = [url, title] + nil + end + end + + def check_refs( text ) + ret = @urlrefs[text.downcase] if text + ret || [text, nil] + end + + IMAGE_RE = / + (

    |.|^) # start of line? + \! # opening + (\<|\=|\>)? # optional alignment atts + (#{C}) # optional style,class atts + (?:\. )? # optional dot-space + ([^\s(!]+?) # presume this is the src + \s? # optional space + (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title + \! # closing + (?::#{ HYPERLINK })? # optional href + /x + + def inline_textile_image( text ) + text.gsub!( IMAGE_RE ) do |m| + stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8] + atts = pba( atts ) + atts = " src=\"#{ url }\"#{ atts }" + atts << " title=\"#{ title }\"" if title + atts << " alt=\"#{ title }\"" + # size = @getimagesize($url); + # if($size) $atts.= " $size[3]"; + + href, alt_title = check_refs( href ) if href + url, url_title = check_refs( url ) + + out = '' + out << "" if href + out << "" + out << "#{ href_a1 }#{ href_a2 }" if href + + if algn + algn = h_align( algn ) + if stln == "

    " + out = "

    #{ out }" + else + out = "#{ stln }

    #{ out }
    " + end + else + out = stln + out + end + + out + end + end + + def shelve( val ) + @shelf << val + " <#{ @shelf.length }>" + end + + def retrieve( text ) + @shelf.each_with_index do |r, i| + text.gsub!( " <#{ i + 1 }>", r ) + end + end + + def incoming_entities( text ) + ## turn any incoming ampersands into a dummy character for now. + ## This uses a negative lookahead for alphanumerics followed by a semicolon, + ## implying an incoming html entity, to be skipped + + text.gsub!( /&(?![#a-z0-9]+;)/i, "x%x%" ) + end + + def clean_white_space( text ) + # normalize line breaks + text.gsub!( /\r\n/, "\n" ) + text.gsub!( /\r/, "\n" ) + text.gsub!( /\t/, ' ' ) + text.gsub!( /^ +$/, '' ) + text.gsub!( /\n{3,}/, "\n\n" ) + text.gsub!( /"$/, "\" " ) + + # if entire document is indented, flush + # to the left side + flush_left text + end + + def flush_left( text ) + indt = 0 + while text !~ /^ {#{indt}}\S/ + indt += 1 + end + if indt.nonzero? + text.gsub!( /^ {#{indt}}/, '' ) + end + end + + def footnote_ref( text ) + text.gsub!( /\b\[([0-9]+?)\](\s)?/, + '\1\2' ) + end + + OFFTAGS = /(code|pre|kbd|notextile)/ + OFFTAG_MATCH = /(?:(<\/#{ OFFTAGS }>)|(<#{ OFFTAGS }[^>]*>))(.*?)(?=<\/?#{ OFFTAGS }|\Z)/mi + OFFTAG_OPEN = /<#{ OFFTAGS }/ + OFFTAG_CLOSE = /<\/?#{ OFFTAGS }/ + HASTAG_MATCH = /(<\/?\w[^\n]*?>)/m + ALLTAG_MATCH = /(<\/?\w[^\n]*?>)|.*?(?=<\/?\w[^\n]*?>|$)/m + + def inline_textile_glyphs( text, level = 0 ) + if text !~ HASTAG_MATCH + pgl text + footnote_ref text + else + codepre = 0 + text.gsub!( ALLTAG_MATCH ) do |line| + ## matches are off if we're between ,
     etc.
    +                if $1
    +                    if @filter_html
    +                        htmlesc( line, :NoQuotes )
    +                    elsif line =~ OFFTAG_OPEN
    +                        codepre += 1
    +                    elsif line =~ OFFTAG_CLOSE
    +                        codepre -= 1
    +                        codepre = 0 if codepre < 0
    +                    end 
    +                elsif codepre.zero?
    +                    inline_textile_glyphs( line, level + 1 )
    +                else
    +                    htmlesc( line, :NoQuotes )
    +                end
    +                ## p [level, codepre, orig_line, line]
    +
    +                line
    +            end
    +        end
    +    end
    +
    +    def rip_offtags( text )
    +        pre_list = []
    +        if text =~ /<.*>/
    +            ## strip and encode 
     content
    +            codepre, used_offtags = 0, {}
    +            text.gsub!( OFFTAG_MATCH ) do |line|
    +                if $3
    +                    offtag, aftertag = $4, $5
    +                    codepre += 1
    +                    used_offtags[offtag] = true
    +                    if codepre - used_offtags.length > 0
    +                        htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
    +                        pre_list.last << line
    +                        line = ""
    +                    else
    +                        htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
    +                        line = ""
    +                        pre_list << "#{ $3 }#{ aftertag }"
    +                    end
    +                elsif $1 and codepre > 0
    +                    if codepre - used_offtags.length > 0
    +                        htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
    +                        pre_list.last << line
    +                        line = ""
    +                    end
    +                    codepre -= 1 unless codepre.zero?
    +                    used_offtags = {} if codepre.zero?
    +                end 
    +                line
    +            end
    +        end
    +        pre_list
    +    end
    +
    +    def smooth_offtags( text, pre_list )
    +        unless pre_list.empty?
    +            ## replace 
     content
    +            text.gsub!( // ) { pre_list[$1.to_i] }
    +        end
    +    end
    +
    +    def inline( text ) 
    +        @rules.each do |rule_name|
    +            method( rule_name ).call( text ) if rule_name.to_s.match /^inline_/
    +        end
    +    end
    +
    +    def h_align( text ) 
    +        H_ALGN_VALS[text]
    +    end
    +
    +    def v_align( text ) 
    +        V_ALGN_VALS[text]
    +    end
    +
    +    def textile_popup_help( name, windowW, windowH )
    +        ' ' + name + '
    ' + end + +end + diff --git a/tracks/log.tmpl/apache.log b/tracks/log.tmpl/apache.log new file mode 100755 index 00000000..e69de29b diff --git a/tracks/log.tmpl/development.log b/tracks/log.tmpl/development.log new file mode 100755 index 00000000..e69de29b diff --git a/tracks/log.tmpl/production.log b/tracks/log.tmpl/production.log new file mode 100755 index 00000000..e69de29b diff --git a/tracks/log.tmpl/test.log b/tracks/log.tmpl/test.log new file mode 100755 index 00000000..e69de29b diff --git a/tracks/public/.htaccess b/tracks/public/.htaccess new file mode 100644 index 00000000..3da2844d --- /dev/null +++ b/tracks/public/.htaccess @@ -0,0 +1,29 @@ +# General Apache options +AddHandler fastcgi-script .fcgi +AddHandler cgi-script .cgi +Options +FollowSymLinks +ExecCGI + +# Make sure that mod_ruby.c has been added and loaded as a module with Apache +RewriteEngine On + +# Change extension from .cgi to .fcgi to switch to FCGI and to .rb to switch to mod_ruby +RewriteBase /dispatch.cgi + +# Enable this rewrite rule to point to the controller/action that should serve root. +# RewriteRule ^$ /controller/action [R] + +# Add missing slash +RewriteRule ^([-_a-zA-Z0-9]+)$ /$1/ [R] + +# Default rewriting rules. +RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([0-9]+)$ ?controller=$1&action=$2&id=$3 [QSA,L] +RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$ ?controller=$1&action=$2 [QSA,L] +RewriteRule ^([-_a-zA-Z0-9]+)/$ ?controller=$1&action=index [QSA,L] + +RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([0-9]+)$ ?module=$1&controller=$2&action=$3&id=$4 [QSA,L] +RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)$ ?module=$1&controller=$2&action=$3 [QSA,L] +RewriteRule ^([-_a-zA-Z0-9]+)/([-_a-zA-Z0-9]+)/$ ?module=$1&controller=$2&action=index [QSA,L] + +# You can also point these error messages to a controller/action +ErrorDocument 500 /500.html +ErrorDocument 404 /404.html \ No newline at end of file diff --git a/tracks/public/404.html b/tracks/public/404.html new file mode 100644 index 00000000..edbc89bf --- /dev/null +++ b/tracks/public/404.html @@ -0,0 +1,6 @@ + + +

    File not found

    +

    Change this error message for pages not found in public/404.html

    + + \ No newline at end of file diff --git a/tracks/public/500.html b/tracks/public/500.html new file mode 100644 index 00000000..ee0c919c --- /dev/null +++ b/tracks/public/500.html @@ -0,0 +1,6 @@ + + +

    Application error (Apache)

    +

    Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html

    + + \ No newline at end of file diff --git a/tracks/public/_doc/index.html b/tracks/public/_doc/index.html new file mode 100644 index 00000000..522a88cd --- /dev/null +++ b/tracks/public/_doc/index.html @@ -0,0 +1,70 @@ + + + Rails: Welcome on board + + + + +

    Congratulations, you've put Ruby on Rails!

    + +

    Before you move on, verify that the following conditions have been met:

    + +
      +
    1. The log directory and the empty log files must be writable to the web server (chmod -R 777 log). +
    2. + The shebang line in the public/dispatch* files must reference your Ruby installation.
      + You might need to change it to #!/usr/bin/env ruby or point directly at the installation. +
    3. +
    4. + Rails on Apache needs to have the cgi handler and mod_rewrite enabled.
      + Somewhere in your httpd.conf, you should have:
      + AddHandler cgi-script .cgi
      + LoadModule rewrite_module libexec/httpd/mod_rewrite.so
      + AddModule mod_rewrite.c +
    5. +
    + +

    Take the following steps to get started:

    + +
      +
    1. Create empty development and test databases for your application.
      + Recommendation: Use *_development and *_test names, such as basecamp_development and basecamp_test
      + Warning: Don't point your test database at your development database, it'll destroy the latter on test runs! +
    2. Edit config/database.yml with your database settings. +
    3. Create controllers and models using the generator in script/generate
      + Help: Run the generator with no arguments for documentation +
    4. See all the tests run by running rake. +
    5. Develop your Rails application! +
    6. Setup Apache with FastCGI (and Ruby bindings), if you need better performance +
    + +

    + Having problems getting up and running? First try debugging it yourself by looking at the log files.
    + Then try the friendly Rails community on the web or on IRC + (FreeNode#rubyonrails). +

    + + + \ No newline at end of file diff --git a/tracks/public/dispatch.cgi b/tracks/public/dispatch.cgi new file mode 100755 index 00000000..1f0914f8 --- /dev/null +++ b/tracks/public/dispatch.cgi @@ -0,0 +1,10 @@ +#!/usr/local/bin/ruby + +require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) + +# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: +# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired +require "dispatcher" + +ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } +Dispatcher.dispatch \ No newline at end of file diff --git a/tracks/public/dispatch.fcgi b/tracks/public/dispatch.fcgi new file mode 100755 index 00000000..0f1b9b3a --- /dev/null +++ b/tracks/public/dispatch.fcgi @@ -0,0 +1,7 @@ +#!/usr/local/bin/ruby + +require File.dirname(__FILE__) + "/../config/environment" +require 'dispatcher' +require 'fcgi' + +FCGI.each_cgi { |cgi| Dispatcher.dispatch(cgi) } \ No newline at end of file diff --git a/tracks/public/dispatch.rb b/tracks/public/dispatch.rb new file mode 100755 index 00000000..1f0914f8 --- /dev/null +++ b/tracks/public/dispatch.rb @@ -0,0 +1,10 @@ +#!/usr/local/bin/ruby + +require File.dirname(__FILE__) + "/../config/environment" unless defined?(RAILS_ROOT) + +# If you're using RubyGems and mod_ruby, this require should be changed to an absolute path one, like: +# "/usr/local/lib/ruby/gems/1.8/gems/rails-0.8.0/lib/dispatcher" -- otherwise performance is severely impaired +require "dispatcher" + +ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) } +Dispatcher.dispatch \ No newline at end of file diff --git a/tracks/public/images/delete.png b/tracks/public/images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..c697ec94e6ea626dfcfc949b7c7da309b5406854 GIT binary patch literal 3128 zcmV-849D|{P)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0aZyvK~#9!Ou@T~8c_g%;s4CJSVa&92G&8a3G)h@CJ;f< zpg0Ed3?Xe&Y`x7k+k_y5kQ8}@q_9gnSS;g!-4qGfc`Hs~;T-3fHs2@a^Euvoy!Wq^ zQtyFVKmt#7U4I3BNht}u_k>}1*XeYAJLld2H$dc^dmTm5&vv{0Sl2Z{UDq$0&E~^o zGNIe;az3A_s){IzNYj)s3_p19-^%fL{As`6C#TbiUa!aDaKIQtnx^D=PM+szt#1@? ztF>mmUQ<;S{eB;S<#NetwW85z06bSf03d|GT8oqttu@wK{u2a1?wlizV`j4%S(dR_ zEEo=lBuPS5RRDk0<#PEP$8mo;oswl4MNx3QUXfBV7z_|XP?qHvIU0@1R;%@FyWPGh ziUMN{N-1``9oAZe5I>H`5XP84Wm$d~LVQz7@oxYk5{)v_ Sj7N(A0000e29P*U^sTI*Qlu9>h_HHhoVlD4Zd8z z{9{{vcIWN5PAo@*vJ|VW+&8|e{-jBot~TYI9vQgh6B|>RXstO}b)34v12|qPs@9jS zzpYH6wy=iIP#f8H7JDw%GwaM_EBnVqi@Hk(Lf2wrr(9B*en}rFE6J-lw&7S}F?yrc zYFA%<`Ab7bbNQD9;E4_D7alEA2FmQOC>FDHYTiJZPt(j9JXV7Rq82%7J z7>SLRsDPE#nbI!H%nS@=(lUGkK@dw(87Ui(hVL4ztCKgAxEe>N7Ooap>C0*E3bh?n ztre|Ke!-SBzv1gO4TYubxpnD9;jU2I!Nr)1EczPF->tdwNG$S7EETVIJZBXGmY9=% z%RiXs&wbBMOMKf}(dsPW)Y#W(UeTlbCK8j!9USoZpEMej!ZJ)&s;qacypngnC_j>v zz3qFA*b3{jDVcfNje9IS+rnmzptU9^Et@Bc!-Ups=k@Q8@@xD$S+e|mWqxU`gnORZ z^rtg3x#gy7dFQgO3o(uj1Svm-zg~m&_+)Ac8Us0xF}%ERv#$ zY58d?iML9jD9H%2F}swtfgrQaJQr^c^8DqOi6&>!B8UhGm>6?w(t{BJ*w=q}+i>i+ z^GTgZTVZCiyXa9;ZiKLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0Srk*K~#9!T+Y90no$78;paO?bjl_a3$9wh#jynuD!4cn zat$Fvp)^D0ELpl0p_F#$CA5p66lWpy0*W*U4vIJ6A3jeP4U*sd@bYGZ!N9S%xW;Ql zJmh))HOBZ0!V%r#H{RhTUK|dGez{!!8AAAUf*ssDYqQyCHk-QLE__TWT?TwON89aI zqtVc6wT#DO`~6;)W$(lB(-W%IYTE6#;c#fZUW+k8Eu4Ptc01K-Rj1Q28jUQMOT}XG zv=IXSP)KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C0I*3!K~#9!jF2%3gFp~P-?&>DMM$%ao zWhqz*l0w14oitHgNg%(Q;m_m$5mlvWnnzVtw(QvH48w2%umB>$x~_CxhlsH4ilW$OyImS%aL#ciswx4|d(SwII|0Bs7ZNvfmSq7^ zL_*@HVL%iS;yC`EG0*d#86*Cb1z474>id2@KU`JQd7dYL_W;h8O!4Uu&~5+#002ov JPDHLkV1kEjpAG;3 literal 0 HcmV?d00001 diff --git a/tracks/public/index.html b/tracks/public/index.html new file mode 100644 index 00000000..4949c64a --- /dev/null +++ b/tracks/public/index.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tracks/public/javascripts/toggle_notes.js b/tracks/public/javascripts/toggle_notes.js new file mode 100644 index 00000000..21ec62fd --- /dev/null +++ b/tracks/public/javascripts/toggle_notes.js @@ -0,0 +1,13 @@ +function toggleAll(itemname,state) +{ + tmp = document.getElementsByTagName('div'); +   for (i=0;i", "") + data.scan(/^Sense \d+\n.+?\n\n/m) + end +rescue Exception + return nil +end + +unless ARGV.empty? + begin + name = ARGV.shift + generator = Rails::Generator.instance(name, ARGV) + + if msg = generator.collision_with_builtin? then + $stderr.puts msg + + if synonyms = find_synonyms(generator.class_name) then + $stderr.puts( + "", "Here are a few synonyms from WordNet. Maybe they will help you find an alternative name.", + "", synonyms + ) + end + else + generator.generate + end + rescue Rails::Generator::UsageError => e + puts e.message + end +else + builtin_generators = Rails::Generator.builtin_generators.join(', ') + contrib_generators = Rails::Generator.contrib_generators.join(', ') + + $stderr.puts < 3000, + :ip => "127.0.0.1", + :environment => "development", + :server_root => File.expand_path(File.dirname(__FILE__) + "/../public/"), + :server_type => WEBrick::SimpleServer +} + +ARGV.options do |opts| + script_name = File.basename($0) + opts.banner = "Usage: ruby #{script_name} [options]" + + opts.separator "" + + opts.on("-p", "--port=port", Integer, + "Runs Rails on the specified port.", + "Default: 3000") { |OPTIONS[:port]| } + opts.on("-b", "--binding=ip", String, + "Binds Rails to the specified ip.", + "Default: 127.0.0.1") { |OPTIONS[:ip]| } + opts.on("-i", "--index=controller", String, + "Specifies an index controller that requests for root will go to (instead of congratulations screen)." + ) { |OPTIONS[:index_controller]| } + opts.on("-e", "--environment=name", String, + "Specifies the environment to run this server under (test/development/production).", + "Default: development") { |OPTIONS[:environment]| } + opts.on("-d", "--daemon", + "Make Rails run as a Daemon (only works if fork is available -- meaning on *nix)." + ) { OPTIONS[:server_type] = WEBrick::Daemon } + + opts.separator "" + + opts.on("-h", "--help", + "Show this help message.") { puts opts; exit } + + opts.parse! +end + +ENV["RAILS_ENV"] = OPTIONS[:environment] +require File.dirname(__FILE__) + "/../config/environment" +require 'webrick_server' + +puts "=> Rails application started on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}" +DispatchServlet.dispatch(OPTIONS) \ No newline at end of file diff --git a/tracks/test/fixtures/contexts.yml b/tracks/test/fixtures/contexts.yml new file mode 100644 index 00000000..a56c1646 --- /dev/null +++ b/tracks/test/fixtures/contexts.yml @@ -0,0 +1 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html diff --git a/tracks/test/fixtures/projects.yml b/tracks/test/fixtures/projects.yml new file mode 100644 index 00000000..a56c1646 --- /dev/null +++ b/tracks/test/fixtures/projects.yml @@ -0,0 +1 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html diff --git a/tracks/test/fixtures/todos.yml b/tracks/test/fixtures/todos.yml new file mode 100644 index 00000000..a56c1646 --- /dev/null +++ b/tracks/test/fixtures/todos.yml @@ -0,0 +1 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html diff --git a/tracks/test/fixtures/users.yml b/tracks/test/fixtures/users.yml new file mode 100644 index 00000000..a56c1646 --- /dev/null +++ b/tracks/test/fixtures/users.yml @@ -0,0 +1 @@ +# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html diff --git a/tracks/test/functional/context_controller_test.rb b/tracks/test/functional/context_controller_test.rb new file mode 100644 index 00000000..50889fa8 --- /dev/null +++ b/tracks/test/functional/context_controller_test.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'context_controller' + +# Re-raise errors caught by the controller. +class ContextController; def rescue_action(e) raise e end; end + +class ContextControllerTest < Test::Unit::TestCase + def setup + @controller = ContextController.new + @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new + end + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/functional/feed_controller_test.rb b/tracks/test/functional/feed_controller_test.rb new file mode 100644 index 00000000..ec035b24 --- /dev/null +++ b/tracks/test/functional/feed_controller_test.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'feed_controller' + +# Re-raise errors caught by the controller. +class FeedController; def rescue_action(e) raise e end; end + +class FeedControllerTest < Test::Unit::TestCase + def setup + @controller = FeedController.new + @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new + end + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/functional/login_controller_test.rb b/tracks/test/functional/login_controller_test.rb new file mode 100644 index 00000000..4a1efef6 --- /dev/null +++ b/tracks/test/functional/login_controller_test.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'login_controller' + +# Re-raise errors caught by the controller. +class LoginController; def rescue_action(e) raise e end; end + +class LoginControllerTest < Test::Unit::TestCase + def setup + @controller = LoginController.new + @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new + end + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/functional/project_controller_test.rb b/tracks/test/functional/project_controller_test.rb new file mode 100644 index 00000000..330dbef5 --- /dev/null +++ b/tracks/test/functional/project_controller_test.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'project_controller' + +# Re-raise errors caught by the controller. +class ProjectController; def rescue_action(e) raise e end; end + +class ProjectControllerTest < Test::Unit::TestCase + def setup + @controller = ProjectController.new + @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new + end + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/functional/todo_controller_test.rb b/tracks/test/functional/todo_controller_test.rb new file mode 100644 index 00000000..39f8770d --- /dev/null +++ b/tracks/test/functional/todo_controller_test.rb @@ -0,0 +1,17 @@ +require File.dirname(__FILE__) + '/../test_helper' +require 'todo_controller' + +# Re-raise errors caught by the controller. +class TodoController; def rescue_action(e) raise e end; end + +class TodoControllerTest < Test::Unit::TestCase + def setup + @controller = TodoController.new + @request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new + end + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/test_helper.rb b/tracks/test/test_helper.rb new file mode 100644 index 00000000..83f4069f --- /dev/null +++ b/tracks/test/test_helper.rb @@ -0,0 +1,14 @@ +ENV["RAILS_ENV"] ||= "test" +require File.dirname(__FILE__) + "/../config/environment" +require 'application' + +require 'test/unit' +require 'active_record/fixtures' +require 'action_controller/test_process' +require 'breakpoint' + +def create_fixtures(*table_names) + Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures", table_names) +end + +Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/" \ No newline at end of file diff --git a/tracks/test/unit/context_test.rb b/tracks/test/unit/context_test.rb new file mode 100644 index 00000000..a6e624ce --- /dev/null +++ b/tracks/test/unit/context_test.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ContextTest < Test::Unit::TestCase + fixtures :contexts + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/unit/project_test.rb b/tracks/test/unit/project_test.rb new file mode 100644 index 00000000..42820373 --- /dev/null +++ b/tracks/test/unit/project_test.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class ProjectTest < Test::Unit::TestCase + fixtures :projects + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/unit/todo_test.rb b/tracks/test/unit/todo_test.rb new file mode 100644 index 00000000..8c8599f5 --- /dev/null +++ b/tracks/test/unit/todo_test.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class TodoTest < Test::Unit::TestCase + fixtures :todos + + # Replace this with your real tests. + def test_truth + assert true + end +end diff --git a/tracks/test/unit/user_test.rb b/tracks/test/unit/user_test.rb new file mode 100644 index 00000000..5468f7a2 --- /dev/null +++ b/tracks/test/unit/user_test.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class UserTest < Test::Unit::TestCase + fixtures :users + + # Replace this with your real tests. + def test_truth + assert true + end +end