Commit 490ae8a6 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'ee/master' into ce-to-ee-2017-07-28

* ee/master:
  And end
  Squashed commit of the following:
  Trailing Spaces removed
  Moves the Performance Bar to the top instead of being at the bottom
  Fix remote mirror last_update_at nil error in view
  Adds restrict group owners to admins option to admin application settings dashboard
  Working File Locks
  Update CHANGELOG.md for 9.4.2
  Update CHANGELOG-EE.md for 9.4.2-ee
  Converting it to true string if :file_locks available
  Merge issuable "reopened" state into "opened"
  Remove :remove_default_access_levels and use
  Replace autodeploy guide image to use blank namespace
  Latest Changes from CE applied
  Removed Path Locks from _tree_content.html.haml
  removed similar code
  fixed form submitting
  EE port of ph-inline-js
  Applied Patch of inline-js-removal-projects-other from CE Also removed Additional EE Inline Script on _tree_content.html.haml
parents 2b84e500 d8aec0b7
Please view this file on the master branch, on stable branches it's out of date.
## 9.4.2 (2017-07-28)
- Adds lower bound to pull mirror scheduling feature. !2366
- Add warning and option toggle when rebuilding authorized_keys. !2508
- Fix CSS for mini graph with downstream pipeline.
- Renamed board to boards in new project sidebar.
- Fix Rebasing not working with Merge Requests.
- Fixed issue boards focus mode when new navigation is turned on.
## 9.4.1 (2017-07-25)
- Cleans up mirror capacity in project destroy service if project is a scheduled mirror. !2445
......
......@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 9.4.2 (2017-07-28)
- Fix job merge request link to a forked source project. !12965
- Improve redirect route query performance. !13062
- Allow admin to read_users_list even if it's restricted. !13066
- Fixes 500 error caused by pending delete projects in admin dashboard. !13067
- Add instrumentation to MarkupHelper#link_to_gfm. !13069
- Pending delete projects should not show in deploy keys. !13088
- Fix sizing of custom header logo in new navigation.
- Fix crash on /help/ui.
- Fix creating merge request diffs when diff contains bytes that are invalid in UTF-8.
- fix vertical alignment of New Project button.
- Add LDAP SSL certificate verification option.
- Fix vertical alignment in firefox and safari for pipeline mini graph.
## 9.4.1 (2017-07-25)
- Fix pipeline_schedules pages throwing error 500 (when ref is empty). !12983
......
......@@ -24,11 +24,6 @@ class AuditLogs {
$('.project-item-select').on('click', () => {
$('form.filter-form').submit();
});
$('form.filter-form').on('submit', function applyFilters(event) {
event.preventDefault();
gl.utils.visitUrl(`${this.action}?${$(this).serialize()}`);
});
}
initFilterDropdown($dropdown, fieldName, searchFields, cb) {
......
......@@ -8,6 +8,7 @@
/* global LabelsSelect */
/* global MilestoneSelect */
/* global Commit */
/* global NewBranchForm */
/* global NotificationsForm */
/* global NotificationsDropdown */
/* global GroupAvatar */
......@@ -23,6 +24,7 @@
/* global MergeRequest */
/* global Compare */
/* global CompareAutocomplete */
/* global PathLocks */
/* global ProjectNew */
/* global ProjectShow */
/* global Labels */
......@@ -68,6 +70,9 @@ import initExperimentalFlags from './experimental_flags';
import OAuthRememberMe from './oauth_remember_me';
import PerformanceBar from './performance_bar';
import GpgBadges from './gpg_badges';
import initNotes from './init_notes';
import initLegacyFilters from './init_legacy_filters';
import initIssuableSidebar from './init_issuable_sidebar';
// EE-only
import ApproversSelect from './approvers_select';
......@@ -166,6 +171,8 @@ import AuditLogs from './audit_logs';
new Issue();
shortcut_handler = new ShortcutsIssuable();
new ZenMode();
initIssuableSidebar();
initNotes();
break;
case 'dashboard:milestones:index':
new ProjectSelect();
......@@ -176,10 +183,12 @@ import AuditLogs from './audit_logs';
new Milestone();
new Sidebar();
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
case 'groups:issues':
case 'groups:merge_requests':
new UsersSelect();
new ProjectSelect();
initLegacyFilters();
break;
case 'dashboard:todos:index':
new Todos();
......@@ -259,7 +268,10 @@ import AuditLogs from './audit_logs';
case 'projects:tags:new':
new ZenMode();
new gl.GLForm($('.tag-form'), true);
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
new RefSelectDropdown($('.js-branch-select'));
break;
case 'projects:snippets:show':
initNotes();
break;
case 'projects:snippets:new':
case 'projects:snippets:edit':
......@@ -285,15 +297,12 @@ import AuditLogs from './audit_logs';
window.mergeRequest = new MergeRequest({
action: mrShowNode.dataset.mrAction,
});
initIssuableSidebar();
initNotes();
break;
case 'dashboard:activity':
new gl.Activities();
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
new ProjectSelect();
new UsersSelect();
break;
case 'projects:commit:show':
new Commit();
new gl.Diff();
......@@ -302,6 +311,7 @@ import AuditLogs from './audit_logs';
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
initNotes();
break;
case 'projects:commit:pipelines':
new MiniPipelineGraph({
......@@ -331,6 +341,9 @@ import AuditLogs from './audit_logs';
case 'projects:edit':
setupProjectEdit();
break;
case 'projects:pipelines:new':
new NewBranchForm($('.js-new-pipeline-form'));
break;
case 'projects:pipelines:builds':
case 'projects:pipelines:failures':
case 'projects:pipelines:show':
......@@ -384,6 +397,17 @@ import AuditLogs from './audit_logs';
shortcut_handler = new ShortcutsNavigation();
new TreeView();
new BlobViewer();
if (document.querySelector('.js-tree-content').dataset.pathLocksAvailable === 'true') {
PathLocks.init(
document.querySelector('.js-tree-content').dataset.pathLocksToggle,
document.querySelector('.js-tree-content').dataset.pathLocksPath,
);
}
$('#tree-slider').waitForImages(function() {
gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
break;
case 'projects:find_file:show':
shortcut_handler = true;
......@@ -401,10 +425,20 @@ import AuditLogs from './audit_logs';
case 'projects:labels:edit':
new Labels();
break;
case 'groups:labels:index':
case 'projects:labels:index':
if ($('.prioritized-labels').length) {
new gl.LabelManager();
}
$('.label-subscription').each((i, el) => {
const $el = $(el);
if ($el.find('.dropdown-group-label').length) {
new gl.GroupLabelSubscription($el);
} else {
new gl.ProjectLabelSubscription($el);
}
});
break;
case 'projects:network:show':
// Ensure we don't create a particular shortcut handler here. This is
......@@ -460,10 +494,15 @@ import AuditLogs from './audit_logs';
case 'snippets:show':
new LineHighlighter();
new BlobViewer();
initNotes();
break;
case 'import:fogbugz:new_user_map':
new UsersSelect();
break;
case 'profiles:personal_access_tokens:index':
case 'admin:impersonation_tokens:index':
new gl.DueDateSelectors();
break;
}
switch (path.first()) {
case 'sessions':
......@@ -541,6 +580,7 @@ import AuditLogs from './audit_logs';
shortcut_handler = new ShortcutsWiki();
new ZenMode();
new gl.GLForm($('.wiki-form'), true);
new Sidebar();
break;
case 'snippets':
shortcut_handler = new ShortcutsNavigation();
......
/* eslint-disable no-new */
/* global MilestoneSelect */
/* global LabelsSelect */
/* global WeightSelect */
/* global IssuableContext */
/* global Sidebar */
export default () => {
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
new MilestoneSelect({
full_path: sidebarOptions.fullPath,
});
new LabelsSelect();
new WeightSelect();
new IssuableContext(sidebarOptions.currentUser);
gl.Subscription.bindAll('.subscription');
new gl.DueDateSelectors();
window.sidebar = new Sidebar();
};
/* eslint-disable no-new */
/* global LabelsSelect */
/* global MilestoneSelect */
/* global IssueStatusSelect */
/* global SubscriptionSelect */
/* global WeightSelect */
import UsersSelect from './users_select';
export default () => {
new UsersSelect();
new LabelsSelect();
new MilestoneSelect();
new IssueStatusSelect();
new SubscriptionSelect();
new WeightSelect();
};
/* global Notes */
export default () => {
const dataEl = document.querySelector('.js-notes-data');
const {
notesUrl,
notesIds,
now,
diffView,
autocomplete,
} = JSON.parse(dataEl.innerHTML);
window.notes = new Notes(notesUrl, notesIds, now, diffView, autocomplete);
};
......@@ -53,7 +53,7 @@ export default {
return this.state && this.state.length > 0;
},
isOpen() {
return this.state === 'opened' || this.state === 'reopened';
return this.state === 'opened';
},
isClosed() {
return this.state === 'closed';
......
......@@ -4,6 +4,8 @@
import Cookies from 'js-cookie';
import UsersSelect from './users_select';
const PARTICIPANTS_ROW_COUNT = 7;
(function() {
this.IssuableContext = (function() {
function IssuableContext(currentUser) {
......@@ -50,11 +52,9 @@ import UsersSelect from './users_select';
}
IssuableContext.prototype.initParticipants = function() {
var _this;
_this = this;
$(document).on("click", ".js-participants-more", this.toggleHiddenParticipants);
return $(".js-participants-author").each(function(i) {
if (i >= _this.PARTICIPANTS_ROW_COUNT) {
if (i >= PARTICIPANTS_ROW_COUNT) {
return $(this).addClass("js-participants-hidden").hide();
}
});
......
......@@ -145,7 +145,6 @@ import './right_sidebar';
import './search';
import './search_autocomplete';
import './smart_interval';
import './snippets_list';
import './star';
import './subscription';
import './subscription_select';
......@@ -365,4 +364,14 @@ $(function () {
gl.utils.renderTimeago();
$(document).trigger('init.scrolling-tabs');
$('form.filter-form').on('submit', function (event) {
const link = document.createElement('a');
link.href = this.action;
const action = `${this.action}${link.search === '' ? '?' : '&'}`;
event.preventDefault();
gl.utils.visitUrl(`${action}${$(this).serialize()}`);
});
});
......@@ -2,6 +2,7 @@
/* global Flash */
import Vue from 'vue';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import './merge_conflict_service';
import './mixins/line_conflict_utils';
......@@ -19,6 +20,8 @@ $(() => {
resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath
});
initIssuableSidebar();
gl.MergeConflictsResolverApp = new Vue({
el: '#conflicts',
data: mergeConflictsStore.state,
......
......@@ -8,7 +8,7 @@
var _this, $els;
if (currentProject != null) {
_this = this;
this.currentProject = JSON.parse(currentProject);
this.currentProject = typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject;
}
$els = $(els);
......
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const buildChart = (chartScope) => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#7f8fa4',
strokeColor: '#7f8fa4',
pointColor: '#7f8fa4',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#44aa22',
strokeColor: '#44aa22',
pointColor: '#44aa22',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Line(data, options);
};
chartData.forEach(scope => buildChart(scope));
});
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
const data = {
labels: chartData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartData.values,
}],
};
const ctx = $('#build_timesChart').get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Bar(data, options);
});
......@@ -6,21 +6,27 @@ import Cookies from 'js-cookie';
(function() {
this.Project = (function() {
function Project() {
$('ul.clone-options-dropdown a').click(function() {
var url;
if ($(this).hasClass('active')) {
return;
}
$('.active').not($(this)).removeClass('active');
$(this).toggleClass('active');
url = $("#project_clone").val();
$('#project_clone').val(url);
const $cloneOptions = $('ul.clone-options-dropdown');
const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span');
$('a', $cloneOptions).on('click', (e) => {
const $this = $(e.currentTarget);
const url = $this.attr('href');
e.preventDefault();
$('.active', $cloneOptions).not($this).removeClass('active');
$this.toggleClass('active');
$projectCloneField.val(url);
$cloneBtnText.text($this.text());
$('#modal-geo-info').data({
cloneUrlSecondary: $this.attr('href'),
cloneUrlPrimary: $this.data('primaryUrl') || ''
});
return $('.clone').text(url);
// Git protocol switcher
// Remove the active class for all buttons (ssh, http, kerberos if shown)
// Add the active class for the clicked button
// Update the input field
// Update the command line instructions
});
// Ref switcher
this.initRefSwitcher();
......
document.addEventListener('DOMContentLoaded', () => {
const importBtnTooltip = 'Please enter a valid project name.';
const $importBtnWrapper = $('.import_gitlab_project');
$('.how_to_import_link').on('click', (e) => {
e.preventDefault();
$('.how_to_import_link').next('.modal').show();
});
$('.modal-header .close').on('click', () => {
$('.modal').hide();
});
$('.btn_import_gitlab_project').on('click', () => {
const importHref = $('a.btn_import_gitlab_project').attr('href');
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`);
});
$('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length);
$importBtnWrapper.attr('title', importBtnTooltip);
$('#new_project').on('submit', () => {
const $path = $('#project_path');
$path.val($path.val().trim());
});
$('#project_path').on('keyup', () => {
if ($('#project_path').val().trim().length) {
$('.btn_import_gitlab_project').attr('disabled', false);
$importBtnWrapper.attr('title', '');
$importBtnWrapper.removeClass('has-tooltip');
} else {
$('.btn_import_gitlab_project').attr('disabled', true);
$importBtnWrapper.addClass('has-tooltip');
}
});
$('#project_import_url').disable();
$('.import_git').on('click', () => {
const $projectImportUrl = $('#project_import_url');
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
});
});
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
const availableRefsValue = availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
$dropdownButton.glDropdown({
data: availableRefs,
data: availableRefsValue,
filterable: true,
filterByText: true,
remote: false,
......
......@@ -5,7 +5,8 @@ import sidebarAssignees from './components/assignees/sidebar_assignees';
import Mediator from './sidebar_mediator';
function domContentLoaded() {
const mediator = new Mediator(gl.sidebarOptions);
const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
const mediator = new Mediator(sidebarOptions);
mediator.fetch();
const sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
......
function SnippetsList() {
const $holder = $('.snippets-list-holder');
$holder.find('.pagination').on('ajax:success', (e, data) => {
$holder.replaceWith(data.html);
});
}
window.gl.SnippetsList = SnippetsList;
......@@ -37,10 +37,6 @@ export default class Todos {
this.initFilterDropdown($('.js-type-search'), 'type');
this.initFilterDropdown($('.js-action-search'), 'action_id');
$('form.filter-form').on('submit', function applyFilters(event) {
event.preventDefault();
gl.utils.visitUrl(`${this.action}&${$(this).serialize()}`);
});
return new UsersSelect();
}
......
......@@ -67,7 +67,7 @@ export default class MergeRequestStore {
this.mergeCheckPath = data.merge_check_path;
this.mergeActionsContentPath = data.commit_change_content_path;
this.isRemovingSourceBranch = this.isRemovingSourceBranch || false;
this.isOpen = data.state === 'opened' || data.state === 'reopened' || false;
this.isOpen = data.state === 'opened';
this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false;
this.canRemoveSourceBranch = currentUser.can_remove_source_branch || false;
this.canMerge = !!data.merge_path;
......
......@@ -315,6 +315,10 @@ header {
}
}
.with-performance-bar header.navbar-gitlab {
top: $performance-bar-height;
}
.navbar-nav {
li {
.badge {
......
......@@ -120,3 +120,7 @@ of the body element here, we negate cascading side effects but allow momentum sc
.page-with-sidebar {
-webkit-overflow-scrolling: auto;
}
.with-performance-bar .page-with-sidebar {
margin-top: $header-height + $performance-bar-height;
}
......@@ -374,6 +374,10 @@
}
}
.with-performance-bar .layout-nav {
margin-top: $header-height + $performance-bar-height;
}
.scrolling-tabs-container {
position: relative;
......@@ -468,6 +472,22 @@
}
}
.with-performance-bar .page-with-layout-nav {
.right-sidebar {
top: ($header-height + 1) * 2 + $performance-bar-height;
}
&.page-with-sub-nav {
.right-sidebar {
top: ($header-height + 1) * 3 + $performance-bar-height;
&.affix {
top: $header-height + $performance-bar-height;
}
}
}
}
.nav-block {
&.activities {
border-bottom: 1px solid $border-color;
......
......@@ -89,6 +89,10 @@
}
}
.with-performance-bar .right-sidebar.affix {
top: $header-height + $performance-bar-height;
}
@mixin maintain-sidebar-dimensions {
display: block;
width: $gutter-width;
......
......@@ -205,6 +205,8 @@ $divergence-graph-separator-bg: #ccc;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px;
$issue-box-upcoming-bg: #8f8f8f;
$pages-group-name-color: #4c4e54;
$ldap-members-override-bg: $orange-50;
......
......@@ -118,7 +118,7 @@ $new-sidebar-width: 220px;
z-index: 400;
width: $new-sidebar-width;
transition: left $sidebar-transition-duration;
top: 50px;
top: $header-height;
bottom: 0;
left: 0;
overflow: auto;
......@@ -163,6 +163,10 @@ $new-sidebar-width: 220px;
}
}
.with-performance-bar .nav-sidebar {
top: $header-height + $performance-bar-height;
}
.sidebar-sub-level-items {
display: none;
padding-bottom: 8px;
......@@ -260,7 +264,7 @@ $new-sidebar-width: 220px;
// Make issue boards full-height now that sub-nav is gone
.boards-list {
height: calc(100vh - 50px);
height: calc(100vh - #{$header-height});
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
......@@ -270,6 +274,10 @@ $new-sidebar-width: 220px;
}
}
.with-performance-bar .boards-list {
height: calc(100vh - #{$header-height} - #{$performance-bar-height});
}
// Change color of all horizontal tabs to match the new indigo color
.nav-links li.active a {
......
......@@ -64,10 +64,10 @@
color: $gl-text-color;
position: sticky;
position: -webkit-sticky;
top: 50px;
top: $header-height;
&.affix {
top: 50px;
top: $header-height;
}
// with sidebar
......@@ -171,6 +171,16 @@
}
}
.with-performance-bar .build-page {
.top-bar {
top: $header-height + $performance-bar-height;
&.affix {
top: $header-height + $performance-bar-height;
}
}
}
.build-header {
.ci-header-container,
.header-action-buttons {
......
......@@ -445,6 +445,14 @@
}
}
.with-performance-bar .right-sidebar {
top: $header-height + $performance-bar-height;
.issuable-sidebar {
height: calc(100% - #{$header-height} - #{$performance-bar-height});
}
}
.detail-page-description {
padding: 16px 0;
......
......@@ -759,6 +759,10 @@
}
}
.with-performance-bar .merge-request-tabs-holder {
top: $header-height + $performance-bar-height;
}
.merge-request-tabs {
display: flex;
margin-bottom: 0;
......
......@@ -3,9 +3,16 @@
@import "peek/views/rblineprof";
#peek {
height: 35px;
position: fixed;
left: 0;
top: 0;
width: 100%;
z-index: 2000;
overflow-x: hidden;
height: $performance-bar-height;
background: $black;
line-height: 35px;
line-height: $performance-bar-height;
color: $perf-bar-text;
&.disabled {
......@@ -25,7 +32,8 @@
}
.wrapper {
width: 1000px;
width: 80%;
height: $performance-bar-height;
margin: 0 auto;
}
......
......@@ -2,6 +2,7 @@ class Groups::LdapGroupLinksController < Groups::ApplicationController
before_action :group
before_action :require_ldap_enabled
before_action :authorize_admin_group!
before_action :authorize_manage_ldap_group_links!
layout 'group_settings'
......@@ -31,6 +32,12 @@ class Groups::LdapGroupLinksController < Groups::ApplicationController
private
def authorize_manage_ldap_group_links!
unless can?(current_user, :admin_ldap_group_links, group)
return render_404
end
end
def require_ldap_enabled
render_404 unless Gitlab.config.ldap.enabled
end
......
......@@ -86,7 +86,6 @@ class IssuableFinder
end
counts[:all] = counts.values.sum
counts[:opened] += counts[:reopened]
counts.with_indifferent_access
end
......
......@@ -268,7 +268,11 @@ module ApplicationHelper
end
def page_class
"issue-boards-page" if current_controller?(:boards)
class_names = []
class_names << 'issue-boards-page' if current_controller?(:boards)
class_names << 'with-performance-bar' if performance_bar_enabled?
class_names
end
# Returns active css class when condition returns true
......
......@@ -21,7 +21,8 @@ module EE
:slack_app_enabled,
:slack_app_id,
:slack_app_secret,
:slack_app_verification_token
:slack_app_verification_token,
:allow_group_owners_to_manage_ldap
]
end
......
......@@ -362,4 +362,14 @@ module IssuablesHelper
params[:format] = :json if issuable.is_a?(Issue)
end
end
def issuable_sidebar_options(issuable, can_edit_issuable)
{
endpoint: "#{issuable_json_path(issuable)}?basic=true",
editable: can_edit_issuable,
currentUser: current_user.as_json(only: [:username, :id, :name], methods: :avatar_url),
rootPath: root_path,
fullPath: @project.full_path
}
end
end
module MembersHelper
# Returns a `<action>_<source>_member` association, e.g.:
# - admin_project_member, update_project_member, destroy_project_member
# - admin_group_member, update_group_member, destroy_group_member
# - admin_group_member, update_group_member, destroy_group_member, override_group_member
def action_member_permission(action, member)
"#{action}_#{member.type.underscore}".to_sym
end
......
module NavHelper
def page_with_sidebar_class
class_name = page_gutter_class
class_name << 'page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar
class_name
end
def page_gutter_class
if current_path?('merge_requests#show') ||
current_path?('projects/merge_requests/conflicts#show') ||
current_path?('issues#show') ||
current_path?('milestones#show')
if cookies[:collapsed_gutter] == 'true'
"page-gutter right-sidebar-collapsed"
%w[page-gutter right-sidebar-collapsed]
else
"page-gutter right-sidebar-expanded"
%w[page-gutter right-sidebar-expanded]
end
elsif current_path?('jobs#show')
"page-gutter build-sidebar right-sidebar-expanded"
%w[page-gutter build-sidebar right-sidebar-expanded]
elsif current_path?('wikis#show') ||
current_path?('wikis#edit') ||
current_path?('wikis#update') ||
current_path?('wikis#history') ||
current_path?('wikis#git_access')
"page-gutter wiki-sidebar right-sidebar-expanded"
%w[page-gutter wiki-sidebar right-sidebar-expanded]
else
[]
end
end
def nav_header_class
class_name = ''
class_name << " with-horizontal-nav" if defined?(nav) && nav
class_names = []
class_names << 'with-horizontal-nav' if defined?(nav) && nav
class_name
class_names
end
def layout_nav_class
class_name = ''
class_name << " page-with-layout-nav" if defined?(nav) && nav
class_name << " page-with-sub-nav" if content_for?(:sub_nav)
return [] if show_new_nav?
class_name
class_names = []
class_names << 'page-with-layout-nav' if defined?(nav) && nav
class_names << 'page-with-sub-nav' if content_for?(:sub_nav)
class_names
end
def nav_control_class
......
......@@ -130,4 +130,14 @@ module NotesHelper
can?(current_user, :create_note, @project)
end
end
def initial_notes_data(autocomplete)
{
notesUrl: notes_url,
notesIds: @notes.map(&:id),
now: Time.now.to_i,
diffView: diff_view,
autocomplete: autocomplete
}
end
end
class Burndown
Issue = Struct.new(:closed_at, :weight, :state)
class Issue
attr_reader :closed_at, :weight, :state
def initialize(closed_at, weight, state)
@closed_at = closed_at
@weight = weight
@state = state
end
def reopened?
@state == 'opened' && @closed_at.present?
end
end
attr_reader :start_date, :due_date, :end_date, :issues_count, :issues_weight, :accurate, :legacy_data
alias_method :accurate?, :accurate
......@@ -12,8 +24,8 @@ class Burndown
@end_date = @milestone.due_date
@end_date = Date.today if @end_date.present? && @end_date > Date.today
@accurate = milestone_closed_issues.all?(&:closed_at)
@legacy_data = milestone_closed_issues.any? && milestone_closed_issues.none?(&:closed_at)
@accurate = milestone_issues.all?(&:closed_at)
@legacy_data = milestone_issues.any? && milestone_issues.none?(&:closed_at)
@issues_count, @issues_weight = milestone.issues.reorder(nil).pluck('COUNT(*), COALESCE(SUM(weight), 0)').first
end
......@@ -59,21 +71,21 @@ class Burndown
current_date = date.to_date
closed =
milestone_closed_issues.select do |issue|
milestone_issues.select do |issue|
(issue.closed_at&.to_date || start_date) == current_date
end
reopened = closed.select { |issue| issue.state == 'reopened' }
reopened = closed.select(&:reopened?)
[closed, reopened]
end
def milestone_closed_issues
@milestone_closed_issues ||=
def milestone_issues
@milestone_issues ||=
@milestone.issues
.where("state IN ('reopened', 'closed')")
.order("closed_at ASC")
.where("state = 'closed' OR (state = 'opened' AND closed_at IS NOT NULL)")
.reorder("closed_at ASC")
.pluck("closed_at, weight, state")
.map {|attrs| ::Burndown::Issue.new(*attrs) }
.map {|attrs| Issue.new(*attrs) }
end
end
......@@ -223,6 +223,7 @@ module Ci
variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group
variables += secret_variables(environment: environment)
variables += trigger_request.user_variables if trigger_request
variables += pipeline.variables.map(&:to_runner_variable)
variables += pipeline.pipeline_schedule.job_variables if pipeline.pipeline_schedule
variables += persisted_environment_variables if environment
......
......@@ -27,6 +27,7 @@ module Ci
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
......
module Ci
class PipelineVariable < ActiveRecord::Base
extend Ci::Model
include HasVariable
belongs_to :pipeline
validates :key, uniqueness: { scope: :pipeline_id }
end
end
......@@ -71,9 +71,8 @@ module Issuable
scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_milestones, ->(ids) { where(milestone_id: ids) }
scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
scope :opened, -> { with_state(:opened, :reopened) }
scope :opened, -> { with_state(:opened) }
scope :only_opened, -> { with_state(:opened) }
scope :only_reopened, -> { with_state(:reopened) }
scope :closed, -> { with_state(:closed) }
scope :order_milestone_due_desc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date DESC, milestones.id DESC') }
scope :order_milestone_due_asc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date ASC, milestones.id ASC') }
......@@ -241,7 +240,7 @@ module Issuable
end
def open?
opened? || reopened?
opened?
end
def user_notes_count
......
......@@ -39,7 +39,8 @@ module EE
repository_size_limit: 0,
mirror_max_delay: Settings.gitlab['mirror_max_delay'],
mirror_max_capacity: Settings.gitlab['mirror_max_capacity'],
mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold']
mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'],
allow_group_owners_to_manage_ldap: true
)
end
end
......
......@@ -73,15 +73,14 @@ class Issue < ActiveRecord::Base
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
transition [:opened] => :closed
end
event :reopen do
transition closed: :reopened
transition closed: :opened
end
state :opened
state :reopened
state :closed
before_transition any => :closed do |issue|
......
......@@ -45,23 +45,23 @@ class MergeRequest < ActiveRecord::Base
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
transition [:opened] => :closed
end
event :mark_as_merged do
transition [:reopened, :opened, :locked] => :merged
transition [:opened, :locked] => :merged
end
event :reopen do
transition closed: :reopened
transition closed: :opened
end
event :lock_mr do
transition [:reopened, :opened] => :locked
transition [:opened] => :locked
end
event :unlock_mr do
transition locked: :reopened
transition locked: :opened
end
after_transition any => :locked do |merge_request, transition|
......@@ -75,7 +75,6 @@ class MergeRequest < ActiveRecord::Base
end
state :opened
state :reopened
state :closed
state :merged
state :locked
......@@ -372,7 +371,7 @@ class MergeRequest < ActiveRecord::Base
errors.add :branch_conflict, "You can not use same project/branch for source and target"
end
if opened? || reopened?
if opened?
similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.try(:id)).opened
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any?
......
......@@ -114,7 +114,7 @@ class DroneCiService < CiService
end
def merge_request_valid?(data)
%w(opened reopened).include?(data[:object_attributes][:state]) &&
data[:object_attributes][:state] == 'opened' &&
data[:object_attributes][:merge_status] == 'unchecked'
end
end
......@@ -6,19 +6,19 @@ module EE
with_scope :subject
condition(:ldap_synced) { @subject.ldap_synced? }
rule { ldap_synced }.prevent :admin_group_member
rule { ldap_synced & admin }.policy do
enable :override_group_member
enable :update_group_member
end
rule { ldap_synced & owner }.policy do
enable :override_group_member
enable :update_group_member
condition(:can_owners_manage_ldap, scope: :global) do
current_application_settings.allow_group_owners_to_manage_ldap
end
rule { auditor }.enable :read_group
rule { admin | (can_owners_manage_ldap & owner) }.enable :admin_ldap_group_links
rule { ldap_synced }.prevent :admin_group_member
rule { ldap_synced & (admin | owner) }.enable :update_group_member
rule { ldap_synced & (admin | (can_owners_manage_ldap & owner)) }.enable :override_group_member
end
end
end
......@@ -2,7 +2,7 @@ module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, mirror_update: false, &block)
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, mirror_update: false)
@pipeline = Ci::Pipeline.new(
source: source,
project: project,
......@@ -22,7 +22,27 @@ module Ci
return result if result
_create_pipeline(source, &block)
begin
Ci::Pipeline.transaction do
pipeline.save!
yield(pipeline) if block_given?
Ci::CreatePipelineStagesService
.new(project, current_user)
.execute(pipeline)
end
rescue ActiveRecord::RecordInvalid => e
return error("Failed to persist the pipeline: #{e}")
end
update_merge_requests_head_pipeline
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
pipeline_created_counter.increment(source: source)
pipeline.tap(&:process!)
end
private
......@@ -69,24 +89,6 @@ module Ci
end
end
def _create_pipeline(source)
Ci::Pipeline.transaction do
update_merge_requests_head_pipeline if pipeline.save
yield(pipeline) if block_given?
Ci::CreatePipelineStagesService
.new(project, current_user)
.execute(pipeline)
end
cancel_pending_pipelines if project.auto_cancel_pending_pipelines?
pipeline_created_counter.increment(source: source)
pipeline.tap(&:process!)
end
def allowed_to_trigger_pipeline?(triggering_user)
if triggering_user
allowed_to_create?(triggering_user)
......
# This class is deprecated because we're closing Ci::TriggerRequest.
# New class is PipelineTriggerService (app/services/ci/pipeline_trigger_service.rb)
# which is integrated with Ci::PipelineVariable instaed of Ci::TriggerRequest.
# We remove this class after we removed v1 and v3 API. This class is still being
# referred by such legacy code.
module Ci
module CreateTriggerRequestService
Result = Struct.new(:trigger_request, :pipeline)
......
......@@ -14,9 +14,11 @@ module Ci
# this check is to not leak the presence of the project if user cannot read it
return unless trigger.project == project
trigger_request = trigger.trigger_requests.create(variables: params[:variables])
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref])
.execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
.execute(:trigger, ignore_skip_ci: true) do |pipeline|
trigger.trigger_requests.create!(pipeline: pipeline)
create_pipeline_variables!(pipeline)
end
if pipeline.persisted?
success(pipeline: pipeline)
......@@ -47,17 +49,27 @@ module Ci
error(pipeline.errors.messages, 400)
end
end
def trigger_from_token
return @trigger if defined?(@trigger)
@trigger = Ci::Trigger.find_by_token(params[:token].to_s)
end
def job_from_token
return @job if defined?(@job)
@job = Ci::Build.find_by_token(params[:token].to_s)
end
def create_pipeline_variables!(pipeline)
return unless params[:variables]
variables = params[:variables].map do |key, value|
{ key: key, value: value }
end
pipeline.variables.create!(variables)
end
end
end
......@@ -5,7 +5,7 @@ module Issues
if issue.reopen
event_service.reopen_issue(issue, current_user)
create_note(issue)
create_note(issue, 'reopened')
notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen')
invalidate_cache_counts(issue, users: issue.assignees)
......@@ -16,8 +16,8 @@ module Issues
private
def create_note(issue)
SystemNoteService.change_status(issue, issue.project, current_user, issue.state, nil)
def create_note(issue, state = issue.state)
SystemNoteService.change_status(issue, issue.project, current_user, state, nil)
end
end
end
......@@ -2,8 +2,8 @@ module MergeRequests
class BaseService < ::IssuableBaseService
prepend EE::MergeRequests::BaseService
def create_note(merge_request)
SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
def create_note(merge_request, state = merge_request.state)
SystemNoteService.change_status(merge_request, merge_request.target_project, current_user, state, nil)
end
def create_title_change_note(issuable, old_title)
......@@ -46,7 +46,7 @@ module MergeRequests
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
def merge_requests_for(source_branch, mr_states: [:opened, :reopened])
def merge_requests_for(source_branch, mr_states: [:opened])
MergeRequest
.with_state(mr_states)
.where(source_branch: source_branch, source_project_id: @project.id)
......
......@@ -82,7 +82,7 @@ module MergeRequests
# Note: Closed merge requests also need approvals reset.
def reset_approvals_for_merge_requests
merge_requests = merge_requests_for(@branch_name, mr_states: [:opened, :reopened, :closed])
merge_requests = merge_requests_for(@branch_name, mr_states: [:opened, :closed])
merge_requests.each do |merge_request|
target_project = merge_request.target_project
......
......@@ -5,7 +5,7 @@ module MergeRequests
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
create_note(merge_request, 'reopened')
notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user)
......
......@@ -48,6 +48,17 @@
= select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
%span.help-block#clone-protocol-help
Allow only the selected protocols to be used for Git access.
- if ldap_enabled?
.form-group
= f.label :allow_group_owners_to_manage_ldap, 'LDAP settings', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :allow_group_owners_to_manage_ldap do
= f.check_box :allow_group_owners_to_manage_ldap
Allow group owners to manage LDAP-related settings
%span.help-block
If checked, group owners can manage LDAP group links and LDAP member overrides
= link_to icon('question-circle'), help_page_path('administration/auth/ldap-ee')
%fieldset
%legend Account and Limit Settings
......
- if Gitlab::LDAP::Config.enabled_extras?
- if Gitlab::LDAP::Config.enabled_extras? && can?(current_user, :admin_ldap_group_links, @group)
= nav_link(path: 'ldap_group_links#index') do
= link_to group_ldap_group_links_path(@group), title: 'LDAP Group' do
%span
......
.page-with-sidebar{ class: "#{('page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar)} #{page_gutter_class}" }
.page-with-sidebar{ class: page_with_sidebar_class }
- if show_new_nav?
- if defined?(nav) && nav
= render "layouts/nav/#{nav}"
......@@ -9,7 +9,7 @@
= render "layouts/nav/#{nav}"
- if content_for?(:sub_nav)
= yield :sub_nav
.content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" }
.content-wrapper{ class: layout_nav_class }
- if show_new_nav?
.mobile-overlay
.alert-wrapper
......
!!! 5
%html{ lang: I18n.locale, class: "#{page_class}" }
%html{ lang: I18n.locale, class: page_class }
= render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar'
- if show_new_nav?
= render "layouts/header/new"
- else
......@@ -10,5 +11,3 @@
= render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body
= render 'peek/bar'
%span.current-host
= truncate(view.hostname)
......@@ -17,7 +17,11 @@
- if @remote_mirror.last_error.present?
.panel.panel-danger
.panel-heading
The remote repository failed to update #{time_ago_with_tooltip(@remote_mirror.last_update_at)}.
- if @remote_mirror.last_update_at
The remote repository failed to update #{time_ago_with_tooltip(@remote_mirror.last_update_at)}.
- else
The remote repository failed to update.
- if @remote_mirror.last_successful_update_at
Last successful update #{time_ago_with_tooltip(@remote_mirror.last_successful_update_at)}.
.panel-body
......
......@@ -4,6 +4,8 @@
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'project_new'
.project-edit-container
.project-edit-errors
......@@ -111,49 +113,3 @@
%i.fa.fa-spinner.fa-spin
Creating project &amp; repository.
%p Please wait a moment, this page will automatically refresh when ready.
:javascript
var importBtnTooltip = "Please enter a valid project name.";
var $importBtnWrapper = $('.import_gitlab_project');
$('.how_to_import_link').bind('click', function (e) {
e.preventDefault();
var import_modal = $(this).next(".modal").show();
});
$('.modal-header .close').bind('click', function() {
$(".modal").hide();
});
$('.btn_import_gitlab_project').bind('click', function() {
var _href = $("a.btn_import_gitlab_project").attr("href");
$(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
});
$('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0);
$importBtnWrapper.attr('title', importBtnTooltip);
$('#new_project').submit(function(){
var $path = $('#project_path');
$path.val($path.val().trim());
});
$('#project_path').keyup(function(){
if($(this).val().trim().length !== 0) {
$('.btn_import_gitlab_project').attr('disabled', false);
$importBtnWrapper.attr('title','');
$importBtnWrapper.removeClass('has-tooltip');
} else {
$('.btn_import_gitlab_project').attr('disabled',true);
$importBtnWrapper.addClass('has-tooltip');
}
});
$('#project_import_url').disable();
$('.import_git').click(function( event ) {
$projectImportUrl = $('#project_import_url');
$projectMirror = $('#project_mirror');
$projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
$projectMirror.attr('disabled', !$projectMirror.attr('disabled'));
});
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_times')
%div
%p.light
= _("Commit duration in minutes for last 30 commits")
%canvas#build_timesChart{ height: 200 }
:javascript
var data = {
labels : #{@charts[:pipeline_times].labels.to_json},
datasets : [
{
fillColor : "rgba(220,220,220,0.5)",
strokeColor : "rgba(220,220,220,1)",
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data : #{@charts[:pipeline_times].pipeline_times.to_json}
}
]
}
var ctx = $("#build_timesChart").get(0).getContext("2d");
var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8
}
new Chart(ctx).Bar(data, options);
%script#pipelinesTimesChartsData{ type: "application/json" }= { :labels => @charts[:pipeline_times].labels, :values => @charts[:pipeline_times].pipeline_times }.to_json.html_safe
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_charts')
%h4= _("Pipelines charts")
%p
&nbsp;
......@@ -26,31 +29,8 @@
= _("Jobs for last year")
%canvas#yearChart.padded{ height: 250 }
- [:week, :month, :year].each do |scope|
:javascript
var data = {
labels : #{@charts[scope].labels.to_json},
datasets : [
{
fillColor : "#7f8fa4",
strokeColor : "#7f8fa4",
pointColor : "#7f8fa4",
pointStrokeColor : "#EEE",
data : #{@charts[scope].total.to_json}
},
{
fillColor : "#44aa22",
strokeColor : "#44aa22",
pointColor : "#44aa22",
pointStrokeColor : "#fff",
data : #{@charts[scope].success.to_json}
}
]
}
var ctx = $("##{scope}Chart").get(0).getContext("2d");
var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8
}
new Chart(ctx).Line(data, options);
%script#pipelinesChartsData{ type: "application/json" }
- chartData = []
- [:week, :month, :year].each do |scope|
- chartData.push({ 'scope' => scope, 'labels' => @charts[scope].labels, 'totalValues' => @charts[scope].total, 'successValues' => @charts[scope].success })
= chartData.to_json.html_safe
......@@ -20,7 +20,4 @@
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel'
:javascript
var availableRefs = #{@project.repository.ref_names.to_json};
new NewBranchForm($('.js-new-pipeline-form'), availableRefs)
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
......@@ -40,7 +40,4 @@
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
:javascript
window.gl = window.gl || { };
window.gl.availableRefs = #{@project.repository.ref_names.to_json};
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
.tree-content-holder
.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path, 'data-path-locks-available': (@project.feature_available?(:file_locks) ? 'true' : 'false'), 'data-path-locks-toggle': toggle_project_path_locks_path(@project), 'data-path-locks-path': @path }
.table-holder
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
%thead
......@@ -22,17 +22,3 @@
- if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post
= render 'projects/blob/new_dir'
- if @project.feature_available?(:file_locks)
:javascript
PathLocks.init(
'#{toggle_project_path_locks_path(@project)}',
'#{@path}'
);
:javascript
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
gl.utils.ajaxGet("#{escape_javascript(@logs_path)}");
});
......@@ -19,6 +19,3 @@
More Pages
= render 'projects/wikis/new'
:javascript
new Sidebar();
......@@ -26,23 +26,4 @@
= geo_button(modal_target: '#modal-geo-info') if Gitlab::Geo.secondary?
:javascript
$('ul.clone-options-dropdown a').on('click',function(e){
e.preventDefault();
var $this = $(this);
$('a.clone-dropdown-btn span').text($this.text());
$('#project_clone').val($this.attr('href'));
});
// Change URLs in Geo Clone Dialog information text
$('ul.clone-options-dropdown a').on('click',function(e){
e.preventDefault();
var $this = $(this);
$('#modal-geo-info').data({
cloneUrlSecondary: $this.attr('href'),
cloneUrlPrimary: $this.data('primaryUrl') || ''
});
});
= render 'shared/geo_info_modal', project: project if Gitlab::Geo.secondary?
......@@ -76,11 +76,3 @@
= link_to destroy_label_path(label), title: "Delete", class: 'btn btn-transparent btn-action remove-row', method: :delete, data: {confirm: label_deletion_confirm_text(label), toggle: "tooltip"} do
%span.sr-only Delete
= icon('trash-o')
- if current_user
- if can_subscribe_to_label_in_different_levels?(label)
:javascript
new gl.GroupLabelSubscription('##{dom_id(label)} .label-subscription');
- else
:javascript
new gl.ProjectLabelSubscription('##{dom_id(label)} .label-subscription');
......@@ -23,18 +23,3 @@
.prepend-top-default
= f.submit "Create #{type} token", class: "btn btn-create"
:javascript
var $dateField = $('.datepicker');
var date = $dateField.val();
new Pikaday({
field: $dateField.get(0),
theme: 'gitlab-theme animate-picker',
format: 'yyyy-mm-dd',
minDate: new Date(),
container: $dateField.parent().get(0),
onSelect: function(dateText) {
$dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
......@@ -37,14 +37,3 @@
.row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) }
- if has_labels
= render 'shared/labels_row', labels: @labels
:javascript
new LabelsSelect();
new MilestoneSelect();
new IssueStatusSelect();
new WeightSelect();
new SubscriptionSelect();
$('form.filter-form').on('submit', function (event) {
event.preventDefault();
gl.utils.visitUrl(this.action + '&' + $(this).serialize());
});
......@@ -16,5 +16,3 @@
.hide-collapsed.participants-more
%a.js-participants-more{ href: "#", data: { original_text: "+ #{participants_size - 7} more", less_text: "- show less" } }
+ #{participants_extra} more
:javascript
IssuableContext.prototype.PARTICIPANTS_ROW_COUNT = #{participants_row};
......@@ -128,12 +128,3 @@
#js-add-issues-btn.prepend-left-10
- elsif type != :boards_modal
= render 'shared/sort_dropdown'
- unless type === :boards_modal
:javascript
$(document).off('page:restore').on('page:restore', function (event) {
if (gl.FilteredSearchManager) {
const filteredSearchManager = new gl.FilteredSearchManager();
filteredSearchManager.setup();
}
});
......@@ -165,18 +165,4 @@
= project_ref
= clipboard_button(text: project_ref, title: "Copy reference to clipboard", placement: "left")
:javascript
gl.sidebarOptions = {
endpoint: "#{issuable_json_path(issuable)}?basic=true",
editable: #{can_edit_issuable ? true : false},
currentUser: #{current_user.to_json(only: [:username, :id, :name], methods: :avatar_url)},
rootPath: "#{root_path}"
};
new MilestoneSelect('{"full_path":"#{@project.full_path}"}');
new LabelsSelect();
new WeightSelect();
new IssuableContext('#{escape_javascript(current_user.to_json(only: [:username, :id, :name]))}');
gl.Subscription.bindAll('.subscription');
new gl.DueDateSelectors();
window.sidebar = new Sidebar();
%script.js-sidebar-options{ type: "application/json" }= issuable_sidebar_options(issuable, can_edit_issuable).to_json.html_safe
......@@ -22,5 +22,4 @@
= link_to "sign in", new_session_path(:user, redirect_to_referer: 'yes')
to comment
:javascript
var notes = new Notes("#{notes_url}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}", #{autocomplete})
%script.js-notes-data{ type: "application/json" }= initial_notes_data(autocomplete).to_json.html_safe
- remote = local_assigns.fetch(:remote, false)
- link_project = local_assigns.fetch(:link_project, false)
.snippets-list-holder
......@@ -8,7 +7,4 @@
%li
.nothing-here-block Nothing here.
= paginate @snippets, theme: 'gitlab', remote: remote
:javascript
gl.SnippetsList();
= paginate @snippets, theme: 'gitlab'
---
title: Fix CSS for mini graph with downstream pipeline
merge_request:
author:
---
title: Add admin application setting to allow group owners to manage LDAP.
merge_request: 2529
author:
---
title: Adds lower bound to pull mirror scheduling feature
merge_request: 2366
author:
---
title: Renamed board to boards in new project sidebar
merge_request:
author:
---
title: Add warning and option toggle when rebuilding authorized_keys.
merge_request: 2508
author:
---
title: Fixed issue boards focus mode when new navigation is turned on
merge_request:
author:
---
title: Fix vertical alignment in firefox and safari for pipeline mini graph
merge_request:
author:
---
title: Fix crash on /help/ui
merge_request:
author:
---
title: Pending delete projects should not show in deploy keys.
merge_request: 13088
author:
---
title: Fixes 500 error caused by pending delete projects in admin dashboard
merge_request: 13067
author:
---
title: Allow admin to read_users_list even if it's restricted
merge_request: 13066
author:
---
title: Add instrumentation to MarkupHelper#link_to_gfm
merge_request: 13069
author:
---
title: Fix job merge request link to a forked source project
merge_request: 12965
author:
---
title: Fix Rebasing not working with Merge Requests
title: Merge issuable "reopened" state into "opened"
merge_request:
author:
---
title: Fix sizing of custom header logo in new navigation
merge_request:
author:
......@@ -57,8 +57,11 @@ var config = {
notebook_viewer: './blob/notebook_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
pipelines: './pipelines/pipelines_bundle.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
pipelines_charts: './pipelines/pipelines_charts.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js',
project_new: './projects/project_new.js',
prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches',
ee_protected_branches: './protected_branches/ee',
......
class CreateCiPipelineVariables < ActiveRecord::Migration
DOWNTIME = false
def up
create_table :ci_pipeline_variables do |t|
t.string :key, null: false
t.text :value
t.text :encrypted_value
t.string :encrypted_value_salt
t.string :encrypted_value_iv
t.integer :pipeline_id, null: false
end
add_index :ci_pipeline_variables, [:pipeline_id, :key], unique: true
end
def down
drop_table :ci_pipeline_variables
end
end
class AddForeignKeyToCiPipelineVariables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key(:ci_pipeline_variables, :ci_pipelines, column: :pipeline_id)
end
def down
remove_foreign_key(:ci_pipeline_variables, column: :pipeline_id)
end
end
class AddRestrictGroupOwnersToAdminsOptionToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :allow_group_owners_to_manage_ldap, :boolean, default: true)
end
def down
remove_column(:application_settings, :allow_group_owners_to_manage_ldap)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class MergeIssuableReopenedIntoOpenedState < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
class Issue < ActiveRecord::Base
self.table_name = 'issues'
include EachBatch
end
class MergeRequest < ActiveRecord::Base
self.table_name = 'merge_requests'
include EachBatch
end
def up
[Issue, MergeRequest].each do |model|
say "Changing #{model.table_name}.state from 'reopened' to 'opened'"
model.where(state: 'reopened').each_batch do |batch|
batch.update_all(state: 'opened')
end
end
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170725145659) do
ActiveRecord::Schema.define(version: 20170726111039) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -149,6 +149,7 @@ ActiveRecord::Schema.define(version: 20170725145659) do
t.string "slack_app_verification_token"
t.integer "performance_bar_allowed_group_id"
t.boolean "password_authentication_enabled"
t.boolean "allow_group_owners_to_manage_ldap", default: true, null: false
end
create_table "approvals", force: :cascade do |t|
......@@ -356,6 +357,17 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_index "ci_pipeline_schedules", ["next_run_at", "active"], name: "index_ci_pipeline_schedules_on_next_run_at_and_active", using: :btree
add_index "ci_pipeline_schedules", ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree
create_table "ci_pipeline_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
t.integer "pipeline_id", null: false
end
add_index "ci_pipeline_variables", ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree
create_table "ci_pipelines", force: :cascade do |t|
t.string "ref"
t.string "sha"
......@@ -1937,6 +1949,7 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
add_foreign_key "ci_pipelines", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_3d34ab2e06", on_delete: :nullify
add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
......
......@@ -33,7 +33,8 @@ describe Admin::ApplicationSettingsController do # rubocop:disable RSpec/FilePat
slack_app_enabled: true,
slack_app_id: 'slack_app_id',
slack_app_secret: 'slack_app_secret',
slack_app_verification_token: 'slack_app_verification_token'
slack_app_verification_token: 'slack_app_verification_token',
allow_group_owners_to_manage_ldap: false
}
put :update, application_setting: settings
......
FactoryGirl.define do
factory :ci_pipeline_variable, class: Ci::PipelineVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
pipeline factory: :ci_empty_pipeline
end
end
......@@ -17,12 +17,8 @@ FactoryGirl.define do
closed_at Time.now
end
trait :reopened do
state :reopened
end
factory :closed_issue, traits: [:closed]
factory :reopened_issue, traits: [:reopened]
factory :reopened_issue, traits: [:opened]
factory :labeled_issue do
transient do
......
......@@ -44,10 +44,6 @@ FactoryGirl.define do
state :opened
end
trait :reopened do
state :reopened
end
trait :locked do
state :locked
end
......@@ -80,7 +76,7 @@ FactoryGirl.define do
factory :merged_merge_request, traits: [:merged]
factory :closed_merge_request, traits: [:closed]
factory :reopened_merge_request, traits: [:reopened]
factory :reopened_merge_request, traits: [:opened]
factory :merge_request_with_diffs, traits: [:with_diffs]
factory :merge_request_with_approver, traits: [:with_approver]
factory :merge_request_with_diff_notes do
......
......@@ -3,56 +3,80 @@ FactoryGirl.define do
name
project
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
end
transient do
# EE
authorize_user_to_push nil
authorize_user_to_merge nil
authorize_group_to_push nil
authorize_group_to_merge nil
end
trait :remove_default_access_levels do
after(:build) do |protected_branch|
protected_branch.push_access_levels = []
protected_branch.merge_access_levels = []
end
default_push_level true
default_merge_level true
default_access_level true
end
trait :developers_can_push do
after(:create) do |protected_branch|
protected_branch.push_access_levels.create!(access_level: Gitlab::Access::DEVELOPER)
transient do
default_push_level false
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
trait :developers_can_merge do
after(:create) do |protected_branch|
protected_branch.merge_access_levels.create!(access_level: Gitlab::Access::DEVELOPER)
transient do
default_merge_level false
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
trait :no_one_can_push do
after(:create) do |protected_branch|
protected_branch.push_access_levels.create!(access_level: Gitlab::Access::NO_ACCESS)
transient do
default_push_level false
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
trait :masters_can_push do
after(:create) do |protected_branch|
protected_branch.push_access_levels.create!(access_level: Gitlab::Access::MASTER)
transient do
default_push_level false
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
end
end
after(:create) do |protected_branch, evaluator|
protected_branch.push_access_levels.create!(user: evaluator.authorize_user_to_push) if evaluator.authorize_user_to_push
protected_branch.merge_access_levels.create!(user: evaluator.authorize_user_to_merge) if evaluator.authorize_user_to_merge
after(:build) do |protected_branch, evaluator|
# EE
if user = evaluator.authorize_user_to_push
protected_branch.push_access_levels.new(user: user)
end
if user = evaluator.authorize_user_to_merge
protected_branch.merge_access_levels.new(user: user)
end
if group = evaluator.authorize_group_to_push
protected_branch.push_access_levels.new(group: group)
end
if group = evaluator.authorize_group_to_merge
protected_branch.merge_access_levels.new(group: group)
end
next unless protected_branch.merge_access_levels.empty?
protected_branch.push_access_levels.create!(group: evaluator.authorize_group_to_push) if evaluator.authorize_group_to_push
protected_branch.merge_access_levels.create!(group: evaluator.authorize_group_to_merge) if evaluator.authorize_group_to_merge
if evaluator.default_access_level && evaluator.default_push_level
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MASTER)
end
if evaluator.default_access_level && evaluator.default_merge_level
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MASTER)
end
end
end
end
......@@ -3,42 +3,56 @@ FactoryGirl.define do
name
project
after(:build) do |protected_tag|
protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
end
transient do
# EE
authorize_user_to_create nil
authorize_group_to_create nil
end
trait :remove_default_access_levels do
after(:build) do |protected_tag|
protected_tag.create_access_levels = []
end
default_access_level true
end
trait :developers_can_create do
after(:create) do |protected_tag|
protected_tag.create_access_levels.create!(access_level: Gitlab::Access::DEVELOPER)
transient do
default_access_level false
end
after(:build) do |protected_tag|
protected_tag.create_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
trait :no_one_can_create do
after(:create) do |protected_tag|
protected_tag.create_access_levels.create!(access_level: Gitlab::Access::NO_ACCESS)
transient do
default_access_level false
end
after(:build) do |protected_tag|
protected_tag.create_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
trait :masters_can_create do
after(:create) do |protected_tag|
protected_tag.create_access_levels.create!(access_level: Gitlab::Access::MASTER)
transient do
default_access_level false
end
after(:build) do |protected_tag|
protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
end
end
after(:create) do |protected_tag, evaluator|
protected_tag.create_access_levels.create!(user: evaluator.authorize_user_to_create) if evaluator.authorize_user_to_create
protected_tag.create_access_levels.create!(group: evaluator.authorize_group_to_create) if evaluator.authorize_group_to_create
after(:build) do |protected_tag, evaluator|
# EE
if evaluator.authorize_user_to_create
protected_tag.create_access_levels.new(user: evaluator.authorize_user_to_create)
end
if evaluator.authorize_group_to_create
protected_tag.create_access_levels.new(group: evaluator.authorize_group_to_create)
end
if evaluator.default_access_level
protected_tag.create_access_levels.new(access_level: Gitlab::Access::MASTER)
end
end
end
end
......@@ -29,6 +29,29 @@ feature 'Admin updates settings' do
expect(find('#application_setting_visibility_level_20')).not_to be_checked
end
describe 'LDAP settings' do
context 'with LDAP enabled' do
scenario 'Change allow group owners to manage ldap' do
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
visit admin_application_settings_path
find('#application_setting_allow_group_owners_to_manage_ldap').set(false)
click_button 'Save'
expect(page).to have_content('Application settings saved successfully')
expect(find('#application_setting_allow_group_owners_to_manage_ldap')).not_to be_checked
end
end
context 'with LDAP disabled' do
scenario 'Does not show option to allow group owners to manage ldap' do
visit admin_application_settings_path
expect(page).not_to have_css('#application_setting_allow_group_owners_to_manage_ldap')
end
end
end
scenario 'Change application settings' do
uncheck 'Gravatar enabled'
fill_in 'Home page URL', with: 'https://about.gitlab.com/'
......
......@@ -9,6 +9,31 @@ feature 'Edit group settings' do
sign_in(user)
end
describe 'navbar' do
context 'with LDAP enabled' do
before do
allow_any_instance_of(Group).to receive(:ldap_synced?).and_return(true)
allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
end
scenario 'is able to navigate to LDAP group section' do
visit edit_group_path(group)
expect(find('div.sub-nav')).to have_content('LDAP Group')
end
context 'with owners not being able to manage LDAP' do
scenario 'is not able to navigate to LDAP group section' do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
visit edit_group_path(group)
expect(find('div.sub-nav')).not_to have_content('LDAP Group')
end
end
end
end
describe 'when the group path is changed' do
let(:new_group_path) { 'bar' }
let(:old_group_full_path) { "/#{group.path}" }
......
......@@ -26,6 +26,18 @@ feature 'Groups > Members > Master/Owner can override LDAP access levels' do
expect(page).not_to have_button 'Edit permissions'
end
scenario 'owner cannot override LDAP access level', js: true do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
visit group_group_members_path(group)
within "#group_member_#{ldap_member.id}" do
expect(page).not_to have_content 'LDAP'
expect(page).not_to have_button 'Guest'
expect(page).not_to have_button 'Edit permissions'
end
end
scenario 'owner can override LDAP access level', js: true do
ldap_override_message = 'John Doe is currently an LDAP user. Editing their permissions will override the settings from the LDAP group sync.'
......
require 'spec_helper'
feature 'Project remote mirror', feature: true do
let(:project) { create(:project, :remote_mirror) }
let(:remote_mirror) { project.remote_mirrors.first }
let(:user) { create(:user) }
describe 'On a project', js: true do
before do
project.team << [user, :master]
sign_in user
end
context 'when last_error is present but last_update_at is not' do
it 'renders error message without timstamp' do
remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: nil)
visit project_mirror_path(project)
expect(page).to have_content('The remote repository failed to update.')
end
end
context 'when last_error and last_update_at are present' do
it 'renders error message with timestamp' do
remote_mirror.update_attributes(last_error: 'Some new error', last_update_at: Time.now - 5.minutes)
visit project_mirror_path(project)
expect(page).to have_content('The remote repository failed to update 5 minutes ago.')
end
end
end
end
......@@ -107,14 +107,6 @@ describe Banzai::Filter::IssuableStateFilter do
expect(doc.css('a').last.text).to eq(issue.to_reference)
end
it 'ignores reopened issue references' do
issue = create_issue(:reopened)
link = create_link(issue.to_reference, issue: issue.id, reference_type: 'issue')
doc = filter(link, context)
expect(doc.css('a').last.text).to eq(issue.to_reference)
end
it 'appends state to closed issue references' do
link = create_link(closed_issue.to_reference, issue: closed_issue.id, reference_type: 'issue')
doc = filter(link, context)
......@@ -139,7 +131,7 @@ describe Banzai::Filter::IssuableStateFilter do
end
it 'ignores reopened merge request references' do
merge_request = create_merge_request(:reopened)
merge_request = create_merge_request(:opened)
link = create_link(
merge_request.to_reference,
......
......@@ -556,7 +556,7 @@ describe Gitlab::GitAccess do
[%w(feature exact), ['feat*', 'wildcard']].each do |protected_branch_name, protected_branch_type|
context do
before do
create(:protected_branch, :remove_default_access_levels, :masters_can_push, name: protected_branch_name, project: project)
create(:protected_branch, :masters_can_push, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix)
......@@ -564,7 +564,7 @@ describe Gitlab::GitAccess do
context "when developers are allowed to push into the #{protected_branch_type} protected branch" do
before do
create(:protected_branch, :remove_default_access_levels, :masters_can_push, :developers_can_push, name: protected_branch_name, project: project)
create(:protected_branch, :masters_can_push, :developers_can_push, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
......@@ -572,7 +572,7 @@ describe Gitlab::GitAccess do
context "developers are allowed to merge into the #{protected_branch_type} protected branch" do
before do
create(:protected_branch, :remove_default_access_levels, :masters_can_push, :developers_can_merge, name: protected_branch_name, project: project)
create(:protected_branch, :masters_can_push, :developers_can_merge, name: protected_branch_name, project: project)
end
context "when a merge request exists for the given source/target branch" do
......@@ -601,7 +601,7 @@ describe Gitlab::GitAccess do
context "when developers are allowed to push and merge into the #{protected_branch_type} protected branch" do
before do
create(:protected_branch, :remove_default_access_levels, :masters_can_push, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project)
create(:protected_branch, :masters_can_push, :developers_can_merge, :developers_can_push, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
......@@ -612,7 +612,7 @@ describe Gitlab::GitAccess do
let(:user) { create(:user) }
before do
create(:protected_branch, :remove_default_access_levels, authorize_user_to_push: user, name: protected_branch_name, project: project)
create(:protected_branch, authorize_user_to_push: user, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true },
......@@ -625,7 +625,7 @@ describe Gitlab::GitAccess do
before do
create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
create(:protected_branch, :remove_default_access_levels, authorize_user_to_merge: user, name: protected_branch_name, project: project)
create(:protected_branch, authorize_user_to_merge: user, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(admin: { push_protected_branch: false, push_all: false, merge_into_protected_branch: true },
......@@ -640,7 +640,7 @@ describe Gitlab::GitAccess do
before do
create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
create(:protected_branch, :remove_default_access_levels, authorize_user_to_push: user, authorize_user_to_merge: user, name: protected_branch_name, project: project)
create(:protected_branch, authorize_user_to_push: user, authorize_user_to_merge: user, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true },
......@@ -656,7 +656,7 @@ describe Gitlab::GitAccess do
before do
group.add_master(user)
create(:protected_branch, :remove_default_access_levels, authorize_group_to_push: group, name: protected_branch_name, project: project)
create(:protected_branch, authorize_group_to_push: group, name: protected_branch_name, project: project)
end
permissions = permissions_matrix.except(:admin).deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true },
......@@ -673,7 +673,7 @@ describe Gitlab::GitAccess do
before do
group.add_master(user)
create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
create(:protected_branch, :remove_default_access_levels, authorize_group_to_merge: group, name: protected_branch_name, project: project)
create(:protected_branch, authorize_group_to_merge: group, name: protected_branch_name, project: project)
end
permissions = permissions_matrix.except(:admin).deep_merge(master: { push_protected_branch: false, push_all: false, merge_into_protected_branch: true },
......@@ -691,7 +691,7 @@ describe Gitlab::GitAccess do
before do
group.add_master(user)
create(:merge_request, source_project: project, source_branch: unprotected_branch, target_branch: 'feature', state: 'locked', in_progress_merge_commit_sha: merge_into_protected_branch)
create(:protected_branch, :remove_default_access_levels, authorize_group_to_push: group, authorize_group_to_merge: group, name: protected_branch_name, project: project)
create(:protected_branch, authorize_group_to_push: group, authorize_group_to_merge: group, name: protected_branch_name, project: project)
end
permissions = permissions_matrix.except(:admin).deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true },
......@@ -704,7 +704,7 @@ describe Gitlab::GitAccess do
context "when no one is allowed to push to the #{protected_branch_name} protected branch" do
before do
create(:protected_branch, :remove_default_access_levels, :no_one_can_push, name: protected_branch_name, project: project)
create(:protected_branch, :no_one_can_push, name: protected_branch_name, project: project)
end
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: false, push_all: false, merge_into_protected_branch: false },
......
......@@ -106,6 +106,7 @@ pipelines:
- statuses
- builds
- trigger_requests
- variables
- auto_canceled_by
- auto_canceled_pipelines
- auto_canceled_jobs
......@@ -120,6 +121,8 @@ pipelines:
- sourced_pipelines
- triggered_by_pipeline
- triggered_pipelines
pipeline_variables:
- pipeline
stages:
- project
- pipeline
......
......@@ -63,14 +63,14 @@ describe Burndown do
expect(burndown).to be_accurate
end
context "when all closed and reopened issues does not have closed_at" do
context "when all closed issues does not have closed_at" do
before do
milestone.issues.update_all(closed_at: nil)
end
it "considers closed_at as milestone start date" do
expect(subject).to eq([
["2017-03-01", 15, 30],
["2017-03-01", 27, 54],
["2017-03-02", 27, 54],
["2017-03-03", 27, 54],
["2017-03-04", 27, 54],
......@@ -85,7 +85,7 @@ describe Burndown do
end
end
context "when one or more closed or reopened issues does not have closed_at" do
context "when one or more closed issues does not have closed_at" do
before do
milestone.issues.closed.first.update(closed_at: nil)
end
......
......@@ -1487,6 +1487,12 @@ describe Ci::Build do
it { is_expected.to include(predefined_trigger_variable) }
end
context 'when pipeline has a variable' do
let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline) }
it { is_expected.to include(pipeline_variable.to_runner_variable) }
end
context 'when a job was triggered by a pipeline schedule' do
let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
......
......@@ -17,6 +17,7 @@ describe Ci::Pipeline do
it { is_expected.to have_many(:statuses) }
it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:builds) }
it { is_expected.to have_many(:auto_canceled_pipelines) }
it { is_expected.to have_many(:auto_canceled_jobs) }
......
require 'spec_helper'
describe Ci::PipelineVariable, models: true do
subject { build(:ci_pipeline_variable) }
it { is_expected.to include_module(HasVariable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:pipeline_id) }
end
......@@ -16,8 +16,8 @@ describe ProtectedBranch do
context "while checking uniqueness of a role-based #{human_association_name}" do
it "allows a single #{human_association_name} for a role (per protected branch)" do
first_protected_branch = create(:protected_branch, :remove_default_access_levels)
second_protected_branch = create(:protected_branch, :remove_default_access_levels)
first_protected_branch = create(:protected_branch, default_access_level: false)
second_protected_branch = create(:protected_branch, default_access_level: false)
first_protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
second_protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
......@@ -31,7 +31,7 @@ describe ProtectedBranch do
end
it "does not count a user-based #{human_association_name} with an `access_level` set" do
protected_branch = create(:protected_branch, :remove_default_access_levels)
protected_branch = create(:protected_branch, default_access_level: false)
protected_branch.send(association_name) << build(factory_name, user: user, access_level: Gitlab::Access::MASTER)
protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
......@@ -41,7 +41,7 @@ describe ProtectedBranch do
it "does not count a group-based #{human_association_name} with an `access_level` set" do
group = create(:group)
protected_branch = create(:protected_branch, :remove_default_access_levels)
protected_branch = create(:protected_branch, default_access_level: false)
protected_branch.send(association_name) << build(factory_name, group: group, access_level: Gitlab::Access::MASTER)
protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
......@@ -52,8 +52,8 @@ describe ProtectedBranch do
context "while checking uniqueness of a user-based #{human_association_name}" do
it "allows a single #{human_association_name} for a user (per protected branch)" do
first_protected_branch = create(:protected_branch, :remove_default_access_levels)
second_protected_branch = create(:protected_branch, :remove_default_access_levels)
first_protected_branch = create(:protected_branch, default_access_level: false)
second_protected_branch = create(:protected_branch, default_access_level: false)
first_protected_branch.send(association_name) << build(factory_name, user: user)
second_protected_branch.send(association_name) << build(factory_name, user: user)
......@@ -67,7 +67,7 @@ describe ProtectedBranch do
end
it "ignores the `access_level` while validating a user-based #{human_association_name}" do
protected_branch = create(:protected_branch, :remove_default_access_levels)
protected_branch = create(:protected_branch, default_access_level: false)
protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
protected_branch.send(association_name) << build(factory_name, user: user, access_level: Gitlab::Access::MASTER)
......@@ -80,8 +80,8 @@ describe ProtectedBranch do
let(:group) { create(:group) }
it "allows a single #{human_association_name} for a group (per protected branch)" do
first_protected_branch = create(:protected_branch, :remove_default_access_levels)
second_protected_branch = create(:protected_branch, :remove_default_access_levels)
first_protected_branch = create(:protected_branch, default_access_level: false)
second_protected_branch = create(:protected_branch, default_access_level: false)
first_protected_branch.send(association_name) << build(factory_name, group: group)
second_protected_branch.send(association_name) << build(factory_name, group: group)
......@@ -95,7 +95,7 @@ describe ProtectedBranch do
end
it "ignores the `access_level` while validating a group-based #{human_association_name}" do
protected_branch = create(:protected_branch, :remove_default_access_levels)
protected_branch = create(:protected_branch, default_access_level: false)
protected_branch.send(association_name) << build(factory_name, access_level: Gitlab::Access::MASTER)
protected_branch.send(association_name) << build(factory_name, group: group, access_level: Gitlab::Access::MASTER)
......
......@@ -25,12 +25,22 @@ describe GroupPolicy do
let(:current_user) { owner }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
context 'does not allow group owners to manage ldap' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
end
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
end
end
......@@ -43,42 +53,59 @@ describe GroupPolicy do
let(:current_user) { nil }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'guests' do
let(:current_user) { guest }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'reporter' do
let(:current_user) { reporter }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'developer' do
let(:current_user) { developer }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'master' do
let(:current_user) { master }
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
context 'owner' do
let(:current_user) { owner }
it { is_expected.to be_allowed(:override_group_member) }
context 'allow group owners to manage ldap' do
it { is_expected.to be_allowed(:override_group_member) }
end
context 'does not allow group owners to manage ldap' do
before do
stub_application_setting(allow_group_owners_to_manage_ldap: false)
end
it { is_expected.to be_disallowed(:override_group_member) }
it { is_expected.to be_disallowed(:admin_ldap_group_links) }
end
end
context 'admin' do
let(:current_user) { admin }
it { is_expected.to be_allowed(:override_group_member) }
it { is_expected.to be_allowed(:admin_ldap_group_links) }
end
end
end
......@@ -1263,7 +1263,7 @@ describe API::Issues do
put api("/projects/#{project.id}/issues/#{closed_issue.iid}", user), state_event: 'reopen'
expect(response).to have_http_status(200)
expect(json_response['state']).to eq 'reopened'
expect(json_response['state']).to eq 'opened'
end
context 'when an admin or owner makes the request' do
......
......@@ -22,6 +22,7 @@ describe API::Triggers do
before do
stub_ci_pipeline_to_return_yaml_file
trigger.update(owner: user)
end
context 'Handles errors' do
......@@ -55,8 +56,7 @@ describe API::Triggers do
post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'other-branch')
expect(response).to have_http_status(400)
expect(json_response['message']['base'])
.to contain_exactly('Reference not found')
expect(json_response['message']).to eq('base' => ["Reference not found"])
end
context 'Validates variables' do
......@@ -82,7 +82,7 @@ describe API::Triggers do
post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: variables, ref: 'master')
expect(response).to have_http_status(201)
expect(pipeline.builds.reload.first.trigger_request.variables).to eq(variables)
expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables)
end
end
end
......
......@@ -1116,7 +1116,7 @@ describe API::V3::Issues do
put v3_api("/projects/#{project.id}/issues/#{closed_issue.id}", user), state_event: 'reopen'
expect(response).to have_http_status(200)
expect(json_response['state']).to eq 'reopened'
expect(json_response['state']).to eq 'opened'
end
context 'when an admin or owner makes the request' do
......
......@@ -23,7 +23,7 @@ describe Boards::Issues::ListService do
let!(:opened_issue1) { create(:labeled_issue, project: project, milestone: m1, title: 'Issue 1', labels: [bug]) }
let!(:opened_issue2) { create(:labeled_issue, project: project, milestone: m2, title: 'Issue 2', labels: [p2]) }
let!(:reopened_issue1) { create(:issue, :reopened, project: project, title: 'Issue 3' ) }
let!(:reopened_issue1) { create(:issue, :opened, project: project, title: 'Issue 3' ) }
let!(:list1_issue1) { create(:labeled_issue, project: project, milestone: m1, labels: [p2, development]) }
let!(:list1_issue2) { create(:labeled_issue, project: project, milestone: m2, labels: [development]) }
......
......@@ -73,7 +73,7 @@ describe Boards::Issues::MoveService do
issue.reload
expect(issue.labels).to contain_exactly(bug, testing)
expect(issue).to be_reopened
expect(issue).to be_opened
end
end
......
......@@ -532,7 +532,6 @@ describe Ci::CreatePipelineService do
context 'when no one can create the tag' do
let!(:protected_tag) do
create(:protected_tag,
:remove_default_access_levels,
:no_one_can_create,
project: project,
name: ref)
......
require 'spec_helper'
describe Ci::PipelineTriggerService, services: true do
let(:project) { create(:project, :repository) }
before do
stub_ci_pipeline_to_return_yaml_file
end
describe '#execute' do
let(:user) { create(:user) }
let(:trigger) { create(:ci_trigger, project: project, owner: user) }
let(:result) { described_class.new(project, user, params).execute }
before do
project.add_developer(user)
end
context 'when trigger belongs to a different project' do
let(:params) { { token: trigger.token, ref: 'master', variables: nil } }
let(:trigger) { create(:ci_trigger, project: create(:empty_project), owner: user) }
it 'does nothing' do
expect { result }.not_to change { Ci::Pipeline.count }
end
end
context 'when params have an existsed trigger token' do
context 'when params have an existsed ref' do
let(:params) { { token: trigger.token, ref: 'master', variables: nil } }
it 'triggers a pipeline' do
expect { result }.to change { Ci::Pipeline.count }.by(1)
expect(result[:pipeline].ref).to eq('master')
expect(result[:pipeline].project).to eq(project)
expect(result[:pipeline].user).to eq(trigger.owner)
expect(result[:status]).to eq(:success)
end
context 'when commit message has [ci skip]' do
before do
allow_any_instance_of(Ci::Pipeline).to receive(:git_commit_message) { '[ci skip]' }
end
it 'ignores [ci skip] and create as general' do
expect { result }.to change { Ci::Pipeline.count }.by(1)
expect(result[:status]).to eq(:success)
end
end
context 'when params have a variable' do
let(:params) { { token: trigger.token, ref: 'master', variables: variables } }
let(:variables) { { 'AAA' => 'AAA123' } }
it 'has a variable' do
expect { result }.to change { Ci::PipelineVariable.count }.by(1)
.and change { Ci::TriggerRequest.count }.by(1)
expect(result[:pipeline].variables.map { |v| { v.key => v.value } }.first).to eq(variables)
expect(result[:pipeline].trigger_requests.last.variables).to be_nil
end
end
end
context 'when params have a non-existsed ref' do
let(:params) { { token: trigger.token, ref: 'invalid-ref', variables: nil } }
it 'does not trigger a pipeline' do
expect { result }.not_to change { Ci::Pipeline.count }
expect(result[:http_status]).to eq(400)
end
end
end
context 'when params have a non-existsed trigger token' do
let(:params) { { token: 'invalid-token', ref: nil, variables: nil } }
it 'does not trigger a pipeline' do
expect { result }.not_to change { Ci::Pipeline.count }
expect(result).to be_nil
end
end
end
end
......@@ -43,7 +43,7 @@ describe DeleteMergedBranchesService do
context 'open merge requests' do
it 'does not delete branches from open merge requests' do
fork_link = create(:forked_project_link, forked_from_project: project)
create(:merge_request, :reopened, source_project: project, target_project: project, source_branch: 'branch-merged', target_branch: 'master')
create(:merge_request, :opened, source_project: project, target_project: project, source_branch: 'branch-merged', target_branch: 'master')
create(:merge_request, :opened, source_project: fork_link.forked_to_project, target_project: project, target_branch: 'improve/awesome', source_branch: 'master')
service.execute
......
......@@ -271,7 +271,6 @@ describe GitPushService do
stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
create(:protected_branch, :no_one_can_push, :developers_can_merge,
:remove_default_access_levels,
project: project, name: 'master')
expect(project).to receive(:execute_hooks)
expect(project.default_branch).to eq("master")
......
......@@ -35,7 +35,7 @@ describe Issues::ExportCsvService do
issue.update!(milestone: milestone,
assignees: [user],
description: 'Issue with details',
state: :reopened,
state: :opened,
due_date: DateTime.new(2014, 3, 2),
created_at: DateTime.new(2015, 4, 3, 2, 1, 0),
updated_at: DateTime.new(2016, 5, 4, 3, 2, 1),
......
......@@ -78,7 +78,7 @@ describe MergeRequests::GetUrlsService do
end
context 'pushing to existing branch and merge request is reopened' do
let!(:merge_request) { create(:merge_request, :reopened, source_project: project, source_branch: source_branch) }
let!(:merge_request) { create(:merge_request, :opened, source_project: project, source_branch: source_branch) }
let(:changes) { existing_branch_changes }
it_behaves_like 'show_merge_request_url'
end
......
......@@ -86,7 +86,7 @@ describe MergeRequests::RefreshService do
let(:refresh_service) { service.new(@project, @user) }
before do
@merge_request.update(state: :reopened)
@merge_request.update(state: :opened)
allow(refresh_service).to receive(:execute_hooks)
refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
......
......@@ -28,7 +28,7 @@ describe MergeRequests::ReopenService do
end
it { expect(merge_request).to be_valid }
it { expect(merge_request).to be_reopened }
it { expect(merge_request).to be_opened }
it 'executes hooks with reopen action' do
expect(service).to have_received(:execute_hooks)
......
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