Commit 3ffd40ae authored by Marin Jankovski's avatar Marin Jankovski

Merge branch 'ce-to-ee-2018-02-23' into 'master'

CE upstream - 2018-02-23 12:10 UTC

See merge request gitlab-org/gitlab-ee!4692
parents abd17639 f382321f
...@@ -418,6 +418,16 @@ export const convertObjectPropsToCamelCase = (obj = {}) => { ...@@ -418,6 +418,16 @@ export const convertObjectPropsToCamelCase = (obj = {}) => {
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`; export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
// Click a .js-select-on-focus field, select the contents
// Prevent a mouseup event from deselecting the input
$(selector).on('focusin', function selectOnFocusCallback() {
$(this).select().one('mouseup', (e) => {
e.preventDefault();
});
});
};
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.utils = { window.gl.utils = {
...(window.gl.utils || {}), ...(window.gl.utils || {}),
......
...@@ -10,7 +10,7 @@ window.jQuery = jQuery; ...@@ -10,7 +10,7 @@ window.jQuery = jQuery;
window.$ = jQuery; window.$ = jQuery;
// lib/utils // lib/utils
import { handleLocationHash } from './lib/utils/common_utils'; import { handleLocationHash, addSelectOnFocusBehaviour } from './lib/utils/common_utils';
import { localTimeAgo } from './lib/utils/datetime_utility'; import { localTimeAgo } from './lib/utils/datetime_utility';
import { getLocationHash, visitUrl } from './lib/utils/url_utility'; import { getLocationHash, visitUrl } from './lib/utils/url_utility';
...@@ -107,13 +107,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -107,13 +107,7 @@ document.addEventListener('DOMContentLoaded', () => {
return true; return true;
}); });
// Click a .js-select-on-focus field, select the contents addSelectOnFocusBehaviour('.js-select-on-focus');
// Prevent a mouseup event from deselecting the input
$('.js-select-on-focus').on('focusin', function selectOnFocusCallback() {
$(this).select().one('mouseup', (e) => {
e.preventDefault();
});
});
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() { $('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
$(this).tooltip('destroy') $(this).tooltip('destroy')
......
import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
let hasUserDefinedProjectPath = false; let hasUserDefinedProjectPath = false;
const deriveProjectPathFromUrl = ($projectImportUrl) => { const deriveProjectPathFromUrl = ($projectImportUrl) => {
...@@ -36,6 +38,7 @@ const bindEvents = () => { ...@@ -36,6 +38,7 @@ const bindEvents = () => {
const $changeTemplateBtn = $('.change-template'); const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon svg'); const $selectedIcon = $('.selected-icon svg');
const $templateProjectNameInput = $('#template-project-name #project_path'); const $templateProjectNameInput = $('#template-project-name #project_path');
const $pushNewProjectTipTrigger = $('.push-new-project-tip');
if ($newProjectForm.length !== 1) { if ($newProjectForm.length !== 1) {
return; return;
...@@ -55,6 +58,34 @@ const bindEvents = () => { ...@@ -55,6 +58,34 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`); $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
}); });
if ($pushNewProjectTipTrigger) {
$pushNewProjectTipTrigger
.removeAttr('rel')
.removeAttr('target')
.on('click', (e) => { e.preventDefault(); })
.popover({
title: $pushNewProjectTipTrigger.data('title'),
placement: 'auto bottom',
html: 'true',
content: $('.push-new-project-tip-template').html(),
})
.on('shown.bs.popover', () => {
$(document).on('click.popover touchstart.popover', (event) => {
if ($(event.target).closest('.popover').length === 0) {
$pushNewProjectTipTrigger.trigger('click');
}
});
const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus');
addSelectOnFocusBehaviour(target);
target.focus();
})
.on('hide.bs.popover', () => {
$(document).off('click.popover touchstart.popover');
});
}
function chooseTemplate() { function chooseTemplate() {
$('.template-option').hide(); $('.template-option').hide();
$projectFieldsForm.addClass('selected'); $projectFieldsForm.addClass('selected');
......
...@@ -333,6 +333,10 @@ a > code { ...@@ -333,6 +333,10 @@ a > code {
font-family: $monospace_font; font-family: $monospace_font;
} }
.weight-normal {
font-weight: $gl-font-weight-normal;
}
.commit-sha, .commit-sha,
.ref-name { .ref-name {
@extend .monospace; @extend .monospace;
......
...@@ -908,6 +908,12 @@ a.allowed-to-push { ...@@ -908,6 +908,12 @@ a.allowed-to-push {
} }
} }
.project-tip-command {
> .input-group-btn:first-child {
width: auto;
}
}
.protected-branches-list, .protected-branches-list,
.protected-tags-list { .protected-tags-list {
margin-bottom: 30px; margin-bottom: 30px;
......
class Groups::ApplicationController < ApplicationController class Groups::ApplicationController < ApplicationController
include RoutableActions
prepend EE::Groups::ApplicationController prepend EE::Groups::ApplicationController
include RoutableActions
include ControllerWithCrossProjectAccessCheck include ControllerWithCrossProjectAccessCheck
layout 'group' layout 'group'
......
...@@ -9,6 +9,10 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -9,6 +9,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access, :update, :override] before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access, :update, :override]
before_action :authorize_update_group_member!, only: [:update, :override] before_action :authorize_update_group_member!, only: [:update, :override]
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,
:override
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access, skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite, :approve_access_request, :leave, :resend_invite,
:override :override
......
...@@ -47,7 +47,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -47,7 +47,7 @@ class ProjectsController < Projects::ApplicationController
notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name } notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name }
) )
else else
render 'new' render 'new', locals: { active_tab: ('import' if project_params[:import_url].present?) }
end end
end end
......
...@@ -263,6 +263,17 @@ module ProjectsHelper ...@@ -263,6 +263,17 @@ module ProjectsHelper
!!(params[:personal] || params[:name] || any_projects?(projects)) !!(params[:personal] || params[:name] || any_projects?(projects))
end end
def push_to_create_project_command(user = current_user)
repository_url =
if Gitlab::CurrentSettings.current_application_settings.enabled_git_access_protocol == 'http'
user_url(user)
else
Gitlab.config.gitlab_shell.ssh_path_prefix + user.username
end
"git push --set-upstream #{repository_url}/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)"
end
private private
def repo_children_classes(field) def repo_children_classes(field)
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
- merge_requests_count = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute.count - merge_requests_count = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute.count
- issues_sub_menu_items = ['groups#issues', 'labels#index', 'milestones#index'] - issues_sub_menu_items = ['groups#issues', 'labels#index', 'milestones#index']
- if @group.feature_available?(:group_issue_boards) - if @group.feature_available?(:group_issue_boards)
- issues_sub_menu_items.push('boards#index', 'boards#show') - issues_sub_menu_items.push('boards#index', 'boards#show')
......
.push-to-create-popover
%p
= label_tag(:push_to_create_tip, _("Private projects can be created in your personal namespace with:"), class: "weight-normal")
%p.input-group.project-tip-command
%span.input-group-btn
= text_field_tag :push_to_create_tip, push_to_create_project_command, class: "js-select-on-focus form-control monospace", readonly: true, aria: { label: _("Push project from command line") }
%span.input-group-btn
= clipboard_button(text: push_to_create_project_command, title: _("Copy command to clipboard"), placement: "right")
%p
= link_to("What does this command do?", help_page_path("gitlab-basics/create-project", anchor: "push-to-create-a-new-project"), target: "_blank")
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
- page_title 'New Project' - page_title 'New Project'
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility - visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
- active_tab = local_assigns.fetch(:active_tab, 'blank')
.project-edit-container .project-edit-container
.project-edit-errors .project-edit-errors
...@@ -18,34 +19,41 @@ ...@@ -18,34 +19,41 @@
All features are enabled when you create a project, but you can disable the ones you don’t need in the project settings. All features are enabled when you create a project, but you can disable the ones you don’t need in the project settings.
.md .md
= brand_new_project_guidelines = brand_new_project_guidelines
%p
%strong= _("Tip:")
= _("You can also create a project from the command line.")
%a.push-new-project-tip{ data: { title: _("Push to create a project") }, href: help_page_path('gitlab-basics/create-project', anchor: 'push-to-create-a-new-project'), target: "_blank", rel: "noopener noreferrer" }
= _("Show command")
%template.push-new-project-tip-template= render partial: "new_project_push_tip"
.col-lg-9.js-toggle-container .col-lg-9.js-toggle-container
%ul.nav-links.gitlab-tabs{ role: 'tablist' } %ul.nav-links.gitlab-tabs{ role: 'tablist' }
%li.active{ role: 'presentation' } %li{ class: ('active' if active_tab == 'blank'), role: 'presentation' }
%a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' } %a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Blank project %span.hidden-xs Blank project
%span.visible-xs Blank %span.visible-xs Blank
%li{ role: 'presentation' } %li{ class: ('active' if active_tab == 'template'), role: 'presentation' }
%a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' } %a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Create from template %span.hidden-xs Create from template
%span.visible-xs Template %span.visible-xs Template
%li{ role: 'presentation' } %li{ class: ('active' if active_tab == 'import'), role: 'presentation' }
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' } %a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Import project %span.hidden-xs Import project
%span.visible-xs Import %span.visible-xs Import
.tab-content.gitlab-tab-content .tab-content.gitlab-tab-content
.tab-pane.active{ id: 'blank-project-pane', role: 'tabpanel' } .tab-pane{ id: 'blank-project-pane', class: ('active' if active_tab == 'blank'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
= render 'new_project_fields', f: f, project_name_id: "blank-project-name" = render 'new_project_fields', f: f, project_name_id: "blank-project-name"
.tab-pane.no-padding{ id: 'create-from-template-pane', role: 'tabpanel' } .tab-pane.no-padding{ id: 'create-from-template-pane', class: ('active' if active_tab == 'template'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
.project-template .project-template
.form-group .form-group
%div %div
= render 'project_templates', f: f = render 'project_templates', f: f
.tab-pane.import-project-pane{ id: 'import-project-pane', role: 'tabpanel' } .tab-pane.import-project-pane{ id: 'import-project-pane', class: ('active' if active_tab == 'import'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f| = form_for @project, html: { class: 'new_project' } do |f|
- if import_sources_enabled? - if import_sources_enabled?
.project-import.row .project-import.row
...@@ -92,7 +100,7 @@ ...@@ -92,7 +100,7 @@
%button.btn.js-toggle-button.import_git{ type: "button" } %button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL') = icon('git', text: 'Repo by URL')
.col-lg-12 .col-lg-12
.js-toggle-content.hide.toggle-import-form .js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
%hr %hr
= render "shared/import_form", f: f = render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name" = render 'new_project_fields', f: f, project_name_id: "import-url-name"
......
---
title: Keep "Import project" tab/form active when validation fails trying to import
"Repo by URL"
merge_request: 17136
author:
type: fixed
---
title: Allow token authentication on go-get request
merge_request:
author:
type: changed
...@@ -47,10 +47,10 @@ This can be done by using either SSH or HTTP: ...@@ -47,10 +47,10 @@ This can be done by using either SSH or HTTP:
``` ```
## Git push using SSH ## Git push using SSH
git push git@gitlab.example.com:namespace/nonexistent-project.git git push --set-upstream git@gitlab.example.com:namespace/nonexistent-project.git master
## Git push using HTTP ## Git push using HTTP
git push https://gitlab.example.com/namespace/nonexistent-project.git git push --set-upstream https://gitlab.example.com/namespace/nonexistent-project.git master
``` ```
Once the push finishes successfully, a remote message will indicate Once the push finishes successfully, a remote message will indicate
......
...@@ -19,8 +19,9 @@ module Banzai ...@@ -19,8 +19,9 @@ module Banzai
# #
# Returns the documents passed as the first argument. # Returns the documents passed as the first argument.
def redact(documents) def redact(documents)
all_document_nodes = document_nodes(documents) redact_cross_project_references(documents) unless can_read_cross_project?
all_document_nodes = document_nodes(documents)
redact_document_nodes(all_document_nodes) redact_document_nodes(all_document_nodes)
end end
...@@ -51,6 +52,18 @@ module Banzai ...@@ -51,6 +52,18 @@ module Banzai
metadata metadata
end end
def redact_cross_project_references(documents)
extractor = Banzai::IssuableExtractor.new(project, user)
issuables = extractor.extract(documents)
issuables.each do |node, issuable|
next if issuable.project == project
node['class'] = node['class'].gsub('has-tooltip', '')
node['title'] = nil
end
end
# Returns the nodes visible to the current user. # Returns the nodes visible to the current user.
# #
# nodes - The input nodes to check. # nodes - The input nodes to check.
...@@ -78,5 +91,11 @@ module Banzai ...@@ -78,5 +91,11 @@ module Banzai
{ document: document, nodes: Querying.css(document, 'a.gfm[data-reference-type]') } { document: document, nodes: Querying.css(document, 'a.gfm[data-reference-type]') }
end end
end end
private
def can_read_cross_project?
Ability.allowed?(user, :read_cross_project)
end
end end
end end
...@@ -20,6 +20,14 @@ module Gitlab ...@@ -20,6 +20,14 @@ module Gitlab
rescue Gitlab::Auth::AuthenticationError rescue Gitlab::Auth::AuthenticationError
nil nil
end end
def valid_access_token?(scopes: [])
validate_access_token!(scopes: scopes)
true
rescue Gitlab::Auth::AuthenticationError
false
end
end end
end end
end end
...@@ -114,7 +114,15 @@ module Gitlab ...@@ -114,7 +114,15 @@ module Gitlab
end end
def current_user(request) def current_user(request)
request.env['warden']&.authenticate authenticator = Gitlab::Auth::RequestAuthenticator.new(request)
user = authenticator.find_user_from_access_token || authenticator.find_user_from_warden
return unless user&.can?(:access_api)
# Right now, the `api` scope is the only one that should be able to determine private project existence.
return unless authenticator.valid_access_token?(scopes: [:api])
user
end end
end end
end end
......
...@@ -8,8 +8,8 @@ msgid "" ...@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-22 17:55+0100\n" "POT-Creation-Date: 2018-02-23 13:23+0100\n"
"PO-Revision-Date: 2018-02-22 17:55+0100\n" "PO-Revision-Date: 2018-02-23 13:23+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -135,9 +135,15 @@ msgstr "" ...@@ -135,9 +135,15 @@ msgstr ""
msgid "Add Group Webhooks and GitLab Enterprise Edition." msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr "" msgstr ""
msgid "Add Kubernetes cluster"
msgstr ""
msgid "Add License" msgid "Add License"
msgstr "" msgstr ""
msgid "Add Readme"
msgstr ""
msgid "Add new directory" msgid "Add new directory"
msgstr "" msgstr ""
...@@ -354,6 +360,9 @@ msgstr "" ...@@ -354,6 +360,9 @@ msgstr ""
msgid "Authors: %{authors}" msgid "Authors: %{authors}"
msgstr "" msgstr ""
msgid "Auto DevOps enabled"
msgstr ""
msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly." msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr "" msgstr ""
...@@ -378,7 +387,13 @@ msgstr "" ...@@ -378,7 +387,13 @@ msgstr ""
msgid "AutoDevOps|Learn more in the %{link_to_documentation}" msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "" msgstr ""
msgid "AutoDevOps|You can activate %{link_to_settings} for this project." msgid "AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}."
msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
msgid "AutoDevOps|enable Auto DevOps (Beta)"
msgstr "" msgstr ""
msgid "Available" msgid "Available"
...@@ -447,8 +462,8 @@ msgstr "" ...@@ -447,8 +462,8 @@ msgstr ""
msgid "BillingPlans|per user" msgid "BillingPlans|per user"
msgstr "" msgstr ""
msgid "Branch" msgid "Branch (%{branch_count})"
msgid_plural "Branches" msgid_plural "Branches (%{branch_count})"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
...@@ -1042,6 +1057,11 @@ msgid_plural "Commits" ...@@ -1042,6 +1057,11 @@ msgid_plural "Commits"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "Commit (%{commit_count})"
msgid_plural "Commits (%{commit_count})"
msgstr[0] ""
msgstr[1] ""
msgid "Commit Message" msgid "Commit Message"
msgstr "" msgstr ""
...@@ -1195,6 +1215,9 @@ msgstr "" ...@@ -1195,6 +1215,9 @@ msgstr ""
msgid "Copy branch name to clipboard" msgid "Copy branch name to clipboard"
msgstr "" msgstr ""
msgid "Copy command to clipboard"
msgstr ""
msgid "Copy commit SHA to clipboard" msgid "Copy commit SHA to clipboard"
msgstr "" msgstr ""
...@@ -1404,6 +1427,9 @@ msgstr "" ...@@ -1404,6 +1427,9 @@ msgstr ""
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
msgid "Environments|An error occurred while fetching the environments." msgid "Environments|An error occurred while fetching the environments."
msgstr "" msgstr ""
...@@ -1575,6 +1601,9 @@ msgstr "" ...@@ -1575,6 +1601,9 @@ msgstr ""
msgid "Files" msgid "Files"
msgstr "" msgstr ""
msgid "Files (%{human_size})"
msgstr ""
msgid "Filter by commit message" msgid "Filter by commit message"
msgstr "" msgstr ""
...@@ -1873,6 +1902,9 @@ msgstr "" ...@@ -1873,6 +1902,9 @@ msgstr ""
msgid "Housekeeping successfully started" msgid "Housekeeping successfully started"
msgstr "" msgstr ""
msgid "If you already have files you can push them using the %{link_to_cli} below."
msgstr ""
msgid "Import repository" msgid "Import repository"
msgstr "" msgstr ""
...@@ -1968,6 +2000,9 @@ msgstr "" ...@@ -1968,6 +2000,9 @@ msgstr ""
msgid "Kubernetes cluster was successfully updated." msgid "Kubernetes cluster was successfully updated."
msgstr "" msgstr ""
msgid "Kubernetes configured"
msgstr ""
msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page" msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
msgstr "" msgstr ""
...@@ -2015,6 +2050,9 @@ msgstr "" ...@@ -2015,6 +2050,9 @@ msgstr ""
msgid "Learn more" msgid "Learn more"
msgstr "" msgstr ""
msgid "Learn more about protected branches"
msgstr ""
msgid "Learn more in the" msgid "Learn more in the"
msgstr "" msgstr ""
...@@ -2251,6 +2289,9 @@ msgstr "" ...@@ -2251,6 +2289,9 @@ msgstr ""
msgid "Not enough data" msgid "Not enough data"
msgstr "" msgstr ""
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
msgid "Notification events" msgid "Notification events"
msgstr "" msgstr ""
...@@ -2353,6 +2394,9 @@ msgstr "" ...@@ -2353,6 +2394,9 @@ msgstr ""
msgid "Options" msgid "Options"
msgstr "" msgstr ""
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
msgid "Overview" msgid "Overview"
msgstr "" msgstr ""
...@@ -2461,19 +2505,19 @@ msgstr "" ...@@ -2461,19 +2505,19 @@ msgstr ""
msgid "Pipeline|Retry pipeline" msgid "Pipeline|Retry pipeline"
msgstr "" msgstr ""
msgid "Pipeline|Retry pipeline #%{id}?" msgid "Pipeline|Retry pipeline #%{pipelineId}?"
msgstr "" msgstr ""
msgid "Pipeline|Stop pipeline" msgid "Pipeline|Stop pipeline"
msgstr "" msgstr ""
msgid "Pipeline|Stop pipeline #%{id}?" msgid "Pipeline|Stop pipeline #%{pipelineId}?"
msgstr "" msgstr ""
msgid "Pipeline|You’re about to retry pipeline %{id}." msgid "Pipeline|You’re about to retry pipeline %{pipelineId}."
msgstr "" msgstr ""
msgid "Pipeline|You’re about to stop pipeline %{id}." msgid "Pipeline|You’re about to stop pipeline %{pipelineId}."
msgstr "" msgstr ""
msgid "Pipeline|all" msgid "Pipeline|all"
...@@ -2509,6 +2553,9 @@ msgstr "" ...@@ -2509,6 +2553,9 @@ msgstr ""
msgid "Private - The group and its projects can only be viewed by members." msgid "Private - The group and its projects can only be viewed by members."
msgstr "" msgstr ""
msgid "Private projects can be created in your personal namespace with:"
msgstr ""
msgid "Profile" msgid "Profile"
msgstr "" msgstr ""
...@@ -2746,6 +2793,12 @@ msgstr "" ...@@ -2746,6 +2793,12 @@ msgstr ""
msgid "Push events" msgid "Push events"
msgstr "" msgstr ""
msgid "Push project from command line"
msgstr ""
msgid "Push to create a project"
msgstr ""
msgid "PushRule|Committer restriction" msgid "PushRule|Committer restriction"
msgstr "" msgstr ""
...@@ -2928,9 +2981,6 @@ msgstr "" ...@@ -2928,9 +2981,6 @@ msgstr ""
msgid "Set up Koding" msgid "Set up Koding"
msgstr "" msgstr ""
msgid "Set up auto deploy"
msgstr ""
msgid "SetPasswordToCloneLink|set a password" msgid "SetPasswordToCloneLink|set a password"
msgstr "" msgstr ""
...@@ -2946,6 +2996,9 @@ msgstr "" ...@@ -2946,6 +2996,9 @@ msgstr ""
msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes" msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
msgstr "" msgstr ""
msgid "Show command"
msgstr ""
msgid "Show parent pages" msgid "Show parent pages"
msgstr "" msgstr ""
...@@ -3149,8 +3202,8 @@ msgstr "" ...@@ -3149,8 +3202,8 @@ msgstr ""
msgid "System Hooks" msgid "System Hooks"
msgstr "" msgstr ""
msgid "Tag" msgid "Tag (%{tag_count})"
msgid_plural "Tags" msgid_plural "Tags (%{tag_count})"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
...@@ -3283,6 +3336,9 @@ msgstr "" ...@@ -3283,6 +3336,9 @@ msgstr ""
msgid "The repository for this project does not exist." msgid "The repository for this project does not exist."
msgstr "" msgstr ""
msgid "The repository for this project is empty"
msgstr ""
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request." msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
msgstr "" msgstr ""
...@@ -3554,6 +3610,9 @@ msgstr[1] "" ...@@ -3554,6 +3610,9 @@ msgstr[1] ""
msgid "Time|s" msgid "Time|s"
msgstr "" msgstr ""
msgid "Tip:"
msgstr ""
msgid "Title" msgid "Title"
msgstr "" msgstr ""
...@@ -3827,6 +3886,9 @@ msgstr "" ...@@ -3827,6 +3886,9 @@ msgstr ""
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?" msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "" msgstr ""
msgid "You can also create a project from the command line."
msgstr ""
msgid "You can also star a label to make it a priority label." msgid "You can also star a label to make it a priority label."
msgstr "" msgstr ""
...@@ -3974,6 +4036,9 @@ msgstr "" ...@@ -3974,6 +4036,9 @@ msgstr ""
msgid "ciReport|no security vulnerabilities" msgid "ciReport|no security vulnerabilities"
msgstr "" msgstr ""
msgid "command line instructions"
msgstr ""
msgid "commit" msgid "commit"
msgstr "" msgstr ""
......
...@@ -140,7 +140,7 @@ feature 'New project' do ...@@ -140,7 +140,7 @@ feature 'New project' do
find('#import-project-tab').click find('#import-project-tab').click
end end
context 'from git repository url' do context 'from git repository url, "Repo by URL"' do
before do before do
first('.import_git').click first('.import_git').click
end end
...@@ -157,6 +157,18 @@ feature 'New project' do ...@@ -157,6 +157,18 @@ feature 'New project' do
expect(git_import_instructions).to be_visible expect(git_import_instructions).to be_visible
expect(git_import_instructions).to have_content 'Git repository URL' expect(git_import_instructions).to have_content 'Git repository URL'
end end
it 'keeps "Import project" tab open after form validation error' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
fill_in 'project_import_url', with: collision_project.http_url_to_repo
fill_in 'project_path', with: collision_project.path
click_on 'Create project'
expect(page).to have_css('#import-project-pane.active')
expect(page).not_to have_css('.toggle-import-form.hide')
end
end end
context 'from GitHub' do context 'from GitHub' do
......
...@@ -25,6 +25,24 @@ feature 'Project' do ...@@ -25,6 +25,24 @@ feature 'Project' do
end end
end end
describe 'shows tip about push to create git command' do
let(:user) { create(:user) }
before do
sign_in user
visit new_project_path
end
it 'shows the command in a popover', :js do
page.within '.profile-settings-sidebar' do
click_link 'Show command'
end
expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip')
expect(page).to have_content 'Private projects can be created in your personal namespace with:'
end
end
describe 'description' do describe 'description' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:path) { project_path(project) } let(:path) { project_path(project) }
......
...@@ -178,9 +178,27 @@ describe SnippetsFinder do ...@@ -178,9 +178,27 @@ describe SnippetsFinder do
end end
end end
describe "#execute" do describe '#execute' do
# Snippet visibility scenarios are included in more details in spec/support/snippet_visibility.rb let(:project) { create(:project, :public) }
include_examples 'snippet visibility', described_class let!(:project_snippet) { create(:project_snippet, :public, project: project) }
let!(:personal_snippet) { create(:personal_snippet, :public) }
let(:user) { create(:user) }
subject(:finder) { described_class.new(user) }
it 'returns project- and personal snippets' do
expect(finder.execute).to contain_exactly(project_snippet, personal_snippet)
end
context 'when the user cannot read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
end
it 'returns only personal snippets when the user cannot read cross project' do
expect(finder.execute).to contain_exactly(personal_snippet)
end
end
end end
describe '#execute' do describe '#execute' do
......
...@@ -455,6 +455,22 @@ describe ProjectsHelper do ...@@ -455,6 +455,22 @@ describe ProjectsHelper do
end end
end end
describe('#push_to_create_project_command') do
let(:user) { create(:user, username: 'john') }
it 'returns the command to push to create project over HTTP' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'http' }
expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream http://test.host/john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
end
it 'returns the command to push to create project over SSH' do
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream git@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
end
end
describe '#any_projects?' do describe '#any_projects?' do
let!(:project) { create(:project) } let!(:project) { create(:project) }
......
...@@ -6,7 +6,7 @@ describe Banzai::Filter::RedactorFilter do ...@@ -6,7 +6,7 @@ describe Banzai::Filter::RedactorFilter do
it 'ignores non-GFM links' do it 'ignores non-GFM links' do
html = %(See <a href="https://google.com/">Google</a>) html = %(See <a href="https://google.com/">Google</a>)
doc = filter(html, current_user: double) doc = filter(html, current_user: build(:user))
expect(doc.css('a').length).to eq 1 expect(doc.css('a').length).to eq 1
end end
......
...@@ -88,6 +88,55 @@ describe Banzai::Redactor do ...@@ -88,6 +88,55 @@ describe Banzai::Redactor do
end end
end end
context 'when the user cannot read cross project' do
include ActionView::Helpers::UrlHelper
let(:project) { create(:project) }
let(:other_project) { create(:project, :public) }
def create_link(issuable)
type = issuable.class.name.underscore.downcase
link_to(issuable.to_reference, '',
class: 'gfm has-tooltip',
title: issuable.title,
data: {
reference_type: type,
"#{type}": issuable.id
})
end
before do
project.add_developer(user)
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
end
it 'skips links to issues within the same project' do
issue = create(:issue, project: project)
link = create_link(issue)
doc = Nokogiri::HTML.fragment(link)
redactor.redact([doc])
result = doc.css('a').last
expect(result['class']).to include('has-tooltip')
expect(result['title']).to eq(issue.title)
end
it 'removes info from a cross project reference' do
issue = create(:issue, project: other_project)
link = create_link(issue)
doc = Nokogiri::HTML.fragment(link)
redactor.redact([doc])
result = doc.css('a').last
expect(result['class']).not_to include('has-tooltip')
expect(result['title']).to be_empty
end
end
describe '#redact_nodes' do describe '#redact_nodes' do
it 'redacts an Array of nodes' do it 'redacts an Array of nodes' do
doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>') doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
......
...@@ -3,19 +3,30 @@ require 'spec_helper' ...@@ -3,19 +3,30 @@ require 'spec_helper'
describe Gitlab::Middleware::Go do describe Gitlab::Middleware::Go do
let(:app) { double(:app) } let(:app) { double(:app) }
let(:middleware) { described_class.new(app) } let(:middleware) { described_class.new(app) }
let(:env) do
{
'rack.input' => '',
'REQUEST_METHOD' => 'GET'
}
end
describe '#call' do describe '#call' do
describe 'when go-get=0' do describe 'when go-get=0' do
before do
env['QUERY_STRING'] = 'go-get=0'
end
it 'skips go-import generation' do it 'skips go-import generation' do
env = { 'rack.input' => '',
'QUERY_STRING' => 'go-get=0' }
expect(app).to receive(:call).with(env).and_return('no-go') expect(app).to receive(:call).with(env).and_return('no-go')
middleware.call(env) middleware.call(env)
end end
end end
describe 'when go-get=1' do describe 'when go-get=1' do
let(:current_user) { nil } before do
env['QUERY_STRING'] = 'go-get=1'
env['PATH_INFO'] = "/#{path}"
end
shared_examples 'go-get=1' do |enabled_protocol:| shared_examples 'go-get=1' do |enabled_protocol:|
context 'with simple 2-segment project path' do context 'with simple 2-segment project path' do
...@@ -54,21 +65,75 @@ describe Gitlab::Middleware::Go do ...@@ -54,21 +65,75 @@ describe Gitlab::Middleware::Go do
project.update_attribute(:visibility_level, Project::PRIVATE) project.update_attribute(:visibility_level, Project::PRIVATE)
end end
context 'with access to the project' do shared_examples 'unauthorized' do
it 'returns the 2-segment group path' do
expect_response_with_path(go, enabled_protocol, group.full_path)
end
end
context 'when not authenticated' do
it_behaves_like 'unauthorized'
end
context 'when authenticated' do
let(:current_user) { project.creator } let(:current_user) { project.creator }
before do before do
project.team.add_master(current_user) project.team.add_master(current_user)
end end
it 'returns the full project path' do shared_examples 'authenticated' do
expect_response_with_path(go, enabled_protocol, project.full_path) context 'with access to the project' do
it 'returns the full project path' do
expect_response_with_path(go, enabled_protocol, project.full_path)
end
end
context 'without access to the project' do
before do
project.team.find_member(current_user).destroy
end
it_behaves_like 'unauthorized'
end
end end
end
context 'without access to the project' do context 'using warden' do
it 'returns the 2-segment group path' do before do
expect_response_with_path(go, enabled_protocol, group.full_path) env['warden'] = double(authenticate: current_user)
end
context 'when active' do
it_behaves_like 'authenticated'
end
context 'when blocked' do
before do
current_user.block!
end
it_behaves_like 'unauthorized'
end
end
context 'using a personal access token' do
let(:personal_access_token) { create(:personal_access_token, user: current_user) }
before do
env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token
end
context 'with api scope' do
it_behaves_like 'authenticated'
end
context 'with read_user scope' do
before do
personal_access_token.update_attribute(:scopes, [:read_user])
end
it_behaves_like 'unauthorized'
end
end end
end end
end end
...@@ -138,12 +203,6 @@ describe Gitlab::Middleware::Go do ...@@ -138,12 +203,6 @@ describe Gitlab::Middleware::Go do
end end
def go def go
env = {
'rack.input' => '',
'QUERY_STRING' => 'go-get=1',
'PATH_INFO' => "/#{path}",
'warden' => double(authenticate: current_user)
}
middleware.call(env) middleware.call(env)
end end
......
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