diff --git a/.bundle/config b/.bundle/config new file mode 100644 index 0000000..b8d4716 --- /dev/null +++ b/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_WITHOUT: "" diff --git a/.bundle/environment.rb b/.bundle/environment.rb new file mode 100644 index 0000000..bea4d53 --- /dev/null +++ b/.bundle/environment.rb @@ -0,0 +1,207 @@ +# DO NOT MODIFY THIS FILE + +require 'digest/sha1' +require "rubygems" + +module Gem + class Dependency + if !instance_methods.map { |m| m.to_s }.include?("requirement") + def requirement + version_requirements + end + end + end +end + +module Bundler + module SharedHelpers + + def default_gemfile + gemfile = find_gemfile + gemfile or raise GemfileNotFound, "The default Gemfile was not found" + Pathname.new(gemfile) + end + + def in_bundle? + find_gemfile + end + + private + + def find_gemfile + return ENV['BUNDLE_GEMFILE'] if ENV['BUNDLE_GEMFILE'] + + previous = nil + current = File.expand_path(Dir.pwd) + + until !File.directory?(current) || current == previous + filename = File.join(current, 'Gemfile') + return filename if File.file?(filename) + current, previous = File.expand_path("..", current), current + end + end + + def clean_load_path + # handle 1.9 where system gems are always on the load path + if defined?(::Gem) + me = File.expand_path("../../", __FILE__) + $LOAD_PATH.reject! do |p| + next if File.expand_path(p).include?(me) + p != File.dirname(__FILE__) && + Gem.path.any? { |gp| p.include?(gp) } + end + $LOAD_PATH.uniq! + end + end + + def reverse_rubygems_kernel_mixin + # Disable rubygems' gem activation system + ::Kernel.class_eval do + if private_method_defined?(:gem_original_require) + alias rubygems_require require + alias require gem_original_require + end + + undef gem + end + end + + def cripple_rubygems(specs) + reverse_rubygems_kernel_mixin + + executables = specs.map { |s| s.executables }.flatten + + :: Kernel.class_eval do + private + def gem(*) ; end + end + Gem.source_index # ensure RubyGems is fully loaded + + ::Kernel.send(:define_method, :gem) do |dep, *reqs| + if executables.include? File.basename(caller.first.split(':').first) + return + end + opts = reqs.last.is_a?(Hash) ? reqs.pop : {} + + unless dep.respond_to?(:name) && dep.respond_to?(:requirement) + dep = Gem::Dependency.new(dep, reqs) + end + + spec = specs.find { |s| s.name == dep.name } + + if spec.nil? + e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile." + e.name = dep.name + e.version_requirement = dep.requirement + raise e + elsif dep !~ spec + e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \ + "Make sure all dependencies are added to Gemfile." + e.name = dep.name + e.version_requirement = dep.requirement + raise e + end + + true + end + + # === Following hacks are to improve on the generated bin wrappers === + + # Yeah, talk about a hack + source_index_class = (class << Gem::SourceIndex ; self ; end) + source_index_class.send(:define_method, :from_gems_in) do |*args| + source_index = Gem::SourceIndex.new + source_index.spec_dirs = *args + source_index.add_specs(*specs) + source_index + end + + # OMG more hacks + gem_class = (class << Gem ; self ; end) + gem_class.send(:define_method, :bin_path) do |name, *args| + exec_name, *reqs = args + + spec = nil + + if exec_name + spec = specs.find { |s| s.executables.include?(exec_name) } + spec or raise Gem::Exception, "can't find executable #{exec_name}" + else + spec = specs.find { |s| s.name == name } + exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}" + end + + File.join(spec.full_gem_path, spec.bindir, exec_name) + end + end + + extend self + end +end + +module Bundler + LOCKED_BY = '0.9.11' + FINGERPRINT = "bc067d01abbb21b054cfc123f404a7874dfc8f94" + AUTOREQUIRES = {:test=>[["faker", false], ["shoulda", false], ["factory_girl", false]], :default=>[["mysql", false], ["formtastic", false], ["paperclip", false], ["will_paginate", false], ["clearance", false]], :development=>[["mongrel", false]], :developemtn=>[["faker", false]]} + SPECS = [ + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/faker-0.3.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/faker-0.3.1/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/mongrel-1.1.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mongrel-1.1.5/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mongrel-1.1.5/ext"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/fastthread-1.0.7.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/fastthread-1.0.7/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/fastthread-1.0.7/ext"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/mysql-2.8.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mysql-2.8.1/lib", "/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/mysql-2.8.1/ext"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/shoulda-2.10.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/shoulda-2.10.3/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/formtastic-0.9.7.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/formtastic-0.9.7/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/rack-1.0.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/rack-1.0.1/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/gem_plugin-0.2.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/gem_plugin-0.2.3/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/cgi_multipart_eof_fix-2.5.0.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/cgi_multipart_eof_fix-2.5.0/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/paperclip-2.3.1.1.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/paperclip-2.3.1.1/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/will_paginate-2.3.12.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/will_paginate-2.3.12/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/factory_girl-1.2.3.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/factory_girl-1.2.3/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/actionpack-2.3.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/actionpack-2.3.5/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/daemons-1.0.10.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/daemons-1.0.10/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/clearance-0.8.8.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/clearance-0.8.8/lib"]}, + {:loaded_from=>"/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/specifications/activesupport-2.3.5.gemspec", :load_paths=>["/Users/sdavis/.rvm/gems/ruby-1.8.7-p249/gems/activesupport-2.3.5/lib"]}, + ].map do |hash| + spec = eval(File.read(hash[:loaded_from]), binding, hash[:loaded_from]) + spec.loaded_from = hash[:loaded_from] + spec.require_paths = hash[:load_paths] + spec + end + + extend SharedHelpers + + def self.match_fingerprint + print = Digest::SHA1.hexdigest(File.read(File.expand_path('../../Gemfile', __FILE__))) + unless print == FINGERPRINT + abort 'Gemfile changed since you last locked. Please `bundle lock` to relock.' + end + end + + def self.setup(*groups) + match_fingerprint + clean_load_path + cripple_rubygems(SPECS) + SPECS.each do |spec| + Gem.loaded_specs[spec.name] = spec + $LOAD_PATH.unshift(*spec.require_paths) + end + end + + def self.require(*groups) + groups = [:default] if groups.empty? + groups.each do |group| + (AUTOREQUIRES[group.to_sym] || []).each do |file, explicit| + if explicit + Kernel.require file + else + begin + Kernel.require file + rescue LoadError + end + end + end + end + end + + # Setup bundle when it's required. + setup +end diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..5a1cf25 --- /dev/null +++ b/Gemfile @@ -0,0 +1,23 @@ +# A sample Gemfile +source :gemcutter +# +#gem "rails", "2.3.5" + +gem 'clearance' +gem 'will_paginate' + +gem 'paperclip' +gem 'formtastic' +gem 'mysql' +#gem "matthuhiggins-foreigner", :require => "foreigner" + + +group :test do + gem 'shoulda' + gem 'factory_girl' + gem 'faker', :group => [:test, :developemtn] +end + +group :development do + gem 'mongrel' +end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..1359169 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,76 @@ +--- +dependencies: + faker: + group: + - :test + - :developemtn + version: ">= 0" + mongrel: + group: + - :development + version: ">= 0" + mysql: + group: + - :default + version: ">= 0" + shoulda: + group: + - :test + version: ">= 0" + formtastic: + group: + - :default + version: ">= 0" + factory_girl: + group: + - :test + version: ">= 0" + paperclip: + group: + - :default + version: ">= 0" + will_paginate: + group: + - :default + version: ">= 0" + clearance: + group: + - :default + version: ">= 0" +specs: +- faker: + version: 0.3.1 +- fastthread: + version: 1.0.7 +- mongrel: + version: 1.1.5 +- mysql: + version: 2.8.1 +- shoulda: + version: 2.10.3 +- rack: + version: 1.0.1 +- gem_plugin: + version: 0.2.3 +- cgi_multipart_eof_fix: + version: 2.5.0 +- formtastic: + version: 0.9.7 +- will_paginate: + version: 2.3.12 +- paperclip: + version: 2.3.1.1 +- factory_girl: + version: 1.2.3 +- daemons: + version: 1.0.10 +- clearance: + version: 0.8.8 +- actionpack: + version: 2.3.5 +- activesupport: + version: 2.3.5 +hash: bc067d01abbb21b054cfc123f404a7874dfc8f94 +sources: +- Rubygems: + uri: http://gemcutter.org diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 63552c3..88d2fdb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,4 +11,6 @@ class ApplicationController < ActionController::Base def set_page @page_title = "Collab" end + + end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 5cf0b2d..703dcb0 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -10,12 +10,35 @@ class ProjectsController < ApplicationController format.xml { render :xml => @projects } end end + + def join + @project = Project.find(params[:project_id]) + @project.users << current_user + if @project.save + flash[:success] = "You have joined #{@project.name}" + else + flash[:error] = "There was a problem joining this project" + end + redirect_to project_path(@project) + end + + def leave + @project = Project.find(params[:project_id]) + @project.users.delete current_user + if @project.save + flash[:success] = "You have been removed from #{@project.name}" + else + flash[:error] = "There was an error removing you form this project" + end + redirect_to project_path(@project) + end # GET /projects/1 # GET /projects/1.xml def show - @project = Project.find(params[:id], :include => [:users]) + @project = Project.find(params[:id], :include => [:users, :wall_posts]) @tasks = @project.tasks.parents.paginate(:per_page => 30, :page => params[:page], :include => :tasks) + @wall_posts = @project.wall_posts.paginate(:per_page => 15, :page => params[:post_page]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @project } diff --git a/app/controllers/wall_controller.rb b/app/controllers/wall_controller.rb new file mode 100644 index 0000000..dace532 --- /dev/null +++ b/app/controllers/wall_controller.rb @@ -0,0 +1,18 @@ +class WallController < ApplicationController + before_filter :authenticate, :load_project + + def create + @wall = Wall.new(params[:wall]) + @wall.project = @project + @wall.author = current_user + @wall.save + end + + +private + + def load_project + @project = Project.find(params[:project_id]) + end + +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index db5c5ce..ef7a621 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -1,2 +1,13 @@ module ProjectsHelper + + def parse_wall_string(string) + user_matches = string.scan(/@([a-zA-Z_0-9]+)/).flatten + return string if user_matches.empty? + User.find_all_by_short_name(user_matches).each do |user| + string.gsub!("@#{user.short_name}", link_to("@#{user.short_name}", user_path(user.short_name))) + end + string + end + + end diff --git a/app/helpers/wall_helper.rb b/app/helpers/wall_helper.rb new file mode 100644 index 0000000..91a9f10 --- /dev/null +++ b/app/helpers/wall_helper.rb @@ -0,0 +1,3 @@ +module WallHelper + include ProjectsHelper +end diff --git a/app/models/project.rb b/app/models/project.rb index 75eedc7..4d0beee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,5 +1,6 @@ class Project < ActiveRecord::Base has_many :tasks + has_many :wall_posts, :class_name => "Wall", :foreign_key => "project_id", :order => "created_at DESC" belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" validates_presence_of :name has_and_belongs_to_many :users diff --git a/app/models/task.rb b/app/models/task.rb index 314be49..9b3fcf7 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -7,7 +7,7 @@ class Task < ActiveRecord::Base named_scope :parents, :conditions => {:parent => nil} #validations validates_presence_of :name, :description, :project_id - validates_associated :project + validate :assignee_validation, :owner_validation def assignee_validation diff --git a/app/models/user.rb b/app/models/user.rb index dbcf911..84edebb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,13 +5,29 @@ class User < ActiveRecord::Base has_many :owned_tasks, :class_name => "Task", :foreign_key => "owner_id" has_many :assigned_tasks, :class_name => "Task", :foreign_key => "assigned_id" validates_uniqueness_of :short_name - validates_presence_of :short_name + validates_presence_of :email, :password, :short_name validates_format_of :phone, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.phone.blank?} validates_format_of :mobile, :with => /^[0-9]{3}-[0-9]{3}-[0-9]{4}$/, :if => Proc.new {|user| !user.mobile.blank?} + before_validation :set_short_name + + def set_short_name + if short_name.blank? + short_name = email.split('@')[0] + end + end + def gravatar(size = 48) hash = Digest::MD5.hexdigest email "http://www.gravatar.com/avatar/#{hash}.jpg?s=#{size}" end + def send_post_notification(post) + project_id = post.project_id + end + + def send_project_notification(project) + + end + end diff --git a/app/models/wall.rb b/app/models/wall.rb new file mode 100644 index 0000000..17dd8ba --- /dev/null +++ b/app/models/wall.rb @@ -0,0 +1,22 @@ +class Wall < ActiveRecord::Base + + belongs_to :author, :class_name => "User", :foreign_key => "user_id" + belongs_to :project + + after_create :check_for_mentions, :check_for_hash_tags + + def check_for_mentions + mentions = text.scan(/@([a-zA-Z_0-9])/).flatten + User.find_all_by_short_name(mentions).each{ |user| user.send_post_notification(self) } + end + + def check_for_hash_tags + tags = text.scan(/#([a-zA-Z0-9_]+)/).flatten + send_notice_to_members(project) if tags.include?('project') + end + + def send_notice_to_members(project) + project.users.each {|user| user.send_project_notification(project)} + end + +end diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 0cac856..706801b 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -1,15 +1,40 @@ -
- Name: - <%=h @project.name %> -
-<%= link_to 'Edit', edit_project_path(@project) %>
+Name | Project | Description | Children | +|||||||
---|---|---|---|---|---|---|---|---|---|---|
<%=h task.name %> | +<%= link_to h(task.name), project_task_path(@project, task) %> | <%= link_to h(task.project.name), project_path(task.project) %> | -<%=h task.description %> | -<%=h task.tasks.size %> | -<%= link_to 'Show', project_task_path(@project, task) %> | -<%= link_to 'Edit', edit_project_task_path(@project, task) %> | -<%= link_to 'Destroy', project_task_path(@project, task), :confirm => 'Are you sure?', :method => :delete %> | +<%=h truncate(task.description, :length => 158) %> | +<%= link_to h(task.tasks.size), project_task_path(@project, task) %> | ++ <%= link_to 'Edit', edit_project_task_path(@project, task) %> + <%= link_to 'Destroy', project_task_path(@project, task), :confirm => 'Are you sure?', :method => :delete %> + |
- Name: - <%=h @task.name %> -
+Project: <%=h @task.project.name %>
-+
Description: <%=h @task.description %>
diff --git a/app/views/wall/_wall_posts.erb b/app/views/wall/_wall_posts.erb new file mode 100644 index 0000000..c2d59a5 --- /dev/null +++ b/app/views/wall/_wall_posts.erb @@ -0,0 +1,4 @@ +k))&&((e>=g&&e<=c)||(d>=g&&d<=c)||(e","
"]||!O.indexOf("","