Commit b01f8b63c2c13f8d6b9111771fb4f1422214d91c
1 parent
44209861
Exists in
master
and in
4 other branches
added NamespacedProject role. Extended project info displayed for admin. Fixed project limit
Showing
18 changed files
with
201 additions
and
131 deletions
Show diff stats
app/controllers/groups_controller.rb
| ... | ... | @@ -49,6 +49,7 @@ class GroupsController < ApplicationController |
| 49 | 49 | def people |
| 50 | 50 | @project = group.projects.find(params[:project_id]) if params[:project_id] |
| 51 | 51 | @users = @project ? @project.users : group.users |
| 52 | + @users.sort_by!(&:name) | |
| 52 | 53 | |
| 53 | 54 | if @project |
| 54 | 55 | @team_member = @project.users_projects.new | ... | ... |
app/controllers/snippets_controller.rb
| ... | ... | @@ -16,7 +16,7 @@ class SnippetsController < ProjectResourceController |
| 16 | 16 | respond_to :html |
| 17 | 17 | |
| 18 | 18 | def index |
| 19 | - @snippets = @project.snippets | |
| 19 | + @snippets = @project.snippets.fresh | |
| 20 | 20 | end |
| 21 | 21 | |
| 22 | 22 | def new |
| ... | ... | @@ -60,7 +60,7 @@ class SnippetsController < ProjectResourceController |
| 60 | 60 | redirect_to project_snippets_path(@project) |
| 61 | 61 | end |
| 62 | 62 | |
| 63 | - def raw | |
| 63 | + def raw | |
| 64 | 64 | send_data( |
| 65 | 65 | @snippet.content, |
| 66 | 66 | type: "text/plain", | ... | ... |
app/models/project.rb
| ... | ... | @@ -25,6 +25,7 @@ class Project < ActiveRecord::Base |
| 25 | 25 | include PushObserver |
| 26 | 26 | include Authority |
| 27 | 27 | include Team |
| 28 | + include NamespacedProject | |
| 28 | 29 | |
| 29 | 30 | class TransferError < StandardError; end |
| 30 | 31 | |
| ... | ... | @@ -178,7 +179,7 @@ class Project < ActiveRecord::Base |
| 178 | 179 | end |
| 179 | 180 | |
| 180 | 181 | def repo_name |
| 181 | - denied_paths = %w(gitolite-admin groups projects dashboard) | |
| 182 | + denied_paths = %w(gitolite-admin groups projects dashboard help ) | |
| 182 | 183 | |
| 183 | 184 | if denied_paths.include?(path) |
| 184 | 185 | errors.add(:path, "like #{path} is not allowed") |
| ... | ... | @@ -245,57 +246,11 @@ class Project < ActiveRecord::Base |
| 245 | 246 | gitlab_ci_service && gitlab_ci_service.active |
| 246 | 247 | end |
| 247 | 248 | |
| 248 | - def path_with_namespace | |
| 249 | - if namespace | |
| 250 | - namespace.path + '/' + path | |
| 251 | - else | |
| 252 | - path | |
| 253 | - end | |
| 254 | - end | |
| 255 | - | |
| 256 | 249 | # For compatibility with old code |
| 257 | 250 | def code |
| 258 | 251 | path |
| 259 | 252 | end |
| 260 | 253 | |
| 261 | - def transfer(new_namespace) | |
| 262 | - Project.transaction do | |
| 263 | - old_namespace = namespace | |
| 264 | - self.namespace = new_namespace | |
| 265 | - | |
| 266 | - old_dir = old_namespace.try(:path) || '' | |
| 267 | - new_dir = new_namespace.try(:path) || '' | |
| 268 | - | |
| 269 | - old_repo = if old_dir.present? | |
| 270 | - File.join(old_dir, self.path) | |
| 271 | - else | |
| 272 | - self.path | |
| 273 | - end | |
| 274 | - | |
| 275 | - if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present? | |
| 276 | - raise TransferError.new("Project with same path in target namespace already exists") | |
| 277 | - end | |
| 278 | - | |
| 279 | - Gitlab::ProjectMover.new(self, old_dir, new_dir).execute | |
| 280 | - | |
| 281 | - git_host.move_repository(old_repo, self) | |
| 282 | - | |
| 283 | - save! | |
| 284 | - end | |
| 285 | - rescue Gitlab::ProjectMover::ProjectMoveError => ex | |
| 286 | - raise TransferError.new(ex.message) | |
| 287 | - end | |
| 288 | - | |
| 289 | - def name_with_namespace | |
| 290 | - @name_with_namespace ||= begin | |
| 291 | - if namespace | |
| 292 | - namespace.human_name + " / " + name | |
| 293 | - else | |
| 294 | - name | |
| 295 | - end | |
| 296 | - end | |
| 297 | - end | |
| 298 | - | |
| 299 | 254 | def items_for entity |
| 300 | 255 | case entity |
| 301 | 256 | when 'issue' then |
| ... | ... | @@ -304,16 +259,4 @@ class Project < ActiveRecord::Base |
| 304 | 259 | merge_requests |
| 305 | 260 | end |
| 306 | 261 | end |
| 307 | - | |
| 308 | - def namespace_owner | |
| 309 | - namespace.try(:owner) | |
| 310 | - end | |
| 311 | - | |
| 312 | - def chief | |
| 313 | - if namespace | |
| 314 | - namespace_owner | |
| 315 | - else | |
| 316 | - owner | |
| 317 | - end | |
| 318 | - end | |
| 319 | 262 | end | ... | ... |
app/models/user.rb
| ... | ... | @@ -56,7 +56,6 @@ class User < ActiveRecord::Base |
| 56 | 56 | has_many :issues, foreign_key: :author_id, dependent: :destroy |
| 57 | 57 | has_many :notes, foreign_key: :author_id, dependent: :destroy |
| 58 | 58 | has_many :merge_requests, foreign_key: :author_id, dependent: :destroy |
| 59 | - has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id | |
| 60 | 59 | has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy |
| 61 | 60 | has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC" |
| 62 | 61 | has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy |
| ... | ... | @@ -124,16 +123,4 @@ class User < ActiveRecord::Base |
| 124 | 123 | self.password = self.password_confirmation = Devise.friendly_token.first(8) |
| 125 | 124 | end |
| 126 | 125 | end |
| 127 | - | |
| 128 | - def authorized_groups | |
| 129 | - @authorized_groups ||= begin | |
| 130 | - groups = Group.where(id: self.projects.pluck(:namespace_id)).all | |
| 131 | - groups = groups + self.groups | |
| 132 | - groups.uniq | |
| 133 | - end | |
| 134 | - end | |
| 135 | - | |
| 136 | - def authorized_projects | |
| 137 | - Project.authorized_for(self) | |
| 138 | - end | |
| 139 | 126 | end | ... | ... |
app/roles/account.rb
| ... | ... | @@ -105,4 +105,20 @@ module Account |
| 105 | 105 | def namespace_id |
| 106 | 106 | namespace.try :id |
| 107 | 107 | end |
| 108 | + | |
| 109 | + def authorized_groups | |
| 110 | + @authorized_groups ||= begin | |
| 111 | + groups = Group.where(id: self.projects.pluck(:namespace_id)).all | |
| 112 | + groups = groups + self.groups | |
| 113 | + groups.uniq | |
| 114 | + end | |
| 115 | + end | |
| 116 | + | |
| 117 | + def authorized_projects | |
| 118 | + Project.authorized_for(self) | |
| 119 | + end | |
| 120 | + | |
| 121 | + def my_own_projects | |
| 122 | + Project.personal(self) | |
| 123 | + end | |
| 108 | 124 | end | ... | ... |
| ... | ... | @@ -0,0 +1,59 @@ |
| 1 | +module NamespacedProject | |
| 2 | + def transfer(new_namespace) | |
| 3 | + Project.transaction do | |
| 4 | + old_namespace = namespace | |
| 5 | + self.namespace = new_namespace | |
| 6 | + | |
| 7 | + old_dir = old_namespace.try(:path) || '' | |
| 8 | + new_dir = new_namespace.try(:path) || '' | |
| 9 | + | |
| 10 | + old_repo = if old_dir.present? | |
| 11 | + File.join(old_dir, self.path) | |
| 12 | + else | |
| 13 | + self.path | |
| 14 | + end | |
| 15 | + | |
| 16 | + if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present? | |
| 17 | + raise TransferError.new("Project with same path in target namespace already exists") | |
| 18 | + end | |
| 19 | + | |
| 20 | + Gitlab::ProjectMover.new(self, old_dir, new_dir).execute | |
| 21 | + | |
| 22 | + git_host.move_repository(old_repo, self) | |
| 23 | + | |
| 24 | + save! | |
| 25 | + end | |
| 26 | + rescue Gitlab::ProjectMover::ProjectMoveError => ex | |
| 27 | + raise TransferError.new(ex.message) | |
| 28 | + end | |
| 29 | + | |
| 30 | + def name_with_namespace | |
| 31 | + @name_with_namespace ||= begin | |
| 32 | + if namespace | |
| 33 | + namespace.human_name + " / " + name | |
| 34 | + else | |
| 35 | + name | |
| 36 | + end | |
| 37 | + end | |
| 38 | + end | |
| 39 | + | |
| 40 | + def namespace_owner | |
| 41 | + namespace.try(:owner) | |
| 42 | + end | |
| 43 | + | |
| 44 | + def chief | |
| 45 | + if namespace | |
| 46 | + namespace_owner | |
| 47 | + else | |
| 48 | + owner | |
| 49 | + end | |
| 50 | + end | |
| 51 | + | |
| 52 | + def path_with_namespace | |
| 53 | + if namespace | |
| 54 | + namespace.path + '/' + path | |
| 55 | + else | |
| 56 | + path | |
| 57 | + end | |
| 58 | + end | |
| 59 | +end | ... | ... |
app/views/admin/projects/_form.html.haml
| ... | ... | @@ -19,43 +19,47 @@ |
| 19 | 19 | .input |
| 20 | 20 | = text_field_tag :ppath, @project.path_to_repo, class: "xlarge", disabled: true |
| 21 | 21 | |
| 22 | - - unless project.new_record? | |
| 22 | + - if project.repo_exists? | |
| 23 | 23 | .clearfix |
| 24 | - = f.label :namespace_id | |
| 25 | - .input | |
| 26 | - = f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'} | |
| 27 | - | |
| 28 | - %span.cred Be careful. Changing project namespace can have unintended side effects | |
| 24 | + = f.label :default_branch, "Default Branch" | |
| 25 | + .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;") | |
| 29 | 26 | |
| 30 | - - if project.repo_exists? | |
| 31 | - .clearfix | |
| 32 | - = f.label :default_branch, "Default Branch" | |
| 33 | - .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;") | |
| 27 | + %fieldset.adv_settings | |
| 28 | + %legend Features: | |
| 34 | 29 | |
| 35 | - - unless project.new_record? | |
| 36 | - %fieldset.adv_settings | |
| 37 | - %legend Features: | |
| 30 | + .clearfix | |
| 31 | + = f.label :issues_enabled, "Issues" | |
| 32 | + .input= f.check_box :issues_enabled | |
| 38 | 33 | |
| 39 | - .clearfix | |
| 40 | - = f.label :issues_enabled, "Issues" | |
| 41 | - .input= f.check_box :issues_enabled | |
| 34 | + .clearfix | |
| 35 | + = f.label :merge_requests_enabled, "Merge Requests" | |
| 36 | + .input= f.check_box :merge_requests_enabled | |
| 42 | 37 | |
| 43 | - .clearfix | |
| 44 | - = f.label :merge_requests_enabled, "Merge Requests" | |
| 45 | - .input= f.check_box :merge_requests_enabled | |
| 38 | + .clearfix | |
| 39 | + = f.label :wall_enabled, "Wall" | |
| 40 | + .input= f.check_box :wall_enabled | |
| 46 | 41 | |
| 47 | - .clearfix | |
| 48 | - = f.label :wall_enabled, "Wall" | |
| 49 | - .input= f.check_box :wall_enabled | |
| 42 | + .clearfix | |
| 43 | + = f.label :wiki_enabled, "Wiki" | |
| 44 | + .input= f.check_box :wiki_enabled | |
| 45 | + | |
| 46 | + %fieldset.features | |
| 47 | + %legend Transfer: | |
| 48 | + .control-group | |
| 49 | + = f.label :namespace_id do | |
| 50 | + %span Namespace | |
| 51 | + .controls | |
| 52 | + = f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'} | |
| 53 | + %br | |
| 54 | + %ul.prepend-top-10.cred | |
| 55 | + %li Be careful. Changing project namespace can have unintended side effects | |
| 56 | + %li You can transfer project only to namespaces you can manage | |
| 57 | + %li You will need to update your local repositories to point to the new location. | |
| 50 | 58 | |
| 51 | - .clearfix | |
| 52 | - = f.label :wiki_enabled, "Wiki" | |
| 53 | - .input= f.check_box :wiki_enabled | |
| 54 | 59 | |
| 55 | - - unless project.new_record? | |
| 56 | - .actions | |
| 57 | - = f.submit 'Save Project', class: "btn save-btn" | |
| 58 | - = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn" | |
| 60 | + .actions | |
| 61 | + = f.submit 'Save Project', class: "btn save-btn" | |
| 62 | + = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn" | |
| 59 | 63 | |
| 60 | 64 | |
| 61 | 65 | ... | ... |
app/views/admin/projects/index.html.haml
app/views/admin/projects/show.html.haml
| ... | ... | @@ -47,9 +47,12 @@ |
| 47 | 47 | %tr |
| 48 | 48 | %td |
| 49 | 49 | %b |
| 50 | - Path: | |
| 50 | + Owned by: | |
| 51 | 51 | %td |
| 52 | - %code= @project.path_to_repo | |
| 52 | + - if @project.chief | |
| 53 | + = link_to @project.chief.name, admin_user_path(@project.chief) | |
| 54 | + - else | |
| 55 | + (deleted) | |
| 53 | 56 | %tr |
| 54 | 57 | %td |
| 55 | 58 | %b |
| ... | ... | @@ -59,9 +62,46 @@ |
| 59 | 62 | %tr |
| 60 | 63 | %td |
| 61 | 64 | %b |
| 65 | + Created at: | |
| 66 | + %td | |
| 67 | + = @project.created_at.stamp("March 1, 1999") | |
| 68 | + | |
| 69 | +%table.zebra-striped | |
| 70 | + %thead | |
| 71 | + %tr | |
| 72 | + %th Repository | |
| 73 | + %th | |
| 74 | + %tr | |
| 75 | + %td | |
| 76 | + %b | |
| 77 | + FS Path: | |
| 78 | + %td | |
| 79 | + %code= @project.path_to_repo | |
| 80 | + %tr | |
| 81 | + %td | |
| 82 | + %b | |
| 83 | + Smart HTTP: | |
| 84 | + %td | |
| 85 | + = link_to @project.http_url_to_repo | |
| 86 | + %tr | |
| 87 | + %td | |
| 88 | + %b | |
| 89 | + SSH: | |
| 90 | + %td | |
| 91 | + = link_to @project.ssh_url_to_repo | |
| 92 | + %tr | |
| 93 | + %td | |
| 94 | + %b | |
| 95 | + Last commit at: | |
| 96 | + %td | |
| 97 | + = last_commit(@project) | |
| 98 | + %tr | |
| 99 | + %td | |
| 100 | + %b | |
| 62 | 101 | Post Receive File: |
| 63 | 102 | %td |
| 64 | 103 | = check_box_tag :post_receive_file, 1, @project.has_post_receive_file?, disabled: true |
| 104 | + | |
| 65 | 105 | %br |
| 66 | 106 | %h5 |
| 67 | 107 | Team | ... | ... |
app/views/admin/users/index.html.haml
| 1 | 1 | %h3.page_title |
| 2 | - Users | |
| 2 | + Users (#{@admin_users.count}) | |
| 3 | 3 | = link_to 'New User', new_admin_user_path, class: "btn small right" |
| 4 | 4 | %br |
| 5 | 5 | |
| ... | ... | @@ -40,10 +40,13 @@ |
| 40 | 40 | %td= user.users_projects.count |
| 41 | 41 | %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small" |
| 42 | 42 | %td.bgred |
| 43 | - - if user.blocked | |
| 44 | - = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success" | |
| 43 | + - if user == current_user | |
| 44 | + %span.cred It's you! | |
| 45 | 45 | - else |
| 46 | - = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger" | |
| 47 | - = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger" | |
| 46 | + - if user.blocked | |
| 47 | + = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success" | |
| 48 | + - else | |
| 49 | + = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger" | |
| 50 | + = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger" | |
| 48 | 51 | |
| 49 | 52 | = paginate @admin_users, theme: "admin" | ... | ... |
app/views/admin/users/show.html.haml
app/views/hooks/index.html.haml
| ... | ... | @@ -22,22 +22,21 @@ |
| 22 | 22 | %hr |
| 23 | 23 | |
| 24 | 24 | -if @hooks.any? |
| 25 | - %h3 | |
| 26 | - Hooks | |
| 27 | - %small (#{@hooks.count}) | |
| 25 | + %h3.page_title | |
| 26 | + Hooks (#{@hooks.count}) | |
| 28 | 27 | %br |
| 29 | 28 | %table |
| 30 | 29 | %thead |
| 31 | 30 | %tr |
| 32 | 31 | %th URL |
| 33 | - %th Method | |
| 34 | 32 | %th |
| 35 | 33 | - @hooks.each do |hook| |
| 36 | 34 | %tr |
| 37 | 35 | %td |
| 36 | + %span.badge.badge-info POST | |
| 38 | 37 | = link_to project_hook_path(@project, hook) do |
| 39 | 38 | %strong= hook.url |
| 40 | - = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small right" | |
| 41 | - %td POST | |
| 42 | 39 | %td |
| 43 | - = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small right" | |
| 40 | + .right | |
| 41 | + = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn small grouped" | |
| 42 | + = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "danger btn small grouped" | ... | ... |
app/views/projects/create.js.haml
app/views/projects/files.html.haml
| ... | ... | @@ -17,7 +17,6 @@ |
| 17 | 17 | = time_ago_in_words(note.created_at) |
| 18 | 18 | ago |
| 19 | 19 | - else |
| 20 | - .alert-message.block-message | |
| 21 | - %span All files attached to project wall, issues etc will be displayed here | |
| 20 | + %p.slead All files attached to project wall, issues etc will be displayed here | |
| 22 | 21 | |
| 23 | 22 | ... | ... |
app/views/snippets/_snippet.html.haml
| 1 | 1 | %tr |
| 2 | 2 | %td |
| 3 | + = image_tag gravatar_icon(snippet.author_email), class: "avatar s24" | |
| 3 | 4 | %a{href: project_snippet_path(snippet.project, snippet)} |
| 4 | 5 | %strong= truncate(snippet.title, length: 60) |
| 5 | 6 | %td |
| 6 | 7 | = snippet.file_name |
| 7 | 8 | %td |
| 8 | 9 | %span.cgray |
| 9 | - - if snippet.expires_at | |
| 10 | + - if snippet.expires_at | |
| 10 | 11 | = snippet.expires_at.to_date.to_s(:short) |
| 11 | 12 | - else |
| 12 | 13 | Never | ... | ... |
app/views/snippets/index.html.haml
| 1 | 1 | = render "projects/project_head" |
| 2 | 2 | |
| 3 | -- if can? current_user, :write_snippet, @project | |
| 4 | - .alert-message.block-message | |
| 3 | +%h3.page_title | |
| 4 | + Snippets | |
| 5 | + %small share code pastes with others out of git repository | |
| 6 | + | |
| 7 | + - if can? current_user, :write_snippet, @project | |
| 5 | 8 | = link_to new_project_snippet_path(@project), class: "btn small add_new right", title: "New Snippet" do |
| 6 | 9 | Add new snippet |
| 7 | - Share code pastes with others if it can't be in a git repository | |
| 8 | - %br | |
| 9 | - To add new snippet - click on button. | |
| 10 | - | |
| 10 | +%br | |
| 11 | 11 | %table |
| 12 | 12 | %thead |
| 13 | 13 | %tr |
| 14 | 14 | %th Title |
| 15 | 15 | %th File Name |
| 16 | 16 | %th Expires At |
| 17 | - = render @snippets.fresh | |
| 18 | - - if @snippets.fresh.empty? | |
| 17 | + = render @snippets | |
| 18 | + - if @snippets.empty? | |
| 19 | 19 | %tr |
| 20 | 20 | %td{colspan: 3} |
| 21 | 21 | %h3.nothing_here_message Nothing here. | ... | ... |
app/views/team_members/_team.html.haml
spec/models/user_spec.rb
| ... | ... | @@ -41,7 +41,6 @@ describe User do |
| 41 | 41 | it { should have_many(:users_projects).dependent(:destroy) } |
| 42 | 42 | it { should have_many(:projects) } |
| 43 | 43 | it { should have_many(:groups) } |
| 44 | - it { should have_many(:my_own_projects).class_name('Project') } | |
| 45 | 44 | it { should have_many(:keys).dependent(:destroy) } |
| 46 | 45 | it { should have_many(:events).class_name('Event').dependent(:destroy) } |
| 47 | 46 | it { should have_many(:recent_events).class_name('Event') } |
| ... | ... | @@ -116,4 +115,16 @@ describe User do |
| 116 | 115 | user.authentication_token.should_not be_blank |
| 117 | 116 | end |
| 118 | 117 | end |
| 118 | + | |
| 119 | + describe 'projects and namespaces' do | |
| 120 | + before do | |
| 121 | + ActiveRecord::Base.observers.enable(:user_observer) | |
| 122 | + @user = create :user | |
| 123 | + @project = create :project, namespace: @user.namespace | |
| 124 | + end | |
| 125 | + | |
| 126 | + it { @user.authorized_projects.should include(@project) } | |
| 127 | + it { @user.my_own_projects.should include(@project) } | |
| 128 | + it { @user.several_namespaces?.should be_false } | |
| 129 | + end | |
| 119 | 130 | end | ... | ... |