Commit f40ad6ce authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge pull request #5841 from Popl7/archiving_old_projects

Archiving old projects for feature request
parents ca29617d 37383966
...@@ -73,6 +73,6 @@ class DashboardController < ApplicationController ...@@ -73,6 +73,6 @@ class DashboardController < ApplicationController
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 @@ class ProjectsController < ApplicationController ...@@ -116,6 +116,24 @@ class ProjectsController < ApplicationController
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 @@ module SearchHelper ...@@ -73,14 +73,14 @@ module SearchHelper
# 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 @@ class Ability ...@@ -59,31 +59,35 @@ class Ability
# 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 @@ class Ability ...@@ -125,6 +129,16 @@ class Ability
] ]
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 @@ class Ability ...@@ -147,7 +161,8 @@ class Ability
: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 @@ class Ability ...@@ -160,7 +175,7 @@ class Ability
# 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 @@ class Ability ...@@ -174,7 +189,7 @@ class Ability
# 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 @@ class Project < ActiveRecord::Base ...@@ -132,7 +134,7 @@ class Project < ActiveRecord::Base
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 @@ class Project < ActiveRecord::Base ...@@ -472,4 +474,12 @@ class Project < ActiveRecord::Base
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 @@ Gitlab::Application.routes.draw do ...@@ -170,6 +170,8 @@ Gitlab::Application.routes.draw do
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 @@ ActiveRecord::Schema.define(version: 20131214224427) do ...@@ -192,6 +192,7 @@ ActiveRecord::Schema.define(version: 20131214224427) do
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 @@ describe API::API do ...@@ -103,6 +103,33 @@ describe API::API do
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