diff options
author | Felipe Artur <felipefac@gmail.com> | 2016-09-16 22:15:39 +0300 |
---|---|---|
committer | Felipe Artur <felipefac@gmail.com> | 2016-10-17 23:12:18 +0300 |
commit | da07c2e4d3d382c05ec287ee60f639b870074fe7 (patch) | |
tree | f6ffa7fd29fe177d4d78c0e043a1fdcff5b6eba4 /app | |
parent | c49e152605ad1fe77bea6414c383cf70669ca110 (diff) |
Add visibility level to project repository
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/project_new.js | 38 | ||||
-rw-r--r-- | app/assets/stylesheets/pages/projects.scss | 83 | ||||
-rw-r--r-- | app/controllers/projects_controller.rb | 35 | ||||
-rw-r--r-- | app/helpers/preferences_helper.rb | 16 | ||||
-rw-r--r-- | app/helpers/projects_helper.rb | 33 | ||||
-rw-r--r-- | app/models/project_feature.rb | 19 | ||||
-rw-r--r-- | app/policies/project_policy.rb | 14 | ||||
-rw-r--r-- | app/views/projects/_customize_workflow.html.haml | 8 | ||||
-rw-r--r-- | app/views/projects/_home_panel.html.haml | 5 | ||||
-rw-r--r-- | app/views/projects/_wiki.html.haml | 19 | ||||
-rw-r--r-- | app/views/projects/edit.html.haml | 56 | ||||
-rw-r--r-- | app/views/projects/issues/_issues.html.haml | 2 | ||||
-rw-r--r-- | app/views/projects/show.html.haml | 130 |
13 files changed, 286 insertions, 172 deletions
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js index 3cf41505814..478e82aa14d 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 d30f02340b9..1062d7effb0 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 62916270172..76b730198d4 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 c3832cf5d65..a46f2c6e17d 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 e667c9e4e2e..d26b4018be6 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 530f7d5a30e..b37ce1d3cf6 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 be4721d7a51..fbb3d4507d6 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 00000000000..d2c1e943db1 --- /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 5590198a20e..d3987fc9c4f 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 00000000000..f00422dd7c0 --- /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 c8f84b96cb7..fb776e3a3e7 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 a2c31c0b4c5..a4b752ad86d 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 ea4deb6cb28..ba16c641462 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 |