Created
June 29, 2009 23:10
-
-
Save TMorgan99/137873 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
## Build Agility | |
rm -fr agility | |
hobo agility | |
cd agility | |
# add db:seeds rake task | |
cat <<__PATCH |patch -p1 | |
patch rails for db:seeds | |
diff --git a/lib/tasks/database.rake b/lib/tasks/database.rake | |
new file mode 100644 | |
index 0000000..c8114f0 | |
--- /dev/null | |
+++ b/lib/tasks/database.rake | |
@@ -0,0 +1,15 @@ | |
+# until rails 3.0, we grab this task from the edge... | |
+namespace :db do | |
+ desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' | |
+ task :reset => [ 'db:drop', 'db:setup' ] | |
+ | |
+ desc 'Create the database, load the schema, and initialize with the seed data' | |
+ task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] | |
+ | |
+ desc 'Load the seed data from db/seeds.rb' | |
+ task :seed => :environment do | |
+ seed_file = File.join(Rails.root, 'db', 'seeds.rb') | |
+ load(seed_file) if File.exist?(seed_file) | |
+ end | |
+end | |
+ | |
__PATCH | |
# add cucumber and rspec | |
./script/generate rspec -q | |
./script/generate cucumber -q | |
# Generate the app, models and resources | |
./script/generate hobo_model_resource project name:string | |
./script/generate hobo_model_resource story title:string body:text status:string | |
./script/generate hobo_model_resource task description:string | |
./script/generate hobo_model task_assignment | |
# Associations | |
cat <<__PATCH |patch -p1 | |
* The Models associations | |
diff --git a/app/models/project.rb b/app/models/project.rb | |
index 7dc4ebc..a6f4e3b 100644 | |
--- a/app/models/project.rb | |
+++ b/app/models/project.rb | |
@@ -7,6 +7,7 @@ class Project < ActiveRecord::Base | |
timestamps | |
end | |
+ has_many :stories, :dependent => :destroy | |
# --- Permissions --- # | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index 7f2e550..1d3b609 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -9,6 +9,9 @@ class Story < ActiveRecord::Base | |
timestamps | |
end | |
+ belongs_to :project | |
+ | |
+ has_many :tasks, :dependent => :destroy | |
# --- Permissions --- # | |
diff --git a/app/models/task.rb b/app/models/task.rb | |
index 23479ba..f79516b 100644 | |
--- a/app/models/task.rb | |
+++ b/app/models/task.rb | |
@@ -7,6 +7,10 @@ class Task < ActiveRecord::Base | |
timestamps | |
end | |
+ belongs_to :story | |
+ | |
+ has_many :task_assignments, :dependent => :destroy | |
+ has_many :users, :through => :task_assignments | |
# --- Permissions --- # | |
diff --git a/app/models/task_assignment.rb b/app/models/task_assignment.rb | |
index 486f72f..b420c68 100644 | |
--- a/app/models/task_assignment.rb | |
+++ b/app/models/task_assignment.rb | |
@@ -6,6 +6,8 @@ class TaskAssignment < ActiveRecord::Base | |
timestamps | |
end | |
+ belongs_to :user | |
+ belongs_to :task | |
# --- Permissions --- # | |
diff --git a/app/models/user.rb b/app/models/user.rb | |
index 21366ba..8ab0d4b 100644 | |
--- a/app/models/user.rb | |
+++ b/app/models/user.rb | |
@@ -9,6 +9,9 @@ class User < ActiveRecord::Base | |
timestamps | |
end | |
+ has_many :task_assignments, :dependent => :destroy | |
+ has_many :tasks, :through => :task_assignments | |
+ | |
# This gives admin rights to the first sign-up. | |
# Just remove it if you don't want that | |
before_create { |user| user.administrator = true if RAILS_ENV != "test" && count == 0 } | |
__PATCH | |
# Run the Hobo Migrator | |
./script/generate hobo_migration initial_models --default-name --migrate | |
# Removing Actions / Permissions | |
cat <<__PATCH |patch -p1 | |
* The Models actions and permissions | |
diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb | |
index f0dd368..a54eec1 100644 | |
--- a/app/controllers/stories_controller.rb | |
+++ b/app/controllers/stories_controller.rb | |
@@ -2,6 +2,8 @@ class StoriesController < ApplicationController | |
hobo_model_controller | |
- auto_actions :all | |
+ auto_actions :all, :except => :index | |
+ | |
+ auto_actions_for :project, [:new, :create] | |
end | |
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb | |
index f5a4f00..7423c3b 100644 | |
--- a/app/controllers/tasks_controller.rb | |
+++ b/app/controllers/tasks_controller.rb | |
@@ -2,6 +2,8 @@ class TasksController < ApplicationController | |
hobo_model_controller | |
- auto_actions :all | |
+ auto_actions :write_only, :edit | |
+ | |
+ auto_actions_for :story, :create | |
end | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index 1d3b609..aaae40a 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -20,7 +20,7 @@ class Story < ActiveRecord::Base | |
end | |
def update_permitted? | |
- acting_user.administrator? | |
+ acting_user.signed_up? && !project_changed? | |
end | |
def destroy_permitted? | |
diff --git a/app/models/task.rb b/app/models/task.rb | |
index f79516b..8fad2b5 100644 | |
--- a/app/models/task.rb | |
+++ b/app/models/task.rb | |
@@ -10,7 +10,7 @@ class Task < ActiveRecord::Base | |
belongs_to :story | |
has_many :task_assignments, :dependent => :destroy | |
- has_many :users, :through => :task_assignments | |
+ has_many :users, :through => :task_assignments, :accessible => true | |
# --- Permissions --- # | |
@@ -19,7 +19,7 @@ class Task < ActiveRecord::Base | |
end | |
def update_permitted? | |
- acting_user.administrator? | |
+ acting_user.signed_up? && !story_changed? | |
end | |
def destroy_permitted? | |
__PATCH | |
# Customising views | |
cat <<__PATCH |patch -p1 | |
* Dryml intro | |
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb | |
index 210b07d..a14339c 100644 | |
--- a/app/controllers/projects_controller.rb | |
+++ b/app/controllers/projects_controller.rb | |
@@ -4,4 +4,11 @@ class ProjectsController < ApplicationController | |
auto_actions :all | |
+ def show | |
+ @project = find_instance | |
+ @stories = | |
+ @project.stories.apply_scopes(:search => [params[:search], :title], | |
+ :order_by => parse_sort_param(:title, :status)) | |
+ end | |
+ | |
end | |
diff --git a/app/views/projects/show.dryml b/app/views/projects/show.dryml | |
new file mode 100644 | |
index 0000000..2e8a3a2 | |
--- /dev/null | |
+++ b/app/views/projects/show.dryml | |
@@ -0,0 +1,7 @@ | |
+<show-page> | |
+ <collection: replace> | |
+ <table-plus with="&@stories" fields="this, tasks.count, status"> | |
+ <empty-message:>No stories match your criteria</empty-message:> | |
+ </table-plus> | |
+ </collection:> | |
+</show-page> | |
diff --git a/app/views/taglibs/application.dryml b/app/views/taglibs/application.dryml | |
index 92c7ebe..c9548da 100644 | |
--- a/app/views/taglibs/application.dryml | |
+++ b/app/views/taglibs/application.dryml | |
@@ -8,4 +8,12 @@ | |
<def tag="app-name">Agility</def> | |
- | |
+<extend tag="card" for="Task"> | |
+ <old-card merge> | |
+ <append-body:> | |
+ <div class="users"> | |
+ Assigned users: <repeat:users join=", "><a/></repeat><else>None</else> | |
+ </div> | |
+ </append-body:> | |
+ </old-card> | |
+</extend> | |
diff --git a/app/views/users/show.dryml b/app/views/users/show.dryml | |
new file mode 100644 | |
index 0000000..a2e5c01 | |
--- /dev/null | |
+++ b/app/views/users/show.dryml | |
@@ -0,0 +1,9 @@ | |
+<show-page> | |
+ <content-body:> | |
+ <h3><Your/> Assigned Tasks</h3> | |
+ <repeat with="&@user.tasks.group_by(&:story)"> | |
+ <h4>Story: <a with="&this_key"/></h4> | |
+ <collection/> | |
+ </repeat> | |
+ </content-body:> | |
+</show-page> | |
__PATCH | |
# Add User Activation | |
cat <<__PATCH |patch -p1 | |
* User Activation | |
diff --git a/app/models/user.rb b/app/models/user.rb | |
index 8ab0d4b..db88be2 100644 | |
--- a/app/models/user.rb | |
+++ b/app/models/user.rb | |
@@ -20,12 +20,16 @@ class User < ActiveRecord::Base | |
# --- Signup lifecycle --- # | |
lifecycle do | |
- | |
- state :active, :default => true | |
+ state :inactive, :default => true | |
+ state :active | |
create :signup, :available_to => "Guest", | |
:params => [:name, :email_address, :password, :password_confirmation], | |
- :become => :active | |
+ :become => :inactive, :new_key => true do | |
+ UserMailer.deliver_activation(self, lifecycle.key) | |
+ end | |
+ | |
+ transition :activate, { :inactive => :active }, :available_to => :key_holder | |
transition :request_password_reset, { :active => :active }, :new_key => true do | |
UserMailer.deliver_forgot_password(self, lifecycle.key) | |
diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb | |
index 76eb31c..a1bd10c 100644 | |
--- a/app/models/user_mailer.rb | |
+++ b/app/models/user_mailer.rb | |
@@ -11,4 +11,15 @@ class UserMailer < ActionMailer::Base | |
@headers = {} | |
end | |
+ def activation(user, key) | |
+ host = Hobo::Controller.request_host | |
+ app_name = Hobo::Controller.app_name || host | |
+ @subject = "#{app_name} -- activate" | |
+ @body = { :user => user, :key => key, :host => host, :app_name => app_name } | |
+ @recipients = user.email_address | |
+ @from = "no-reply@#{host}" | |
+ @sent_on = Time.now | |
+ @headers = {} | |
+ end | |
+ | |
end | |
diff --git a/app/views/user_mailer/activation.erb b/app/views/user_mailer/activation.erb | |
new file mode 100644 | |
index 0000000..d8f0b56 | |
--- /dev/null | |
+++ b/app/views/user_mailer/activation.erb | |
@@ -0,0 +1,9 @@ | |
+<%= @user %>, | |
+ | |
+To activate your account for <%= @app_name %>, click on this link: | |
+ | |
+ <%= user_activate_url :host => @host, :id => @user, :key => @key %> | |
+ | |
+Thank you, | |
+ | |
+The <%= @app_name %> team. | |
diff --git a/config/initializers/mailer.rb b/config/initializers/mailer.rb | |
new file mode 100644 | |
index 0000000..82dbdeb | |
--- /dev/null | |
+++ b/config/initializers/mailer.rb | |
@@ -0,0 +1,9 @@ | |
+ActionMailer::Base.delivery_method = :smtp | |
+ActionMailer::Base.smtp_settings = { | |
+# :address => "smtp.example.com", | |
+# :port => 25, | |
+# :domain => "example.com", | |
+# :authentication => :login, | |
+# :user_name => "username", | |
+# :password => "password", | |
+} | |
__PATCH | |
# Run the Hobo Migrator | |
./script/generate hobo_migration user_activation --default-name --migrate | |
# Odds and ends | |
# Generate StoryStatus resource | |
./script/generate hobo_model_resource story_status name:string | |
# Story Status association/action/permission | |
cat <<__PATCH |patch -p1 | |
* Story Status seed/associations/actions/permissions | |
diff --git a/app/controllers/story_statuses_controller.rb b/app/controllers/story_statuses_controller.rb | |
index 53a3817..318bad5 100644 | |
--- a/app/controllers/story_statuses_controller.rb | |
+++ b/app/controllers/story_statuses_controller.rb | |
@@ -2,6 +2,6 @@ class StoryStatusesController < ApplicationController | |
hobo_model_controller | |
- auto_actions :all | |
+ auto_actions :write_only, :new, :index | |
end | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index aaae40a..e497310 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -5,11 +5,11 @@ class Story < ActiveRecord::Base | |
fields do | |
title :string | |
body :text | |
- status :string | |
timestamps | |
end | |
belongs_to :project | |
+ belongs_to :status, :class_name => "StoryStatus" | |
has_many :tasks, :dependent => :destroy | |
@@ -31,4 +31,9 @@ class Story < ActiveRecord::Base | |
true | |
end | |
+ # force 'new' status at create. | |
+ def before_create | |
+ self.status = @default_status ||= StoryStatus.find_by_name( 'new' ) | |
+ end | |
+ | |
end | |
diff --git a/app/views/stories/show.dryml b/app/views/stories/show.dryml | |
new file mode 100644 | |
index 0000000..122a7a2 | |
--- /dev/null | |
+++ b/app/views/stories/show.dryml | |
@@ -0,0 +1,3 @@ | |
+<show-page> | |
+ <field-list: tag="editor"/> | |
+</show-page> | |
diff --git a/db/seeds.rb b/db/seeds.rb | |
new file mode 100644 | |
index 0000000..ac68552 | |
--- /dev/null | |
+++ b/db/seeds.rb | |
@@ -0,0 +1,3 @@ | |
+# Story Status | |
+statuses = %w(new accepted discussion implementation user_testing deployed rejected) | |
+statuses.each { |status| StoryStatus.create :name => status } | |
__PATCH | |
# Run the Hobo Migrator | |
./script/generate hobo_migration story_status_model --default-name --force-drop --migrate | |
# Use the new (Rails 3.0) rake task to seed the storystatus table | |
rake -s db:seed | |
# Filtering stories by status | |
cat <<__PATCH |patch -p1 | |
* Story Status filter on Project | |
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb | |
index a14339c..3a75081 100644 | |
--- a/app/controllers/projects_controller.rb | |
+++ b/app/controllers/projects_controller.rb | |
@@ -8,6 +8,7 @@ class ProjectsController < ApplicationController | |
@project = find_instance | |
@stories = | |
@project.stories.apply_scopes(:search => [params[:search], :title], | |
+ :status_is => params[:status], | |
:order_by => parse_sort_param(:title, :status)) | |
end | |
diff --git a/app/views/projects/show.dryml b/app/views/projects/show.dryml | |
index 2e8a3a2..ccf08e2 100644 | |
--- a/app/views/projects/show.dryml | |
+++ b/app/views/projects/show.dryml | |
@@ -1,6 +1,11 @@ | |
<show-page> | |
<collection: replace> | |
<table-plus with="&@stories" fields="this, tasks.count, status"> | |
+ <prepend-header:> | |
+ <div class="filter"> | |
+ Display by status: <filter-menu param-name="status" options="&StoryStatus.all"/> | |
+ </div> | |
+ </prepend-header:> | |
<empty-message:>No stories match your criteria</empty-message:> | |
</table-plus> | |
</collection:> | |
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css | |
index e69de29..9fc31c9 100644 | |
--- a/public/stylesheets/application.css | |
+++ b/public/stylesheets/application.css | |
@@ -0,0 +1,2 @@ | |
+.show-page.project .filter {float: left;} | |
+.show-page.project .filter form, .show-page.project .filter form div {display: inline;} | |
__PATCH | |
# Add Acts as List plugin | |
./script/plugin install acts_as_list | |
cat <<__PATCH |patch -p1 | |
* Acting as list | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index e497310..93c31bd 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -11,7 +11,7 @@ class Story < ActiveRecord::Base | |
belongs_to :project | |
belongs_to :status, :class_name => "StoryStatus" | |
- has_many :tasks, :dependent => :destroy | |
+ has_many :tasks, :dependent => :destroy, :order => :position | |
# --- Permissions --- # | |
diff --git a/app/models/task.rb b/app/models/task.rb | |
index 8fad2b5..6c14685 100644 | |
--- a/app/models/task.rb | |
+++ b/app/models/task.rb | |
@@ -9,6 +9,9 @@ class Task < ActiveRecord::Base | |
belongs_to :story | |
+ acts_as_list :scope => :story | |
+ attr_protected :position | |
+ | |
has_many :task_assignments, :dependent => :destroy | |
has_many :users, :through => :task_assignments, :accessible => true | |
diff --git a/app/views/tasks/edit.dryml b/app/views/tasks/edit.dryml | |
new file mode 100644 | |
index 0000000..fdee2fe | |
--- /dev/null | |
+++ b/app/views/tasks/edit.dryml | |
@@ -0,0 +1,5 @@ | |
+<edit-page> | |
+ <form:> | |
+ <cancel: with="&this.story"/> | |
+ </form:> | |
+</edit-page> | |
__PATCH | |
# Run the Hobo Migrator | |
./script/generate hobo_migration acts_as_list --default-name --migrate | |
# Markdown / Textile formatting of stories | |
cat <<__PATCH |patch -p1 | |
* Markdown | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index 93c31bd..39651df 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -4,7 +4,7 @@ class Story < ActiveRecord::Base | |
fields do | |
title :string | |
- body :text | |
+ body :markdown | |
timestamps | |
end | |
diff --git a/config/environment.rb b/config/environment.rb | |
index f95dfb7..2b0ea3a 100644 | |
--- a/config/environment.rb | |
+++ b/config/environment.rb | |
@@ -9,6 +9,8 @@ require File.join(File.dirname(__FILE__), 'boot') | |
Rails::Initializer.run do |config| | |
config.gem 'hobo' | |
+ config.gem 'bluecloth' | |
+ | |
# Settings in config/environments/* take precedence over those specified here. | |
# Application configuration should go into files in config/initializers | |
# -- all .rb files in that directory are automatically loaded. | |
__PATCH | |
rake -s gems:install | |
# touch up front page. | |
cat <<__PATCH |patch -p1 | |
* User front page | |
diff --git a/app/views/front/index.dryml b/app/views/front/index.dryml | |
index ef5c3a5..670c62e 100644 | |
--- a/app/views/front/index.dryml | |
+++ b/app/views/front/index.dryml | |
@@ -13,7 +13,9 @@ | |
</section> | |
</header> | |
- <section class="content-body"> | |
+ <section class="content-body" if="&logged_in?"> | |
+ <h3>Your Projects</h3> | |
+ <collection:projects with="¤t_user"><card without-creator-link/></collection> | |
</section> | |
</content:> | |
__PATCH | |
# Project ownership | |
cat <<__PATCH |patch -p1 | |
* Project Ownership - association/permission | |
diff --git a/app/models/project.rb b/app/models/project.rb | |
index a6f4e3b..f3a877a 100644 | |
--- a/app/models/project.rb | |
+++ b/app/models/project.rb | |
@@ -9,18 +9,20 @@ class Project < ActiveRecord::Base | |
has_many :stories, :dependent => :destroy | |
+ belongs_to :owner, :class_name => "User", :creator => true | |
+ | |
# --- Permissions --- # | |
def create_permitted? | |
- acting_user.administrator? | |
+ owner_is? acting_user | |
end | |
def update_permitted? | |
- acting_user.administrator? | |
+ acting_user.administrator? || (owner_is?(acting_user) && !owner_changed?) | |
end | |
def destroy_permitted? | |
- acting_user.administrator? | |
+ acting_user.administrator? || owner_is?(acting_user) | |
end | |
def view_permitted?(field) | |
diff --git a/app/models/user.rb b/app/models/user.rb | |
index db88be2..e85cef0 100644 | |
--- a/app/models/user.rb | |
+++ b/app/models/user.rb | |
@@ -11,6 +11,7 @@ class User < ActiveRecord::Base | |
has_many :task_assignments, :dependent => :destroy | |
has_many :tasks, :through => :task_assignments | |
+ has_many :projects, :class_name => "Project", :foreign_key => "owner_id" | |
# This gives admin rights to the first sign-up. | |
# Just remove it if you don't want that | |
__PATCH | |
# Project Ownership - migration | |
./script/generate hobo_migration project_ownership --default-name --migrate | |
# Granting read access to others | |
./script/generate hobo_model_resource project_membership | |
# Project Membership - association/action | |
cat <<__PATCH |patch -p1 | |
* Project Membership - association/action | |
diff --git a/app/controllers/project_memberships_controller.rb b/app/controllers/project_memberships_controller.rb | |
index aa14a80..4e32ec5 100644 | |
--- a/app/controllers/project_memberships_controller.rb | |
+++ b/app/controllers/project_memberships_controller.rb | |
@@ -2,6 +2,6 @@ class ProjectMembershipsController < ApplicationController | |
hobo_model_controller | |
- auto_actions :all | |
+ auto_actions :write_only | |
end | |
diff --git a/app/models/project_membership.rb b/app/models/project_membership.rb | |
index b0a4acb..f5e6703 100644 | |
--- a/app/models/project_membership.rb | |
+++ b/app/models/project_membership.rb | |
@@ -6,6 +6,8 @@ class ProjectMembership < ActiveRecord::Base | |
timestamps | |
end | |
+ belongs_to :project | |
+ belongs_to :user | |
# --- Permissions --- # | |
__PATCH | |
# Project Membership migration | |
./script/generate hobo_migration project_memberships --default-name --migrate | |
# Project Membership associations/actions/permissions | |
cat <<__PATCH |patch -p1 | |
* Project Membership associations/actions/permissions | |
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb | |
index 3a75081..caa8d5a 100644 | |
--- a/app/controllers/projects_controller.rb | |
+++ b/app/controllers/projects_controller.rb | |
@@ -2,7 +2,9 @@ class ProjectsController < ApplicationController | |
hobo_model_controller | |
- auto_actions :all | |
+ auto_actions :show, :edit, :update, :destroy | |
+ | |
+ auto_actions_for :owner, [:new, :create] | |
def show | |
@project = find_instance | |
diff --git a/app/models/project.rb b/app/models/project.rb | |
index f3a877a..be59f11 100644 | |
--- a/app/models/project.rb | |
+++ b/app/models/project.rb | |
@@ -8,6 +8,8 @@ class Project < ActiveRecord::Base | |
end | |
has_many :stories, :dependent => :destroy | |
+ has_many :memberships, :class_name => "ProjectMembership", :dependent => :destroy | |
+ has_many :members, :through => :memberships, :source => :user | |
belongs_to :owner, :class_name => "User", :creator => true | |
@@ -26,7 +28,7 @@ class Project < ActiveRecord::Base | |
end | |
def view_permitted?(field) | |
- true | |
+ acting_user.administrator? || acting_user == owner || acting_user.in?(members) | |
end | |
end | |
diff --git a/app/models/project_membership.rb b/app/models/project_membership.rb | |
index f5e6703..b7cc12e 100644 | |
--- a/app/models/project_membership.rb | |
+++ b/app/models/project_membership.rb | |
@@ -12,15 +12,15 @@ class ProjectMembership < ActiveRecord::Base | |
# --- Permissions --- # | |
def create_permitted? | |
- acting_user.administrator? | |
+ acting_user.administrator? || project.owner_is?(acting_user) | |
end | |
def update_permitted? | |
- acting_user.administrator? | |
+ acting_user.administrator? || project.owner_is?(acting_user) | |
end | |
def destroy_permitted? | |
- acting_user.administrator? | |
+ acting_user.administrator? || project.owner_is?(acting_user) | |
end | |
def view_permitted?(field) | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index 39651df..f08e141 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -28,7 +28,7 @@ class Story < ActiveRecord::Base | |
end | |
def view_permitted?(field) | |
- true | |
+ project.viewable_by?(acting_user) | |
end | |
# force 'new' status at create. | |
diff --git a/app/models/task.rb b/app/models/task.rb | |
index 6c14685..a1053d2 100644 | |
--- a/app/models/task.rb | |
+++ b/app/models/task.rb | |
@@ -30,7 +30,7 @@ class Task < ActiveRecord::Base | |
end | |
def view_permitted?(field) | |
- true | |
+ story.viewable_by?(acting_user) | |
end | |
end | |
diff --git a/app/models/user.rb b/app/models/user.rb | |
index e85cef0..8b9ef5a 100644 | |
--- a/app/models/user.rb | |
+++ b/app/models/user.rb | |
@@ -12,6 +12,8 @@ class User < ActiveRecord::Base | |
has_many :task_assignments, :dependent => :destroy | |
has_many :tasks, :through => :task_assignments | |
has_many :projects, :class_name => "Project", :foreign_key => "owner_id" | |
+ has_many :project_memberships, :dependent => :destroy | |
+ has_many :joined_projects, :through => :project_memberships, :source => :project | |
# This gives admin rights to the first sign-up. | |
# Just remove it if you don't want that | |
__PATCH | |
# Project Contributor associations/actions/permissions | |
cat <<__PATCH |patch -p1 | |
* Project Contributor associations/actions/permissions | |
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb | |
index caa8d5a..4f11ceb 100644 | |
--- a/app/controllers/projects_controller.rb | |
+++ b/app/controllers/projects_controller.rb | |
@@ -6,6 +6,11 @@ class ProjectsController < ApplicationController | |
auto_actions_for :owner, [:new, :create] | |
+ autocomplete :new_member_name do | |
+ project = find_instance | |
+ hobo_completions :name, User.without_project(project).is_not(project.owner) | |
+ end | |
+ | |
def show | |
@project = find_instance | |
@stories = | |
diff --git a/app/models/project.rb b/app/models/project.rb | |
index be59f11..bfc7c8e 100644 | |
--- a/app/models/project.rb | |
+++ b/app/models/project.rb | |
@@ -13,6 +13,14 @@ class Project < ActiveRecord::Base | |
belongs_to :owner, :class_name => "User", :creator => true | |
+ has_many :contributor_memberships, :class_name => "ProjectMembership", :scope => :contributor | |
+ has_many :contributors, :through => :contributor_memberships, :source => :user | |
+ | |
+ # permission helper | |
+ def accepts_changes_from?(user) | |
+ user.administrator? || user == owner || user.in?(contributors) | |
+ end | |
+ | |
# --- Permissions --- # | |
def create_permitted? | |
@@ -20,7 +28,7 @@ class Project < ActiveRecord::Base | |
end | |
def update_permitted? | |
- acting_user.administrator? || (owner_is?(acting_user) && !owner_changed?) | |
+ accepts_changes_from?(acting_user) && !owner_changed? | |
end | |
def destroy_permitted? | |
diff --git a/app/models/project_membership.rb b/app/models/project_membership.rb | |
index b7cc12e..33c2285 100644 | |
--- a/app/models/project_membership.rb | |
+++ b/app/models/project_membership.rb | |
@@ -3,6 +3,7 @@ class ProjectMembership < ActiveRecord::Base | |
hobo_model # Don't put anything above this | |
fields do | |
+ contributor :boolean, :default => false | |
timestamps | |
end | |
diff --git a/app/models/story.rb b/app/models/story.rb | |
index f08e141..2846d76 100644 | |
--- a/app/models/story.rb | |
+++ b/app/models/story.rb | |
@@ -16,15 +16,15 @@ class Story < ActiveRecord::Base | |
# --- Permissions --- # | |
def create_permitted? | |
- acting_user.administrator? | |
+ project.creatable_by?(acting_user) | |
end | |
def update_permitted? | |
- acting_user.signed_up? && !project_changed? | |
+ project.updatable_by?(acting_user) | |
end | |
def destroy_permitted? | |
- acting_user.administrator? | |
+ project.destroyable_by?(acting_user) | |
end | |
def view_permitted?(field) | |
diff --git a/app/models/task.rb b/app/models/task.rb | |
index a1053d2..0c058dd 100644 | |
--- a/app/models/task.rb | |
+++ b/app/models/task.rb | |
@@ -18,15 +18,15 @@ class Task < ActiveRecord::Base | |
# --- Permissions --- # | |
def create_permitted? | |
- acting_user.administrator? | |
+ story.creatable_by?(acting_user) | |
end | |
def update_permitted? | |
- acting_user.signed_up? && !story_changed? | |
+ story.updatable_by?(acting_user) | |
end | |
def destroy_permitted? | |
- acting_user.administrator? | |
+ story.destroyable_by?(acting_user) | |
end | |
def view_permitted?(field) | |
diff --git a/app/models/task_assignment.rb b/app/models/task_assignment.rb | |
index b420c68..8f11a46 100644 | |
--- a/app/models/task_assignment.rb | |
+++ b/app/models/task_assignment.rb | |
@@ -12,19 +12,19 @@ class TaskAssignment < ActiveRecord::Base | |
# --- Permissions --- # | |
def create_permitted? | |
- acting_user.administrator? | |
+ task.creatable_by?(acting_user) | |
end | |
def update_permitted? | |
- acting_user.administrator? | |
+ task.updatable_by?(acting_user) | |
end | |
def destroy_permitted? | |
- acting_user.administrator? | |
+ task.destroyable_by?(acting_user) | |
end | |
def view_permitted?(field) | |
- true | |
+ task.viewable_by?(acting_user) | |
end | |
end | |
diff --git a/app/viewhints/project_hints.rb b/app/viewhints/project_hints.rb | |
index c0c7e8c..21a1a45 100644 | |
--- a/app/viewhints/project_hints.rb | |
+++ b/app/viewhints/project_hints.rb | |
@@ -1,4 +1,3 @@ | |
class ProjectHints < Hobo::ViewHints | |
- | |
- | |
+ children :stories, :memberships | |
end | |
__PATCH | |
# Project Contributor migration | |
./script/generate hobo_migration project_contributorships --default-name --migrate | |
# Project Contributor view layer | |
cat <<__PATCH |patch -p1 | |
* Project Contributor view layer | |
diff --git a/app/views/front/index.dryml b/app/views/front/index.dryml | |
index 670c62e..a7ff9bc 100644 | |
--- a/app/views/front/index.dryml | |
+++ b/app/views/front/index.dryml | |
@@ -13,9 +13,14 @@ | |
</section> | |
</header> | |
- <section class="content-body" if="&logged_in?"> | |
+ <section with="¤t_user" class="content-body" if="&logged_in?"> | |
<h3>Your Projects</h3> | |
- <collection:projects with="¤t_user"><card without-creator-link/></collection> | |
+ <collection:projects><card without-creator-link/></collection> | |
+ | |
+ <a:projects action="new">New Project</a> | |
+ | |
+ <h3>Projects you have joined</h3> | |
+ <collection:joined-projects><card without-creator-link/></collection> | |
</section> | |
</content:> | |
diff --git a/app/views/projects/show.dryml b/app/views/projects/show.dryml | |
index ccf08e2..8c11a0b 100644 | |
--- a/app/views/projects/show.dryml | |
+++ b/app/views/projects/show.dryml | |
@@ -9,4 +9,19 @@ | |
<empty-message:>No stories match your criteria</empty-message:> | |
</table-plus> | |
</collection:> | |
+ | |
+ <aside:> | |
+ <h2>Project Members</h2> | |
+ <collection:memberships part="members"> | |
+ <card><heading:><a:user/></heading:></card> | |
+ </collection> | |
+ | |
+ <form:memberships.new update="members" reset-form refocus-form> | |
+ <div> | |
+ Add a member: | |
+ <name-one:user complete-target="&@project" completer="new_member_name"/> | |
+ </div> | |
+ </form> | |
+ </aside:> | |
+ | |
</show-page> | |
diff --git a/app/views/taglibs/application.dryml b/app/views/taglibs/application.dryml | |
index c9548da..c4c2888 100644 | |
--- a/app/views/taglibs/application.dryml | |
+++ b/app/views/taglibs/application.dryml | |
@@ -17,3 +17,13 @@ | |
</append-body:> | |
</old-card> | |
</extend> | |
+ | |
+<extend tag="card" for="ProjectMembership"> | |
+ <old-card merge> | |
+ <body:> | |
+ <span>Contributor?</span> | |
+ <editor:contributor/> | |
+ </body:> | |
+ </old-card> | |
+</extend> | |
+ | |
__PATCH | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment