Commit 37383966 authored by Steven Thonus's avatar Steven Thonus
Browse files

Archiving old projects; archived projects aren't shown on dashboard

features for archive projects
abilities for archived project
other abilities for archive projects

only limit commits and merges for archived projects

ability changed to prohibited actions on archived projects

added spec and feature tests for archive projects

changed search bar not to include archived projects
parent 73c7715cd76c
...@@ -73,6 +73,6 @@ def issues ...@@ -73,6 +73,6 @@ def issues
protected protected
def load_projects def load_projects
@projects = current_user.authorized_projects.sorted_by_activity @projects = current_user.authorized_projects.sorted_by_activity.non_archived
end end
end end
...@@ -5,7 +5,7 @@ class ProjectsController < ApplicationController ...@@ -5,7 +5,7 @@ class ProjectsController < ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create] before_filter :authorize_read_project!, except: [:index, :new, :create]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer] before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph] before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'navless', only: [:new, :create, :fork] layout 'navless', only: [:new, :create, :fork]
...@@ -116,6 +116,24 @@ def autocomplete_sources ...@@ -116,6 +116,24 @@ def autocomplete_sources
end end
end end
def archive
return access_denied! unless can?(current_user, :archive_project, project)
project.archive!
respond_to do |format|
format.html { redirect_to @project }
end
end
def unarchive
return access_denied! unless can?(current_user, :archive_project, project)
project.unarchive!
respond_to do |format|
format.html { redirect_to @project }
end
end
private private
def set_title def set_title
......
...@@ -73,14 +73,14 @@ def groups_autocomplete ...@@ -73,14 +73,14 @@ def groups_autocomplete
# Autocomplete results for the current user's projects # Autocomplete results for the current user's projects
def projects_autocomplete def projects_autocomplete
current_user.authorized_projects.map do |p| current_user.authorized_projects.non_archived.map do |p|
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end end
end end
# Autocomplete results for the current user's projects # Autocomplete results for the current user's projects
def public_projects_autocomplete def public_projects_autocomplete
Project.public_or_internal_only(current_user).map do |p| Project.public_or_internal_only(current_user).non_archived.map do |p|
{ label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) }
end end
end end
......
...@@ -59,31 +59,35 @@ def project_abilities(user, project) ...@@ -59,31 +59,35 @@ def project_abilities(user, project)
# Rules based on role in project # Rules based on role in project
if team.masters.include?(user) if team.masters.include?(user)
rules << project_master_rules rules += project_master_rules
elsif team.developers.include?(user) elsif team.developers.include?(user)
rules << project_dev_rules rules += project_dev_rules
elsif team.reporters.include?(user) elsif team.reporters.include?(user)
rules << project_report_rules rules += project_report_rules
elsif team.guests.include?(user) elsif team.guests.include?(user)
rules << project_guest_rules rules += project_guest_rules
end end
if project.public? || project.internal? if project.public? || project.internal?
rules << public_project_rules rules += public_project_rules
end end
if project.owner == user || user.admin? if project.owner == user || user.admin?
rules << project_admin_rules rules += project_admin_rules
end end
if project.group && project.group.has_owner?(user) if project.group && project.group.has_owner?(user)
rules << project_admin_rules rules += project_admin_rules
end end
rules.flatten if project.archived?
rules -= project_archived_rules
end
rules
end end
def public_project_rules def public_project_rules
...@@ -125,6 +129,16 @@ def project_dev_rules ...@@ -125,6 +129,16 @@ def project_dev_rules
] ]
end end
def project_archived_rules
[
:write_merge_request,
:push_code,
:push_code_to_protected_branches,
:modify_merge_request,
:admin_merge_request
]
end
def project_master_rules def project_master_rules
project_dev_rules + [ project_dev_rules + [
:push_code_to_protected_branches, :push_code_to_protected_branches,
...@@ -147,7 +161,8 @@ def project_admin_rules ...@@ -147,7 +161,8 @@ def project_admin_rules
:change_namespace, :change_namespace,
:change_visibility_level, :change_visibility_level,
:rename_project, :rename_project,
:remove_project :remove_project,
:archive_project
] ]
end end
...@@ -160,7 +175,7 @@ def group_abilities user, group ...@@ -160,7 +175,7 @@ def group_abilities user, group
# Only group owner and administrators can manage group # Only group owner and administrators can manage group
if group.has_owner?(user) || user.admin? if group.has_owner?(user) || user.admin?
rules << [ rules += [
:manage_group, :manage_group,
:manage_namespace :manage_namespace
] ]
...@@ -174,7 +189,7 @@ def namespace_abilities user, namespace ...@@ -174,7 +189,7 @@ def namespace_abilities user, namespace
# Only namespace owner and administrators can manage it # Only namespace owner and administrators can manage it
if namespace.owner == user || user.admin? if namespace.owner == user || user.admin?
rules << [ rules += [
:manage_namespace :manage_namespace
] ]
end end
......
...@@ -116,6 +116,8 @@ class Project < ActiveRecord::Base ...@@ -116,6 +116,8 @@ class Project < ActiveRecord::Base
scope :public_only, -> { where(visibility_level: PUBLIC) } scope :public_only, -> { where(visibility_level: PUBLIC) }
scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) } scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) }
scope :non_archived, -> { where(archived: false) }
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
class << self class << self
...@@ -132,7 +134,7 @@ def active ...@@ -132,7 +134,7 @@ def active
end end
def search query def search query
joins(:namespace).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%") joins(:namespace).where("projects.archived = ?", false).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%")
end end
def find_with_namespace(id) def find_with_namespace(id)
...@@ -472,4 +474,12 @@ def reload_default_branch ...@@ -472,4 +474,12 @@ def reload_default_branch
def visibility_level_field def visibility_level_field
visibility_level visibility_level
end end
def archive!
update_attribute(:archived, true)
end
def unarchive!
update_attribute(:archived, false)
end
end end
...@@ -82,6 +82,10 @@ ...@@ -82,6 +82,10 @@
= link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project) = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project)
.project-info .project-info
.pull-right .pull-right
- if project.archived?
%span.label
%i.icon-book
Archived
- project.labels.each do |label| - project.labels.each do |label|
%span.label.label-info %span.label.label-info
%i.icon-tag %i.icon-tag
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
%span.visibility-level-label %span.visibility-level-label
= visibility_level_icon(@project.visibility_level) = visibility_level_icon(@project.visibility_level)
= visibility_level_label(@project.visibility_level) = visibility_level_label(@project.visibility_level)
- if @project.archived?
%span.visibility-level-label
%i.icon-book
Archived
.span7 .span7
- unless empty_repo - unless empty_repo
......
...@@ -98,6 +98,33 @@ ...@@ -98,6 +98,33 @@
%i.icon-chevron-down %i.icon-chevron-down
.js-toggle-visibility-container.hide .js-toggle-visibility-container.hide
- if can? current_user, :archive_project, @project
.ui-box.ui-box-danger
.title
- if @project.archived?
Unarchive project
- else
Archive project
.ui-box-body
- if @project.archived?
%p
Unarchiving the project will mark its repository as active.
%br
The project can be committed to.
%br
%strong Once active this project shows up in the search and on the dashboard.
= link_to 'Unarchive', unarchive_project_path(@project), confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be comitted to again.", method: :post, class: "btn btn-remove"
- else
%p
Archiving the project will mark its repository as read-only.
%br
It is hidden from the dashboard and doesn't show up in searches.
%br
%strong Archived projects cannot be committed to!
= link_to 'Archive', archive_project_path(@project), confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to.", method: :post, class: "btn btn-remove"
- else
%p.nothing_here_message Only the project owner can archive a project
- if can?(current_user, :change_namespace, @project) - if can?(current_user, :change_namespace, @project)
.ui-box.ui-box-danger .ui-box.ui-box-danger
.title Transfer project .title Transfer project
......
...@@ -170,6 +170,8 @@ ...@@ -170,6 +170,8 @@
member do member do
put :transfer put :transfer
post :fork post :fork
post :archive
post :unarchive
get :autocomplete_sources get :autocomplete_sources
end end
......
class AddArchivedToProjects < ActiveRecord::Migration
def change
add_column :projects, :archived, :boolean, default: false, null: false
end
end
...@@ -192,6 +192,7 @@ ...@@ -192,6 +192,7 @@
t.boolean "imported", default: false, null: false t.boolean "imported", default: false, null: false
t.string "import_url" t.string "import_url"
t.integer "visibility_level", default: 0, null: false t.integer "visibility_level", default: 0, null: false
t.boolean "archived", default: false, null: false
end end
add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree
......
Feature: Dashboard with archived projects
Background:
Given I sign in as a user
And I own project "Shop"
And I own project "Forum"
And project "Forum" is archived
And I visit dashboard page
Scenario: I should see non-archived projects on dashboard
Then I should see "Shop" project link
And I should not see "Forum" project link
Scenario: I should see all projects on projects page
And I visit dashboard projects page
Then I should see "Shop" project link
And I should see "Forum" project link
Feature: Project Archived
Background:
Given I sign in as a user
And I own project "Shop"
And I own project "Forum"
Scenario: I should not see archived on project page of not-archive project
And project "Forum" is archived
And I visit project "Shop" page
Then I should not see "Archived"
Scenario: I should see archived on project page of archive project
And project "Forum" is archived
And I visit project "Forum" page
Then I should see "Archived"
Scenario: I should not see archived on projects page with no archived projects
And I visit dashboard projects page
Then I should not see "Archived"
Scenario: I should see archived on projects page with archived projects
And project "Forum" is archived
And I visit dashboard projects page
Then I should see "Archived"
Scenario: I archive project
When project "Shop" has push event
And I visit project "Shop" page
And I visit edit project "Shop" page
And I set project archived
Then I should see "Archived"
Scenario: I unarchive project
When project "Shop" has push event
And project "Shop" is archived
And I visit project "Shop" page
And I visit edit project "Shop" page
And I set project unarchived
Then I should not see "Archived"
class DashboardWithArchivedProjects < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
When 'project "Forum" is archived' do
project = Project.find_by_name "Forum"
project.update_attribute(:archived, true)
end
Then 'I should see "Shop" project link' do
page.should have_link "Shop"
end
Then 'I should not see "Forum" project link' do
page.should_not have_link "Forum"
end
Then 'I should see "Forum" project link' do
page.should have_link "Forum"
end
end
class ProjectArchived < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
When 'project "Forum" is archived' do
project = Project.find_by_name "Forum"
project.update_attribute(:archived, true)
end
When 'project "Shop" is archived' do
project = Project.find_by_name "Shop"
project.update_attribute(:archived, true)
end
When 'I visit project "Forum" page' do
project = Project.find_by_name "Forum"
visit project_path(project)
end
Then 'I should not see "Archived"' do
page.should_not have_content "Archived"
end
Then 'I should see "Archived"' do
page.should have_content "Archived"
end
When 'I set project archived' do
click_link "Archive"
end
When 'I set project unarchived' do
click_link "Unarchive"
end
end
\ No newline at end of file
...@@ -14,6 +14,13 @@ module SharedProject ...@@ -14,6 +14,13 @@ module SharedProject
@project.team << [@user, :master] @project.team << [@user, :master]
end end
# Create another specific project called "Forum"
And 'I own project "Forum"' do
@project = Project.find_by_name "Forum"
@project ||= create(:project_with_code, name: "Forum", namespace: @user.namespace, path: 'forum_project')
@project.team << [@user, :master]
end
And 'project "Shop" has push event' do And 'project "Shop" has push event' do
@project = Project.find_by_name("Shop") @project = Project.find_by_name("Shop")
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
# imported :boolean default(FALSE), not null # imported :boolean default(FALSE), not null
# import_url :string(255) # import_url :string(255)
# visibility_level :integer default(0), not null # visibility_level :integer default(0), not null
# archived :boolean default(FALSE), not null
# #
require 'spec_helper' require 'spec_helper'
......
...@@ -103,6 +103,33 @@ ...@@ -103,6 +103,33 @@
end end
end end
context "archived project" do
let(:personal_project) { create(:project, namespace: user.namespace) }
before do
project.team << [user, :developer]
project.archive!
end
context "git pull" do
it do
pull(key, project)
response.status.should == 200
response.body.should == 'true'
end
end
context "git push" do
it do
push(key, project)
response.status.should == 200
response.body.should == 'false'
end
end
end
context "deploy key" do context "deploy key" do
let(:key) { create(:deploy_key) } let(:key) { create(:deploy_key) }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment