Commit 10593a8a authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents abca813a 2573818f
...@@ -11,7 +11,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { ...@@ -11,7 +11,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
// Issue boards is slightly different, we handle all the requests async // Issue boards is slightly different, we handle all the requests async
// instead or reloading the page, we just re-fire the list ajax requests // instead or reloading the page, we just re-fire the list ajax requests
this.isHandledAsync = true; this.isHandledAsync = true;
this.cantEdit = cantEdit; this.cantEdit = cantEdit.filter(i => typeof i === 'string');
this.cantEditWithValue = cantEdit.filter(i => typeof i === 'object');
} }
updateObject(path) { updateObject(path) {
...@@ -42,7 +43,9 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { ...@@ -42,7 +43,9 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
this.filteredSearchInput.dispatchEvent(new Event('input')); this.filteredSearchInput.dispatchEvent(new Event('input'));
} }
canEdit(tokenName) { canEdit(tokenName, tokenValue) {
return this.cantEdit.indexOf(tokenName) === -1; if (this.cantEdit.includes(tokenName)) return false;
return this.cantEditWithValue.findIndex(token => token.name === tokenName &&
token.value === tokenValue) === -1;
} }
} }
...@@ -15,16 +15,18 @@ gl.issueBoards.BoardsStore = { ...@@ -15,16 +15,18 @@ gl.issueBoards.BoardsStore = {
}, },
state: {}, state: {},
detail: { detail: {
issue: {} issue: {},
}, },
moving: { moving: {
issue: {}, issue: {},
list: {} list: {},
}, },
create () { create () {
this.state.lists = []; this.state.lists = [];
this.filter.path = getUrlParamsArray().join('&'); this.filter.path = getUrlParamsArray().join('&');
this.detail = { issue: {} }; this.detail = {
issue: {},
};
}, },
createNewListDropdownData() { createNewListDropdownData() {
this.state.currentBoard = {}; this.state.currentBoard = {};
......
...@@ -147,6 +147,16 @@ class DropdownUtils { ...@@ -147,6 +147,16 @@ class DropdownUtils {
return dataValue !== null; return dataValue !== null;
} }
static getVisualTokenValues(visualToken) {
const tokenName = visualToken && visualToken.querySelector('.name').textContent.trim();
let tokenValue = visualToken && visualToken.querySelector('.value') && visualToken.querySelector('.value').textContent.trim();
if (tokenName === 'label' && tokenValue) {
// remove leading symbol and wrapping quotes
tokenValue = tokenValue.replace(/^~("|')?(.*)/, '$2').replace(/("|')$/, '');
}
return { tokenName, tokenValue };
}
// Determines the full search query (visual tokens + input) // Determines the full search query (visual tokens + input)
static getSearchQuery(untilInput = false) { static getSearchQuery(untilInput = false) {
const container = FilteredSearchContainer.container; const container = FilteredSearchContainer.container;
......
...@@ -198,8 +198,8 @@ class FilteredSearchManager { ...@@ -198,8 +198,8 @@ class FilteredSearchManager {
if (e.keyCode === 8 || e.keyCode === 46) { if (e.keyCode === 8 || e.keyCode === 46) {
const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
const sanitizedTokenName = lastVisualToken && lastVisualToken.querySelector('.name').textContent.trim(); const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken);
const canEdit = sanitizedTokenName && this.canEdit && this.canEdit(sanitizedTokenName); const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue);
if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) {
this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial();
gl.FilteredSearchVisualTokens.removeLastTokenPartial(); gl.FilteredSearchVisualTokens.removeLastTokenPartial();
...@@ -349,8 +349,8 @@ class FilteredSearchManager { ...@@ -349,8 +349,8 @@ class FilteredSearchManager {
let canClearToken = t.classList.contains('js-visual-token'); let canClearToken = t.classList.contains('js-visual-token');
if (canClearToken) { if (canClearToken) {
const tokenKey = t.querySelector('.name').textContent.trim(); const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(t);
canClearToken = this.canEdit && this.canEdit(tokenKey); canClearToken = this.canEdit && this.canEdit(tokenName, tokenValue);
} }
if (canClearToken) { if (canClearToken) {
...@@ -482,7 +482,7 @@ class FilteredSearchManager { ...@@ -482,7 +482,7 @@ class FilteredSearchManager {
} }
hasFilteredSearch = true; hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(sanitizedKey); const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue);
gl.FilteredSearchVisualTokens.addFilterVisualToken( gl.FilteredSearchVisualTokens.addFilterVisualToken(
sanitizedKey, sanitizedKey,
`${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`,
......
...@@ -38,21 +38,14 @@ class FilteredSearchVisualTokens { ...@@ -38,21 +38,14 @@ class FilteredSearchVisualTokens {
} }
static createVisualTokenElementHTML(canEdit = true) { static createVisualTokenElementHTML(canEdit = true) {
let removeTokenMarkup = '';
if (canEdit) {
removeTokenMarkup = `
<div class="remove-token" role="button">
<i class="fa fa-close"></i>
</div>
`;
}
return ` return `
<div class="selectable" role="button"> <div class="${canEdit ? 'selectable' : 'hidden'}" role="button">
<div class="name"></div> <div class="name"></div>
<div class="value-container"> <div class="value-container">
<div class="value"></div> <div class="value"></div>
${removeTokenMarkup} <div class="remove-token" role="button">
<i class="fa fa-close"></i>
</div>
</div> </div>
</div> </div>
`; `;
......
...@@ -8,7 +8,7 @@ import CreateLabelDropdown from './create_label'; ...@@ -8,7 +8,7 @@ import CreateLabelDropdown from './create_label';
(function() { (function() {
this.LabelsSelect = (function() { this.LabelsSelect = (function() {
function LabelsSelect(els) { function LabelsSelect(els, options = {}) {
var _this, $els; var _this, $els;
_this = this; _this = this;
...@@ -58,6 +58,7 @@ import CreateLabelDropdown from './create_label'; ...@@ -58,6 +58,7 @@ import CreateLabelDropdown from './create_label';
labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>'); labelHTMLTemplate = _.template('<% _.each(labels, function(label){ %> <a href="<%- ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%- encodeURIComponent(label.title) %>"> <span class="label has-tooltip color-label" title="<%- label.description %>" style="background-color: <%- label.color %>; color: <%- label.text_color %>;"> <%- label.title %> </span> </a> <% }); %>');
labelNoneHTMLTemplate = '<span class="no-value">None</span>'; labelNoneHTMLTemplate = '<span class="no-value">None</span>';
} }
const handleClick = options.handleClick;
$sidebarLabelTooltip.tooltip(); $sidebarLabelTooltip.tooltip();
...@@ -316,9 +317,9 @@ import CreateLabelDropdown from './create_label'; ...@@ -316,9 +317,9 @@ import CreateLabelDropdown from './create_label';
}, },
multiSelect: $dropdown.hasClass('js-multiselect'), multiSelect: $dropdown.hasClass('js-multiselect'),
vue: $dropdown.hasClass('js-issue-board-sidebar'), vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(options) { clicked: function(clickEvent) {
const { $el, e, isMarking } = options; const { $el, e, isMarking } = clickEvent;
const label = options.selectedObj; const label = clickEvent.selectedObj;
var isIssueIndex, isMRIndex, page, boardsModel; var isIssueIndex, isMRIndex, page, boardsModel;
var fadeOutLoader = () => { var fadeOutLoader = () => {
...@@ -391,6 +392,10 @@ import CreateLabelDropdown from './create_label'; ...@@ -391,6 +392,10 @@ import CreateLabelDropdown from './create_label';
.then(fadeOutLoader) .then(fadeOutLoader)
.catch(fadeOutLoader); .catch(fadeOutLoader);
} }
else if (handleClick) {
e.preventDefault();
handleClick(label);
}
else { else {
if ($dropdown.hasClass('js-multiselect')) { if ($dropdown.hasClass('js-multiselect')) {
......
...@@ -5,7 +5,7 @@ import _ from 'underscore'; ...@@ -5,7 +5,7 @@ import _ from 'underscore';
(function() { (function() {
this.MilestoneSelect = (function() { this.MilestoneSelect = (function() {
function MilestoneSelect(currentProject, els) { function MilestoneSelect(currentProject, els, options = {}) {
var _this, $els; var _this, $els;
if (currentProject != null) { if (currentProject != null) {
_this = this; _this = this;
...@@ -141,13 +141,14 @@ import _ from 'underscore'; ...@@ -141,13 +141,14 @@ import _ from 'underscore';
}, },
opened: function(e) { opened: function(e) {
const $el = $(e.currentTarget); const $el = $(e.currentTarget);
if ($dropdown.hasClass('js-issue-board-sidebar')) { if ($dropdown.hasClass('js-issue-board-sidebar') || options.handleClick) {
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault; selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
} }
$('a.is-active', $el).removeClass('is-active'); $('a.is-active', $el).removeClass('is-active');
$(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active'); $(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active');
}, },
vue: $dropdown.hasClass('js-issue-board-sidebar'), vue: $dropdown.hasClass('js-issue-board-sidebar'),
<<<<<<< HEAD
hideRow: function(milestone) { hideRow: function(milestone) {
if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar') && if ($('html').hasClass('issue-boards-page') && !$dropdown.hasClass('js-issue-board-sidebar') &&
!$dropdown.closest('.add-issues-modal').length && gl.issueBoards.BoardsStore.state.currentBoard.milestone) { !$dropdown.closest('.add-issues-modal').length && gl.issueBoards.BoardsStore.state.currentBoard.milestone) {
...@@ -167,9 +168,21 @@ import _ from 'underscore'; ...@@ -167,9 +168,21 @@ import _ from 'underscore';
clicked: function(options) { clicked: function(options) {
const { $el, e } = options; const { $el, e } = options;
let selected = options.selectedObj; let selected = options.selectedObj;
=======
clicked: function(clickEvent) {
const { $el, e } = clickEvent;
let selected = clickEvent.selectedObj;
>>>>>>> ce-com/master
var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore; var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore;
if (!selected) return; if (!selected) return;
if (options.handleClick) {
e.preventDefault();
options.handleClick(selected);
return;
}
page = $('body').attr('data-page'); page = $('body').attr('data-page');
isIssueIndex = page === 'projects:issues:index'; isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index'); isMRIndex = (page === page && page === 'projects:merge_requests:index');
......
...@@ -21,7 +21,10 @@ import CommentTypeToggle from './comment_type_toggle'; ...@@ -21,7 +21,10 @@ import CommentTypeToggle from './comment_type_toggle';
import GLForm from './gl_form'; import GLForm from './gl_form';
import loadAwardsHandler from './awards_handler'; import loadAwardsHandler from './awards_handler';
import Autosave from './autosave'; import Autosave from './autosave';
<<<<<<< HEAD
import './dropzone_input'; import './dropzone_input';
=======
>>>>>>> ce-com/master
import TaskList from './task_list'; import TaskList from './task_list';
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils'; import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
import imageDiffHelper from './image_diff/helpers/index'; import imageDiffHelper from './image_diff/helpers/index';
......
...@@ -6,7 +6,7 @@ import _ from 'underscore'; ...@@ -6,7 +6,7 @@ import _ from 'underscore';
// TODO: remove eventHub hack after code splitting refactor // TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop; window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
function UsersSelect(currentUser, els) { function UsersSelect(currentUser, els, options = {}) {
var $els; var $els;
this.users = this.users.bind(this); this.users = this.users.bind(this);
this.user = this.user.bind(this); this.user = this.user.bind(this);
...@@ -20,6 +20,8 @@ function UsersSelect(currentUser, els) { ...@@ -20,6 +20,8 @@ function UsersSelect(currentUser, els) {
} }
} }
const { handleClick } = options;
$els = $(els); $els = $(els);
if (!els) { if (!els) {
...@@ -442,6 +444,9 @@ function UsersSelect(currentUser, els) { ...@@ -442,6 +444,9 @@ function UsersSelect(currentUser, els) {
} }
if ($el.closest('.add-issues-modal').length) { if ($el.closest('.add-issues-modal').length) {
gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id;
} else if (handleClick) {
e.preventDefault();
handleClick(user, isMarking);
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
return Issuable.filterResults($dropdown.closest('form')); return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) { } else if ($dropdown.hasClass('js-filter-submit')) {
......
...@@ -18,6 +18,12 @@ ...@@ -18,6 +18,12 @@
required: false, required: false,
default: false, default: false,
}, },
class: {
type: String,
required: false,
default: '',
},
}, },
computed: { computed: {
...@@ -25,7 +31,7 @@ ...@@ -25,7 +31,7 @@
return this.inline ? 'span' : 'div'; return this.inline ? 'span' : 'div';
}, },
cssClass() { cssClass() {
return `fa-${this.size}x`; return `fa-${this.size}x ${this.class}`.trim();
}, },
}, },
}; };
......
...@@ -5,17 +5,27 @@ export default { ...@@ -5,17 +5,27 @@ export default {
props: { props: {
title: { title: {
type: String, type: String,
required: true, required: false,
}, },
text: { text: {
type: String, type: String,
required: false, required: false,
}, },
hideFooter: {
type: Boolean,
required: false,
default: false,
},
kind: { kind: {
type: String, type: String,
required: false, required: false,
default: 'primary', default: 'primary',
}, },
modalDialogClass: {
type: String,
required: false,
default: '',
},
closeKind: { closeKind: {
type: String, type: String,
required: false, required: false,
...@@ -30,6 +40,11 @@ export default { ...@@ -30,6 +40,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
submitDisabled: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
...@@ -57,43 +72,57 @@ export default { ...@@ -57,43 +72,57 @@ export default {
</script> </script>
<template> <template>
<div <div class="modal-open">
class="modal popup-dialog" <div
role="dialog" class="modal popup-dialog"
tabindex="-1"> role="dialog"
<div class="modal-dialog" role="document"> tabindex="-1"
<div class="modal-content"> >
<div class="modal-header"> <div
<button type="button" :class="modalDialogClass"
class="close" class="modal-dialog"
@click="close" role="document"
aria-label="Close"> >
<span aria-hidden="true">&times;</span> <div class="modal-content">
</button> <div class="modal-header">
<h4 class="modal-title">{{this.title}}</h4> <slot name="header">
</div> <h4 class="modal-title pull-left">
<div class="modal-body"> {{this.title}}
<slot name="body" :text="text"> </h4>
<p>{{text}}</p> <button
</slot> type="button"
</div> class="close pull-right"
<div class="modal-footer"> @click="close"
<button aria-label="Close"
type="button" >
class="btn" <span aria-hidden="true">&times;</span>
:class="btnCancelKindClass" </button>
@click="close"> </slot>
{{ closeButtonLabel }} </div>
</button> <div class="modal-body">
<button <slot name="body" :text="text">
type="button" <p>{{this.text}}</p>
class="btn" </slot>
:class="btnKindClass" </div>
@click="emitSubmit(true)"> <div class="modal-footer" v-if="!hideFooter">
{{ primaryButtonLabel }} <button
</button> type="button"
class="btn pull-left"
:class="btnCancelKindClass"
@click="close">
{{ closeButtonLabel }}
</button>
<button
type="button"
class="btn pull-right"
:class="btnKindClass"
@click="emitSubmit(true)">
{{ primaryButtonLabel }}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-backdrop fade in" />
</div> </div>
</template> </template>
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
.cred { color: $common-red; } .cred { color: $common-red; }
.cgreen { color: $common-green; } .cgreen { color: $common-green; }
.cdark { color: $common-gray-dark; } .cdark { color: $common-gray-dark; }
.text-secondary {
color: $gl-text-color-secondary;
}
/** COMMON CLASSES **/ /** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; } .prepend-top-0 { margin-top: 0; }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
.dropdown-menu-nav { .dropdown-menu-nav {
@include set-visible; @include set-visible;
display: block; display: block;
min-height: 40px;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
width: 100%; width: 100%;
......
...@@ -42,3 +42,11 @@ body.modal-open { ...@@ -42,3 +42,11 @@ body.modal-open {
width: 98%; width: 98%;
} }
} }
.modal.popup-dialog {
display: block;
}
.modal-body {
background-color: $modal-body-bg;
}
...@@ -164,3 +164,36 @@ $pre-border-color: $border-color; ...@@ -164,3 +164,36 @@ $pre-border-color: $border-color;
$table-bg-accent: $gray-light; $table-bg-accent: $gray-light;
$zindex-popover: 900; $zindex-popover: 900;
//== Modals
//
//##
//** Padding applied to the modal body
$modal-inner-padding: $gl-padding;
//** Padding applied to the modal title
$modal-title-padding: $gl-padding;
//** Modal title line-height
// $modal-title-line-height: $line-height-base
//** Background color of modal content area
$modal-content-bg: $gray-light;
$modal-body-bg: $white-light;
//** Modal content border color
// $modal-content-border-color: rgba(0,0,0,.2)
//** Modal content border color **for IE8**
// $modal-content-fallback-border-color: #999
//** Modal backdrop background color
// $modal-backdrop-bg: #000
//** Modal backdrop opacity
// $modal-backdrop-opacity: .5
//** Modal header border color
// $modal-header-border-color: #e5e5e5
//** Modal footer border color
// $modal-footer-border-color: $modal-header-border-color
// $modal-lg: 900px
// $modal-md: 600px
// $modal-sm: 300px
...@@ -7,19 +7,6 @@ ...@@ -7,19 +7,6 @@
background: $black-transparent; background: $black-transparent;
} }
.modal.popup-dialog {
display: block;
background-color: $black-transparent;
z-index: 2100;
@media (min-width: $screen-md-min) {
.modal-dialog {
width: 600px;
margin: 30px auto;
}
}
}
.project-refs-form, .project-refs-form,
.project-refs-target-form { .project-refs-target-form {
display: inline-block; display: inline-block;
......
...@@ -12,7 +12,7 @@ class ConfirmationsController < Devise::ConfirmationsController ...@@ -12,7 +12,7 @@ class ConfirmationsController < Devise::ConfirmationsController
users_almost_there_path users_almost_there_path
end end
def after_confirmation_path_for(_resource_name, resource) def after_confirmation_path_for(resource_name, resource)
# incoming resource can either be a :user or an :email # incoming resource can either be a :user or an :email
if signed_in?(:user) if signed_in?(:user)
after_sign_in(resource) after_sign_in(resource)
......
...@@ -22,17 +22,6 @@ module BoardsHelper ...@@ -22,17 +22,6 @@ module BoardsHelper
project_issues_path(@project) project_issues_path(@project)
end end
def current_board_json
board = @board || @boards.first
board.to_json(
only: [:id, :name, :milestone_id],
include: {
milestone: { only: [:title] }
}
)
end
def board_base_url def board_base_url
project_boards_path(@project) project_boards_path(@project)
end end
......
...@@ -14,6 +14,8 @@ class Email < ActiveRecord::Base ...@@ -14,6 +14,8 @@ class Email < ActiveRecord::Base
devise :confirmable devise :confirmable
self.reconfirmable = false # currently email can't be changed, no need to reconfirm self.reconfirmable = false # currently email can't be changed, no need to reconfirm
delegate :username, to: :user
def email=(value) def email=(value)
write_attribute(:email, value.downcase.strip) write_attribute(:email, value.downcase.strip)
end end
......
...@@ -911,6 +911,7 @@ class Repository ...@@ -911,6 +911,7 @@ class Repository
def merged_to_root_ref?(branch_or_name, pre_loaded_merged_branches = nil) def merged_to_root_ref?(branch_or_name, pre_loaded_merged_branches = nil)
branch = Gitlab::Git::Branch.find(self, branch_or_name) branch = Gitlab::Git::Branch.find(self, branch_or_name)
<<<<<<< HEAD
if branch if branch
root_ref_sha = commit(root_ref).sha root_ref_sha = commit(root_ref).sha
...@@ -922,12 +923,26 @@ class Repository ...@@ -922,12 +923,26 @@ class Repository
ancestor?(branch.target, root_ref_sha) ancestor?(branch.target, root_ref_sha)
end end
=======
if branch
root_ref_sha = commit(root_ref).sha
same_head = branch.target == root_ref_sha
merged =
if pre_loaded_merged_branches
pre_loaded_merged_branches.include?(branch.name)
else
ancestor?(branch.target, root_ref_sha)
end
>>>>>>> ce-com/master
!same_head && merged !same_head && merged
else else
nil nil
end end
end end
<<<<<<< HEAD
def fetch_upstream(url) def fetch_upstream(url)
add_remote(Repository::MIRROR_REMOTE, url) add_remote(Repository::MIRROR_REMOTE, url)
fetch_remote(Repository::MIRROR_REMOTE, ssh_auth: project&.import_data) fetch_remote(Repository::MIRROR_REMOTE, ssh_auth: project&.import_data)
...@@ -976,6 +991,8 @@ class Repository ...@@ -976,6 +991,8 @@ class Repository
end end
end end
=======
>>>>>>> ce-com/master
delegate :merged_branch_names, to: :raw_repository delegate :merged_branch_names, to: :raw_repository
def merge_base(first_commit_id, second_commit_id) def merge_base(first_commit_id, second_commit_id)
......
...@@ -80,7 +80,7 @@ class SystemHooksService ...@@ -80,7 +80,7 @@ class SystemHooksService
project_id: model.id, project_id: model.id,
owner_name: owner.name, owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "", owner_email: owner.respond_to?(:email) ? owner.email : "",
project_visibility: Project.visibility_levels.key(model.visibility_level_value).downcase project_visibility: model.visibility.downcase
} }
end end
......
---
title: 'Fix bug preventing secondary emails from being confirmed'
merge_request: 15010
author:
type: fixed
---
title: Use the correct visibility attribute for projects in system hooks
merge_request: 15065
author:
type: fixed
...@@ -3,31 +3,28 @@ ...@@ -3,31 +3,28 @@
Merge requests are useful to integrate separate changes that you've made to a Merge requests are useful to integrate separate changes that you've made to a
project, on different branches. This is a brief guide on how to create a merge project, on different branches. This is a brief guide on how to create a merge
request. For more information, check the request. For more information, check the
[merge requests documentation](../user/project/merge_requests.md). [merge requests documentation](../user/project/merge_requests/index.md).
--- ---
1. Before you start, you should have already [created a branch](create-branch.md) 1. Before you start, you should have already [created a branch](create-branch.md)
and [pushed your changes](basic-git-commands.md) to GitLab. and [pushed your changes](basic-git-commands.md) to GitLab.
1. Go to the project where you'd like to merge your changes and click on the
1. You can then go to the project where you'd like to merge your changes and **Merge requests** tab.
click on the **Merge requests** tab.
![Merge requests](img/project_navbar.png)
1. Click on **New merge request** on the right side of the screen. 1. Click on **New merge request** on the right side of the screen.
1. From there on, you have the option to select the source branch and the target
![New Merge Request](img/merge_request_new.png) branch you'd like to compare to. The default target project is the upstream
repository, but you can choose to compare across any of its forks.
1. Select a source branch and click on the **Compare branches and continue** button.
![Select a branch](img/merge_request_select_branch.png) ![Select a branch](img/merge_request_select_branch.png)
1. When ready, click on the **Compare branches and continue** button.
1. At a minimum, add a title and a description to your merge request. Optionally, 1. At a minimum, add a title and a description to your merge request. Optionally,
select a user to review your merge request and to accept or close it. You may select a user to review your merge request and to accept or close it. You may
also select a milestone and labels. also select a milestone and labels.
![New merge request page](img/merge_request_page.png) ![New merge request page](img/merge_request_page.png)
1. When ready, click on the **Submit merge request** button. Your merge request 1. When ready, click on the **Submit merge request** button.
will be ready to be approved and published.
Your merge request will be ready to be approved and merged.
...@@ -40,4 +40,12 @@ describe Email do ...@@ -40,4 +40,12 @@ describe Email do
expect(user.emails.confirmed.count).to eq 1 expect(user.emails.confirmed.count).to eq 1
end end
end end
describe 'delegation' do
let(:user) { create(:user) }
it 'delegates to :user' do
expect(build(:email, user: user).username).to eq user.username
end
end
end end
...@@ -63,6 +63,12 @@ describe SystemHooksService do ...@@ -63,6 +63,12 @@ describe SystemHooksService do
:group_id, :user_id, :user_username, :user_name, :user_email, :group_access :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
) )
end end
it 'includes the correct project visibility level' do
data = event_data(project, :create)
expect(data[:project_visibility]).to eq('private')
end
end end
context 'event names' do context 'event names' do
......
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