diff --git a/CHANGELOG.md b/CHANGELOG.md index 37383ea1b72b21ed5e040e75e71dbd5c6ec6a488..44ddc1e03a23d7e4fe3f7fb9d65b69992c56aad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps) - Remove redundant mixins (ClemMakesApps) - Added 'Download' button to the Snippets page (Justin DiPierro) + - Add visibility level to project repository - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska) - Fix that manual jobs would no longer block jobs in the next stage. !6604 diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 3cf415058141cc927a257475e336b1bd4c2dce34..478e82aa14d887075358b751a81c2b54accc2eec 100644 --- a/app/assets/javascripts/project_new.js +++ b/app/assets/javascripts/project_new.js @@ -4,9 +4,8 @@ this.ProjectNew = (function() { function ProjectNew() { this.toggleSettings = bind(this.toggleSettings, this); - this.$selects = $('.features select').filter(function () { - return $(this).data('field'); - }); + this.$selects = $('.features select'); + this.$repoSelects = this.$selects.filter('.js-repo-select'); $('.project-edit-container').on('ajax:before', (function(_this) { return function() { @@ -16,6 +15,7 @@ })(this)); this.toggleSettings(); this.toggleSettingsOnclick(); + this.toggleRepoVisibility(); } ProjectNew.prototype.toggleSettings = function() { @@ -43,6 +43,38 @@ } }; + ProjectNew.prototype.toggleRepoVisibility = function () { + var $repoAccessLevel = $('.js-repo-access-level select'); + + this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']") + .nextAll() + .hide(); + + $repoAccessLevel.off('change') + .on('change', function () { + var selectedVal = parseInt($repoAccessLevel.val()); + + this.$repoSelects.each(function () { + var $this = $(this), + repoSelectVal = parseInt($this.val()); + + $this.find('option').show(); + + if (selectedVal < repoSelectVal) { + $this.val(selectedVal); + } + + $this.find("option[value='" + selectedVal + "']").nextAll().hide(); + }); + + if (selectedVal) { + this.$repoSelects.removeClass('disabled'); + } else { + this.$repoSelects.addClass('disabled'); + } + }.bind(this)); + }; + return ProjectNew; })(); diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d30f02340b9b945f5bca413a5e8cd31a8a879860..1062d7effb0b19b3c044cc5bfee05bafd4ed1582 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -761,62 +761,6 @@ pre.light-well { .dropdown-menu { width: 300px; } - - &.from .compare-dropdown-toggle { - width: 237px; - } - - &.to .compare-dropdown-toggle { - width: 254px; - } - - .dropdown-toggle-text { - display: block; - height: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; - } -} - -.compare-ellipsis { - display: inline; -} - -@media (max-width: $screen-xs-max) { - .compare-form-group { - .input-group { - width: 100%; - - & > .compare-dropdown-toggle { - width: 100%; - } - } - - .dropdown-menu { - width: 100%; - } - } - - .compare-switch-container { - text-align: center; - padding: 0 0 $gl-padding; - - .commits-compare-switch { - float: none; - } - } - - .compare-ellipsis { - display: block; - text-align: center; - padding: 0 0 $gl-padding; - } - - .commits-compare-btn { - width: 100%; - } } .clearable-input { @@ -855,3 +799,30 @@ pre.light-well { border-bottom-right-radius: 0; } } + +.project-home-empty { + border-top: 0; + + .container-fluid { + background: none; + } + + p { + margin-left: auto; + margin-right: auto; + max-width: 650px; + } +} + +.project-feature-nested { + @media (min-width: $screen-sm-min) { + padding-left: 45px; + } +} + +.project-repo-select { + &.disabled { + opacity: 0.5; + pointer-events: none; + } +} diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 629162701726045ed67fac7443cd5a593d3cc6dc..76b730198d4083b87fd0c3828e69345a5c082794 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,4 +1,5 @@ class ProjectsController < Projects::ApplicationController + include IssuableCollections include ExtractsPath before_action :authenticate_user!, except: [:show, :activity, :refs] @@ -103,16 +104,7 @@ class ProjectsController < Projects::ApplicationController respond_to do |format| format.html do @notification_setting = current_user.notification_settings_for(@project) if current_user - - if @project.repository_exists? - if @project.empty_repo? - render 'projects/empty' - else - render :show - end - else - render 'projects/no_repo' - end + render_landing_page end format.atom do @@ -285,6 +277,26 @@ class ProjectsController < Projects::ApplicationController private + # Render project landing depending of which features are available + # So if page is not availble in the list it renders the next page + # + # pages list order: repository readme, wiki home, issues list, customize workflow + def render_landing_page + if @project.feature_available?(:repository, current_user) + return render 'projects/no_repo' unless @project.repository_exists? + render 'projects/empty' if @project.empty_repo? + else + if @project.wiki_enabled? + @wiki_home = @project.wiki.find_page('home', params[:version_id]) + elsif @project.feature_available?(:issues, current_user) + @issues = issues_collection + @issues = @issues.page(params[:page]) + end + + render :show + end + end + def determine_layout if [:new, :create].include?(action_name.to_sym) 'application' @@ -308,7 +320,8 @@ class ProjectsController < Projects::ApplicationController project_feature_attributes: [ :issues_access_level, :builds_access_level, - :wiki_access_level, :merge_requests_access_level, :snippets_access_level + :wiki_access_level, :merge_requests_access_level, + :snippets_access_level, :repository_access_level ] } diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index c3832cf5d65e0dbafe336b3d0fd97643cbb6ad11..a46f2c6e17d8be0e2f7db1876814b4851af9bf8b 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -50,6 +50,20 @@ module PreferencesHelper end def default_project_view - current_user ? current_user.project_view : 'readme' + return 'readme' unless current_user + + user_view = current_user.project_view + + if @project.feature_available?(:repository, current_user) + user_view + elsif user_view == "activity" + "activity" + elsif @project.wiki_enabled? + "wiki" + elsif @project.feature_available?(:issues, current_user) + "projects/issues/issues" + else + "customize_workflow" + end end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index e667c9e4e2e98cff089a2e32f786b55ee912a469..d26b4018be66a7d0445e48ddfef9989fb575f787 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -134,16 +134,35 @@ module ProjectsHelper options = project_feature_options if @project.private? + level = @project.project_feature.send(field) options.delete('Everyone with access') - highest_available_option = options.values.max if @project.project_feature.send(field) == ProjectFeature::ENABLED + highest_available_option = options.values.max if level == ProjectFeature::ENABLED end options = options_for_select(options, selected: highest_available_option || @project.project_feature.public_send(field)) - content_tag(:select, options, name: "project[project_feature_attributes][#{field}]", id: "project_project_feature_attributes_#{field}", class: "pull-right form-control", data: { field: field }).html_safe + + content_tag( + :select, + options, + name: "project[project_feature_attributes][#{field}]", + id: "project_project_feature_attributes_#{field}", + class: "pull-right form-control #{repo_children_classes(field)}", + data: { field: field } + ).html_safe end private + def repo_children_classes(field) + needs_repo_check = [:merge_requests_access_level, :builds_access_level] + return unless needs_repo_check.include?(field) + + classes = "project-repo-select js-repo-select" + classes << " disabled" unless @project.feature_available?(:repository, current_user) + + classes + end + def get_project_nav_tabs(project, current_user) nav_tabs = [:home] @@ -155,12 +174,8 @@ module ProjectsHelper nav_tabs << :merge_requests end - if can?(current_user, :read_pipeline, project) - nav_tabs << :pipelines - end - if can?(current_user, :read_build, project) - nav_tabs << :builds + nav_tabs << :pipelines end if Gitlab.config.registry.enabled && can?(current_user, :read_container_image, project) @@ -435,4 +450,8 @@ module ProjectsHelper 'Everyone with access' => ProjectFeature::ENABLED } end + + def project_child_container_class(view_path) + view_path == "projects/issues/issues" ? "prepend-top-default" : "project-show-#{view_path}" + end end diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index 530f7d5a30e6ad182e1aa1a0f68dfafc40c40c99..b37ce1d3cf6d60e9b868a47e5c604889d4f661ab 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -13,23 +13,26 @@ class ProjectFeature < ActiveRecord::Base # Enabled: enabled for everyone able to access the project # - # Permision levels + # Permission levels DISABLED = 0 PRIVATE = 10 ENABLED = 20 - FEATURES = %i(issues merge_requests wiki snippets builds) + FEATURES = %i(issues merge_requests wiki snippets builds repository) # Default scopes force us to unscope here since a service may need to check # permissions for a project in pending_delete # http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to belongs_to :project, -> { unscope(where: :pending_delete) } + validate :repository_children_level + default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :issues_access_level, value: ENABLED, allows_nil: false default_value_for :merge_requests_access_level, value: ENABLED, allows_nil: false default_value_for :snippets_access_level, value: ENABLED, allows_nil: false default_value_for :wiki_access_level, value: ENABLED, allows_nil: false + default_value_for :repository_access_level, value: ENABLED, allows_nil: false def feature_available?(feature, user) raise ArgumentError, 'invalid project feature' unless FEATURES.include?(feature) @@ -57,6 +60,18 @@ class ProjectFeature < ActiveRecord::Base private + # Validates builds and merge requests access level + # which cannot be higher than repository access level + def repository_children_level + validator = lambda do |field| + level = public_send(field) || ProjectFeature::ENABLED + not_allowed = level > repository_access_level + self.errors.add(field, "cannot have higher visibility level than repository access level") if not_allowed + end + + %i(merge_requests_access_level builds_access_level).each(&validator) + end + def get_permission(user, level) case level when DISABLED diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index be4721d7a51cac0d025feb6e62011a71f9aea7c8..fbb3d4507d6e4b16af38100e3b05bec539ec0ef7 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -162,11 +162,13 @@ class ProjectPolicy < BasePolicy end def disabled_features! + repository_enabled = project.feature_available?(:repository, user) + unless project.feature_available?(:issues, user) cannot!(*named_abilities(:issue)) end - unless project.feature_available?(:merge_requests, user) + unless project.feature_available?(:merge_requests, user) && repository_enabled cannot!(*named_abilities(:merge_request)) end @@ -183,13 +185,21 @@ class ProjectPolicy < BasePolicy cannot!(*named_abilities(:wiki)) end - unless project.feature_available?(:builds, user) + unless project.feature_available?(:builds, user) && repository_enabled cannot!(*named_abilities(:build)) cannot!(*named_abilities(:pipeline)) cannot!(*named_abilities(:environment)) cannot!(*named_abilities(:deployment)) end + unless repository_enabled + cannot! :push_code + cannot! :push_code_to_protected_branches + cannot! :download_code + cannot! :fork_project + cannot! :read_commit_status + end + unless project.container_registry_enabled cannot!(*named_abilities(:container_image)) end diff --git a/app/views/projects/_customize_workflow.html.haml b/app/views/projects/_customize_workflow.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..d2c1e943db11911cadf1b525e8971e652b4e5273 --- /dev/null +++ b/app/views/projects/_customize_workflow.html.haml @@ -0,0 +1,8 @@ +.row-content-block.project-home-empty + %div.text-center{ class: container_class } + %h4 + Customize your workflow! + %p + Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and builds, GitLab can help manage your workflow from idea to production! + - if can?(current_user, :admin_project, @project) + = link_to "Get started", edit_project_path(@project), class: "btn btn-success" diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 5590198a20e8fecb38dc14f80e4de76e209986d8..d3987fc9c4f460fa446a87f7017fd67e3c65d7a9 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -22,5 +22,6 @@ = render 'projects/buttons/star' = render 'projects/buttons/fork' - .project-clone-holder - = render "shared/clone_panel" + - if @project.feature_available?(:repository, current_user) + .project-clone-holder + = render "shared/clone_panel" diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..f00422dd7c08846400f874156de7bc9c5765045b --- /dev/null +++ b/app/views/projects/_wiki.html.haml @@ -0,0 +1,19 @@ +- if @wiki_home.present? + %div{ class: container_class } + .wiki-holder.prepend-top-default.append-bottom-default + .wiki + = preserve do + = render_wiki_content(@wiki_home) +- else + - can_create_wiki = can?(current_user, :create_wiki, @project) + .project-home-empty{ class: [('row-content-block' if can_create_wiki), ('content-block' unless can_create_wiki)] } + %div.text-center{ class: container_class } + %h4 + This project does not have a wiki homepage yet + - if can_create_wiki + %p + Add a homepage to your wiki that contains information about your project + %p + We recommend you + = link_to "add a homepage", namespace_project_wiki_path(@project.namespace, @project, :home) + to your project's wiki and GitLab will show it here instead of this message. diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index c8f84b96cb77f1d9e42e23c13ead6f9bd532d850..fb776e3a3e706b42f3e97690e31f6db47261cb89 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -50,24 +50,39 @@ .form_group.prepend-top-20 .row .col-md-9 - = feature_fields.label :issues_access_level, "Issues", class: 'label-light' - %span.help-block Lightweight issue tracking system for this project - .col-md-3 - = project_feature_access_select(:issues_access_level) + = feature_fields.label :repository_access_level, "Repository", class: 'label-light' + %span.help-block Push files to be stored in this project + .col-md-3.js-repo-access-level + = project_feature_access_select(:repository_access_level) + + .col-sm-12 + .row + .col-md-9.project-feature-nested + = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' + %span.help-block Submit changes to be merged upstream + .col-md-3 + = project_feature_access_select(:merge_requests_access_level) + + .row + .col-md-9.project-feature-nested + = feature_fields.label :builds_access_level, "Builds", class: 'label-light' + %span.help-block Submit, test and deploy your changes before merge + .col-md-3 + = project_feature_access_select(:builds_access_level) .row .col-md-9 - = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' - %span.help-block Submit changes to be merged upstream + = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' + %span.help-block Share code pastes with others out of Git repository .col-md-3 - = project_feature_access_select(:merge_requests_access_level) + = project_feature_access_select(:snippets_access_level) .row .col-md-9 - = feature_fields.label :builds_access_level, "Builds", class: 'label-light' - %span.help-block Submit Test and deploy your changes before merge + = feature_fields.label :issues_access_level, "Issues", class: 'label-light' + %span.help-block Lightweight issue tracking system for this project .col-md-3 - = project_feature_access_select(:builds_access_level) + = project_feature_access_select(:issues_access_level) .row .col-md-9 @@ -76,24 +91,17 @@ .col-md-3 = project_feature_access_select(:wiki_access_level) - .row - .col-md-9 - = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' - %span.help-block Share code pastes with others out of Git repository - .col-md-3 - = project_feature_access_select(:snippets_access_level) - - if Gitlab.config.lfs.enabled && current_user.admin? - .row - .col-md-9 - = f.label :lfs_enabled, 'LFS', class: 'label-light' - %span.help-block + .checkbox + = f.label :lfs_enabled do + = f.check_box :lfs_enabled + %strong LFS + %br + %span.descr Git Large File Storage = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') - .col-md-3 - = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control', data: { field: 'lfs_enabled' } - - if Gitlab.config.registry.enabled + - if Gitlab.config.lfs.enabled && current_user.admin? .form-group .checkbox = f.label :container_registry_enabled do diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index a2c31c0b4c5003778badce476ad50291c594055d..a4b752ad86ddcdf9a86d05b8fb3b322ae5698983 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,5 +1,5 @@ %ul.content-list.issues-list.issuable-list - = render @issues + = render partial: "projects/issues/issue", collection: @issues - if @issues.blank? %li .nothing-here-block No issues to show diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index ea4deb6cb28eba506e7ef53bd0cca95b188d53bc..ba16c641462aaebb5b9019c6fc0c0cbce9f8d816 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -12,72 +12,74 @@ = render 'projects/last_push' = render "home_panel" -%nav.project-stats{ class: (container_class) } - %ul.nav - %li - = link_to project_files_path(@project) do - Files (#{repository_size}) - %li - = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do - #{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)}) - %li - = link_to namespace_project_branches_path(@project.namespace, @project) do - #{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) - %li - = link_to namespace_project_tags_path(@project.namespace, @project) do - #{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) - - - if default_project_view != 'readme' && @repository.readme +- if @project.feature_available?(:repository, current_user) + %nav.project-stats{ class: container_class } + %ul.nav %li - = link_to 'Readme', readme_path(@project) - - - if @repository.changelog + = link_to project_files_path(@project) do + Files (#{repository_size}) %li - = link_to 'Changelog', changelog_path(@project) - - - if @repository.license_blob + = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do + #{'Commit'.pluralize(@project.commit_count)} (#{number_with_delimiter(@project.commit_count)}) %li - = link_to license_short_name(@project), license_path(@project) - - - if @repository.contribution_guide + = link_to namespace_project_branches_path(@project.namespace, @project) do + #{'Branch'.pluralize(@repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)}) %li - = link_to 'Contribution guide', contribution_guide_path(@project) + = link_to namespace_project_tags_path(@project.namespace, @project) do + #{'Tag'.pluralize(@repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)}) - - if @repository.gitlab_ci_yml - %li - = link_to 'CI configuration', ci_configuration_path(@project) - - - if current_user && can_push_branch?(@project, @project.default_branch) - - unless @repository.changelog - %li.missing - = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do - Add Changelog - - unless @repository.license_blob - %li.missing - = link_to add_special_file_path(@project, file_name: 'LICENSE') do - Add License - - unless @repository.contribution_guide - %li.missing - = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do - Add Contribution guide - - unless @repository.gitlab_ci_yml - %li.missing - = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do - Set Up CI - - %li.project-repo-buttons-right - .project-repo-buttons.project-right-buttons - - if current_user - = render 'shared/members/access_request_buttons', source: @project - = render "projects/buttons/koding" - - = render 'projects/buttons/download', project: @project, ref: @ref - = render 'projects/buttons/dropdown' - - = render 'shared/notifications/button', notification_setting: @notification_setting -- if @repository.commit - .project-last-commit{ class: container_class } - = render 'projects/last_commit', commit: @repository.commit, project: @project + - if default_project_view != 'readme' && @repository.readme + %li + = link_to 'Readme', readme_path(@project) + + - if @repository.changelog + %li + = link_to 'Changelog', changelog_path(@project) + + - if @repository.license_blob + %li + = link_to license_short_name(@project), license_path(@project) + + - if @repository.contribution_guide + %li + = link_to 'Contribution guide', contribution_guide_path(@project) + + - if @repository.gitlab_ci_yml + %li + = link_to 'CI configuration', ci_configuration_path(@project) + + - if current_user && can_push_branch?(@project, @project.default_branch) + - unless @repository.changelog + %li.missing + = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do + Add Changelog + - unless @repository.license_blob + %li.missing + = link_to add_special_file_path(@project, file_name: 'LICENSE') do + Add License + - unless @repository.contribution_guide + %li.missing + = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do + Add Contribution guide + - unless @repository.gitlab_ci_yml + %li.missing + = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do + Set Up CI + + %li.project-repo-buttons-right + .project-repo-buttons.project-right-buttons + - if current_user + = render 'shared/members/access_request_buttons', source: @project + = render "projects/buttons/koding" + + .btn-group.project-repo-btn-group + = render 'projects/buttons/download', project: @project, ref: @ref + = render 'projects/buttons/dropdown' + + = render 'shared/notifications/button', notification_setting: @notification_setting + - if @repository.commit + .project-last-commit{ class: container_class } + = render 'projects/last_commit', commit: @repository.commit, project: @project %div{ class: container_class } - if @project.archived? @@ -86,5 +88,7 @@ = icon("exclamation-triangle fw") Archived project! Repository is read-only - %div{class: "project-show-#{default_project_view}"} - = render default_project_view + - view_path = default_project_view + + %div{ class: project_child_container_class(view_path) } + = render view_path diff --git a/db/migrate/20161012180455_add_repository_access_level_to_project_feature.rb b/db/migrate/20161012180455_add_repository_access_level_to_project_feature.rb new file mode 100644 index 0000000000000000000000000000000000000000..7b33da3ea111e7938fd23f0a692ff23e91570e36 --- /dev/null +++ b/db/migrate/20161012180455_add_repository_access_level_to_project_feature.rb @@ -0,0 +1,14 @@ +class AddRepositoryAccessLevelToProjectFeature < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + DOWNTIME = false + + def up + add_column_with_default(:project_features, :repository_access_level, :integer, default: ProjectFeature::ENABLED) + end + + def down + remove_column :project_features, :repository_access_level + end +end diff --git a/db/schema.rb b/db/schema.rb index a362fd8f2288256214cae52a6800fc794fdfb0d2..51ac0fbaeb519a9967ae659909b8248d67dcf32b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161007133303) do +ActiveRecord::Schema.define(version: 20161012180455) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -830,6 +830,7 @@ ActiveRecord::Schema.define(version: 20161007133303) do t.integer "builds_access_level" t.datetime "created_at" t.datetime "updated_at" + t.integer "repository_access_level", default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index da0fdce39dbf59b67694be95c81eced33fb7c8c0..8eefa284ba06762032c4b4fb9afacb4088b7ef83 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -41,6 +41,46 @@ describe ProjectsController do end end end + + describe "when project repository is disabled" do + render_views + + before do + project.team << [user, :developer] + project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED) + end + + it 'shows wiki homepage' do + get :show, namespace_id: project.namespace.path, id: project.path + + expect(response).to render_template('projects/_wiki') + end + + it 'shows issues list page if wiki is disabled' do + project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) + + get :show, namespace_id: project.namespace.path, id: project.path + + expect(response).to render_template('projects/issues/_issues') + end + + it 'shows customize workflow page if wiki and issues are disabled' do + project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) + + get :show, namespace_id: project.namespace.path, id: project.path + + expect(response).to render_template("projects/_customize_workflow") + end + + it 'shows activity if enabled by user' do + user.update_attribute(:project_view, 'activity') + + get :show, namespace_id: project.namespace.path, id: project.path + + expect(response).to render_template("projects/_activity") + end + end end context "project with empty repo" do diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 719ef17f57ebf0d598f531f0c335deb5c3903a92..4065e2defbcce1e00597cd3e146f28b628c49c6e 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -45,6 +45,7 @@ FactoryGirl.define do snippets_access_level ProjectFeature::ENABLED issues_access_level ProjectFeature::ENABLED merge_requests_access_level ProjectFeature::ENABLED + repository_access_level ProjectFeature::ENABLED end after(:create) do |project, evaluator| @@ -55,6 +56,7 @@ FactoryGirl.define do snippets_access_level: evaluator.snippets_access_level, issues_access_level: evaluator.issues_access_level, merge_requests_access_level: evaluator.merge_requests_access_level, + repository_access_level: evaluator.repository_access_level ) end end diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index 9b487e350f2c7763ef5112f43bd3eb26b9b18e1e..1d4484a9edda3e9bd7181d0bdfd182449e7f5924 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -2,8 +2,11 @@ require 'spec_helper' include WaitForAjax describe 'Edit Project Settings', feature: true do + include WaitForAjax + let(:member) { create(:user) } let!(:project) { create(:project, :public, path: 'gitlab', name: 'sample') } + let!(:issue) { create(:issue, project: project) } let(:non_member) { create(:user) } describe 'project features visibility selectors', js: true do @@ -119,4 +122,31 @@ describe 'Edit Project Settings', feature: true do end end end + + describe 'repository visibility', js: true do + before do + project.team << [member, :master] + login_as(member) + visit edit_namespace_project_path(project.namespace, project) + end + + it "disables repository related features" do + select "Disabled", from: "project_project_feature_attributes_repository_access_level" + + expect(find(".edit-project")).to have_selector("select.disabled", count: 2) + end + + it "shows empty features project homepage" do + select "Disabled", from: "project_project_feature_attributes_repository_access_level" + select "Disabled", from: "project_project_feature_attributes_issues_access_level" + select "Disabled", from: "project_project_feature_attributes_wiki_access_level" + + click_button "Save changes" + wait_for_ajax + + visit namespace_project_path(project.namespace, project) + + expect(page).to have_content "Customize your workflow!" + end + end end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 8bccd313d6c47482a0049fb68f8d6ae24151a464..8c8be66df9f604b5f75400b5647cc639872ac36f 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -307,6 +307,7 @@ ProjectFeature: - wiki_access_level - snippets_access_level - builds_access_level +- repository_access_level - created_at - updated_at ProtectedBranch::MergeAccessLevel: diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb index 8d554a01be557bc3d9fe4afae26d7e5dc0ab33d2..a55d43ab2f9ac819a62e900b145385881d368d1d 100644 --- a/spec/models/project_feature_spec.rb +++ b/spec/models/project_feature_spec.rb @@ -5,7 +5,7 @@ describe ProjectFeature do let(:user) { create(:user) } describe '#feature_available?' do - let(:features) { %w(issues wiki builds merge_requests snippets) } + let(:features) { %w(issues wiki builds merge_requests snippets repository) } context 'when features are disabled' do it "returns false" do @@ -64,6 +64,27 @@ describe ProjectFeature do end end + context 'repository related features' do + before do + project.project_feature.update_attributes( + merge_requests_access_level: ProjectFeature::DISABLED, + builds_access_level: ProjectFeature::DISABLED, + repository_access_level: ProjectFeature::PRIVATE + ) + end + + it "does not allow repository related features have higher level" do + features = %w(builds merge_requests) + project_feature = project.project_feature + + features.each do |feature| + field = "#{feature}_access_level".to_sym + project_feature.update_attribute(field, ProjectFeature::ENABLED) + expect(project_feature.valid?).to be_falsy + end + end + end + describe '#*_enabled?' do let(:features) { %w(wiki builds merge_requests) }