This patch requires the patch for #144
by Darryl L. Pierce
To test this patch, please first apply the previously submitted patch
"[PATCH] A user's avatar is shown in the user list page. #144".
15 years
[PATCH] A user's avatar is shown in the user list page. #144
by Darryl L. Pierce
Added a new div that floats left in the name column. User's headshot is
shown at a max of 32x32 pixels. If the user does not have an avatar,
then no avatar is shown.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/users/index.html.erb | 3 +++
public/stylesheets/tables.css | 7 ++++++-
2 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index ee02a22..2e8c466 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -17,6 +17,9 @@
<tr class="<%= cycle('odd', 'even') %>">
<td><%= user.id %></td>
<td class="name">
+ <% if user.avatar_url %>
+ <div class="small-icon"><%= image_tag user.avatar_url %></div>
+ <% end %>
<%= link_to user.display_name, user_path(user) %>
<%= RedCloth.new(get_first_sentence(user.introduction)).to_html %>
</td>
diff --git a/public/stylesheets/tables.css b/public/stylesheets/tables.css
index 839a827..aceddbd 100644
--- a/public/stylesheets/tables.css
+++ b/public/stylesheets/tables.css
@@ -7,6 +7,12 @@ td {
text-align: center;
}
+td div.small-icon img {
+ float: left;
+ max-height: 32px;
+ max-width: 32px;
+}
+
table caption {
margin: 0;
padding: 8px 20px;
@@ -122,4 +128,3 @@ table.edit td.value {
padding-right: 15px;
text-align: left;
}
-
--
1.6.0.6
15 years
[PATCH] Homepage content can include wiki markup. #112
by Darryl L. Pierce
This patch requires a migration, since it introduces default homepage
content.
It also introduces a div that contains the content div. That has an
explicit 5px padding on all four sides to keep content from butting up
against the edges.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/home/index.html.erb | 3 +-
app/views/layouts/default.html.erb | 4 +-
db/migrate/031_add_default_homepage_content.rb | 63 ++++++++++++++++++++++++
3 files changed, 67 insertions(+), 3 deletions(-)
create mode 100644 db/migrate/031_add_default_homepage_content.rb
diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb
index e9aeb91..b58cca0 100644
--- a/app/views/home/index.html.erb
+++ b/app/views/home/index.html.erb
@@ -1,2 +1 @@
-<h1>Welcome To Another Installation Of ProjXP!</h1>
-
+<%= RedCloth.new(ConfigProperty.fetch("text.home_page", "")).to_html %>
diff --git a/app/views/layouts/default.html.erb b/app/views/layouts/default.html.erb
index cc36450..2739459 100644
--- a/app/views/layouts/default.html.erb
+++ b/app/views/layouts/default.html.erb
@@ -47,7 +47,9 @@
</div>
<% end %>
- <%= yield :layout %>
+ <div style="padding: 5px">
+ <%= yield :layout %>
+ </div>
<div id="footer">
Copyright © 2006-2008, Darryl L. Pierce. Usage subject to licensing agreement.
diff --git a/db/migrate/031_add_default_homepage_content.rb b/db/migrate/031_add_default_homepage_content.rb
new file mode 100644
index 0000000..ed48a74
--- /dev/null
+++ b/db/migrate/031_add_default_homepage_content.rb
@@ -0,0 +1,63 @@
+# 031_add_default_homepage_content.rb
+# Copyright (C) 2009, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+class AddDefaultHomepageContent < ActiveRecord::Migration
+ def self.up
+ ConfigProperty.store('text.home_page',
+ "h1. Welcome To ProjXP!\r\n\n" +
+ "ProjXP is an agile project management system. Its goal is to enable developers in multiple locations to use agile development practices effectively.\r\n\n" +
+
+ "h2. What Is Agile Development?\r\n\n" +
+ "Agile development is \"defined\":http://en.wikipedia.org/wiki/Agile_software_development as:\r\n\n" +
+ "bq. group of software development methodologies based on iterative development, where requirements and solutions evolve through collaboration between self-organizing cross-functional teams.\r\n\n" +
+ "Agile development enables teams to more quickly and effectively perform development by requiring that features be prioritized, and development to be done in short, achievable cycles, called sprints.\r\n\n" +
+ "*ProjXP* enables teams to use this methodology by providing a way for project and product owners to prioritize the list of features needed for an application. " +
+ "Developers and product owners then work together to define sprints and include those features. " +
+ "Project owners can then monitor development progress without distracting the development team.\r\n\n" +
+
+ "h2. What Are Projects and Products?\r\n\n" +
+ "The term _product_ refers to a group whose goal is to provide a programming artifact, such as a an application, or a website, documentation or even a shared library or web service.\r\n\n" +
+ "The term _project_ is used to group together one or more directly related _products_.\r\n\n" +
+ "For example, a project may be composed of several product teams:\r\n\n" +
+ "* *the web developers*, who provide the user interface for a web application,\r\n" +
+ "* *the desktop developers*, who create a desktop application providing the same features,\r\n" +
+ "* *the web services team*, who create a series of shared services that the web and application teams access.\r\n\n" +
+ "h2. Epic Stories And User Stories\r\n\n" +
+ "An _epic story_ is an overarching feature, one that is provided by more then one product. In the above example, one such epic could include SSO[1].\r\n\n" +
+ "A _user story_ is a feature provided by a single product. User stories can also be related to epic stories if they fulfill that epic's requirements. So, for example, the desktop app could provide an interface for SSO, the web application an HTML template, and the web services group a service to perform such authentication.\r\n\n" +
+ "h2. How Does ProjXP Help?\r\n\n" +
+ "With *ProjXP*, project owners can focus on maintaining the list of epic stories their project wants to achieve. They can focus on coordinating at a higher level what needs to be done to make the project successful.\r\n\n" +
+ "Product owners can draw from the project's epic stories, as well as define their own features that need to be accomplished.\r\n\n" +
+ "Sprint teams can streamline their development efforts by focusing on the next task to be performed, and provide feedback to the team leader and product owner by recording how much work was done. This allows for tracking a teams overall velocity[2] during a sprint. Developers can also have *ProjXP* automatically send a daily email to the team's mailing listing those items he completed the day before, the items that are open, and also what items are current blockers. This helps to keep the rest of the team informed about what everybody else is working on, enabling collaboration.\r\n\n" +
+
+ "h2. Getting Started\r\n" +
+ "# Create a project.\r\n" +
+ "# Create a product.\r\n" +
+ "# Enter a set of user stories.\r\n" +
+ "# Add members to the product team.\r\n" +
+ "# Define a new sprint, and create backlog.\r\n" +
+ "# Start the sprint, and get busy!\r\n\n" +
+
+ "fn1. Shared sign on.\r\n\n" +
+ "fn2. See \"here\":http://www.netobjectives.com/blogs/agile-development-velocity-is-the-measure-you-want for more information on velocity.\r\n"
+ )
+ end
+
+ def self.down
+ ConfigProperty.find_by_name('text.home_page').destroy
+ end
+end
--
1.6.0.6
15 years
Superceded previous patch
by Darryl L. Pierce
This patch supercedes the previous I sent. It accounts for the fixes I
commited for the broken unit tests.
15 years
Superceded patch
by Darryl L. Pierce
This patch supercedes the previous patch. It's rebased to account for
the fixes I did to the failing unit tests.
15 years
Planning for version 0.3.0...
by Darryl L. Pierce
The goal for this development sprint is enabling more collaboration between
team members. Some of the goals I have for it are:
* developers having more say in sprint planning, including voting on items
and providing estimates
* developers and product owners discussing user stories, and capturing that
dialog to help clarify the goal of user stories
* developers and project owners discussing epics, and capturing that dialog
same as user stories
* providing easier means to get to information, such as RSS feeds and links
in emails
If you have any suggestions for features, please provide them within the
next week or so. Version 0.2.0 development is wrapping up and should be done
within the next few weeks. After that, development for 0.3.0 should begin.
--
Darryl L. Pierce <mcpierce(a)gmail.com>
Visit the Infobahn Offramp: <http://mcpierce.multiply.com>
"Bury me next to my wife. Nothing too fancy..." - Ulysses S. Grant
15 years
[PATCH] Fixed the products column of the project list to be a link to the
by Darryl L. Pierce
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/projects/index.html.erb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 74781d2..a3bfa2e 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -27,7 +27,7 @@
<% if project.url %><%= link_to "Homepage", project.url %><% end %>
</td>
<td>0</td>
- <td><%= project.products.count %></td>
+ <td><%= link_to "#{project.products.count}", products_path(:project => project) %></td>
<td><%= link_to project.owner.display_name, user_path(project.owner) %></td>
<td><%= show_date project.created_at %></td>
</tr>
--
1.6.0.6
15 years
[PATCH] A link on the project list page goes to a project's epics list. #151
by Darryl L. Pierce
Changed the Epics column for the project index page to be a link to the
project epics page. The column also properly shows the number of epics
for that project.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/projects/index.html.erb | 2 +-
doc/ChangeLog | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 74781d2..18147d2 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -26,7 +26,7 @@
<% end %>
<% if project.url %><%= link_to "Homepage", project.url %><% end %>
</td>
- <td>0</td>
+ <td><%= link_to "#{project.epics.size}", project_epics_path(project) %></td>
<td><%= project.products.count %></td>
<td><%= link_to project.owner.display_name, user_path(project.owner) %></td>
<td><%= show_date project.created_at %></td>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index d0868f5..5ce270b 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -28,5 +28,6 @@ Change Log (0.2.0):
* #148 - Project owners can edit epics stories.
* #149 - Project owners can delete epics with no user stories.
* #150 - Epics can be closed and reopened.
+ * #151 - Epics can be viewed from the projects list page.
* #152 - Users can view all epic stories for a project.
--
1.6.0.6
15 years
[PATCH] Epics can be marked as closed. #150
by Darryl L. Pierce
Added a controller method and test to mark an epic as closed.
Modified the epic details page to show a close epic method.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/epics_controller.rb | 38 ++++++++++-
app/models/epic.rb | 20 +++++-
app/views/epics/show.html.erb | 16 +++++
config/routes.rb | 6 ++-
test/fixtures/epics.yml | 8 ++
test/functional/epics_controller_test.rb | 108 ++++++++++++++++++++++++++++++
test/unit/epic_test.rb | 34 +++++++++
7 files changed, 225 insertions(+), 5 deletions(-)
diff --git a/app/controllers/epics_controller.rb b/app/controllers/epics_controller.rb
index 6f7f819..fa251db 100644
--- a/app/controllers/epics_controller.rb
+++ b/app/controllers/epics_controller.rb
@@ -16,9 +16,9 @@
# +EpicsController+ allows users to work with +Epic+ stories.
class EpicsController < ApplicationController
- before_filter :authenticated, :only => [:new, :edit, :create, :update, :destroy]
+ before_filter :authenticated, :except => [:index, :show]
before_filter :load_project
- before_filter :load_epic, :only => [:show, :edit, :update, :destroy]
+ before_filter :load_epic, :except => [:index, :new, :create]
# GET /projects/1/epics
def index
@@ -122,6 +122,40 @@ class EpicsController < ApplicationController
end
end
+ # PUT /projects/1/epics/1/close
+ def close
+ respond_to do |format|
+ if @epic.can_close?(@user)
+ Epic.transaction do
+ @epic.closed = true
+ @epic.save!
+ flash[:message] = "Epic is now marked as closed."
+ end
+ else
+ flash[:error] = "You may not mark this epic completed."
+ end
+
+ format.html { redirect_to project_epic_path(@project, @epic) }
+
+ end
+ end
+
+ # PUT /projects/1/epics/1/reopen
+ def reopen
+ respond_to do |format|
+ if @epic.can_reopen?(@user)
+ @epic.closed = false
+ @epic.save!
+ flash[:message] = "Epic now reopened."
+ else
+ flash[:error] = "You may not reopen this epic."
+ end
+
+ format.html { redirect_to project_epic_path(@project, @epic) }
+
+ end
+ end
+
private
def load_project
diff --git a/app/models/epic.rb b/app/models/epic.rb
index e2e33b6..a1620c5 100644
--- a/app/models/epic.rb
+++ b/app/models/epic.rb
@@ -42,11 +42,27 @@ class Epic < ActiveRecord::Base
# Returns whether the user can edit this epic.
def can_edit?(user)
- (user != nil) && (user.id == project.owner_id)
+ is_owner? user
end
# Returns whether this epic can be deleted.
def can_delete?(user)
- (user != nil) && (user.id == project.owner_id) && user_stories.empty?
+ is_owner?(user) && user_stories.empty?
+ end
+
+ # Returns whether the user can close this epic.
+ def can_close?(user)
+ is_owner?(user) && !closed
+ end
+
+ # Returns whether the user can reopen this epic.
+ def can_reopen?(user)
+ is_owner?(user) && closed
+ end
+
+ private
+
+ def is_owner?(user)
+ (user != nil) && (user.id == project.owner_id)
end
end
diff --git a/app/views/epics/show.html.erb b/app/views/epics/show.html.erb
index 08aa7fa..75db19c 100644
--- a/app/views/epics/show.html.erb
+++ b/app/views/epics/show.html.erb
@@ -3,6 +3,8 @@
<dl>
<dt><%= "#{(a)epic.title} (Priority: #{(a)epic.priority})" %></dt>
<dd><%= RedCloth.new((a)epic.description).to_html %></dd>
+
+ <dt><%= "This epic is #{(a)epic.closed ? 'closed' : 'open'}." %></dt>
</dl>
</div>
</div>
@@ -15,8 +17,22 @@
project_epics_path(@project), :class => "command" %>
<% if @epic.can_edit?(@user) %>
+
<%= link_to "Edit this epic",
edit_project_epic_path(@project, @epic), :class => "command" %>
+
+ <% if @epic.can_close?(@user) %>
+ <%= link_to "Close epic...",
+ close_project_epic_path(@project, @epic), :class => "command",
+ :confirm => "Close this epic? Are you sure?", :method => :put %>
+ <% end %>
+
+ <% if @epic.can_reopen?(@user) %>
+ <%= link_to "Reopen epic...",
+ reopen_project_epic_path(@project, @epic), :class => "command",
+ :confirm => "Reopen this epic? Are you sure?", :method => :put %>
+ <% end %>
+
<% end %>
<% if @epic.can_delete?(@user) %>
diff --git a/config/routes.rb b/config/routes.rb
index 6fe5168..21d82f4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -22,7 +22,11 @@ ActionController::Routing::Routes.draw do |map|
end
map.resources :projects do |project|
- project.resources :epics
+ project.resources(:epics, :member =>
+ {
+ :close => :put,
+ :reopen => :put
+ })
end
map.resources :projects, :member =>
{
diff --git a/test/fixtures/epics.yml b/test/fixtures/epics.yml
index 3654b8d..dfcb78c 100644
--- a/test/fixtures/epics.yml
+++ b/test/fixtures/epics.yml
@@ -15,3 +15,11 @@ no_user_story_epic:
priority: 3
title: Has no user stories.
description: Epic with no user stories.
+
+closed_epic:
+ project_id: <%= Fixtures.identify(:projxp) %>
+ priority: 4
+ title: This epic is completed.
+ description: Nope, not open at all.
+ closed: true
+
diff --git a/test/functional/epics_controller_test.rb b/test/functional/epics_controller_test.rb
index 5660f32..0d41b19 100644
--- a/test/functional/epics_controller_test.rb
+++ b/test/functional/epics_controller_test.rb
@@ -27,6 +27,14 @@ class EpicsControllerTest < ActionController::TestCase
@epic = epics(:no_user_story_epic)
raise "Epic is not in this project!" unless @epic.project_id == @project.id
+ @open_epic = @epic
+ raise "Open epic cannot be closed!" if @open_epic.closed
+ raise "Open epic is not in this project!" unless @open_epic.project_id == @project.id
+
+ @closed_epic = epics(:closed_epic)
+ raise "Closed epic must be closed!" unless @closed_epic.closed
+ raise "Closed epic is not in this project!" unless @closed_epic.project_id == @project.id
+
@other_project = projects(:teatime)
@other_owner = @other_project.owner
@@ -299,4 +307,104 @@ class EpicsControllerTest < ActionController::TestCase
result = Epic.find_by_id((a)epic.id)
assert_nil result, "Epic should have been deleted."
end
+
+ # Ensures that anonymous users cannot mark epics completed.
+ def test_close_as_anonymous
+ put :close
+
+ assert_redirected_to login_path
+ end
+
+ # Ensures that a valid project must be required.
+ def test_close_with_invalid_project
+ put :close, { }, {:user_id => @owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that a valid epic id is required.
+ def test_close_with_invalid_epic
+ put :close, {:project_id => @project.id}, {:user_id => @owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that the project and epic are a match.
+ def test_close_with_project_epic_mismatch
+ put :close, {:project_id => @other_project.id, :id => @epic.id}, {:user_id => @other_owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that only the owner can mark a project completed.
+ def test_close_with_nonowner
+ put :close, {:project_id => @project.id, :id => @epic.id}, {:user_id => @nonowner.id}
+
+ assert_redirected_to project_epic_path(@project, @epic)
+ end
+
+ # Ensures that only a closed epic cannot be closed again.
+ def test_closed_with_closed_epic
+ put :close, {:project_id => @project.id, :id => @closed_epic.id}, {:user_id => @owner.id}
+
+ assert_redirected_to project_epic_path(@project, @closed_epic)
+ end
+
+ # Ensures that closing an epic works as expected.
+ def test_close
+ put :close, {:project_id => @project.id, :id => @epic.id}, {:user_id => @owner.id}
+
+ assert_redirected_to project_epic_path(@project, @epic)
+ assert Epic.find_by_id((a)epic.id).closed, "Epic should have been marked as closed."
+ end
+
+ # Ensures that anonymous users cannot reopen a closed epic.
+ def test_reopen_as_anonymous
+ put :reopen
+
+ assert_redirected_to login_path
+ end
+
+ # Ensures that a valid project must be required.
+ def test_reopen_with_invalid_project
+ put :reopen, { }, {:user_id => @owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that a valid epic id is required.
+ def test_reopen_with_invalid_epic
+ put :reopen, {:project_id => @project.id}, {:user_id => @owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that the project and epic are a match.
+ def test_reopen_with_project_epic_mismatch
+ put :reopen, {:project_id => @other_project.id, :id => @closed_epic.id}, {:user_id => @other_owner.id}
+
+ assert_redirected_to error_path
+ end
+
+ # Ensures that only the owner can reopen a closed epic.
+ def test_reopen_with_nonowner
+ put :reopen, {:project_id => @project.id, :id => @closed_epic.id}, {:user_id => @nonowner.id}
+
+ assert_redirected_to project_epic_path(@project, @closed_epic)
+ end
+
+ # Ensures that only an open epic cannot be reopened.
+ def test_reopen_with_open_epic
+ put :reopen, {:project_id => @project.id, :id => @epic.id}, {:user_id => @owner.id}
+
+ assert_redirected_to project_epic_path(@project, @epic)
+ end
+
+ # Ensures that reopening an epic works as expected.
+ def test_reopen
+ put :reopen, {:project_id => @project.id, :id => @closed_epic.id}, {:user_id => @owner.id}
+
+ assert_redirected_to project_epic_path(@project, @closed_epic)
+ assert !Epic.find_by_id((a)closed_epic.id).closed, "Epic should have been reopened."
+ end
end
diff --git a/test/unit/epic_test.rb b/test/unit/epic_test.rb
index bd13745..dbe5998 100644
--- a/test/unit/epic_test.rb
+++ b/test/unit/epic_test.rb
@@ -31,6 +31,10 @@ class EpicTest < ActiveSupport::TestCase
@new_epic = Epic.new(:project => @project, :priority => 1, :title => "This is an epic")
@epic = epics(:user_stories_epic)
raise "Epic must be part of the project!" unless @epic.project_id == @project.id
+ raise "Epic must be open!" if @epic.closed
+
+ @closed_epic = epics(:closed_epic)
+ raise "Epic must be closed!" unless @closed_epic.closed
end
# Ensures the project_id field is required.
@@ -89,4 +93,34 @@ class EpicTest < ActiveSupport::TestCase
def test_can_edit
flunk "Admins must be able to edit epics." unless @epic.can_edit?(@owner)
end
+
+ # Ensures that non-admins cannot close epics.
+ def test_can_close_as_nonadmin
+ flunk "Non-admins cannot close epics." if @epic.can_close?(@nonadmin)
+ end
+
+ # Ensures that a closed epic cannot be closed again.
+ def test_can_close_when_already_closed
+ flunk "Closed epics cannot be closed again." if @closed_epic.can_close?(@owner)
+ end
+
+ # Ensures that an epic can be closed by the admin.
+ def test_can_close
+ flunk "Admins must be able to close epics." unless @epic.can_close?(@owner)
+ end
+
+ # Ensures that closed epics cannot be reopened.
+ def test_can_reopen_an_open_epic
+ flunk "Users cannot reopen an open epic." if @epic.can_reopen?(@owner)
+ end
+
+ # Ensures non-admins cannot reopen epics.
+ def test_can_reopen_as_nonadmin
+ flunk "Non-admins cannot reopen epics." if @closed_epic.can_reopen?(@nonadmin)
+ end
+
+ # Ensures reopening an epic works as expected..
+ def test_can_reopen
+ flunk "Closed tasks should be able to reopen." unless @closed_epic.can_reopen?(@owner)
+ end
end
--
1.6.0.6
15 years