Commit fee26d77 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into new-nav-fix-contextual-breadcrumbs

parents 15a28238 1a3edcec
...@@ -12,7 +12,7 @@ entry. ...@@ -12,7 +12,7 @@ entry.
## 9.3.4 (2017-07-03) ## 9.3.4 (2017-07-03)
- No changes. - Update gitlab-shell to 5.1.1 !12615
## 9.3.3 (2017-06-30) ## 9.3.3 (2017-06-30)
......
9.3.0-pre 9.4.0-pre
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import './lib/utils/url_utility'; import './lib/utils/url_utility';
import FilesCommentButton from './files_comment_button'; import FilesCommentButton from './files_comment_button';
import SingleFileDiff from './single_file_diff';
const UNFOLD_COUNT = 20; const UNFOLD_COUNT = 20;
let isBound = false; let isBound = false;
...@@ -10,7 +11,11 @@ class Diff { ...@@ -10,7 +11,11 @@ class Diff {
constructor() { constructor() {
const $diffFile = $('.files .diff-file'); const $diffFile = $('.files .diff-file');
$diffFile.singleFileDiff(); $diffFile.each((index, file) => {
if (!$.data(file, 'singleFileDiff')) {
$.data(file, 'singleFileDiff', new SingleFileDiff(file));
}
});
FilesCommentButton.init($diffFile); FilesCommentButton.init($diffFile);
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
/* global ProjectSelect */ /* global ProjectSelect */
/* global UsernameValidator */
/* global ActiveTabMemoizer */
/* global ShortcutsNavigation */ /* global ShortcutsNavigation */
/* global IssuableIndex */ /* global IssuableIndex */
/* global ShortcutsIssuable */ /* global ShortcutsIssuable */
/* global ZenMode */
/* global Milestone */ /* global Milestone */
/* global IssuableForm */ /* global IssuableForm */
/* global LabelsSelect */ /* global LabelsSelect */
/* global MilestoneSelect */ /* global MilestoneSelect */
/* global Commit */ /* global Commit */
/* global NotificationsForm */ /* global NotificationsForm */
/* global TreeView */
/* global NotificationsDropdown */ /* global NotificationsDropdown */
/* global GroupAvatar */ /* global GroupAvatar */
/* global LineHighlighter */ /* global LineHighlighter */
...@@ -26,7 +22,6 @@ ...@@ -26,7 +22,6 @@
/* global ProjectAvatar */ /* global ProjectAvatar */
/* global CompareAutocomplete */ /* global CompareAutocomplete */
/* global ProjectNew */ /* global ProjectNew */
/* global Star */
/* global ProjectShow */ /* global ProjectShow */
/* global Labels */ /* global Labels */
/* global Shortcuts */ /* global Shortcuts */
...@@ -55,9 +50,19 @@ import UsersSelect from './users_select'; ...@@ -55,9 +50,19 @@ import UsersSelect from './users_select';
import RefSelectDropdown from './ref_select_dropdown'; import RefSelectDropdown from './ref_select_dropdown';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import ShortcutsBlob from './shortcuts_blob'; import ShortcutsBlob from './shortcuts_blob';
import SigninTabsMemoizer from './signin_tabs_memoizer';
import Star from './star';
import Todos from './todos';
import TreeView from './tree';
import UsagePing from './usage_ping';
import UsernameValidator from './username_validator';
import VersionCheckImage from './version_check_image';
import Wikis from './wikis';
import ZenMode from './zen_mode';
import initSettingsPanels from './settings_panels'; import initSettingsPanels from './settings_panels';
import initExperimentalFlags from './experimental_flags'; import initExperimentalFlags from './experimental_flags';
import OAuthRememberMe from './oauth_remember_me'; import OAuthRememberMe from './oauth_remember_me';
import PerformanceBar from './performance_bar';
(function() { (function() {
var Dispatcher; var Dispatcher;
...@@ -128,7 +133,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -128,7 +133,7 @@ import OAuthRememberMe from './oauth_remember_me';
break; break;
case 'sessions:new': case 'sessions:new':
new UsernameValidator(); new UsernameValidator();
new ActiveTabMemoizer(); new SigninTabsMemoizer();
new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents(); new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents();
break; break;
case 'projects:boards:show': case 'projects:boards:show':
...@@ -168,7 +173,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -168,7 +173,7 @@ import OAuthRememberMe from './oauth_remember_me';
new ProjectSelect(); new ProjectSelect();
break; break;
case 'dashboard:todos:index': case 'dashboard:todos:index':
new gl.Todos(); new Todos();
break; break;
case 'dashboard:projects:index': case 'dashboard:projects:index':
case 'dashboard:projects:starred': case 'dashboard:projects:starred':
...@@ -323,7 +328,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -323,7 +328,7 @@ import OAuthRememberMe from './oauth_remember_me';
new gl.Members(); new gl.Members();
new UsersSelect(); new UsersSelect();
break; break;
case 'projects:settings:members:show': case 'projects:project_members:index':
new gl.MemberExpirationDate('.js-access-expiration-date-groups'); new gl.MemberExpirationDate('.js-access-expiration-date-groups');
new GroupsSelect(); new GroupsSelect();
new gl.MemberExpirationDate(); new gl.MemberExpirationDate();
...@@ -385,7 +390,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -385,7 +390,7 @@ import OAuthRememberMe from './oauth_remember_me';
new BlobViewer(); new BlobViewer();
break; break;
case 'help:index': case 'help:index':
gl.VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); VersionCheckImage.bindErrorEvent($('img.js-version-status-badge'));
break; break;
case 'search:show': case 'search:show':
new Search(); new Search();
...@@ -401,6 +406,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -401,6 +406,7 @@ import OAuthRememberMe from './oauth_remember_me';
initSettingsPanels(); initSettingsPanels();
break; break;
case 'projects:settings:ci_cd:show': case 'projects:settings:ci_cd:show':
case 'groups:settings:ci_cd:show':
new gl.ProjectVariables(); new gl.ProjectVariables();
break; break;
case 'ci:lints:create': case 'ci:lints:create':
...@@ -437,7 +443,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -437,7 +443,7 @@ import OAuthRememberMe from './oauth_remember_me';
new Admin(); new Admin();
switch (path[1]) { switch (path[1]) {
case 'cohorts': case 'cohorts':
new gl.UsagePing(); new UsagePing();
break; break;
case 'groups': case 'groups':
new UsersSelect(); new UsersSelect();
...@@ -489,7 +495,7 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -489,7 +495,7 @@ import OAuthRememberMe from './oauth_remember_me';
new NotificationsDropdown(); new NotificationsDropdown();
break; break;
case 'wikis': case 'wikis':
new gl.Wikis(); new Wikis();
shortcut_handler = new ShortcutsWiki(); shortcut_handler = new ShortcutsWiki();
new ZenMode(); new ZenMode();
new gl.GLForm($('.wiki-form'), true); new gl.GLForm($('.wiki-form'), true);
...@@ -521,6 +527,10 @@ import OAuthRememberMe from './oauth_remember_me'; ...@@ -521,6 +527,10 @@ import OAuthRememberMe from './oauth_remember_me';
if (!shortcut_handler) { if (!shortcut_handler) {
new Shortcuts(); new Shortcuts();
} }
if (document.querySelector('#peek')) {
new PerformanceBar({ container: '#peek' });
}
}; };
Dispatcher.prototype.initSearch = function() { Dispatcher.prototype.initSearch = function() {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */
/* global GitLab */ /* global GitLab */
/* global ZenMode */
/* global Autosave */ /* global Autosave */
/* global dateFormat */ /* global dateFormat */
/* global Pikaday */ /* global Pikaday */
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode';
(function() { (function() {
this.IssuableForm = (function() { this.IssuableForm = (function() {
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
import 'vendor/jquery.waitforimages'; import 'vendor/jquery.waitforimages';
import '~/lib/utils/text_utility'; import '~/lib/utils/text_utility';
import './flash'; import './flash';
import './task_list'; import TaskList from './task_list';
import CreateMergeRequestDropdown from './create_merge_request_dropdown'; import CreateMergeRequestDropdown from './create_merge_request_dropdown';
class Issue { class Issue {
constructor() { constructor() {
if ($('a.btn-close').length) { if ($('a.btn-close').length) {
this.taskList = new gl.TaskList({ this.taskList = new TaskList({
dataType: 'issue', dataType: 'issue',
fieldName: 'description', fieldName: 'description',
selector: '.detail-page-description', selector: '.detail-page-description',
......
<script> <script>
import animateMixin from '../mixins/animate'; import animateMixin from '../mixins/animate';
import TaskList from '../../task_list';
export default { export default {
mixins: [animateMixin], mixins: [animateMixin],
...@@ -46,7 +47,7 @@ ...@@ -46,7 +47,7 @@
if (this.canUpdate) { if (this.canUpdate) {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new gl.TaskList({ new TaskList({
dataType: 'issue', dataType: 'issue',
fieldName: 'description', fieldName: 'description',
selector: '.detail-page-description', selector: '.detail-page-description',
......
...@@ -143,26 +143,13 @@ import './render_math'; ...@@ -143,26 +143,13 @@ import './render_math';
import './right_sidebar'; import './right_sidebar';
import './search'; import './search';
import './search_autocomplete'; import './search_autocomplete';
import './signin_tabs_memoizer';
import './single_file_diff';
import './smart_interval'; import './smart_interval';
import './snippets_list'; import './snippets_list';
import './star'; import './star';
import './subscription'; import './subscription';
import './subscription_select'; import './subscription_select';
import './syntax_highlight'; import './syntax_highlight';
import './task_list';
import './todos';
import './tree';
import './usage_ping';
import './user'; import './user';
import './user_tabs';
import './username_validator';
import './users_select';
import './version_check_image';
import './visibility_select';
import './wikis';
import './zen_mode';
// eslint-disable-next-line global-require, import/no-commonjs // eslint-disable-next-line global-require, import/no-commonjs
if (process.env.NODE_ENV !== 'production') require('./test_utils/'); if (process.env.NODE_ENV !== 'production') require('./test_utils/');
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* global MergeRequestTabs */ /* global MergeRequestTabs */
import 'vendor/jquery.waitforimages'; import 'vendor/jquery.waitforimages';
import './task_list'; import TaskList from './task_list';
import './merge_request_tabs'; import './merge_request_tabs';
(function() { (function() {
...@@ -25,7 +25,7 @@ import './merge_request_tabs'; ...@@ -25,7 +25,7 @@ import './merge_request_tabs';
this.initMRBtnListeners(); this.initMRBtnListeners();
this.initCommitMessageListeners(); this.initCommitMessageListeners();
if ($("a.btn-close").length) { if ($("a.btn-close").length) {
this.taskList = new gl.TaskList({ this.taskList = new TaskList({
dataType: 'merge_request', dataType: 'merge_request',
fieldName: 'description', fieldName: 'description',
selector: '.detail-page-description', selector: '.detail-page-description',
......
...@@ -105,9 +105,9 @@ ...@@ -105,9 +105,9 @@
this.measurements = measurements.small; this.measurements = measurements.small;
} }
this.data = query.result[0].values; this.data = query.result[0].values;
this.unitOfDisplay = query.unit || 'N/A'; this.unitOfDisplay = query.unit || '';
this.yAxisLabel = this.columnData.y_label || 'Values'; this.yAxisLabel = this.columnData.y_label || 'Values';
this.legendTitle = query.legend || 'Average'; this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth - this.graphWidth = this.$refs.baseSvg.clientWidth -
this.margin.left - this.margin.right; this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom; this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
...@@ -219,16 +219,16 @@ ...@@ -219,16 +219,16 @@
}; };
</script> </script>
<template> <template>
<div <div
:class="classType"> :class="classType">
<h5 <h5
class="text-center graph-title"> class="text-center graph-title">
{{columnData.title}} {{columnData.title}}
</h5> </h5>
<div <div
class="prometheus-svg-container" class="prometheus-svg-container"
:style="paddingBottomRootSvg"> :style="paddingBottomRootSvg">
<svg <svg
:viewBox="outterViewBox" :viewBox="outterViewBox"
ref="baseSvg"> ref="baseSvg">
<g <g
...@@ -239,7 +239,7 @@ ...@@ -239,7 +239,7 @@
class="y-axis" class="y-axis"
transform="translate(70, 20)"> transform="translate(70, 20)">
</g> </g>
<monitoring-legends <monitoring-legends
:graph-width="graphWidth" :graph-width="graphWidth"
:graph-height="graphHeight" :graph-height="graphHeight"
:margin="margin" :margin="margin"
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
:y-axis-label="yAxisLabel" :y-axis-label="yAxisLabel"
:metric-usage="metricUsage" :metric-usage="metricUsage"
/> />
<svg <svg
class="graph-data" class="graph-data"
:viewBox="innerViewBox" :viewBox="innerViewBox"
ref="graphData"> ref="graphData">
...@@ -267,7 +267,7 @@ ...@@ -267,7 +267,7 @@
stroke-width="2" stroke-width="2"
transform="translate(-5, 20)"> transform="translate(-5, 20)">
</path> </path>
<rect <rect
class="prometheus-graph-overlay" class="prometheus-graph-overlay"
:width="(graphWidth - 70)" :width="(graphWidth - 70)"
:height="(graphHeight - 100)" :height="(graphHeight - 100)"
...@@ -281,7 +281,7 @@ ...@@ -281,7 +281,7 @@
:graph-height="graphHeight" :graph-height="graphHeight"
:graph-height-offset="graphHeightOffset" :graph-height-offset="graphHeightOffset"
/> />
<monitoring-flag <monitoring-flag
v-if="showFlag" v-if="showFlag"
:current-x-coordinate="currentXCoordinate" :current-x-coordinate="currentXCoordinate"
:current-y-coordinate="currentYCoordinate" :current-y-coordinate="currentYCoordinate"
......
...@@ -21,7 +21,7 @@ import CommentTypeToggle from './comment_type_toggle'; ...@@ -21,7 +21,7 @@ import CommentTypeToggle from './comment_type_toggle';
import loadAwardsHandler from './awards_handler'; import loadAwardsHandler from './awards_handler';
import './autosave'; import './autosave';
import './dropzone_input'; import './dropzone_input';
import './task_list'; import TaskList from './task_list';
window.autosize = autosize; window.autosize = autosize;
window.Dropzone = Dropzone; window.Dropzone = Dropzone;
...@@ -71,7 +71,7 @@ export default class Notes { ...@@ -71,7 +71,7 @@ export default class Notes {
this.addBinding(); this.addBinding();
this.setPollingInterval(); this.setPollingInterval();
this.setupMainTargetNoteForm(); this.setupMainTargetNoteForm();
this.taskList = new gl.TaskList({ this.taskList = new TaskList({
dataType: 'note', dataType: 'note',
fieldName: 'note', fieldName: 'note',
selector: '.notes' selector: '.notes'
......
import 'vendor/peek';
import 'vendor/peek.performance_bar';
$(document).on('click', '#peek-show-queries', (e) => {
e.preventDefault();
$('.peek-rblineprof-modal').hide();
const $modal = $('#modal-peek-pg-queries');
if ($modal.length) {
$modal.modal('toggle');
}
});
$(document).on('click', '.js-lineprof-file', (e) => {
e.preventDefault();
$(e.target).parents('.peek-rblineprof-file').find('.data').toggle();
});
import 'vendor/peek';
import 'vendor/peek.performance_bar';
export default class PerformanceBar {
constructor(opts) {
if (!PerformanceBar.singleton) {
this.init(opts);
PerformanceBar.singleton = this;
}
return PerformanceBar.singleton;
}
init(opts) {
const $container = $(opts.container);
this.$sqlProfileLink = $container.find('.js-toggle-modal-peek-sql');
this.$sqlProfileModal = $container.find('#modal-peek-pg-queries');
this.$lineProfileLink = $container.find('.js-toggle-modal-peek-line-profile');
this.$lineProfileModal = $('#modal-peek-line-profile');
this.initEventListeners();
this.showModalOnLoad();
}
initEventListeners() {
this.$sqlProfileLink.on('click', () => this.handleSQLProfileLink());
this.$lineProfileLink.on('click', e => this.handleLineProfileLink(e));
$(document).on('click', '.js-lineprof-file', PerformanceBar.toggleLineProfileFile);
}
showModalOnLoad() {
// When a lineprofiler query-string param is present, we show the line
// profiler modal upon page load
if (/lineprofiler/.test(window.location.search)) {
PerformanceBar.toggleModal(this.$lineProfileModal);
}
}
handleSQLProfileLink() {
PerformanceBar.toggleModal(this.$sqlProfileModal);
}
handleLineProfileLink(e) {
const lineProfilerParameter = gl.utils.getParameterValues('lineprofiler');
const lineProfilerParameterRegex = new RegExp(`lineprofiler=${lineProfilerParameter[0]}`);
const shouldToggleModal = lineProfilerParameter.length > 0 &&
lineProfilerParameterRegex.test(e.currentTarget.href);
if (shouldToggleModal) {
e.preventDefault();
PerformanceBar.toggleModal(this.$lineProfileModal);
}
}
static toggleModal($modal) {
if ($modal.length) {
$modal.modal('toggle');
}
}
static toggleLineProfileFile(e) {
$(e.currentTarget).parents('.peek-rblineprof-file').find('.data').toggle();
}
}
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */
import VisibilitySelect from './visibility_select';
function highlightChanges($elm) { function highlightChanges($elm) {
$elm.addClass('highlight-changes'); $elm.addClass('highlight-changes');
setTimeout(() => $elm.removeClass('highlight-changes'), 10); setTimeout(() => $elm.removeClass('highlight-changes'), 10);
...@@ -30,7 +32,7 @@ function highlightChanges($elm) { ...@@ -30,7 +32,7 @@ function highlightChanges($elm) {
ProjectNew.prototype.initVisibilitySelect = function() { ProjectNew.prototype.initVisibilitySelect = function() {
const visibilityContainer = document.querySelector('.js-visibility-select'); const visibilityContainer = document.querySelector('.js-visibility-select');
if (!visibilityContainer) return; if (!visibilityContainer) return;
const visibilitySelect = new gl.VisibilitySelect(visibilityContainer); const visibilitySelect = new VisibilitySelect(visibilityContainer);
visibilitySelect.init(); visibilitySelect.init();
const $visibilitySelect = $(visibilityContainer).find('select'); const $visibilitySelect = $(visibilityContainer).find('select');
......
...@@ -6,7 +6,7 @@ import AccessorUtilities from './lib/utils/accessor'; ...@@ -6,7 +6,7 @@ import AccessorUtilities from './lib/utils/accessor';
* Memorize the last selected tab after reloading a page. * Memorize the last selected tab after reloading a page.
* Does that setting the current selected tab in the localStorage * Does that setting the current selected tab in the localStorage
*/ */
class ActiveTabMemoizer { export default class SigninTabsMemoizer {
constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) { constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) {
this.currentTabKey = currentTabKey; this.currentTabKey = currentTabKey;
this.tabSelector = tabSelector; this.tabSelector = tabSelector;
...@@ -51,5 +51,3 @@ class ActiveTabMemoizer { ...@@ -51,5 +51,3 @@ class ActiveTabMemoizer {
return window.localStorage.getItem(this.currentTabKey); return window.localStorage.getItem(this.currentTabKey);
} }
} }
window.ActiveTabMemoizer = ActiveTabMemoizer;
...@@ -2,18 +2,13 @@ ...@@ -2,18 +2,13 @@
import FilesCommentButton from './files_comment_button'; import FilesCommentButton from './files_comment_button';
window.SingleFileDiff = (function() { const WRAPPER = '<div class="diff-content"></div>';
var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER; const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
WRAPPER = '<div class="diff-content"></div>'; export default class SingleFileDiff {
constructor(file) {
LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
function SingleFileDiff(file) {
this.file = file; this.file = file;
this.toggleDiff = this.toggleDiff.bind(this); this.toggleDiff = this.toggleDiff.bind(this);
this.content = $('.diff-content', this.file); this.content = $('.diff-content', this.file);
...@@ -37,7 +32,7 @@ window.SingleFileDiff = (function() { ...@@ -37,7 +32,7 @@ window.SingleFileDiff = (function() {
}).bind(this)); }).bind(this));
} }
SingleFileDiff.prototype.toggleDiff = function($target, cb) { toggleDiff($target, cb) {
if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return; if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
this.isOpen = !this.isOpen; this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) { if (!this.isOpen && !this.hasError) {
...@@ -58,9 +53,9 @@ window.SingleFileDiff = (function() { ...@@ -58,9 +53,9 @@ window.SingleFileDiff = (function() {
this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right'); this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
return this.getContentHTML(cb); return this.getContentHTML(cb);
} }
}; }
SingleFileDiff.prototype.getContentHTML = function(cb) { getContentHTML(cb) {
this.collapsedContent.hide(); this.collapsedContent.hide();
this.loadingContent.show(); this.loadingContent.show();
$.get(this.diffForPath, (function(_this) { $.get(this.diffForPath, (function(_this) {
...@@ -84,15 +79,5 @@ window.SingleFileDiff = (function() { ...@@ -84,15 +79,5 @@ window.SingleFileDiff = (function() {
if (cb) cb(); if (cb) cb();
}; };
})(this)); })(this));
}; }
}
return SingleFileDiff;
})();
$.fn.singleFileDiff = function() {
return this.each(function() {
if (!$.data(this, 'singleFileDiff')) {
return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this));
}
});
};
/* eslint-disable arrow-parens, no-param-reassign, space-before-function-paren, func-names, no-var, max-len */ function SnippetsList() {
const $holder = $('.snippets-list-holder');
window.gl.SnippetsList = function() {
var $holder = $('.snippets-list-holder');
$holder.find('.pagination').on('ajax:success', (e, data) => { $holder.find('.pagination').on('ajax:success', (e, data) => {
$holder.replaceWith(data.html); $holder.replaceWith(data.html);
}); });
}; }
window.gl.SnippetsList = SnippetsList;
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */
/* global Flash */ /* global Flash */
window.Star = (function() { export default class Star {
function Star() { constructor() {
$('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) { $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) {
var $starIcon, $starSpan, $this, toggleStar; var $starIcon, $starSpan, $this, toggleStar;
$this = $(this); $this = $(this);
...@@ -23,6 +23,4 @@ window.Star = (function() { ...@@ -23,6 +23,4 @@ window.Star = (function() {
new Flash('Star toggle failed. Try again later.', 'alert'); new Flash('Star toggle failed. Try again later.', 'alert');
}); });
} }
}
return Star;
})();
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */
window.SubscriptionSelect = (function() { class SubscriptionSelect {
function SubscriptionSelect() { constructor() {
$('.js-subscription-event').each(function(i, el) { $('.js-subscription-event').each(function(i, el) {
var fieldName; var fieldName;
fieldName = $(el).data("field-name"); fieldName = $(el).data("field-name");
...@@ -28,6 +28,6 @@ window.SubscriptionSelect = (function() { ...@@ -28,6 +28,6 @@ window.SubscriptionSelect = (function() {
}); });
}); });
} }
}
return SubscriptionSelect; window.SubscriptionSelect = SubscriptionSelect;
})();
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import 'deckar01-task_list'; import 'deckar01-task_list';
class TaskList { export default class TaskList {
constructor(options = {}) { constructor(options = {}) {
this.selector = options.selector; this.selector = options.selector;
this.dataType = options.dataType; this.dataType = options.dataType;
...@@ -48,6 +48,3 @@ class TaskList { ...@@ -48,6 +48,3 @@ class TaskList {
}); });
} }
} }
window.gl = window.gl || {};
window.gl.TaskList = TaskList;
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import UsersSelect from './users_select'; import UsersSelect from './users_select';
class Todos { export default class Todos {
constructor() { constructor() {
this.initFilters(); this.initFilters();
this.bindEvents(); this.bindEvents();
...@@ -159,6 +159,3 @@ class Todos { ...@@ -159,6 +159,3 @@ class Todos {
} }
} }
} }
window.gl = window.gl || {};
gl.Todos = Todos;
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, class-methods-use-this */
window.TreeView = (function() { export default class TreeView {
function TreeView() { constructor() {
this.initKeyNav(); this.initKeyNav();
// Code browser tree slider // Code browser tree slider
// Make the entire tree-item row clickable, but not if clicking another link (like a commit message) // Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
...@@ -22,7 +22,7 @@ window.TreeView = (function() { ...@@ -22,7 +22,7 @@ window.TreeView = (function() {
$('span.log_loading:first').removeClass('hide'); $('span.log_loading:first').removeClass('hide');
} }
TreeView.prototype.initKeyNav = function() { initKeyNav() {
var li, liSelected; var li, liSelected;
li = $("tr.tree-item"); li = $("tr.tree-item");
liSelected = null; liSelected = null;
...@@ -60,7 +60,5 @@ window.TreeView = (function() { ...@@ -60,7 +60,5 @@ window.TreeView = (function() {
} }
} }
}); });
}; }
}
return TreeView;
})();
function UsagePing() { export default function UsagePing() {
const usageDataUrl = $('.usage-data').data('endpoint'); const usageDataUrl = $('.usage-data').data('endpoint');
$.ajax({ $.ajax({
...@@ -10,6 +10,3 @@ function UsagePing() { ...@@ -10,6 +10,3 @@ function UsagePing() {
}, },
}); });
} }
window.gl = window.gl || {};
window.gl.UsagePing = UsagePing;
/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */ /* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import UserTabs from './user_tabs';
class User { class User {
constructor({ action }) { constructor({ action }) {
...@@ -17,7 +18,7 @@ class User { ...@@ -17,7 +18,7 @@ class User {
} }
initTabs() { initTabs() {
return new window.gl.UserTabs({ return new UserTabs({
parentEl: '.user-profile', parentEl: '.user-profile',
action: this.action action: this.action
}); });
......
...@@ -60,7 +60,7 @@ content on the Users#show page. ...@@ -60,7 +60,7 @@ content on the Users#show page.
</div> </div>
*/ */
class UserTabs { export default class UserTabs {
constructor ({ defaultAction, action, parentEl }) { constructor ({ defaultAction, action, parentEl }) {
this.loaded = {}; this.loaded = {};
this.defaultAction = defaultAction || 'activity'; this.defaultAction = defaultAction || 'activity';
...@@ -171,6 +171,3 @@ class UserTabs { ...@@ -171,6 +171,3 @@ class UserTabs {
return this.$parentEl.find('.nav-links .active a').data('action'); return this.$parentEl.find('.nav-links .active a').data('action');
} }
} }
window.gl = window.gl || {};
window.gl.UserTabs = UserTabs;
...@@ -8,7 +8,7 @@ const successMessageSelector = '.username .validation-success'; ...@@ -8,7 +8,7 @@ const successMessageSelector = '.username .validation-success';
const pendingMessageSelector = '.username .validation-pending'; const pendingMessageSelector = '.username .validation-pending';
const invalidMessageSelector = '.username .gl-field-error'; const invalidMessageSelector = '.username .gl-field-error';
class UsernameValidator { export default class UsernameValidator {
constructor() { constructor() {
this.inputElement = $('#new_user_username'); this.inputElement = $('#new_user_username');
this.inputDomElement = this.inputElement.get(0); this.inputDomElement = this.inputElement.get(0);
...@@ -129,5 +129,3 @@ class UsernameValidator { ...@@ -129,5 +129,3 @@ class UsernameValidator {
$inputErrorMessage.show(); $inputErrorMessage.show();
} }
} }
window.UsernameValidator = UsernameValidator;
...@@ -3,6 +3,3 @@ export default class VersionCheckImage { ...@@ -3,6 +3,3 @@ export default class VersionCheckImage {
imageElement.off('error').on('error', () => imageElement.hide()); imageElement.off('error').on('error', () => imageElement.hide());
} }
} }
window.gl = window.gl || {};
gl.VersionCheckImage = VersionCheckImage;
class VisibilitySelect { export default class VisibilitySelect {
constructor(container) { constructor(container) {
if (!container) throw new Error('VisibilitySelect requires a container element as argument 1'); if (!container) throw new Error('VisibilitySelect requires a container element as argument 1');
this.container = container; this.container = container;
...@@ -19,6 +19,3 @@ class VisibilitySelect { ...@@ -19,6 +19,3 @@ class VisibilitySelect {
this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description; this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description;
} }
} }
window.gl = window.gl || {};
window.gl.VisibilitySelect = VisibilitySelect;
/* eslint-disable no-param-reassign */
/* global Breakpoints */ /* global Breakpoints */
import 'vendor/jquery.nicescroll'; import 'vendor/jquery.nicescroll';
import './breakpoints'; import './breakpoints';
class Wikis { export default class Wikis {
constructor() { constructor() {
this.bp = Breakpoints.get(); this.bp = Breakpoints.get();
this.sidebarEl = document.querySelector('.js-wiki-sidebar'); this.sidebarEl = document.querySelector('.js-wiki-sidebar');
...@@ -63,6 +62,3 @@ class Wikis { ...@@ -63,6 +62,3 @@ class Wikis {
} }
} }
} }
window.gl = window.gl || {};
window.gl.Wikis = Wikis;
/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, comma-dangle, max-len, class-methods-use-this */
/* global Mousetrap */ /* global Mousetrap */
// Zen Mode (full screen) textarea // Zen Mode (full screen) textarea
...@@ -35,8 +35,8 @@ window.Dropzone = Dropzone; ...@@ -35,8 +35,8 @@ window.Dropzone = Dropzone;
// **Target** a.js-zen-leave // **Target** a.js-zen-leave
// //
window.ZenMode = (function() { export default class ZenMode {
function ZenMode() { constructor() {
this.active_backdrop = null; this.active_backdrop = null;
this.active_textarea = null; this.active_textarea = null;
$(document).on('click', '.js-zen-enter', function(e) { $(document).on('click', '.js-zen-enter', function(e) {
...@@ -66,7 +66,7 @@ window.ZenMode = (function() { ...@@ -66,7 +66,7 @@ window.ZenMode = (function() {
}); });
} }
ZenMode.prototype.enter = function(backdrop) { enter(backdrop) {
Mousetrap.pause(); Mousetrap.pause();
this.active_backdrop = $(backdrop); this.active_backdrop = $(backdrop);
this.active_backdrop.addClass('fullscreen'); this.active_backdrop.addClass('fullscreen');
...@@ -74,9 +74,9 @@ window.ZenMode = (function() { ...@@ -74,9 +74,9 @@ window.ZenMode = (function() {
// Prevent a user-resized textarea from persisting to fullscreen // Prevent a user-resized textarea from persisting to fullscreen
this.active_textarea.removeAttr('style'); this.active_textarea.removeAttr('style');
return this.active_textarea.focus(); return this.active_textarea.focus();
}; }
ZenMode.prototype.exit = function() { exit() {
if (this.active_textarea) { if (this.active_textarea) {
Mousetrap.unpause(); Mousetrap.unpause();
this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen'); this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen');
...@@ -85,13 +85,11 @@ window.ZenMode = (function() { ...@@ -85,13 +85,11 @@ window.ZenMode = (function() {
this.active_backdrop = null; this.active_backdrop = null;
return Dropzone.forElement('.div-dropzone').enable(); return Dropzone.forElement('.div-dropzone').enable();
} }
}; }
ZenMode.prototype.scrollTo = function(zen_area) { scrollTo(zen_area) {
return $.scrollTo(zen_area, 0, { return $.scrollTo(zen_area, 0, {
offset: -150 offset: -150
}); });
}; }
}
return ZenMode;
})();
...@@ -21,3 +21,9 @@ body.modal-open { ...@@ -21,3 +21,9 @@ body.modal-open {
width: 860px; width: 860px;
} }
} }
@media (min-width: $screen-lg-min) {
.modal-full {
width: 98%;
}
}
...@@ -594,3 +594,15 @@ Convdev Index ...@@ -594,3 +594,15 @@ Convdev Index
$color-high-score: $green-400; $color-high-score: $green-400;
$color-average-score: $orange-400; $color-average-score: $orange-400;
$color-low-score: $red-400; $color-low-score: $red-400;
/*
Performance Bar
*/
$perf-bar-text: #999;
$perf-bar-production: #222;
$perf-bar-staging: #291430;
$perf-bar-development: #4c1210;
$perf-bar-bucket-bg: #111;
$perf-bar-bucket-color: #ccc;
$perf-bar-bucket-box-shadow-from: rgba($white-light, .2);
$perf-bar-bucket-box-shadow-to: rgba($black, .25);
...@@ -731,11 +731,11 @@ ...@@ -731,11 +731,11 @@
.merge-request-tabs-holder { .merge-request-tabs-holder {
top: $header-height; top: $header-height;
z-index: 100; z-index: 200;
background-color: $white-light; background-color: $white-light;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
@media(min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
position: sticky; position: sticky;
position: -webkit-sticky; position: -webkit-sticky;
} }
...@@ -770,6 +770,12 @@ ...@@ -770,6 +770,12 @@
max-width: $limited-layout-width; max-width: $limited-layout-width;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
.inner-page-scroll-tabs {
background-color: $white-light;
margin-left: -$gl-padding;
padding-left: $gl-padding;
}
} }
} }
......
//= require peek/views/performance_bar @import "framework/variables";
//= require peek/views/rblineprof @import "peek/views/performance_bar";
@import "peek/views/rblineprof";
header.navbar-gitlab.with-peek {
top: 35px;
}
#peek { #peek {
height: 35px; height: 35px;
background: #000; background: $black;
line-height: 35px; line-height: 35px;
color: #999; color: $perf-bar-text;
&.disabled { &.disabled {
display: none; display: none;
} }
&.production { &.production {
background-color: #222; background-color: $perf-bar-production;
} }
&.staging { &.staging {
background-color: #291430; background-color: $perf-bar-staging;
} }
&.development { &.development {
background-color: #4c1210; background-color: $perf-bar-development;
} }
.wrapper { .wrapper {
width: 800px; width: 1000px;
margin: 0 auto; margin: 0 auto;
} }
// UI Elements // UI Elements
.bucket { .bucket {
background: #111; background: $perf-bar-bucket-bg;
display: inline-block; display: inline-block;
padding: 4px 6px; padding: 4px 6px;
font-family: Consolas, "Liberation Mono", Courier, monospace; font-family: Consolas, "Liberation Mono", Courier, monospace;
line-height: 1; line-height: 1;
color: #ccc; color: $perf-bar-bucket-color;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 1px 0 rgba(255,255,255,.2), inset 0 1px 2px rgba(0,0,0,.25); box-shadow: 0 1px 0 $perf-bar-bucket-box-shadow-from, inset 0 1px 2px $perf-bar-bucket-box-shadow-to;
.hidden { .hidden {
display: none; display: none;
...@@ -53,12 +50,14 @@ header.navbar-gitlab.with-peek { ...@@ -53,12 +50,14 @@ header.navbar-gitlab.with-peek {
} }
strong { strong {
color: #fff; color: $white-light;
} }
table { table {
color: $black;
strong { strong {
color: #000; color: $black;
} }
} }
...@@ -90,5 +89,15 @@ header.navbar-gitlab.with-peek { ...@@ -90,5 +89,15 @@ header.navbar-gitlab.with-peek {
} }
#modal-peek-pg-queries-content { #modal-peek-pg-queries-content {
color: #000; color: $black;
}
.peek-rblineprof-file {
pre.duration {
width: 280px;
}
.data {
overflow: visible;
}
} }
...@@ -47,7 +47,7 @@ module IssuableCollections ...@@ -47,7 +47,7 @@ module IssuableCollections
end end
def merge_requests_collection def merge_requests_collection
merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :merge_request_diff, :head_pipeline, target_project: :namespace) merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :head_pipeline, target_project: :namespace, merge_request_diff: :merge_request_diff_commits)
end end
def issues_finder def issues_finder
......
...@@ -70,7 +70,7 @@ module MembershipActions ...@@ -70,7 +70,7 @@ module MembershipActions
def members_page_url def members_page_url
if membershipable.is_a?(Project) if membershipable.is_a?(Project)
project_settings_members_path(membershipable) project_project_members_path(membershipable)
else else
polymorphic_url([membershipable, :members]) polymorphic_url([membershipable, :members])
end end
......
module Groups
module Settings
class CiCdController < Groups::ApplicationController
before_action :authorize_admin_pipeline!
def show
define_secret_variables
end
private
def define_secret_variables
@variable = Ci::GroupVariable.new(group: group)
.present(current_user: current_user)
@variables = group.variables.order_key_asc
.map { |variable| variable.present(current_user: current_user) }
end
def authorize_admin_pipeline!
return render_404 unless can?(current_user, :admin_pipeline, group)
end
end
end
end
module Groups
class VariablesController < Groups::ApplicationController
before_action :variable, only: [:show, :update, :destroy]
before_action :authorize_admin_build!
def index
redirect_to group_settings_ci_cd_path(group)
end
def show
end
def update
if variable.update(variable_params)
redirect_to group_variables_path(group),
notice: 'Variable was successfully updated.'
else
render "show"
end
end
def create
@variable = group.variables.create(variable_params)
.present(current_user: current_user)
if @variable.persisted?
redirect_to group_settings_ci_cd_path(group),
notice: 'Variable was successfully created.'
else
render "show"
end
end
def destroy
if variable.destroy
redirect_to group_settings_ci_cd_path(group),
status: 302,
notice: 'Variable was successfully removed.'
else
redirect_to group_settings_ci_cd_path(group),
status: 302,
notice: 'Failed to remove the variable.'
end
end
private
def variable_params
params.require(:variable).permit(*variable_params_attributes)
end
def variable_params_attributes
%i[key value protected]
end
def variable
@variable ||= group.variables.find(params[:id]).present(current_user: current_user)
end
def authorize_admin_build!
return render_404 unless can?(current_user, :admin_build, group)
end
end
end
...@@ -22,7 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -22,7 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
flash[:alert] = 'Please select a group.' flash[:alert] = 'Please select a group.'
end end
redirect_to project_settings_members_path(project) redirect_to project_project_members_path(project)
end end
def update def update
...@@ -36,7 +36,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -36,7 +36,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to project_settings_members_path(project), status: 302 redirect_to project_project_members_path(project), status: 302
end end
format.js { head :ok } format.js { head :ok }
end end
......
...@@ -45,6 +45,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -45,6 +45,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
def show def show
@project_namespace = @project.namespace.becomes(Namespace)
end end
def create def create
......
...@@ -6,8 +6,23 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -6,8 +6,23 @@ class Projects::ProjectMembersController < Projects::ApplicationController
before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
def index def index
sort = params[:sort].presence || sort_value_name @sort = params[:sort].presence || sort_value_name
redirect_to project_settings_members_path(@project, sort: sort) @group_links = @project.project_group_links
@skip_groups = @group_links.pluck(:group_id)
@skip_groups << @project.namespace_id unless @project.personal?
@skip_groups += @project.group.ancestors.pluck(:id) if @project.group
@project_members = MembersFinder.new(@project, current_user).execute
if params[:search].present?
@project_members = @project_members.joins(:user).merge(User.search(params[:search]))
@group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
end
@project_members = @project_members.sort(@sort).page(params[:page])
@requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new
end end
def update def update
...@@ -19,7 +34,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -19,7 +34,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
end end
def resend_invite def resend_invite
redirect_path = project_settings_members_path(@project) redirect_path = project_project_members_path(@project)
@project_member = @project.project_members.find(params[:id]) @project_member = @project.project_members.find(params[:id])
...@@ -42,7 +57,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -42,7 +57,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
return render_404 return render_404
end end
redirect_to(project_settings_members_path(project), redirect_to(project_project_members_path(project),
notice: notice) notice: notice)
end end
......
...@@ -21,7 +21,10 @@ module Projects ...@@ -21,7 +21,10 @@ module Projects
end end
def define_secret_variables def define_secret_variables
@variable = Ci::Variable.new @variable = Ci::Variable.new(project: project)
.present(current_user: current_user)
@variables = project.variables.order_key_asc
.map { |variable| variable.present(current_user: current_user) }
end end
def define_triggers_variables def define_triggers_variables
......
module Projects
module Settings
class MembersController < Projects::ApplicationController
include SortingHelper
def show
@sort = params[:sort].presence || sort_value_name
@group_links = @project.project_group_links
@skip_groups = @group_links.pluck(:group_id)
@skip_groups << @project.namespace_id unless @project.personal?
@skip_groups += @project.group.ancestors.pluck(:id) if @project.group
@project_members = MembersFinder.new(@project, current_user).execute
if params[:search].present?
@project_members = @project_members.joins(:user).merge(User.search(params[:search]))
@group_links = @group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
end
@project_members = @project_members.sort(@sort).page(params[:page])
@requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new
end
end
end
end
class Projects::VariablesController < Projects::ApplicationController class Projects::VariablesController < Projects::ApplicationController
before_action :variable, only: [:show, :update, :destroy]
before_action :authorize_admin_build! before_action :authorize_admin_build!
layout 'project_settings' layout 'project_settings'
...@@ -8,37 +9,39 @@ class Projects::VariablesController < Projects::ApplicationController ...@@ -8,37 +9,39 @@ class Projects::VariablesController < Projects::ApplicationController
end end
def show def show
@variable = @project.variables.find(params[:id])
end end
def update def update
@variable = @project.variables.find(params[:id]) if variable.update(variable_params)
redirect_to project_variables_path(project),
if @variable.update_attributes(variable_params) notice: 'Variable was successfully updated.'
redirect_to project_variables_path(project), notice: 'Variable was successfully updated.'
else else
render action: "show" render "show"
end end
end end
def create def create
@variable = @project.variables.new(variable_params) @variable = project.variables.create(variable_params)
.present(current_user: current_user)
if @variable.save if @variable.persisted?
flash[:notice] = 'Variables were successfully updated.' redirect_to project_settings_ci_cd_path(project),
redirect_to project_settings_ci_cd_path(project) notice: 'Variable was successfully created.'
else else
render "show" render "show"
end end
end end
def destroy def destroy
@key = @project.variables.find(params[:id]) if variable.destroy
@key.destroy redirect_to project_settings_ci_cd_path(project),
status: 302,
redirect_to project_settings_ci_cd_path(project), notice: 'Variable was successfully removed.'
status: 302, else
notice: 'Variable was successfully removed.' redirect_to project_settings_ci_cd_path(project),
status: 302,
notice: 'Failed to remove the variable.'
end
end end
private private
...@@ -50,4 +53,8 @@ class Projects::VariablesController < Projects::ApplicationController ...@@ -50,4 +53,8 @@ class Projects::VariablesController < Projects::ApplicationController
def variable_params_attributes def variable_params_attributes
%i[id key value protected _destroy] %i[id key value protected _destroy]
end end
def variable
@variable ||= project.variables.find(params[:id]).present(current_user: current_user)
end
end end
...@@ -97,7 +97,7 @@ module GitlabRoutingHelper ...@@ -97,7 +97,7 @@ module GitlabRoutingHelper
## Members ## Members
def project_members_url(project, *args) def project_members_url(project, *args)
project_project_members_url(project) project_project_members_url(project, *args)
end end
def project_member_path(project_member, *args) def project_member_path(project_member, *args)
......
...@@ -23,7 +23,6 @@ module NavHelper ...@@ -23,7 +23,6 @@ module NavHelper
def nav_header_class def nav_header_class
class_name = '' class_name = ''
class_name << " with-horizontal-nav" if defined?(nav) && nav class_name << " with-horizontal-nav" if defined?(nav) && nav
class_name << " with-peek" if peek_enabled?
class_name class_name
end end
......
module PerformanceBarHelper
# This is a hack since using `alias_method :performance_bar_enabled?, :peek_enabled?`
# in WithPerformanceBar breaks tests (but works in the browser).
def performance_bar_enabled?
peek_enabled?
end
end
...@@ -267,15 +267,15 @@ module ProjectsHelper ...@@ -267,15 +267,15 @@ module ProjectsHelper
def tab_ability_map def tab_ability_map
{ {
environments: :read_environment, environments: :read_environment,
milestones: :read_milestone, milestones: :read_milestone,
snippets: :read_project_snippet, snippets: :read_project_snippet,
settings: :admin_project, settings: :admin_project,
builds: :read_build, builds: :read_build,
labels: :read_label, labels: :read_label,
issues: :read_issue, issues: :read_issue,
team: :read_project_member, project_members: :read_project_member,
wiki: :read_wiki wiki: :read_wiki
} }
end end
......
...@@ -75,7 +75,7 @@ module SearchHelper ...@@ -75,7 +75,7 @@ module SearchHelper
{ category: "Current Project", label: "Merge Requests", url: project_merge_requests_path(@project) }, { category: "Current Project", label: "Merge Requests", url: project_merge_requests_path(@project) },
{ category: "Current Project", label: "Milestones", url: project_milestones_path(@project) }, { category: "Current Project", label: "Milestones", url: project_milestones_path(@project) },
{ category: "Current Project", label: "Snippets", url: project_snippets_path(@project) }, { category: "Current Project", label: "Snippets", url: project_snippets_path(@project) },
{ category: "Current Project", label: "Members", url: project_settings_members_path(@project) }, { category: "Current Project", label: "Members", url: project_project_members_path(@project) },
{ category: "Current Project", label: "Wiki", url: project_wikis_path(@project) } { category: "Current Project", label: "Wiki", url: project_wikis_path(@project) }
] ]
else else
......
...@@ -10,5 +10,11 @@ module BlobViewer ...@@ -10,5 +10,11 @@ module BlobViewer
def visible_to?(current_user) def visible_to?(current_user)
can?(current_user, :read_wiki, project) can?(current_user, :read_wiki, project)
end end
def render_error
return if project.has_external_wiki? || (project.wiki_enabled? && project.wiki.has_home_page?)
:no_wiki
end
end end
end end
...@@ -200,6 +200,7 @@ module Ci ...@@ -200,6 +200,7 @@ module Ci
variables += project.deployment_variables if has_environment? variables += project.deployment_variables if has_environment?
variables += yaml_variables variables += yaml_variables
variables += user_variables variables += user_variables
variables += project.group.secret_variables_for(ref, project).map(&:to_runner_variable) if project.group
variables += secret_variables(environment: environment) variables += secret_variables(environment: environment)
variables += trigger_request.user_variables if trigger_request variables += trigger_request.user_variables if trigger_request
variables += persisted_environment_variables if environment variables += persisted_environment_variables if environment
...@@ -218,7 +219,7 @@ module Ci ...@@ -218,7 +219,7 @@ module Ci
.reorder(iid: :desc) .reorder(iid: :desc)
merge_requests.find do |merge_request| merge_requests.find do |merge_request|
merge_request.commits_sha.include?(pipeline.sha) merge_request.commit_shas.include?(pipeline.sha)
end end
end end
end end
......
module Ci
class GroupVariable < ActiveRecord::Base
extend Ci::Model
include HasVariable
include Presentable
belongs_to :group
validates :key, uniqueness: { scope: :group_id }
scope :unprotected, -> { where(protected: false) }
end
end
...@@ -2,6 +2,7 @@ module Ci ...@@ -2,6 +2,7 @@ module Ci
class Variable < ActiveRecord::Base class Variable < ActiveRecord::Base
extend Ci::Model extend Ci::Model
include HasVariable include HasVariable
include Presentable
belongs_to :project belongs_to :project
......
...@@ -138,7 +138,7 @@ class Commit ...@@ -138,7 +138,7 @@ class Commit
safe_message.split("\n", 2)[1].try(:chomp) safe_message.split("\n", 2)[1].try(:chomp)
end end
def description? def description?
description.present? description.present?
end end
......
...@@ -3,6 +3,8 @@ module ShaAttribute ...@@ -3,6 +3,8 @@ module ShaAttribute
module ClassMethods module ClassMethods
def sha_attribute(name) def sha_attribute(name)
return unless table_exists?
column = columns.find { |c| c.name == name.to_s } column = columns.find { |c| c.name == name.to_s }
# In case the table doesn't exist we won't be able to find the column, # In case the table doesn't exist we won't be able to find the column,
......
...@@ -22,6 +22,7 @@ class Group < Namespace ...@@ -22,6 +22,7 @@ class Group < Namespace
has_many :shared_projects, through: :project_group_links, source: :project has_many :shared_projects, through: :project_group_links, source: :project
has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
has_many :labels, class_name: 'GroupLabel' has_many :labels, class_name: 'GroupLabel'
has_many :variables, class_name: 'Ci::GroupVariable'
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validate :visibility_level_allowed_by_projects validate :visibility_level_allowed_by_projects
...@@ -248,6 +249,14 @@ class Group < Namespace ...@@ -248,6 +249,14 @@ class Group < Namespace
} }
end end
def secret_variables_for(ref, project)
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten
end
protected protected
def update_two_factor_requirement def update_two_factor_requirement
......
...@@ -31,7 +31,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -31,7 +31,7 @@ class MergeRequest < ActiveRecord::Base
after_create :ensure_merge_request_diff, unless: :importing? after_create :ensure_merge_request_diff, unless: :importing?
after_update :reload_diff_if_branch_changed after_update :reload_diff_if_branch_changed
delegate :commits, :real_size, :commits_sha, :commits_count, delegate :commits, :real_size, :commit_shas, :commits_count,
to: :merge_request_diff, prefix: nil to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
...@@ -518,7 +518,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -518,7 +518,7 @@ class MergeRequest < ActiveRecord::Base
def related_notes def related_notes
# Fetch comments only from last 100 commits # Fetch comments only from last 100 commits
commits_for_notes_limit = 100 commits_for_notes_limit = 100
commit_ids = commits.last(commits_for_notes_limit).map(&:id) commit_ids = commit_shas.take(commits_for_notes_limit)
Note.where( Note.where(
"(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" + "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
...@@ -841,15 +841,18 @@ class MergeRequest < ActiveRecord::Base ...@@ -841,15 +841,18 @@ class MergeRequest < ActiveRecord::Base
return Ci::Pipeline.none unless source_project return Ci::Pipeline.none unless source_project
@all_pipelines ||= source_project.pipelines @all_pipelines ||= source_project.pipelines
.where(sha: all_commits_sha, ref: source_branch) .where(sha: all_commit_shas, ref: source_branch)
.order(id: :desc) .order(id: :desc)
end end
# Note that this could also return SHA from now dangling commits # Note that this could also return SHA from now dangling commits
# #
def all_commits_sha def all_commit_shas
if persisted? if persisted?
merge_request_diffs.flat_map(&:commits_sha).uniq column_shas = MergeRequestDiffCommit.where(merge_request_diff: merge_request_diffs).pluck('DISTINCT(sha)')
serialised_shas = merge_request_diffs.where.not(st_commits: nil).flat_map(&:commit_shas)
(column_shas + serialised_shas).uniq
elsif compare_commits elsif compare_commits
compare_commits.to_a.reverse.map(&:id) compare_commits.to_a.reverse.map(&:id)
else else
......
...@@ -11,6 +11,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -11,6 +11,7 @@ class MergeRequestDiff < ActiveRecord::Base
belongs_to :merge_request belongs_to :merge_request
has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) } has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) }
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }
serialize :st_commits # rubocop:disable Cop/ActiveRecordSerialize serialize :st_commits # rubocop:disable Cop/ActiveRecordSerialize
serialize :st_diffs # rubocop:disable Cop/ActiveRecordSerialize serialize :st_diffs # rubocop:disable Cop/ActiveRecordSerialize
...@@ -47,14 +48,13 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -47,14 +48,13 @@ class MergeRequestDiff < ActiveRecord::Base
# Collect information about commits and diff from repository # Collect information about commits and diff from repository
# and save it to the database as serialized data # and save it to the database as serialized data
def save_git_content def save_git_content
ensure_commits_sha ensure_commit_shas
save_commits save_commits
reload_commits
save_diffs save_diffs
keep_around_commits keep_around_commits
end end
def ensure_commits_sha def ensure_commit_shas
merge_request.fetch_ref merge_request.fetch_ref
self.start_commit_sha ||= merge_request.target_branch_sha self.start_commit_sha ||= merge_request.target_branch_sha
self.head_commit_sha ||= merge_request.source_branch_sha self.head_commit_sha ||= merge_request.source_branch_sha
...@@ -66,7 +66,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -66,7 +66,7 @@ class MergeRequestDiff < ActiveRecord::Base
# created before version 8.4 that does not store head_commit_sha in separate db field. # created before version 8.4 that does not store head_commit_sha in separate db field.
def head_commit_sha def head_commit_sha
if persisted? && super.nil? if persisted? && super.nil?
last_commit.try(:sha) last_commit_sha
else else
super super
end end
...@@ -97,16 +97,11 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -97,16 +97,11 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def commits def commits
@commits ||= load_commits(st_commits) @commits ||= load_commits
end end
def reload_commits def last_commit_sha
@commits = nil commit_shas.first
commits
end
def last_commit
commits.first
end end
def first_commit def first_commit
...@@ -131,8 +126,12 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -131,8 +126,12 @@ class MergeRequestDiff < ActiveRecord::Base
project.commit(head_commit_sha) project.commit(head_commit_sha)
end end
def commits_sha def commit_shas
st_commits.map { |commit| commit[:id] } if st_commits.present?
st_commits.map { |commit| commit[:id] }
else
merge_request_diff_commits.map(&:sha)
end
end end
def diff_refs=(new_diff_refs) def diff_refs=(new_diff_refs)
...@@ -207,7 +206,11 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -207,7 +206,11 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def commits_count def commits_count
st_commits.count if st_commits.present?
st_commits.size
else
merge_request_diff_commits.size
end
end end
def utf8_st_diffs def utf8_st_diffs
...@@ -231,29 +234,6 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -231,29 +234,6 @@ class MergeRequestDiff < ActiveRecord::Base
raw.any? { |element| VALID_CLASSES.include?(element.class) } raw.any? { |element| VALID_CLASSES.include?(element.class) }
end end
def dump_commits(commits)
commits.map(&:to_hash)
end
def load_commits(array)
array.map { |hash| Commit.new(Gitlab::Git::Commit.new(hash), merge_request.source_project) }
end
# Load all commits related to current merge request diff from repo
# and save it as array of hashes in st_commits db field
def save_commits
new_attributes = {}
commits = compare.commits
if commits.present?
commits = Commit.decorate(commits, merge_request.source_project).reverse
new_attributes[:st_commits] = dump_commits(commits)
end
update_columns_serialized(new_attributes)
end
def create_merge_request_diff_files(diffs) def create_merge_request_diff_files(diffs)
rows = diffs.map.with_index do |diff, index| rows = diffs.map.with_index do |diff, index|
diff.to_hash.merge( diff.to_hash.merge(
...@@ -294,12 +274,18 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -294,12 +274,18 @@ class MergeRequestDiff < ActiveRecord::Base
end end
end end
# Load diffs between branches related to current merge request diff from repo def load_commits
# and save it as array of hashes in st_diffs db field commits = st_commits.presence || merge_request_diff_commits
commits.map do |commit|
Commit.new(Gitlab::Git::Commit.new(commit.to_hash), merge_request.source_project)
end
end
def save_diffs def save_diffs
new_attributes = {} new_attributes = {}
if commits.size.zero? if compare.commits.size.zero?
new_attributes[:state] = :empty new_attributes[:state] = :empty
else else
diff_collection = compare.diffs(Commit.max_diff_options) diff_collection = compare.diffs(Commit.max_diff_options)
...@@ -319,7 +305,13 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -319,7 +305,13 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:state] = :overflow if diff_collection.overflow? new_attributes[:state] = :overflow if diff_collection.overflow?
end end
update_columns_serialized(new_attributes) update(new_attributes)
end
def save_commits
MergeRequestDiffCommit.create_bulk(self.id, compare.commits.reverse)
merge_request_diff_commits.reload
end end
def repository def repository
...@@ -332,29 +324,6 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -332,29 +324,6 @@ class MergeRequestDiff < ActiveRecord::Base
project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha) project.merge_base_commit(head_commit_sha, start_commit_sha).try(:sha)
end end
#
# #save or #update_attributes providing changes on serialized attributes do a lot of
# serialization and deserialization calls resulting in bad performance.
# Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide.
# As a tradeoff we need to reload the current instance to properly manage time objects on those serialized
# attributes. So to keep the same behaviour as the attribute assignment we reload the instance.
# The difference is in the usage of
# #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns)
#
# Ex:
#
# new_attributes[:st_commits].first.slice(:committed_date)
# => {:committed_date=>2014-02-27 11:01:38 +0200}
# YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date)))
# => {:committed_date=>2014-02-27 10:01:38 +0100}
#
def update_columns_serialized(new_attributes)
return unless new_attributes.any?
update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone))
reload
end
def keep_around_commits def keep_around_commits
[repository, merge_request.source_project.repository].each do |repo| [repository, merge_request.source_project.repository].each do |repo|
repo.keep_around(start_commit_sha) repo.keep_around(start_commit_sha)
......
class MergeRequestDiffCommit < ActiveRecord::Base
include ShaAttribute
belongs_to :merge_request_diff
sha_attribute :sha
alias_attribute :id, :sha
def self.create_bulk(merge_request_diff_id, commits)
sha_attribute = Gitlab::Database::ShaAttribute.new
rows = commits.map.with_index do |commit, index|
# See #parent_ids.
commit_hash = commit.to_hash.except(:parent_ids)
sha = commit_hash.delete(:id)
commit_hash.merge(
merge_request_diff_id: merge_request_diff_id,
relative_order: index,
sha: sha_attribute.type_cast_for_database(sha)
)
end
Gitlab::Database.bulk_insert(self.table_name, rows)
end
def to_hash
Gitlab::Git::Commit::SERIALIZE_KEYS.each_with_object({}) do |key, hash|
hash[key] = public_send(key)
end
end
# We don't save these, because they would need a table or a serialised
# field. They aren't used anywhere, so just pretend the commit has no parents.
def parent_ids
[]
end
end
...@@ -63,6 +63,10 @@ class ProjectWiki ...@@ -63,6 +63,10 @@ class ProjectWiki
!!repository.exists? !!repository.exists?
end end
def has_home_page?
!!find_page('home')
end
# Returns an Array of Gitlab WikiPage instances or an # Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages. # empty Array if this Wiki has no pages.
def pages def pages
......
...@@ -31,6 +31,8 @@ class GroupPolicy < BasePolicy ...@@ -31,6 +31,8 @@ class GroupPolicy < BasePolicy
rule { master }.policy do rule { master }.policy do
enable :create_projects enable :create_projects
enable :admin_milestones enable :admin_milestones
enable :admin_pipeline
enable :admin_build
end end
rule { owner }.policy do rule { owner }.policy do
......
module Ci
class GroupVariablePresenter < Gitlab::View::Presenter::Delegated
presents :variable
def placeholder
'GROUP_VARIABLE'
end
def form_path
if variable.persisted?
group_variable_path(group, variable)
else
group_variables_path(group)
end
end
def edit_path
group_variable_path(group, variable)
end
def delete_path
group_variable_path(group, variable)
end
end
end
module Ci
class VariablePresenter < Gitlab::View::Presenter::Delegated
presents :variable
def placeholder
'PROJECT_VARIABLE'
end
def form_path
if variable.persisted?
project_variable_path(project, variable)
else
project_variables_path(project)
end
end
def edit_path
project_variable_path(project, variable)
end
def delete_path
project_variable_path(project, variable)
end
end
end
module Boards module Boards
class CreateService < BaseService class CreateService < BaseService
def execute def execute
if project.boards.empty? create_board! if can_create_board?
create_board!
else
project.boards.first
end
end end
private private
def can_create_board?
project.boards.size == 0
end
def create_board! def create_board!
board = project.boards.create board = project.boards.create(params)
board.lists.create(list_type: :backlog)
board.lists.create(list_type: :closed) if board.persisted?
board.lists.create(list_type: :backlog)
board.lists.create(list_type: :closed)
end
board board
end end
......
...@@ -68,7 +68,7 @@ module MergeRequests ...@@ -68,7 +68,7 @@ module MergeRequests
if merge_request.source_branch == @branch_name || force_push? if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff(current_user) merge_request.reload_diff(current_user)
else else
mr_commit_ids = merge_request.commits_sha mr_commit_ids = merge_request.commit_shas
push_commit_ids = @commits.map(&:id) push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids matches = mr_commit_ids & push_commit_ids
merge_request.reload_diff(current_user) if matches.any? merge_request.reload_diff(current_user) if matches.any?
...@@ -128,7 +128,7 @@ module MergeRequests ...@@ -128,7 +128,7 @@ module MergeRequests
return unless @commits.present? return unless @commits.present?
merge_requests_for_source_branch.each do |merge_request| merge_requests_for_source_branch.each do |merge_request|
mr_commit_ids = Set.new(merge_request.commits_sha) mr_commit_ids = Set.new(merge_request.commit_shas)
new_commits, existing_commits = @commits.partition do |commit| new_commits, existing_commits = @commits.partition do |commit|
mr_commit_ids.include?(commit.id) mr_commit_ids.include?(commit.id)
...@@ -144,7 +144,7 @@ module MergeRequests ...@@ -144,7 +144,7 @@ module MergeRequests
return unless @commits.present? return unless @commits.present?
merge_requests_for_source_branch.each do |merge_request| merge_requests_for_source_branch.each do |merge_request|
commit_shas = merge_request.commits_sha commit_shas = merge_request.commit_shas
wip_commit = @commits.detect do |commit| wip_commit = @commits.detect do |commit|
commit.work_in_progress? && commit_shas.include?(commit.sha) commit.work_in_progress? && commit_shas.include?(commit.sha)
......
...@@ -146,32 +146,6 @@ module QuickActions ...@@ -146,32 +146,6 @@ module QuickActions
end end
end end
desc do
"Change assignee#{'(s)' if issuable.allows_multiple_assignees?}"
end
explanation do |users|
users = issuable.allows_multiple_assignees? ? users : users.take(1)
"Change #{'assignee'.pluralize(users.size)} to #{users.map(&:to_reference).to_sentence}."
end
params do
issuable.allows_multiple_assignees? ? '@user1 @user2' : '@user'
end
condition do
issuable.persisted? &&
current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end
parse_params do |assignee_param|
extract_users(assignee_param)
end
command :reassign do |users|
@updates[:assignee_ids] =
if issuable.allows_multiple_assignees?
users.map(&:id)
else
[users.last.id]
end
end
desc 'Set milestone' desc 'Set milestone'
explanation do |milestone| explanation do |milestone|
"Sets the milestone to #{milestone.to_reference}." if milestone "Sets the milestone to #{milestone.to_reference}." if milestone
......
= form_for [@project.namespace.becomes(Namespace), @project, @variable] do |f| = form_for @variable, as: :variable, url: @variable.form_path do |f|
= form_errors(@variable) = form_errors(@variable)
.form-group .form-group
= f.label :key, "Key", class: "label-light" = f.label :key, "Key", class: "label-light"
= f.text_field :key, class: "form-control", placeholder: "PROJECT_VARIABLE", required: true = f.text_field :key, class: "form-control", placeholder: @variable.placeholder, required: true
.form-group .form-group
= f.label :value, "Value", class: "label-light" = f.label :value, "Value", class: "label-light"
= f.text_area :value, class: "form-control", placeholder: "PROJECT_VARIABLE" = f.text_area :value, class: "form-control", placeholder: @variable.placeholder
.form-group .form-group
.checkbox .checkbox
= f.label :protected do = f.label :protected do
......
.row.prepend-top-default.append-bottom-default .row.prepend-top-default.append-bottom-default
.col-lg-4 .col-lg-4
= render "projects/variables/content" = render "ci/variables/content"
.col-lg-8 .col-lg-8
%h5.prepend-top-0 %h5.prepend-top-0
Add a variable Add a variable
= render "projects/variables/form", btn_text: "Add new variable" = render "ci/variables/form", btn_text: "Add new variable"
%hr %hr
%h5.prepend-top-0 %h5.prepend-top-0
Your variables (#{@project.variables.size}) Your variables (#{@variables.size})
- if @project.variables.empty? - if @variables.empty?
%p.settings-message.text-center.append-bottom-0 %p.settings-message.text-center.append-bottom-0
No variables found, add one with the form above. No variables found, add one with the form above.
- else - else
= render "projects/variables/table" = render "ci/variables/table"
%button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values %button.btn.btn-info.js-btn-toggle-reveal-values{ "data-status" => 'hidden' } Reveal Values
- page_title "Variables"
.row.prepend-top-default.append-bottom-default
.col-lg-3
= render "ci/variables/content"
.col-lg-9
%h5.prepend-top-0
Update variable
= render "ci/variables/form", btn_text: "Save variable"
...@@ -11,18 +11,18 @@ ...@@ -11,18 +11,18 @@
%th Protected %th Protected
%th %th
%tbody %tbody
- @project.variables.order_key_asc.each do |variable| - @variables.each do |variable|
- if variable.id? - if variable.id?
%tr %tr
%td.variable-key= variable.key %td.variable-key= variable.key
%td.variable-value{ "data-value" => variable.value }****** %td.variable-value{ "data-value" => variable.value }******
%td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected) %td.variable-protected= Gitlab::Utils.boolean_to_yes_no(variable.protected)
%td.variable-menu %td.variable-menu
= link_to project_variable_path(@project, variable), class: "btn btn-transparent btn-variable-edit" do = link_to variable.edit_path, class: "btn btn-transparent btn-variable-edit" do
%span.sr-only %span.sr-only
Update Update
= icon("pencil") = icon("pencil")
= link_to project_variable_path(@project, variable), class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do = link_to variable.delete_path, class: "btn btn-transparent btn-variable-delete", method: :delete, data: { confirm: "Are you sure?" } do
%span.sr-only %span.sr-only
Remove Remove
= icon("trash") = icon("trash")
...@@ -12,3 +12,8 @@ ...@@ -12,3 +12,8 @@
= link_to projects_group_path(@group), title: 'Projects' do = link_to projects_group_path(@group), title: 'Projects' do
%span %span
Projects Projects
= nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do
%span
Pipelines
- page_title "Pipelines"
= render "groups/settings_head"
= render 'ci/variables/index'
...@@ -27,10 +27,11 @@ ...@@ -27,10 +27,11 @@
%td.shortcut %td.shortcut
.key f .key f
%td Focus Filter %td Focus Filter
%tr - if performance_bar_enabled?
%td.shortcut %tr
.key p b %td.shortcut
%td Show/hide the Performance Bar .key p b
%td Show/hide the Performance Bar
%tr %tr
%td.shortcut %td.shortcut
.key ? .key ?
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
= stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "print", media: "print"
= stylesheet_link_tag "test", media: "all" if Rails.env.test? = stylesheet_link_tag "test", media: "all" if Rails.env.test?
= stylesheet_link_tag 'peek' if peek_enabled? = stylesheet_link_tag 'performance_bar' if performance_bar_enabled?
- if show_new_nav? - if show_new_nav?
= stylesheet_link_tag "new_nav", media: "all" = stylesheet_link_tag "new_nav", media: "all"
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
= webpack_bundle_tag "main" = webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled = webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test? = webpack_bundle_tag "test" if Rails.env.test?
= webpack_bundle_tag 'peek' if peek_enabled? = webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts) - if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts = yield :page_specific_javascripts
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
= render "layouts/head" = render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } %body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
= render "layouts/init_auto_complete" if @gfm_form = render "layouts/init_auto_complete" if @gfm_form
= render 'peek/bar'
- if show_new_nav? - if show_new_nav?
= render "layouts/header/new" = render "layouts/header/new"
- else - else
...@@ -11,3 +10,5 @@ ...@@ -11,3 +10,5 @@
= render 'layouts/page', sidebar: sidebar, nav: nav = render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body = yield :scripts_body
= render 'peek/bar'
...@@ -57,16 +57,17 @@ ...@@ -57,16 +57,17 @@
%span %span
Snippets Snippets
- if project_nav_tab? :project_members
= nav_link(controller: :project_members) do
= link_to project_project_members_path(@project), title: 'Members', class: 'shortcuts-members' do
%span
Members
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span %span
Settings Settings
- else
= nav_link(path: %w[members#show]) do
= link_to project_settings_members_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span
Settings
-# Shortcut to Project > Activity -# Shortcut to Project > Activity
%li.hidden %li.hidden
......
Profile:
= link_to 'all', url_for(lineprofiler: 'true'), class: 'js-toggle-modal-peek-line-profile'
\/
= link_to 'app & lib', url_for(lineprofiler: 'app'), class: 'js-toggle-modal-peek-line-profile'
\/
= link_to 'views', url_for(lineprofiler: 'views'), class: 'js-toggle-modal-peek-line-profile'
%strong %strong
%a#peek-show-queries{ href: '#' } %a.js-toggle-modal-peek-sql
%span{ data: { defer_to: "#{view.defer_key}-duration" } }... %span{ data: { defer_to: "#{view.defer_key}-duration" } }...
\/ \/
%span{ data: { defer_to: "#{view.defer_key}-calls" } }... %span{ data: { defer_to: "#{view.defer_key}-calls" } }...
#modal-peek-pg-queries.modal{ tabindex: -1 } #modal-peek-pg-queries.modal{ tabindex: -1 }
.modal-dialog .modal-dialog.modal-full
#modal-peek-pg-queries-content.modal-content .modal-content
.modal-header .modal-header
%a.close{ href: "#", "data-dismiss" => "modal" } × %button.close.btn.btn-link.btn-sm{ type: 'button', data: { dismiss: 'modal' } } X
%h4 %h4
SQL queries SQL queries
.modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }... .modal-body{ data: { defer_to: "#{view.defer_key}-queries" } }...
= icon('info-circle fw') = icon('info-circle fw')
= succeed '.' do = succeed '.' do
To learn more about this project, read To learn more about this project, read
= link_to "the wiki", project_wikis_path(viewer.project) = link_to "the wiki", get_project_wiki_path(viewer.project)
.panel.panel-default
.panel-heading
Group members with access to
%strong= @group.name
%span.badge= members.size
- if can?(current_user, :admin_group_member, @group)
.controls
= link_to 'Manage group members',
group_group_members_path(@group),
class: 'btn'
%ul.content-list
= render partial: 'shared/members/member',
collection: members.limit(20),
as: :member,
locals: { show_controls: false }
- if members.size > 20
%li
and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)}
- @project_group_links.each do |group_links|
- shared_group = group_links.group
- shared_group_members = shared_group.members
- shared_group_users_count = shared_group_members.size
.panel.panel-default
.panel-heading
Shared with
%strong= shared_group.name
group, members with
%strong= group_links.human_access
role (#{shared_group_users_count})
- if can?(current_user, :admin_group, shared_group)
.panel-head-actions
= link_to group_group_members_path(shared_group), class: 'btn btn-sm' do
%i.fa.fa-pencil-square-o
Edit group members
%ul.content-list
= render partial: 'shared/members/member',
collection: shared_group_members.order(access_level: :desc).limit(20),
as: :member,
locals: { show_controls: false, show_roles: false }
- if shared_group_users_count > 20
%li
and #{shared_group_users_count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(shared_group)}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
%strong %strong
#{@project.name} #{@project.name}
%span.badge= @project_members.total_count %span.badge= @project_members.total_count
= form_tag project_settings_members_path(@project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do = form_tag project_project_members_path(@project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do
.form-group .form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false } = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" } %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
......
...@@ -12,4 +12,4 @@ ...@@ -12,4 +12,4 @@
.form-actions .form-actions
= button_tag 'Import project members', class: "btn btn-create" = button_tag 'Import project members', class: "btn btn-create"
= link_to "Cancel", project_settings_members_path(@project), class: "btn btn-cancel" = link_to "Cancel", project_project_members_path(@project), class: "btn btn-cancel"
- page_title "Members"
.row.prepend-top-default .row.prepend-top-default
.col-lg-4.settings-sidebar .col-lg-12
%h4.prepend-top-0 %h4
Project members Project members
- if can?(current_user, :admin_project_member, @project) - if can?(current_user, :admin_project_member, @project)
%p %p
...@@ -13,7 +15,6 @@ ...@@ -13,7 +15,6 @@
%i Masters %i Masters
or or
%i Owners %i Owners
.col-lg-8
.light .light
- if can?(current_user, :admin_project_member, @project) - if can?(current_user, :admin_project_member, @project)
%ul.nav-links.project-member-tabs{ role: 'tablist' } %ul.nav-links.project-member-tabs{ role: 'tablist' }
......
...@@ -9,10 +9,6 @@ ...@@ -9,10 +9,6 @@
= link_to edit_project_path(@project), title: 'General' do = link_to edit_project_path(@project), title: 'General' do
%span %span
General General
= nav_link(controller: :members) do
= link_to project_settings_members_path(@project), title: 'Members' do
%span
Members
- if can_edit - if can_edit
= nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do = nav_link(controller: [:integrations, :services, :hooks, :hook_logs]) do
= link_to project_settings_integrations_path(@project), title: 'Integrations' do = link_to project_settings_integrations_path(@project), title: 'Integrations' do
......
...@@ -8,6 +8,6 @@ ...@@ -8,6 +8,6 @@
= render "projects/settings/head" = render "projects/settings/head"
= render 'projects/runners/index' = render 'projects/runners/index'
= render 'projects/variables/index' = render 'ci/variables/index'
= render 'projects/triggers/index' = render 'projects/triggers/index'
= render 'projects/pipelines_settings/show' = render 'projects/pipelines_settings/show'
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
= render "projects/commits/head" = render "projects/commits/head"
= render 'projects/last_push' %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
%div{ class: container_class }
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref = render 'projects/files', commit: @last_commit, project: @project, ref: @ref
- page_title "Variables" = render 'ci/variables/show'
.row.prepend-top-default.append-bottom-default
.col-lg-3
= render "content"
.col-lg-9
%h5.prepend-top-0
Update variable
= render "form", btn_text: "Save variable"
-# @project is present when viewing Project's milestone -# @project is present when viewing Project's milestone
- project = @project || issuable.project - project = @project || issuable.project
- namespace = @project_namespace || project.namespace.becomes(Namespace) - namespace = @project_namespace || project.namespace.becomes(Namespace)
- labels = issuable.labels
- assignees = issuable.assignees - assignees = issuable.assignees
- issuable_type = issuable.class.table_name
- base_url_args = [namespace, project] - base_url_args = [namespace, project]
- issuable_type_args = base_url_args + [issuable_type] - issuable_type_args = base_url_args + [issuable.class.table_name]
- issuable_url_args = base_url_args + [issuable] - issuable_url_args = base_url_args + [issuable]
- can_update = can?(current_user, :"update_#{issuable.to_ability_name}", issuable)
%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row #{'is-disabled' unless can_update}", 'data-iid' => issuable.iid, 'data-id' => issuable.id, 'data-url' => polymorphic_path(issuable_url_args) } %li.issuable-row
%span %span
- if show_project_name - if show_project_name
%strong #{project.name} &middot; %strong #{project.name} &middot;
...@@ -18,10 +17,10 @@ ...@@ -18,10 +17,10 @@
= confidential_icon(issuable) = confidential_icon(issuable)
= link_to issuable.title, issuable_url_args, title: issuable.title = link_to issuable.title, issuable_url_args, title: issuable.title
.issuable-detail .issuable-detail
= link_to [project.namespace.becomes(Namespace), project, issuable] do = link_to [namespace, project, issuable] do
%span.issuable-number= issuable.to_reference %span.issuable-number= issuable.to_reference
- issuable.labels.each do |label| - labels.each do |label|
= link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
- render_colored_label(label) - render_colored_label(label)
......
- issues_accessible = milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project)
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
%ul.nav-links.scrolling-tabs.js-milestone-tabs %ul.nav-links.scrolling-tabs.js-milestone-tabs
- if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) - if issues_accessible
%li.active %li.active
= link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do
Issues Issues
...@@ -25,13 +27,14 @@ ...@@ -25,13 +27,14 @@
Labels Labels
%span.badge= milestone.labels.count %span.badge= milestone.labels.count
- issues = milestone.sorted_issues(current_user)
- show_project_name = local_assigns.fetch(:show_project_name, false) - show_project_name = local_assigns.fetch(:show_project_name, false)
- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) - show_full_project_name = local_assigns.fetch(:show_full_project_name, false)
.tab-content.milestone-content .tab-content.milestone-content
- if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project) - if issues_accessible
.tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_project_milestone_path(@project, @milestone) if @project && current_user) } } .tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_project_milestone_path(@project, @milestone) if @project && current_user) } }
= render 'shared/milestones/issues_tab', issues: milestone.sorted_issues(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name = render 'shared/milestones/issues_tab', issues: issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name
.tab-pane#tab-merge-requests .tab-pane#tab-merge-requests
-# loaded async -# loaded async
= render "shared/milestones/tab_loading" = render "shared/milestones/tab_loading"
......
---
title: Moved "Members in a project" menu entry and path locations
merge_request: 11560
---
title: Improve the performance of the project list API
merge_request: 12679
author:
---
title: Fixed the chart legend not being set correctly
merge_request: 12628
author:
---
title: Remove two columned layout from project member settings
merge_request:
author:
---
title: N+1 problems on milestone page
merge_request: 12670
author: Takuya Noguchi
---
title: Don't show auxiliary blob viewer for README when there is no wiki
merge_request:
author:
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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