Commit f40ad6ce403e4365e4b5515cfa593437babb1298
Exists in
master
and in
4 other branches
Merge pull request #5841 from Popl7/archiving_old_projects
Archiving old projects for feature request
Showing
18 changed files
with
251 additions
and
16 deletions
Show diff stats
app/controllers/dashboard_controller.rb
... | ... | @@ -73,6 +73,6 @@ class DashboardController < ApplicationController |
73 | 73 | protected |
74 | 74 | |
75 | 75 | def load_projects |
76 | - @projects = current_user.authorized_projects.sorted_by_activity | |
76 | + @projects = current_user.authorized_projects.sorted_by_activity.non_archived | |
77 | 77 | end |
78 | 78 | end | ... | ... |
app/controllers/projects_controller.rb
... | ... | @@ -5,7 +5,7 @@ class ProjectsController < ApplicationController |
5 | 5 | |
6 | 6 | # Authorize |
7 | 7 | before_filter :authorize_read_project!, except: [:index, :new, :create] |
8 | - before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer] | |
8 | + before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] | |
9 | 9 | before_filter :require_non_empty_project, only: [:blob, :tree, :graph] |
10 | 10 | |
11 | 11 | layout 'navless', only: [:new, :create, :fork] |
... | ... | @@ -116,6 +116,24 @@ class ProjectsController < ApplicationController |
116 | 116 | end |
117 | 117 | end |
118 | 118 | |
119 | + def archive | |
120 | + return access_denied! unless can?(current_user, :archive_project, project) | |
121 | + project.archive! | |
122 | + | |
123 | + respond_to do |format| | |
124 | + format.html { redirect_to @project } | |
125 | + end | |
126 | + end | |
127 | + | |
128 | + def unarchive | |
129 | + return access_denied! unless can?(current_user, :archive_project, project) | |
130 | + project.unarchive! | |
131 | + | |
132 | + respond_to do |format| | |
133 | + format.html { redirect_to @project } | |
134 | + end | |
135 | + end | |
136 | + | |
119 | 137 | private |
120 | 138 | |
121 | 139 | def set_title | ... | ... |
app/helpers/search_helper.rb
... | ... | @@ -73,14 +73,14 @@ module SearchHelper |
73 | 73 | |
74 | 74 | # Autocomplete results for the current user's projects |
75 | 75 | def projects_autocomplete |
76 | - current_user.authorized_projects.map do |p| | |
76 | + current_user.authorized_projects.non_archived.map do |p| | |
77 | 77 | { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } |
78 | 78 | end |
79 | 79 | end |
80 | 80 | |
81 | 81 | # Autocomplete results for the current user's projects |
82 | 82 | def public_projects_autocomplete |
83 | - Project.public_or_internal_only(current_user).map do |p| | |
83 | + Project.public_or_internal_only(current_user).non_archived.map do |p| | |
84 | 84 | { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } |
85 | 85 | end |
86 | 86 | end | ... | ... |
app/models/ability.rb
... | ... | @@ -59,31 +59,35 @@ class Ability |
59 | 59 | |
60 | 60 | # Rules based on role in project |
61 | 61 | if team.masters.include?(user) |
62 | - rules << project_master_rules | |
62 | + rules += project_master_rules | |
63 | 63 | |
64 | 64 | elsif team.developers.include?(user) |
65 | - rules << project_dev_rules | |
65 | + rules += project_dev_rules | |
66 | 66 | |
67 | 67 | elsif team.reporters.include?(user) |
68 | - rules << project_report_rules | |
68 | + rules += project_report_rules | |
69 | 69 | |
70 | 70 | elsif team.guests.include?(user) |
71 | - rules << project_guest_rules | |
71 | + rules += project_guest_rules | |
72 | 72 | end |
73 | 73 | |
74 | 74 | if project.public? || project.internal? |
75 | - rules << public_project_rules | |
75 | + rules += public_project_rules | |
76 | 76 | end |
77 | 77 | |
78 | 78 | if project.owner == user || user.admin? |
79 | - rules << project_admin_rules | |
79 | + rules += project_admin_rules | |
80 | 80 | end |
81 | 81 | |
82 | 82 | if project.group && project.group.has_owner?(user) |
83 | - rules << project_admin_rules | |
83 | + rules += project_admin_rules | |
84 | 84 | end |
85 | 85 | |
86 | - rules.flatten | |
86 | + if project.archived? | |
87 | + rules -= project_archived_rules | |
88 | + end | |
89 | + | |
90 | + rules | |
87 | 91 | end |
88 | 92 | |
89 | 93 | def public_project_rules |
... | ... | @@ -125,6 +129,16 @@ class Ability |
125 | 129 | ] |
126 | 130 | end |
127 | 131 | |
132 | + def project_archived_rules | |
133 | + [ | |
134 | + :write_merge_request, | |
135 | + :push_code, | |
136 | + :push_code_to_protected_branches, | |
137 | + :modify_merge_request, | |
138 | + :admin_merge_request | |
139 | + ] | |
140 | + end | |
141 | + | |
128 | 142 | def project_master_rules |
129 | 143 | project_dev_rules + [ |
130 | 144 | :push_code_to_protected_branches, |
... | ... | @@ -147,7 +161,8 @@ class Ability |
147 | 161 | :change_namespace, |
148 | 162 | :change_visibility_level, |
149 | 163 | :rename_project, |
150 | - :remove_project | |
164 | + :remove_project, | |
165 | + :archive_project | |
151 | 166 | ] |
152 | 167 | end |
153 | 168 | |
... | ... | @@ -160,7 +175,7 @@ class Ability |
160 | 175 | |
161 | 176 | # Only group owner and administrators can manage group |
162 | 177 | if group.has_owner?(user) || user.admin? |
163 | - rules << [ | |
178 | + rules += [ | |
164 | 179 | :manage_group, |
165 | 180 | :manage_namespace |
166 | 181 | ] |
... | ... | @@ -174,7 +189,7 @@ class Ability |
174 | 189 | |
175 | 190 | # Only namespace owner and administrators can manage it |
176 | 191 | if namespace.owner == user || user.admin? |
177 | - rules << [ | |
192 | + rules += [ | |
178 | 193 | :manage_namespace |
179 | 194 | ] |
180 | 195 | end | ... | ... |
app/models/project.rb
... | ... | @@ -116,6 +116,8 @@ class Project < ActiveRecord::Base |
116 | 116 | scope :public_only, -> { where(visibility_level: PUBLIC) } |
117 | 117 | scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) } |
118 | 118 | |
119 | + scope :non_archived, -> { where(archived: false) } | |
120 | + | |
119 | 121 | enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab |
120 | 122 | |
121 | 123 | class << self |
... | ... | @@ -132,7 +134,7 @@ class Project < ActiveRecord::Base |
132 | 134 | end |
133 | 135 | |
134 | 136 | def search query |
135 | - joins(:namespace).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%") | |
137 | + 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}%") | |
136 | 138 | end |
137 | 139 | |
138 | 140 | def find_with_namespace(id) |
... | ... | @@ -472,4 +474,12 @@ class Project < ActiveRecord::Base |
472 | 474 | def visibility_level_field |
473 | 475 | visibility_level |
474 | 476 | end |
477 | + | |
478 | + def archive! | |
479 | + update_attribute(:archived, true) | |
480 | + end | |
481 | + | |
482 | + def unarchive! | |
483 | + update_attribute(:archived, false) | |
484 | + end | |
475 | 485 | end | ... | ... |
app/views/dashboard/projects.html.haml
... | ... | @@ -82,6 +82,10 @@ |
82 | 82 | = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project) |
83 | 83 | .project-info |
84 | 84 | .pull-right |
85 | + - if project.archived? | |
86 | + %span.label | |
87 | + %i.icon-book | |
88 | + Archived | |
85 | 89 | - project.labels.each do |label| |
86 | 90 | %span.label.label-info |
87 | 91 | %i.icon-tag | ... | ... |
app/views/projects/_home_panel.html.haml
... | ... | @@ -7,6 +7,10 @@ |
7 | 7 | %span.visibility-level-label |
8 | 8 | = visibility_level_icon(@project.visibility_level) |
9 | 9 | = visibility_level_label(@project.visibility_level) |
10 | + - if @project.archived? | |
11 | + %span.visibility-level-label | |
12 | + %i.icon-book | |
13 | + Archived | |
10 | 14 | |
11 | 15 | .span7 |
12 | 16 | - unless empty_repo | ... | ... |
app/views/projects/edit.html.haml
... | ... | @@ -98,6 +98,33 @@ |
98 | 98 | %i.icon-chevron-down |
99 | 99 | |
100 | 100 | .js-toggle-visibility-container.hide |
101 | + - if can? current_user, :archive_project, @project | |
102 | + .ui-box.ui-box-danger | |
103 | + .title | |
104 | + - if @project.archived? | |
105 | + Unarchive project | |
106 | + - else | |
107 | + Archive project | |
108 | + .ui-box-body | |
109 | + - if @project.archived? | |
110 | + %p | |
111 | + Unarchiving the project will mark its repository as active. | |
112 | + %br | |
113 | + The project can be committed to. | |
114 | + %br | |
115 | + %strong Once active this project shows up in the search and on the dashboard. | |
116 | + = 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" | |
117 | + - else | |
118 | + %p | |
119 | + Archiving the project will mark its repository as read-only. | |
120 | + %br | |
121 | + It is hidden from the dashboard and doesn't show up in searches. | |
122 | + %br | |
123 | + %strong Archived projects cannot be committed to! | |
124 | + = 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" | |
125 | + - else | |
126 | + %p.nothing_here_message Only the project owner can archive a project | |
127 | + | |
101 | 128 | - if can?(current_user, :change_namespace, @project) |
102 | 129 | .ui-box.ui-box-danger |
103 | 130 | .title Transfer project | ... | ... |
config/routes.rb
db/schema.rb
... | ... | @@ -192,6 +192,7 @@ ActiveRecord::Schema.define(version: 20131214224427) do |
192 | 192 | t.boolean "imported", default: false, null: false |
193 | 193 | t.string "import_url" |
194 | 194 | t.integer "visibility_level", default: 0, null: false |
195 | + t.boolean "archived", default: false, null: false | |
195 | 196 | end |
196 | 197 | |
197 | 198 | add_index "projects", ["creator_id"], name: "index_projects_on_owner_id", using: :btree | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +Feature: Dashboard with archived projects | |
2 | + Background: | |
3 | + Given I sign in as a user | |
4 | + And I own project "Shop" | |
5 | + And I own project "Forum" | |
6 | + And project "Forum" is archived | |
7 | + And I visit dashboard page | |
8 | + | |
9 | + Scenario: I should see non-archived projects on dashboard | |
10 | + Then I should see "Shop" project link | |
11 | + And I should not see "Forum" project link | |
12 | + | |
13 | + Scenario: I should see all projects on projects page | |
14 | + And I visit dashboard projects page | |
15 | + Then I should see "Shop" project link | |
16 | + And I should see "Forum" project link | ... | ... |
... | ... | @@ -0,0 +1,39 @@ |
1 | +Feature: Project Archived | |
2 | + Background: | |
3 | + Given I sign in as a user | |
4 | + And I own project "Shop" | |
5 | + And I own project "Forum" | |
6 | + | |
7 | + Scenario: I should not see archived on project page of not-archive project | |
8 | + And project "Forum" is archived | |
9 | + And I visit project "Shop" page | |
10 | + Then I should not see "Archived" | |
11 | + | |
12 | + Scenario: I should see archived on project page of archive project | |
13 | + And project "Forum" is archived | |
14 | + And I visit project "Forum" page | |
15 | + Then I should see "Archived" | |
16 | + | |
17 | + Scenario: I should not see archived on projects page with no archived projects | |
18 | + And I visit dashboard projects page | |
19 | + Then I should not see "Archived" | |
20 | + | |
21 | + Scenario: I should see archived on projects page with archived projects | |
22 | + And project "Forum" is archived | |
23 | + And I visit dashboard projects page | |
24 | + Then I should see "Archived" | |
25 | + | |
26 | + Scenario: I archive project | |
27 | + When project "Shop" has push event | |
28 | + And I visit project "Shop" page | |
29 | + And I visit edit project "Shop" page | |
30 | + And I set project archived | |
31 | + Then I should see "Archived" | |
32 | + | |
33 | + Scenario: I unarchive project | |
34 | + When project "Shop" has push event | |
35 | + And project "Shop" is archived | |
36 | + And I visit project "Shop" page | |
37 | + And I visit edit project "Shop" page | |
38 | + And I set project unarchived | |
39 | + Then I should not see "Archived" | ... | ... |
features/steps/dashboard/dashboard_with_archived_projects.rb
0 → 100644
... | ... | @@ -0,0 +1,22 @@ |
1 | +class DashboardWithArchivedProjects < Spinach::FeatureSteps | |
2 | + include SharedAuthentication | |
3 | + include SharedPaths | |
4 | + include SharedProject | |
5 | + | |
6 | + When 'project "Forum" is archived' do | |
7 | + project = Project.find_by_name "Forum" | |
8 | + project.update_attribute(:archived, true) | |
9 | + end | |
10 | + | |
11 | + Then 'I should see "Shop" project link' do | |
12 | + page.should have_link "Shop" | |
13 | + end | |
14 | + | |
15 | + Then 'I should not see "Forum" project link' do | |
16 | + page.should_not have_link "Forum" | |
17 | + end | |
18 | + | |
19 | + Then 'I should see "Forum" project link' do | |
20 | + page.should have_link "Forum" | |
21 | + end | |
22 | +end | ... | ... |
... | ... | @@ -0,0 +1,37 @@ |
1 | +class ProjectArchived < Spinach::FeatureSteps | |
2 | + include SharedAuthentication | |
3 | + include SharedProject | |
4 | + include SharedPaths | |
5 | + | |
6 | + When 'project "Forum" is archived' do | |
7 | + project = Project.find_by_name "Forum" | |
8 | + project.update_attribute(:archived, true) | |
9 | + end | |
10 | + | |
11 | + When 'project "Shop" is archived' do | |
12 | + project = Project.find_by_name "Shop" | |
13 | + project.update_attribute(:archived, true) | |
14 | + end | |
15 | + | |
16 | + When 'I visit project "Forum" page' do | |
17 | + project = Project.find_by_name "Forum" | |
18 | + visit project_path(project) | |
19 | + end | |
20 | + | |
21 | + Then 'I should not see "Archived"' do | |
22 | + page.should_not have_content "Archived" | |
23 | + end | |
24 | + | |
25 | + Then 'I should see "Archived"' do | |
26 | + page.should have_content "Archived" | |
27 | + end | |
28 | + | |
29 | + When 'I set project archived' do | |
30 | + click_link "Archive" | |
31 | + end | |
32 | + | |
33 | + When 'I set project unarchived' do | |
34 | + click_link "Unarchive" | |
35 | + end | |
36 | + | |
37 | +end | |
0 | 38 | \ No newline at end of file | ... | ... |
features/steps/shared/project.rb
... | ... | @@ -14,6 +14,13 @@ module SharedProject |
14 | 14 | @project.team << [@user, :master] |
15 | 15 | end |
16 | 16 | |
17 | + # Create another specific project called "Forum" | |
18 | + And 'I own project "Forum"' do | |
19 | + @project = Project.find_by_name "Forum" | |
20 | + @project ||= create(:project_with_code, name: "Forum", namespace: @user.namespace, path: 'forum_project') | |
21 | + @project.team << [@user, :master] | |
22 | + end | |
23 | + | |
17 | 24 | And 'project "Shop" has push event' do |
18 | 25 | @project = Project.find_by_name("Shop") |
19 | 26 | ... | ... |
spec/models/project_spec.rb
spec/requests/api/internal_spec.rb
... | ... | @@ -103,6 +103,33 @@ describe API::API do |
103 | 103 | end |
104 | 104 | end |
105 | 105 | |
106 | + context "archived project" do | |
107 | + let(:personal_project) { create(:project, namespace: user.namespace) } | |
108 | + | |
109 | + before do | |
110 | + project.team << [user, :developer] | |
111 | + project.archive! | |
112 | + end | |
113 | + | |
114 | + context "git pull" do | |
115 | + it do | |
116 | + pull(key, project) | |
117 | + | |
118 | + response.status.should == 200 | |
119 | + response.body.should == 'true' | |
120 | + end | |
121 | + end | |
122 | + | |
123 | + context "git push" do | |
124 | + it do | |
125 | + push(key, project) | |
126 | + | |
127 | + response.status.should == 200 | |
128 | + response.body.should == 'false' | |
129 | + end | |
130 | + end | |
131 | + end | |
132 | + | |
106 | 133 | context "deploy key" do |
107 | 134 | let(:key) { create(:deploy_key) } |
108 | 135 | ... | ... |