Commit 15483879 authored by Nick Thomas's avatar Nick Thomas

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

parents ccbd665a f96e1bf1
...@@ -264,7 +264,6 @@ rake karma: ...@@ -264,7 +264,6 @@ rake karma:
cache: cache:
paths: paths:
- vendor/ruby - vendor/ruby
- node_modules
stage: test stage: test
<<: *use-db <<: *use-db
<<: *dedicated-runner <<: *dedicated-runner
...@@ -363,9 +362,6 @@ coverage: ...@@ -363,9 +362,6 @@ coverage:
lint:javascript: lint:javascript:
<<: *dedicated-runner <<: *dedicated-runner
cache:
paths:
- node_modules/
stage: test stage: test
before_script: [] before_script: []
script: script:
...@@ -373,9 +369,6 @@ lint:javascript: ...@@ -373,9 +369,6 @@ lint:javascript:
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner <<: *dedicated-runner
cache:
paths:
- node_modules/
stage: post-test stage: post-test
before_script: [] before_script: []
script: script:
......
import CANCELED_SVG from 'icons/_icon_status_canceled_borderless.svg';
import CREATED_SVG from 'icons/_icon_status_created_borderless.svg';
import FAILED_SVG from 'icons/_icon_status_failed_borderless.svg';
import MANUAL_SVG from 'icons/_icon_status_manual_borderless.svg';
import PENDING_SVG from 'icons/_icon_status_pending_borderless.svg';
import RUNNING_SVG from 'icons/_icon_status_running_borderless.svg';
import SKIPPED_SVG from 'icons/_icon_status_skipped_borderless.svg';
import SUCCESS_SVG from 'icons/_icon_status_success_borderless.svg';
import WARNING_SVG from 'icons/_icon_status_warning_borderless.svg';
const StatusIconEntityMap = {
icon_status_canceled: CANCELED_SVG,
icon_status_created: CREATED_SVG,
icon_status_failed: FAILED_SVG,
icon_status_manual: MANUAL_SVG,
icon_status_pending: PENDING_SVG,
icon_status_running: RUNNING_SVG,
icon_status_skipped: SKIPPED_SVG,
icon_status_success: SUCCESS_SVG,
icon_status_warning: WARNING_SVG,
};
export {
CANCELED_SVG,
CREATED_SVG,
FAILED_SVG,
MANUAL_SVG,
PENDING_SVG,
RUNNING_SVG,
SKIPPED_SVG,
SUCCESS_SVG,
WARNING_SVG,
StatusIconEntityMap as default,
};
...@@ -19,7 +19,11 @@ const ResolveBtn = Vue.extend({ ...@@ -19,7 +19,11 @@ const ResolveBtn = Vue.extend({
data: function () { data: function () {
return { return {
discussions: CommentsStore.state, discussions: CommentsStore.state,
<<<<<<< HEAD
loading: false, loading: false,
=======
loading: false
>>>>>>> ce/master
}; };
}, },
watch: { watch: {
......
...@@ -156,13 +156,13 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -156,13 +156,13 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:milestones:new': case 'projects:milestones:new':
case 'projects:milestones:edit': case 'projects:milestones:edit':
case 'projects:milestones:update': case 'projects:milestones:update':
case 'groups:milestones:new':
case 'groups:milestones:edit':
case 'groups:milestones:update':
new ZenMode(); new ZenMode();
new gl.DueDateSelectors(); new gl.DueDateSelectors();
new gl.GLForm($('.milestone-form')); new gl.GLForm($('.milestone-form'));
break; break;
case 'groups:milestones:new':
new ZenMode();
break;
case 'projects:compare:show': case 'projects:compare:show':
new gl.Diff(); new gl.Diff();
break; break;
...@@ -389,9 +389,14 @@ const ShortcutsBlob = require('./shortcuts_blob'); ...@@ -389,9 +389,14 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'admin': case 'admin':
new Admin(); new Admin();
switch (path[1]) { switch (path[1]) {
<<<<<<< HEAD
case 'application_settings': case 'application_settings':
case 'cohorts': case 'cohorts':
new gl.ApplicationSettings(); new gl.ApplicationSettings();
=======
case 'cohorts':
new gl.UsagePing();
>>>>>>> ce/master
break; break;
case 'groups': case 'groups':
new UsersSelect(); new UsersSelect();
......
...@@ -2,10 +2,12 @@ const DATA_TRIGGER = 'data-dropdown-trigger'; ...@@ -2,10 +2,12 @@ const DATA_TRIGGER = 'data-dropdown-trigger';
const DATA_DROPDOWN = 'data-dropdown'; const DATA_DROPDOWN = 'data-dropdown';
const SELECTED_CLASS = 'droplab-item-selected'; const SELECTED_CLASS = 'droplab-item-selected';
const ACTIVE_CLASS = 'droplab-item-active'; const ACTIVE_CLASS = 'droplab-item-active';
const IGNORE_CLASS = 'droplab-item-ignore';
export { export {
DATA_TRIGGER, DATA_TRIGGER,
DATA_DROPDOWN, DATA_DROPDOWN,
SELECTED_CLASS, SELECTED_CLASS,
ACTIVE_CLASS, ACTIVE_CLASS,
IGNORE_CLASS,
}; };
/* eslint-disable */ /* eslint-disable */
import utils from './utils'; import utils from './utils';
import { SELECTED_CLASS } from './constants'; import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
var DropDown = function(list) { var DropDown = function(list) {
this.currentIndex = 0; this.currentIndex = 0;
...@@ -36,6 +36,7 @@ Object.assign(DropDown.prototype, { ...@@ -36,6 +36,7 @@ Object.assign(DropDown.prototype, {
clickEvent: function(e) { clickEvent: function(e) {
if (e.target.tagName === 'UL') return; if (e.target.tagName === 'UL') return;
if (e.target.classList.contains(IGNORE_CLASS)) return;
var selected = utils.closest(e.target, 'LI'); var selected = utils.closest(e.target, 'LI');
if (!selected) return; if (!selected) return;
......
...@@ -38,6 +38,9 @@ window.DropzoneInput = (function() { ...@@ -38,6 +38,9 @@ window.DropzoneInput = (function() {
"opacity": 0, "opacity": 0,
"display": "none" "display": "none"
}); });
if (!project_uploads_path) return;
dropzone = form_dropzone.dropzone({ dropzone = form_dropzone.dropzone({
url: project_uploads_path, url: project_uploads_path,
dictDefaultMessage: "", dictDefaultMessage: "",
...@@ -133,8 +136,11 @@ window.DropzoneInput = (function() { ...@@ -133,8 +136,11 @@ window.DropzoneInput = (function() {
const textarea = child.get(0); const textarea = child.get(0);
caretStart = textarea.selectionStart; caretStart = textarea.selectionStart;
caretEnd = textarea.selectionEnd; caretEnd = textarea.selectionEnd;
<<<<<<< HEAD
caretStart = textarea.selectionStart; caretStart = textarea.selectionStart;
caretEnd = textarea.selectionEnd; caretEnd = textarea.selectionEnd;
=======
>>>>>>> ce/master
textEnd = $(child).val().length; textEnd = $(child).val().length;
beforeSelection = $(child).val().substring(0, caretStart); beforeSelection = $(child).val().substring(0, caretStart);
afterSelection = $(child).val().substring(caretEnd, textEnd); afterSelection = $(child).val().substring(caretEnd, textEnd);
......
<script>
/** /**
* Renders the external url link in environments table. * Renders the external url link in environments table.
*/ */
...@@ -5,7 +6,7 @@ export default { ...@@ -5,7 +6,7 @@ export default {
props: { props: {
externalUrl: { externalUrl: {
type: String, type: String,
default: '', required: true,
}, },
}, },
...@@ -14,17 +15,19 @@ export default { ...@@ -14,17 +15,19 @@ export default {
return 'Open'; return 'Open';
}, },
}, },
template: `
<a
class="btn external-url has-tooltip"
data-container="body"
:href="externalUrl"
target="_blank"
rel="noopener noreferrer nofollow"
:title="title"
:aria-label="title">
<i class="fa fa-external-link" aria-hidden="true"></i>
</a>
`,
}; };
</script>
<template>
<a
class="btn external-url has-tooltip"
data-container="body"
target="_blank"
rel="noopener noreferrer nofollow"
:title="title"
:aria-label="title"
:href="externalUrl">
<i
class="fa fa-external-link"
aria-hidden="true" />
</a>
</template>
...@@ -7,10 +7,10 @@ ...@@ -7,10 +7,10 @@
import Timeago from 'timeago.js'; import Timeago from 'timeago.js';
import '../../lib/utils/text_utility'; import '../../lib/utils/text_utility';
import ActionsComponent from './environment_actions'; import ActionsComponent from './environment_actions';
import ExternalUrlComponent from './environment_external_url'; import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop'; import StopComponent from './environment_stop.vue';
import RollbackComponent from './environment_rollback'; import RollbackComponent from './environment_rollback';
import TerminalButtonComponent from './environment_terminal_button'; import TerminalButtonComponent from './environment_terminal_button.vue';
import MonitoringButtonComponent from './environment_monitoring'; import MonitoringButtonComponent from './environment_monitoring';
import CommitComponent from '../../vue_shared/components/commit'; import CommitComponent from '../../vue_shared/components/commit';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
......
...@@ -21,7 +21,6 @@ export default { ...@@ -21,7 +21,6 @@ export default {
class="btn monitoring-url has-tooltip" class="btn monitoring-url has-tooltip"
data-container="body" data-container="body"
:href="monitoringUrl" :href="monitoringUrl"
target="_blank"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
:title="title" :title="title"
:aria-label="title"> :aria-label="title">
......
<script>
/* global Flash */ /* global Flash */
/* eslint-disable no-new, no-alert */ /* eslint-disable no-new, no-alert */
/** /**
...@@ -51,17 +52,23 @@ export default { ...@@ -51,17 +52,23 @@ export default {
} }
}, },
}, },
template: `
<button type="button"
class="btn stop-env-link has-tooltip"
data-container="body"
@click="onClick"
:disabled="isLoading"
:title="title"
:aria-label="title">
<i class="fa fa-stop stop-env-icon" aria-hidden="true"></i>
<i v-if="isLoading" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</button>
`,
}; };
</script>
<template>
<button
type="button"
class="btn stop-env-link has-tooltip"
data-container="body"
@click="onClick"
:disabled="isLoading"
:title="title"
:aria-label="title">
<i
class="fa fa-stop stop-env-icon"
aria-hidden="true" />
<i
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</button>
</template>
<script>
/** /**
* Renders a terminal button to open a web terminal. * Renders a terminal button to open a web terminal.
* Used in environments table. * Used in environments table.
...@@ -24,14 +25,15 @@ export default { ...@@ -24,14 +25,15 @@ export default {
return 'Terminal'; return 'Terminal';
}, },
}, },
template: `
<a class="btn terminal-button has-tooltip"
data-container="body"
:title="title"
:aria-label="title"
:href="terminalPath">
${terminalIconSvg}
</a>
`,
}; };
</script>
<template>
<a
class="btn terminal-button has-tooltip"
data-container="body"
:title="title"
:aria-label="title"
:href="terminalPath"
v-html="terminalIconSvg">
</a>
</template>
...@@ -2,82 +2,80 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -2,82 +2,80 @@ import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown'); require('./filtered_search_dropdown');
(() => { class DropdownHint extends gl.FilteredSearchDropdown {
class DropdownHint extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input, filter) {
constructor(droplab, dropdown, input, filter) { super(droplab, dropdown, input, filter);
super(droplab, dropdown, input, filter); this.config = {
this.config = { Filter: {
Filter: { template: 'hint',
template: 'hint', filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
filterFunction: gl.DropdownUtils.filterHint.bind(null, input), },
}, };
}; }
}
itemClicked(e) {
const { selected } = e.detail;
if (selected.tagName === 'LI') { itemClicked(e) {
if (selected.hasAttribute('data-value')) { const { selected } = e.detail;
this.dismissDropdown();
} else if (selected.getAttribute('data-action') === 'submit') {
this.dismissDropdown();
this.dispatchFormSubmitEvent();
} else {
const token = selected.querySelector('.js-filter-hint').innerText.trim();
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
if (tag.length) { if (selected.tagName === 'LI') {
// Get previous input values in the input field and convert them into visual tokens if (selected.hasAttribute('data-value')) {
const previousInputValues = this.input.value.split(' '); this.dismissDropdown();
const searchTerms = []; } else if (selected.getAttribute('data-action') === 'submit') {
this.dismissDropdown();
this.dispatchFormSubmitEvent();
} else {
const token = selected.querySelector('.js-filter-hint').innerText.trim();
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
previousInputValues.forEach((value, index) => { if (tag.length) {
searchTerms.push(value); // Get previous input values in the input field and convert them into visual tokens
const previousInputValues = this.input.value.split(' ');
const searchTerms = [];
if (index === previousInputValues.length - 1 previousInputValues.forEach((value, index) => {
&& token.indexOf(value.toLowerCase()) !== -1) { searchTerms.push(value);
searchTerms.pop();
}
});
if (searchTerms.length > 0) { if (index === previousInputValues.length - 1
gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); && token.indexOf(value.toLowerCase()) !== -1) {
searchTerms.pop();
} }
});
gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); if (searchTerms.length > 0) {
gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
} }
this.dismissDropdown();
this.dispatchInputEvent(); gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
} }
this.dismissDropdown();
this.dispatchInputEvent();
} }
} }
}
renderContent() { renderContent() {
const dropdownData = []; const dropdownData = [];
[].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => { [].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
const { icon, hint, tag, type } = dropdownMenu.dataset; const { icon, hint, tag, type } = dropdownMenu.dataset;
if (icon && hint && tag) { if (icon && hint && tag) {
dropdownData.push( dropdownData.push(
Object.assign({ Object.assign({
icon: `fa-${icon}`, icon: `fa-${icon}`,
hint, hint,
tag: `&lt;${tag}&gt;`, tag: `&lt;${tag}&gt;`,
}, type && { type }), }, type && { type }),
); );
} }
}); });
this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config); this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
this.droplab.setData(this.hookId, dropdownData); this.droplab.setData(this.hookId, dropdownData);
} }
init() { init() {
this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init(); this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init();
}
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.DropdownHint = DropdownHint; gl.DropdownHint = DropdownHint;
})();
...@@ -5,48 +5,46 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -5,48 +5,46 @@ import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown'); require('./filtered_search_dropdown');
(() => { class DropdownNonUser extends gl.FilteredSearchDropdown {
class DropdownNonUser extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input, filter, endpoint, symbol) {
constructor(droplab, dropdown, input, filter, endpoint, symbol) { super(droplab, dropdown, input, filter);
super(droplab, dropdown, input, filter); this.symbol = symbol;
this.symbol = symbol; this.config = {
this.config = { Ajax: {
Ajax: { endpoint,
endpoint, method: 'setData',
method: 'setData', loadingTemplate: this.loadingTemplate,
loadingTemplate: this.loadingTemplate, onError() {
onError() { /* eslint-disable no-new */
/* eslint-disable no-new */ new Flash('An error occured fetching the dropdown data.');
new Flash('An error occured fetching the dropdown data.'); /* eslint-enable no-new */
/* eslint-enable no-new */
},
}, },
Filter: { },
filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input), Filter: {
template: 'title', filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
}, template: 'title',
}; },
} };
}
itemClicked(e) { itemClicked(e) {
super.itemClicked(e, (selected) => { super.itemClicked(e, (selected) => {
const title = selected.querySelector('.js-data-value').innerText.trim(); const title = selected.querySelector('.js-data-value').innerText.trim();
return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`; return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`;
}); });
} }
renderContent(forceShowList = false) { renderContent(forceShowList = false) {
this.droplab this.droplab
.changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config); .changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
super.renderContent(forceShowList); super.renderContent(forceShowList);
} }
init() { init() {
this.droplab this.droplab
.addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.DropdownNonUser = DropdownNonUser; gl.DropdownNonUser = DropdownNonUser;
})();
...@@ -4,69 +4,67 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter'; ...@@ -4,69 +4,67 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter';
require('./filtered_search_dropdown'); require('./filtered_search_dropdown');
(() => { class DropdownUser extends gl.FilteredSearchDropdown {
class DropdownUser extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input, filter) {
constructor(droplab, dropdown, input, filter) { super(droplab, dropdown, input, filter);
super(droplab, dropdown, input, filter); this.config = {
this.config = { AjaxFilter: {
AjaxFilter: { endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`, searchKey: 'search',
searchKey: 'search', params: {
params: { per_page: 20,
per_page: 20, active: true,
active: true, project_id: this.getProjectId(),
project_id: this.getProjectId(), current_user: true,
current_user: true,
},
searchValueFunction: this.getSearchInput.bind(this),
loadingTemplate: this.loadingTemplate,
onError() {
/* eslint-disable no-new */
new Flash('An error occured fetching the dropdown data.');
/* eslint-enable no-new */
},
}, },
}; searchValueFunction: this.getSearchInput.bind(this),
} loadingTemplate: this.loadingTemplate,
onError() {
itemClicked(e) { /* eslint-disable no-new */
super.itemClicked(e, new Flash('An error occured fetching the dropdown data.');
selected => selected.querySelector('.dropdown-light-content').innerText.trim()); /* eslint-enable no-new */
} },
},
renderContent(forceShowList = false) { };
this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config); }
super.renderContent(forceShowList);
}
getProjectId() { itemClicked(e) {
return this.input.getAttribute('data-project-id'); super.itemClicked(e,
} selected => selected.querySelector('.dropdown-light-content').innerText.trim());
}
getSearchInput() { renderContent(forceShowList = false) {
const query = gl.DropdownUtils.getSearchInput(this.input); this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); super.renderContent(forceShowList);
}
let value = lastToken || ''; getProjectId() {
return this.input.getAttribute('data-project-id');
}
if (value[0] === '@') { getSearchInput() {
value = value.slice(1); const query = gl.DropdownUtils.getSearchInput(this.input);
} const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
// Removes the first character if it is a quotation so that we can search let value = lastToken || '';
// with multiple words
if (value[0] === '"' || value[0] === '\'') {
value = value.slice(1);
}
return value; if (value[0] === '@') {
value = value.slice(1);
} }
init() { // Removes the first character if it is a quotation so that we can search
this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init(); // with multiple words
if (value[0] === '"' || value[0] === '\'') {
value = value.slice(1);
} }
return value;
}
init() {
this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.DropdownUser = DropdownUser; gl.DropdownUser = DropdownUser;
})();
(() => { const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
class FilteredSearchDropdown {
class FilteredSearchDropdown { constructor(droplab, dropdown, input, filter) {
constructor(droplab, dropdown, input, filter) { this.droplab = droplab;
this.droplab = droplab; this.hookId = input && input.id;
this.hookId = input && input.id; this.input = input;
this.input = input; this.filter = filter;
this.filter = filter; this.dropdown = dropdown;
this.dropdown = dropdown; this.loadingTemplate = `<div class="filter-dropdown-loading">
this.loadingTemplate = `<div class="filter-dropdown-loading"> <i class="fa fa-spinner fa-spin"></i>
<i class="fa fa-spinner fa-spin"></i> </div>`;
</div>`; this.bindEvents();
this.bindEvents(); }
}
bindEvents() {
this.itemClickedWrapper = this.itemClicked.bind(this);
this.dropdown.addEventListener('click.dl', this.itemClickedWrapper);
}
unbindEvents() { bindEvents() {
this.dropdown.removeEventListener('click.dl', this.itemClickedWrapper); this.itemClickedWrapper = this.itemClicked.bind(this);
} this.dropdown.addEventListener('click.dl', this.itemClickedWrapper);
}
getCurrentHook() { unbindEvents() {
return this.droplab.hooks.filter(h => h.id === this.hookId)[0] || null; this.dropdown.removeEventListener('click.dl', this.itemClickedWrapper);
} }
itemClicked(e, getValueFunction) { getCurrentHook() {
const { selected } = e.detail; return this.droplab.hooks.filter(h => h.id === this.hookId)[0] || null;
}
if (selected.tagName === 'LI' && selected.innerHTML) { itemClicked(e, getValueFunction) {
const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected); const { selected } = e.detail;
if (!dataValueSet) { if (selected.tagName === 'LI' && selected.innerHTML) {
const value = getValueFunction(selected); const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected);
gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
}
this.resetFilters(); if (!dataValueSet) {
this.dismissDropdown(); const value = getValueFunction(selected);
this.dispatchInputEvent(); gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
} }
}
setAsDropdown() { this.resetFilters();
this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.dropdown.id}`); this.dismissDropdown();
this.dispatchInputEvent();
} }
}
setOffset(offset = 0) { setAsDropdown() {
if (window.innerWidth > 480) { this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.dropdown.id}`);
this.dropdown.style.left = `${offset}px`; }
} else {
this.dropdown.style.left = '0px'; setOffset(offset = 0) {
} if (window.innerWidth > 480) {
this.dropdown.style.left = `${offset}px`;
} else {
this.dropdown.style.left = '0px';
} }
}
renderContent(forceShowList = false) { renderContent(forceShowList = false) {
const currentHook = this.getCurrentHook(); const currentHook = this.getCurrentHook();
if (forceShowList && currentHook && currentHook.list.hidden) { if (forceShowList && currentHook && currentHook.list.hidden) {
currentHook.list.show(); currentHook.list.show();
}
} }
}
render(forceRenderContent = false, forceShowList = false) { render(forceRenderContent = false, forceShowList = false) {
this.setAsDropdown(); this.setAsDropdown();
const currentHook = this.getCurrentHook(); const currentHook = this.getCurrentHook();
const firstTimeInitialized = currentHook === null; const firstTimeInitialized = currentHook === null;
if (firstTimeInitialized || forceRenderContent) { if (firstTimeInitialized || forceRenderContent) {
this.renderContent(forceShowList); this.renderContent(forceShowList);
} else if (currentHook.list.list.id !== this.dropdown.id) { } else if (currentHook.list.list.id !== this.dropdown.id) {
this.renderContent(forceShowList); this.renderContent(forceShowList);
}
} }
}
dismissDropdown() { dismissDropdown() {
// Focusing on the input will dismiss dropdown // Focusing on the input will dismiss dropdown
// (default droplab functionality) // (default droplab functionality)
this.input.focus(); this.input.focus();
} }
dispatchInputEvent() { dispatchInputEvent() {
// Propogate input change to FilteredSearchDropdownManager // Propogate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open // so that it can determine which dropdowns to open
this.input.dispatchEvent(new CustomEvent('input', { this.input.dispatchEvent(new CustomEvent('input', {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
})); }));
} }
dispatchFormSubmitEvent() { dispatchFormSubmitEvent() {
// dispatchEvent() is necessary as form.submit() does not // dispatchEvent() is necessary as form.submit() does not
// trigger event handlers // trigger event handlers
this.input.form.dispatchEvent(new Event('submit')); this.input.form.dispatchEvent(new Event('submit'));
} }
hideDropdown() { hideDropdown() {
const currentHook = this.getCurrentHook(); const currentHook = this.getCurrentHook();
if (currentHook) { if (currentHook) {
currentHook.list.hide(); currentHook.list.hide();
}
} }
}
<<<<<<< HEAD
resetFilters() { resetFilters() {
const hook = this.getCurrentHook(); const hook = this.getCurrentHook();
...@@ -119,9 +119,22 @@ ...@@ -119,9 +119,22 @@
}); });
hook.list.render(results); hook.list.render(results);
} }
=======
resetFilters() {
const hook = this.getCurrentHook();
if (hook) {
const data = hook.list.data || [];
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;
return updated;
});
hook.list.render(results);
>>>>>>> ce/master
} }
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.FilteredSearchDropdown = FilteredSearchDropdown; gl.FilteredSearchDropdown = FilteredSearchDropdown;
})();
(() => { const tokenKeys = [{
const tokenKeys = [{ key: 'author',
key: 'author', type: 'string',
type: 'string', param: 'username',
param: 'username', symbol: '@',
symbol: '@', }, {
}, { key: 'assignee',
key: 'assignee', type: 'string',
type: 'string', param: 'username',
param: 'username', symbol: '@',
symbol: '@', }, {
}, { key: 'milestone',
key: 'milestone', type: 'string',
type: 'string', param: 'title',
param: 'title', symbol: '%',
symbol: '%', }, {
}, { key: 'label',
key: 'label', type: 'array',
type: 'array', param: 'name[]',
param: 'name[]', symbol: '~',
symbol: '~', }];
}];
const alternativeTokenKeys = [{ const alternativeTokenKeys = [{
key: 'label', key: 'label',
type: 'string', type: 'string',
param: 'name', param: 'name',
symbol: '~', symbol: '~',
}]; }];
const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys); const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
const conditions = [{ const conditions = [{
url: 'assignee_id=0', url: 'assignee_id=0',
tokenKey: 'assignee', tokenKey: 'assignee',
value: 'none', value: 'none',
}, { }, {
url: 'milestone_title=No+Milestone', url: 'milestone_title=No+Milestone',
tokenKey: 'milestone', tokenKey: 'milestone',
value: 'none', value: 'none',
}, { }, {
url: 'milestone_title=%23upcoming', url: 'milestone_title=%23upcoming',
tokenKey: 'milestone', tokenKey: 'milestone',
value: 'upcoming', value: 'upcoming',
}, { }, {
url: 'milestone_title=%23started', url: 'milestone_title=%23started',
tokenKey: 'milestone', tokenKey: 'milestone',
value: 'started', value: 'started',
}, { }, {
url: 'label_name[]=No+Label', url: 'label_name[]=No+Label',
tokenKey: 'label', tokenKey: 'label',
value: 'none', value: 'none',
}]; }];
class FilteredSearchTokenKeys { class FilteredSearchTokenKeys {
static get() { static get() {
return tokenKeys; return tokenKeys;
} }
static getAlternatives() { static getAlternatives() {
return alternativeTokenKeys; return alternativeTokenKeys;
} }
static getConditions() { static getConditions() {
return conditions; return conditions;
} }
static searchByKey(key) { static searchByKey(key) {
return tokenKeys.find(tokenKey => tokenKey.key === key) || null; return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
} }
static searchBySymbol(symbol) { static searchBySymbol(symbol) {
return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null; return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
} }
static searchByKeyParam(keyParam) { static searchByKeyParam(keyParam) {
return tokenKeysWithAlternative.find((tokenKey) => { return tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key; let tokenKeyParam = tokenKey.key;
if (tokenKey.param) { if (tokenKey.param) {
tokenKeyParam += `_${tokenKey.param}`; tokenKeyParam += `_${tokenKey.param}`;
} }
return keyParam === tokenKeyParam; return keyParam === tokenKeyParam;
}) || null; }) || null;
} }
static searchByConditionUrl(url) { static searchByConditionUrl(url) {
return conditions.find(condition => condition.url === url) || null; return conditions.find(condition => condition.url === url) || null;
} }
static searchByConditionKeyValue(key, value) { static searchByConditionKeyValue(key, value) {
return conditions return conditions
.find(condition => condition.tokenKey === key && condition.value === value) || null; .find(condition => condition.tokenKey === key && condition.value === value) || null;
}
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys; gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys;
})();
require('./filtered_search_token_keys'); require('./filtered_search_token_keys');
(() => { class FilteredSearchTokenizer {
class FilteredSearchTokenizer { static processTokens(input) {
static processTokens(input) { const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key); // Regex extracts `(token):(symbol)(value)`
// Regex extracts `(token):(symbol)(value)` // Values that start with a double quote must end in a double quote (same for single)
// Values that start with a double quote must end in a double quote (same for single) const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g'); const tokens = [];
const tokens = []; const tokenIndexes = []; // stores key+value for simple search
const tokenIndexes = []; // stores key+value for simple search let lastToken = null;
let lastToken = null; const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => { let tokenValue = v1 || v2 || v3;
let tokenValue = v1 || v2 || v3; let tokenSymbol = symbol;
let tokenSymbol = symbol; let tokenIndex = '';
let tokenIndex = '';
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') { tokenSymbol = tokenValue;
tokenSymbol = tokenValue; tokenValue = '';
tokenValue = '';
}
tokenIndex = `${key}:${tokenValue}`;
// Prevent adding duplicates
if (tokenIndexes.indexOf(tokenIndex) === -1) {
tokenIndexes.push(tokenIndex);
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
}
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
lastToken = input.lastIndexOf(lastString) ===
input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
} }
return { tokenIndex = `${key}:${tokenValue}`;
tokens,
lastToken, // Prevent adding duplicates
searchToken, if (tokenIndexes.indexOf(tokenIndex) === -1) {
}; tokenIndexes.push(tokenIndex);
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
}
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
lastToken = input.lastIndexOf(lastString) ===
input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
} }
return {
tokens,
lastToken,
searchToken,
};
} }
}
window.gl = window.gl || {}; window.gl = window.gl || {};
gl.FilteredSearchTokenizer = FilteredSearchTokenizer; gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
})();
...@@ -368,9 +368,9 @@ ...@@ -368,9 +368,9 @@
}); });
}; };
w.gl.utils.setFavicon = (iconName) => { w.gl.utils.setFavicon = (faviconPath) => {
if (faviconEl && iconName) { if (faviconEl && faviconPath) {
faviconEl.setAttribute('href', `/assets/${iconName}.ico`); faviconEl.setAttribute('href', faviconPath);
} }
}; };
...@@ -385,8 +385,8 @@ ...@@ -385,8 +385,8 @@
url: pageUrl, url: pageUrl,
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
if (data && data.icon) { if (data && data.favicon) {
gl.utils.setFavicon(`ci_favicons/${data.icon}`); gl.utils.setFavicon(data.favicon);
} else { } else {
gl.utils.resetFavicon(); gl.utils.resetFavicon();
} }
......
...@@ -165,6 +165,7 @@ import './syntax_highlight'; ...@@ -165,6 +165,7 @@ import './syntax_highlight';
import './task_list'; import './task_list';
import './todos'; import './todos';
import './tree'; import './tree';
import './usage_ping';
import './user'; import './user';
import './user_tabs'; import './user_tabs';
import './username_validator'; import './username_validator';
...@@ -218,6 +219,14 @@ $(function () { ...@@ -218,6 +219,14 @@ $(function () {
} }
}); });
if (bootstrapBreakpoint === 'xs') {
const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar');
$rightSidebar
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed');
}
// prevent default action for disabled buttons // prevent default action for disabled buttons
$('.btn').click(function(e) { $('.btn').click(function(e) {
if ($(this).hasClass('disabled')) { if ($(this).hasClass('disabled')) {
......
...@@ -308,8 +308,10 @@ require('./task_list'); ...@@ -308,8 +308,10 @@ require('./task_list');
if (this.isNewNote(note)) { if (this.isNewNote(note)) {
this.note_ids.push(note.id); this.note_ids.push(note.id);
$notesList = $('ul.main-notes-list');
$notesList.append(note.html).syntaxHighlight(); $notesList = window.$('ul.main-notes-list');
Notes.animateAppendNote(note.html, $notesList);
// Update datetime format on the recent note // Update datetime format on the recent note
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false); gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
this.collapseLongCommitList(); this.collapseLongCommitList();
...@@ -348,7 +350,7 @@ require('./task_list'); ...@@ -348,7 +350,7 @@ require('./task_list');
lineType = this.isParallelView() ? form.find('#line_type').val() : 'old'; lineType = this.isParallelView() ? form.find('#line_type').val() : 'old';
diffAvatarContainer = row.prevAll('.line_holder').first().find('.js-avatar-container.' + lineType + '_line'); diffAvatarContainer = row.prevAll('.line_holder').first().find('.js-avatar-container.' + lineType + '_line');
// is this the first note of discussion? // is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']"); discussionContainer = window.$(`.notes[data-discussion-id="${note.discussion_id}"]`);
if (!discussionContainer.length) { if (!discussionContainer.length) {
discussionContainer = form.closest('.discussion').find('.notes'); discussionContainer = form.closest('.discussion').find('.notes');
} }
...@@ -370,14 +372,13 @@ require('./task_list'); ...@@ -370,14 +372,13 @@ require('./task_list');
row.find(contentContainerClass + ' .content').append($notes.closest('.content').children()); row.find(contentContainerClass + ' .content').append($notes.closest('.content').children());
} }
} }
// Init discussion on 'Discussion' page if it is merge request page // Init discussion on 'Discussion' page if it is merge request page
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0 || !note.diff_discussion_html) { if (window.$('body').attr('data-page').indexOf('projects:merge_request') === 0 || !note.diff_discussion_html) {
$('ul.main-notes-list').append($(note.discussion_html).renderGFM()); Notes.animateAppendNote(note.discussion_html, window.$('ul.main-notes-list'));
} }
} else { } else {
// append new note to all matching discussions // append new note to all matching discussions
discussionContainer.append($(note.html).renderGFM()); Notes.animateAppendNote(note.html, discussionContainer);
} }
if (typeof gl.diffNotesCompileComponents !== 'undefined' && note.discussion_resolvable) { if (typeof gl.diffNotesCompileComponents !== 'undefined' && note.discussion_resolvable) {
...@@ -1063,6 +1064,13 @@ require('./task_list'); ...@@ -1063,6 +1064,13 @@ require('./task_list');
return $form; return $form;
}; };
Notes.animateAppendNote = function(noteHTML, $notesList) {
const $note = window.$(noteHTML);
$note.addClass('fade-in').renderGFM();
$notesList.append($note);
};
return Notes; return Notes;
})(); })();
}).call(window); }).call(window);
/* global Flash */ /* global Flash */
import canceledSvg from 'icons/_icon_status_canceled_borderless.svg'; import StatusIconEntityMap from '../../ci_status_icons';
import createdSvg from 'icons/_icon_status_created_borderless.svg';
import failedSvg from 'icons/_icon_status_failed_borderless.svg';
import manualSvg from 'icons/_icon_status_manual_borderless.svg';
import pendingSvg from 'icons/_icon_status_pending_borderless.svg';
import runningSvg from 'icons/_icon_status_running_borderless.svg';
import skippedSvg from 'icons/_icon_status_skipped_borderless.svg';
import successSvg from 'icons/_icon_status_success_borderless.svg';
import warningSvg from 'icons/_icon_status_warning_borderless.svg';
export default { export default {
data() { data() {
const svgsDictionary = {
icon_status_canceled: canceledSvg,
icon_status_created: createdSvg,
icon_status_failed: failedSvg,
icon_status_manual: manualSvg,
icon_status_pending: pendingSvg,
icon_status_running: runningSvg,
icon_status_skipped: skippedSvg,
icon_status_success: successSvg,
icon_status_warning: warningSvg,
};
return { return {
builds: '', builds: '',
spinner: '<span class="fa fa-spinner fa-spin"></span>', spinner: '<span class="fa fa-spinner fa-spin"></span>',
svg: svgsDictionary[this.stage.status.icon],
}; };
}, },
...@@ -89,6 +68,9 @@ export default { ...@@ -89,6 +68,9 @@ export default {
triggerButtonClass() { triggerButtonClass() {
return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`; return `mini-pipeline-graph-dropdown-toggle has-tooltip js-builds-dropdown-button ci-status-icon-${this.stage.status.group}`;
}, },
svgHTML() {
return StatusIconEntityMap[this.stage.status.icon];
},
}, },
template: ` template: `
<div> <div>
...@@ -100,7 +82,7 @@ export default { ...@@ -100,7 +82,7 @@ export default {
data-toggle="dropdown" data-toggle="dropdown"
type="button" type="button"
:aria-label="stage.title"> :aria-label="stage.title">
<span v-html="svg" aria-hidden="true"></span> <span v-html="svgHTML" aria-hidden="true"></span>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container"> <ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
......
function UsagePing() {
const usageDataUrl = $('.usage-data').data('endpoint');
$.ajax({
type: 'GET',
url: usageDataUrl,
dataType: 'html',
success(html) {
$('.usage-data').html(html);
},
});
}
window.gl = window.gl || {};
window.gl.UsagePing = UsagePing;
...@@ -145,3 +145,17 @@ a { ...@@ -145,3 +145,17 @@ a {
.dropdown-menu-nav a { .dropdown-menu-nav a {
transition: none; transition: none;
} }
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.fade-in {
animation: fadeIn $fade-in-duration 1;
}
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
line-height: 24px; line-height: 24px;
} }
.bold {
font-weight: 600;
}
.tab-content { .tab-content {
overflow: visible; overflow: visible;
} }
......
...@@ -564,3 +564,7 @@ ...@@ -564,3 +564,7 @@
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
} }
} }
.droplab-item-ignore {
pointer-events: none;
}
...@@ -331,6 +331,14 @@ header { ...@@ -331,6 +331,14 @@ header {
.dropdown-menu-nav { .dropdown-menu-nav {
min-width: 140px; min-width: 140px;
margin-top: -5px; margin-top: -5px;
.current-user {
padding: 5px 18px;
.user-name {
display: block;
}
}
} }
} }
......
...@@ -460,6 +460,11 @@ $label-inverse-bg: #333; ...@@ -460,6 +460,11 @@ $label-inverse-bg: #333;
$label-remove-border: rgba(0, 0, 0, .1); $label-remove-border: rgba(0, 0, 0, .1);
$label-border-radius: 100px; $label-border-radius: 100px;
/*
* Animation
*/
$fade-in-duration: 200ms;
/* /*
* Lint * Lint
*/ */
......
...@@ -210,10 +210,6 @@ ...@@ -210,10 +210,6 @@
} }
} }
.bold {
font-weight: 600;
}
.light { .light {
font-weight: normal; font-weight: normal;
} }
......
...@@ -289,8 +289,12 @@ table.u2f-registrations { ...@@ -289,8 +289,12 @@ table.u2f-registrations {
margin: 0 auto; margin: 0 auto;
.bordered-box { .bordered-box {
border: 1px solid $border-color; border: 1px solid $blue-300;
border-radius: $border-radius-default; border-radius: $border-radius-default;
background-color: $blue-25;
position: relative;
display: flex;
justify-content: center;
} }
.landing { .landing {
...@@ -298,28 +302,59 @@ table.u2f-registrations { ...@@ -298,28 +302,59 @@ table.u2f-registrations {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
.close { .close {
margin-right: 20px; position: absolute;
} right: 20px;
opacity: 1;
.dismiss-icon {
float: right;
cursor: pointer;
color: $blue-300;
}
.dismiss-icon { &:hover {
float: right; background-color: transparent;
cursor: pointer; border: 0;
color: $cycle-analytics-dismiss-icon-color;
.dismiss-icon {
color: $blue-400;
}
}
} }
.svg-container { .svg-container {
text-align: center; margin-right: 30px;
display: inline-block;
svg { svg {
width: 136px; height: 110px;
height: 136px; vertical-align: top;
} }
} }
.user-callout-copy {
display: inline-block;
vertical-align: top;
}
} }
@media(max-width: $screen-xs-max) { @media(max-width: $screen-xs-max) {
.inner-content { text-align: center;
padding-left: 30px;
.bordered-box {
display: block;
}
.landing {
.svg-container,
.user-callout-copy {
margin: 0;
display: block;
svg {
height: 75px;
}
}
} }
} }
} }
...@@ -604,6 +604,10 @@ pre.light-well { ...@@ -604,6 +604,10 @@ pre.light-well {
.avatar-container { .avatar-container {
align-self: flex-start; align-self: flex-start;
> a {
width: 100%;
}
} }
.project-details { .project-details {
......
...@@ -112,4 +112,8 @@ class Projects::GitHttpController < Projects::GitHttpClientController ...@@ -112,4 +112,8 @@ class Projects::GitHttpController < Projects::GitHttpClientController
def access_klass def access_klass
@access_klass ||= wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess @access_klass ||= wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
end end
def log_user_activity
Users::ActivityService.new(user, 'pull').execute
end
end end
...@@ -64,6 +64,14 @@ module SortingHelper ...@@ -64,6 +64,14 @@ module SortingHelper
} }
end end
def branches_sort_options_hash
{
sort_value_name => sort_title_name,
sort_value_recently_updated => sort_title_recently_updated,
sort_value_oldest_updated => sort_title_oldest_updated
}
end
def sort_title_priority def sort_title_priority
'Priority' 'Priority'
end end
......
...@@ -259,6 +259,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -259,6 +259,7 @@ class ApplicationSetting < ActiveRecord::Base
user_default_external: false, user_default_external: false,
polling_interval_multiplier: 1, polling_interval_multiplier: 1,
usage_ping_enabled: true usage_ping_enabled: true
<<<<<<< HEAD
} }
end end
...@@ -269,6 +270,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -269,6 +270,8 @@ class ApplicationSetting < ActiveRecord::Base
elasticsearch_aws_region: ENV['ELASTIC_REGION'] || 'us-east-1', elasticsearch_aws_region: ENV['ELASTIC_REGION'] || 'us-east-1',
minimum_mirror_sync_time: Gitlab::Mirror::FIFTEEN, minimum_mirror_sync_time: Gitlab::Mirror::FIFTEEN,
repository_size_limit: 0 repository_size_limit: 0
=======
>>>>>>> ce/master
} }
end end
......
...@@ -23,7 +23,7 @@ module Issuable ...@@ -23,7 +23,7 @@ module Issuable
included do included do
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description cache_markdown_field :description, issuable_state_filter_enabled: true
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
......
...@@ -7,7 +7,11 @@ class Identity < ActiveRecord::Base ...@@ -7,7 +7,11 @@ class Identity < ActiveRecord::Base
validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider } validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
validates :user_id, uniqueness: { scope: :provider } validates :user_id, uniqueness: { scope: :provider }
<<<<<<< HEAD
scope :with_provider, ->(provider) { where(provider: provider) } scope :with_provider, ->(provider) { where(provider: provider) }
=======
scope :with_extern_uid, ->(provider, extern_uid) { where(extern_uid: extern_uid, provider: provider) }
>>>>>>> ce/master
def ldap? def ldap?
provider.starts_with?('ldap') provider.starts_with?('ldap')
......
...@@ -17,7 +17,7 @@ class Note < ActiveRecord::Base ...@@ -17,7 +17,7 @@ class Note < ActiveRecord::Base
ignore_column :original_discussion_id ignore_column :original_discussion_id
cache_markdown_field :note, pipeline: :note cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true
# Attribute containing rendered and redacted Markdown as generated by # Attribute containing rendered and redacted Markdown as generated by
# Banzai::ObjectRenderer. # Banzai::ObjectRenderer.
......
...@@ -22,7 +22,7 @@ class ChatNotificationService < Service ...@@ -22,7 +22,7 @@ class ChatNotificationService < Service
end end
def can_test? def can_test?
valid? super && valid?
end end
def self.supported_events def self.supported_events
......
class StatusEntity < Grape::Entity class StatusEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :icon, :favicon, :text, :label, :group expose :icon, :text, :label, :group
expose :has_details?, as: :has_details expose :has_details?, as: :has_details
expose :details_path expose :details_path
expose :favicon do |status|
ActionController::Base.helpers.image_path(File.join('ci_favicons', "#{status.favicon}.ico"))
end
end end
...@@ -37,7 +37,10 @@ module Commits ...@@ -37,7 +37,10 @@ module Commits
def validate! def validate!
validate_permissions! validate_permissions!
<<<<<<< HEAD
validate_repository_size! validate_repository_size!
=======
>>>>>>> ce/master
validate_on_branch! validate_on_branch!
validate_branch_existance! validate_branch_existance!
...@@ -52,12 +55,15 @@ module Commits ...@@ -52,12 +55,15 @@ module Commits
end end
end end
<<<<<<< HEAD
def validate_repository_size! def validate_repository_size!
if project.above_size_limit? if project.above_size_limit?
raise_error(Gitlab::RepositorySizeError.new(project).commit_error) raise_error(Gitlab::RepositorySizeError.new(project).commit_error)
end end
end end
=======
>>>>>>> ce/master
def validate_on_branch! def validate_on_branch!
if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch) if !@start_project.empty_repo? && !@start_project.repository.branch_exists?(@start_branch)
raise_error('You can only create or edit files when you are on a branch') raise_error('You can only create or edit files when you are on a branch')
......
...@@ -8,9 +8,20 @@ class DeleteMergedBranchesService < BaseService ...@@ -8,9 +8,20 @@ class DeleteMergedBranchesService < BaseService
branches = project.repository.branch_names branches = project.repository.branch_names
branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) } branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) }
# Prevent deletion of branches relevant to open merge requests
branches -= merge_request_branch_names
branches.each do |branch| branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch) DeleteBranchService.new(project, current_user).execute(branch)
end end
end end
private
def merge_request_branch_names
# reorder(nil) is necessary for SELECT DISTINCT because default scope adds an ORDER BY
source_names = project.origin_merge_requests.opened.reorder(nil).uniq.pluck(:source_branch)
target_names = project.merge_requests.opened.reorder(nil).uniq.pluck(:target_branch)
(source_names + target_names).uniq
end
end end
...@@ -9,6 +9,7 @@ module Search ...@@ -9,6 +9,7 @@ module Search
end end
def execute def execute
<<<<<<< HEAD
if current_application_settings.elasticsearch_search? if current_application_settings.elasticsearch_search?
Gitlab::Elastic::SearchResults.new(current_user, params[:search], elastic_projects, elastic_global) Gitlab::Elastic::SearchResults.new(current_user, params[:search], elastic_projects, elastic_global)
else else
...@@ -33,6 +34,13 @@ module Search ...@@ -33,6 +34,13 @@ module Search
def elastic_global def elastic_global
true true
=======
Gitlab::SearchResults.new(current_user, projects, params[:search])
>>>>>>> ce/master
end
def projects
@projects ||= ProjectsFinder.new(current_user: current_user).execute
end end
def scope def scope
......
...@@ -14,6 +14,7 @@ module Search ...@@ -14,6 +14,7 @@ module Search
@projects = super.inside_path(group.full_path) @projects = super.inside_path(group.full_path)
end end
<<<<<<< HEAD
def elastic_projects def elastic_projects
@elastic_projects ||= projects.pluck(:id) @elastic_projects ||= projects.pluck(:id)
...@@ -22,5 +23,7 @@ module Search ...@@ -22,5 +23,7 @@ module Search
def elastic_global def elastic_global
false false
end end
=======
>>>>>>> ce/master
end end
end end
...@@ -14,7 +14,11 @@ module Users ...@@ -14,7 +14,11 @@ module Users
private private
def record_activity def record_activity
<<<<<<< HEAD
Gitlab::UserActivities.record(@author.id) unless Gitlab::Geo.secondary? Gitlab::UserActivities.record(@author.id) unless Gitlab::Geo.secondary?
=======
Gitlab::UserActivities.record(@author.id)
>>>>>>> ce/master
Rails.logger.debug("Recorded activity: #{@activity} for User ID: #{@author.id} (username: #{@author.username}") Rails.logger.debug("Recorded activity: #{@activity} for User ID: #{@author.id} (username: #{@author.username}")
end end
......
...@@ -515,11 +515,20 @@ ...@@ -515,11 +515,20 @@
= f.check_box :usage_ping_enabled = f.check_box :usage_ping_enabled
Usage ping enabled Usage ping enabled
= link_to icon('question-circle'), help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-data") = link_to icon('question-circle'), help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-data")
<<<<<<< HEAD
.container .container
.help-block .help-block
Every week GitLab will report license usage back to GitLab, Inc. Every week GitLab will report license usage back to GitLab, Inc.
Disable this option if you do not want this to occur. This is the JSON payload that will be sent: Disable this option if you do not want this to occur. This is the JSON payload that will be sent:
%pre.usage-data.js-syntax-highlight.code.highlight{ "data-endpoint" => usage_data_admin_application_settings_path(format: :html) } %pre.usage-data.js-syntax-highlight.code.highlight{ "data-endpoint" => usage_data_admin_application_settings_path(format: :html) }
=======
.help-block
Every week GitLab will report license usage back to GitLab, Inc.
Disable this option if you do not want this to occur. To see the
JSON payload that will be sent, visit the
= succeed '.' do
= link_to "Cohorts page", admin_cohorts_path(anchor: 'usage-ping')
>>>>>>> ce/master
%fieldset %fieldset
%legend Email %legend Email
......
<<<<<<< HEAD
%h2 Usage ping %h2 Usage ping
=======
%h2#usage-ping Usage ping
>>>>>>> ce/master
.bs-callout.clearfix .bs-callout.clearfix
%p %p
......
...@@ -79,6 +79,11 @@ ...@@ -79,6 +79,11 @@
= icon('caret-down') = icon('caret-down')
.dropdown-menu-nav.dropdown-menu-align-right .dropdown-menu-nav.dropdown-menu-align-right
%ul %ul
%li.current-user
.user-name.bold
= current_user.name
@#{current_user.username}
%li.divider
%li %li
= link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username } = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li %li
......
...@@ -11,13 +11,17 @@ ...@@ -11,13 +11,17 @@
Project Project
- if project_nav_tab? :files - if project_nav_tab? :files
<<<<<<< HEAD
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases graphs network path_locks)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases graphs network path_locks)) do
=======
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare projects/repositories tags branches releases graphs network)) do
>>>>>>> ce/master
= link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do = link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do
%span %span
Repository Repository
- if project_nav_tab? :container_registry - if project_nav_tab? :container_registry
= nav_link(controller: %w(container_registry)) do = nav_link(controller: %w[projects/registry/repositories]) do
= link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
%span %span
Registry Registry
......
...@@ -15,16 +15,14 @@ ...@@ -15,16 +15,14 @@
.dropdown.inline> .dropdown.inline>
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light %span.light
= projects_sort_options_hash[@sort] = branches_sort_options_hash[@sort]
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li %li.dropdown-header
= link_to filter_branches_path(sort: sort_value_name) do Sort by
= sort_title_name - branches_sort_options_hash.each do |value, title|
= link_to filter_branches_path(sort: sort_value_recently_updated) do %li
= sort_title_recently_updated = link_to title, filter_branches_path(sort: value), class: ("is-active" if @sort == value)
= link_to filter_branches_path(sort: sort_value_oldest_updated) do
= sort_title_oldest_updated
- if can? current_user, :push_code, @project - if can? current_user, :push_code, @project
= link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do = link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do
......
...@@ -13,9 +13,6 @@ ...@@ -13,9 +13,6 @@
Environment: Environment:
= link_to @environment.name, environment_path(@environment) = link_to @environment.name, environment_path(@environment)
.col-sm-6
.nav-controls
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.prometheus-state .prometheus-state
.js-getting-started.hidden .js-getting-started.hidden
.row .row
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
-# This tab is always loaded via AJAX -# This tab is always loaded via AJAX
- if @pipelines.any? - if @pipelines.any?
#pipelines.pipelines.tab-pane #pipelines.pipelines.tab-pane
= render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)) = render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)), disable_initialization: true
.mr-loading-status .mr-loading-status
= spinner = spinner
......
- endpoint_path = local_assigns[:endpoint] || pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json) - endpoint_path = local_assigns[:endpoint] || pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json)
- disable_initialization = local_assigns.fetch(:disable_initialization, false)
= render 'projects/commit/pipelines_list', endpoint: endpoint_path = render 'projects/commit/pipelines_list', endpoint: endpoint_path, disable_initialization: disable_initialization
...@@ -27,8 +27,11 @@ ...@@ -27,8 +27,11 @@
= render 'projects/merge_requests/widget/open/conflicts' = render 'projects/merge_requests/widget/open/conflicts'
- elsif @merge_request.work_in_progress? - elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip' = render 'projects/merge_requests/widget/open/wip'
<<<<<<< HEAD
- elsif @merge_request.should_be_rebased? - elsif @merge_request.should_be_rebased?
= render 'projects/merge_requests/widget/open/rebase' = render 'projects/merge_requests/widget/open/rebase'
=======
>>>>>>> ce/master
- elsif @merge_request.merge_when_pipeline_succeeds? && @merge_request.merge_error.present? - elsif @merge_request.merge_when_pipeline_succeeds? && @merge_request.merge_error.present?
= render 'projects/merge_requests/widget/open/error' = render 'projects/merge_requests/widget/open/error'
- elsif @merge_request.merge_when_pipeline_succeeds? - elsif @merge_request.merge_when_pipeline_succeeds?
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%p %p
Add a general comment to this #{noteable_name}. Add a general comment to this #{noteable_name}.
%li.divider %li.divider.droplab-item-ignore
%li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => 'Start discussion', 'close-text' => "Start discussion & close #{noteable_name}", 'reopen-text' => "Start discussion & reopen #{noteable_name}" } } %li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => 'Start discussion', 'close-text' => "Start discussion & close #{noteable_name}", 'reopen-text' => "Start discussion & reopen #{noteable_name}" } }
%a{ href: '#' } %a{ href: '#' }
......
...@@ -3,12 +3,11 @@ ...@@ -3,12 +3,11 @@
%button.btn.btn-default.close.js-close-callout{ type: 'button', %button.btn.btn-default.close.js-close-callout{ type: 'button',
'aria-label' => 'Dismiss customize experience box' } 'aria-label' => 'Dismiss customize experience box' }
= icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true') = icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true')
.row .svg-container
.col-sm-3.col-xs-12.svg-container = custom_icon('icon_customization')
= custom_icon('icon_customization') .user-callout-copy
.col-sm-8.col-xs-12.inner-content %h4
%h4 Customize your experience
Customize your experience %p
%p Change syntax themes, default project pages, and more in preferences.
Change syntax themes, default project pages, and more in preferences. = link_to 'Check it out', profile_preferences_path, class: 'btn btn-primary js-close-callout'
= link_to 'Check it out', profile_preferences_path, class: 'btn btn-default js-close-callout'
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
= visibility_level_icon(group.visibility_level, fw: false) = visibility_level_icon(group.visibility_level, fw: false)
.avatar-container.s40 .avatar-container.s40
= image_tag group_icon(group), class: "avatar s40 hidden-xs" = link_to group do
= image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title .title
= link_to group_name, group, class: 'group-name' = link_to group_name, group, class: 'group-name'
......
...@@ -12,10 +12,11 @@ ...@@ -12,10 +12,11 @@
= cache(cache_key) do = cache(cache_key) do
- if avatar - if avatar
.avatar-container.s40 .avatar-container.s40
- if use_creator_avatar = link_to project_path(project), class: dom_class(project) do
= image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:'' - if use_creator_avatar
- else = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
= project_icon(project, alt: '', class: 'avatar project-avatar s40') - else
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
.project-details .project-details
%h3.prepend-top-0.append-bottom-0 %h3.prepend-top-0.append-bottom-0
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
......
...@@ -3,8 +3,11 @@ class ScheduleUpdateUserActivityWorker ...@@ -3,8 +3,11 @@ class ScheduleUpdateUserActivityWorker
include CronjobQueue include CronjobQueue
def perform(batch_size = 500) def perform(batch_size = 500)
<<<<<<< HEAD
return if Gitlab::Geo.secondary? return if Gitlab::Geo.secondary?
=======
>>>>>>> ce/master
Gitlab::UserActivities.new.each_slice(batch_size) do |batch| Gitlab::UserActivities.new.each_slice(batch_size) do |batch|
UpdateUserActivityWorker.perform_async(Hash[batch]) UpdateUserActivityWorker.perform_async(Hash[batch])
end end
......
...@@ -2,6 +2,8 @@ class SystemHookWorker ...@@ -2,6 +2,8 @@ class SystemHookWorker
include Sidekiq::Worker include Sidekiq::Worker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
sidekiq_options retry: 4
def perform(hook_id, data, hook_name) def perform(hook_id, data, hook_name)
SystemHook.find(hook_id).execute(data, hook_name) SystemHook.find(hook_id).execute(data, hook_name)
end end
......
...@@ -3,8 +3,11 @@ class UpdateUserActivityWorker ...@@ -3,8 +3,11 @@ class UpdateUserActivityWorker
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
def perform(pairs) def perform(pairs)
<<<<<<< HEAD
return if Gitlab::Geo.secondary? return if Gitlab::Geo.secondary?
=======
>>>>>>> ce/master
pairs = cast_data(pairs) pairs = cast_data(pairs)
ids = pairs.keys ids = pairs.keys
conditions = 'WHEN id = ? THEN ? ' * ids.length conditions = 'WHEN id = ? THEN ? ' * ids.length
......
---
title: 29595 Update callout design
merge_request:
author:
---
title: Remove pipeline controls for last deployment from Environment monitoring page
merge_request: 10769
author:
---
title: Added quick-update (fade-in) animation to newly rendered notes
merge_request: 10623
author:
---
title: Added profile name to user dropdown
merge_request:
author:
---
title: Disable test settings on chat notification services when repository is empty
merge_request: 10759
author:
---
title: Display custom hook error messages when automatic merge is enabled
merge_request:
author:
---
title: Removed target blank from the metrics action inside the environments list
merge_request: 10726
author:
---
title: Removed orphaned notification settings without a namespace
merge_request:
author:
---
title: Fixed group milestone date dropdowns not opening
merge_request:
author:
---
title: Add retry to system hook worker
merge_request: 10801
author:
---
title: Fix PlantUML integration in GFM
merge_request: 10651
author:
---
title: Implement search by extern_uid in Users API
merge_request: 10509
author: Robin Bobbitt
---
title: Set the issuable sidebar to remain closed for mobile devices
merge_request:
author:
---
title: Add unique index for notes_id to system note metadata table
merge_request:
author:
---
title: Don't delete a branch involved in an open merge request in "Delete all merged
branches" service
merge_request:
author:
---
title: Add usage ping to CE
merge_request:
author:
...@@ -246,8 +246,8 @@ Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings ...@@ -246,8 +246,8 @@ Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings
Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab' Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab'
Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}" Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}"
Settings.gitlab['email_subject_suffix'] ||= ENV['GITLAB_EMAIL_SUBJECT_SUFFIX'] || "" Settings.gitlab['email_subject_suffix'] ||= ENV['GITLAB_EMAIL_SUBJECT_SUFFIX'] || ""
Settings.gitlab['base_url'] ||= Settings.send(:build_base_gitlab_url) Settings.gitlab['base_url'] ||= Settings.__send__(:build_base_gitlab_url)
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['url'] ||= Settings.__send__(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git' Settings.gitlab['user'] ||= 'git'
Settings.gitlab['user_home'] ||= begin Settings.gitlab['user_home'] ||= begin
Etc.getpwnam(Settings.gitlab['user']).dir Etc.getpwnam(Settings.gitlab['user']).dir
...@@ -257,7 +257,7 @@ end ...@@ -257,7 +257,7 @@ end
Settings.gitlab['time_zone'] ||= nil Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil? Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil? Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['default_projects_features'] ||= {}
...@@ -270,7 +270,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settin ...@@ -270,7 +270,7 @@ Settings.gitlab.default_projects_features['wiki'] = true if Settin
Settings.gitlab.default_projects_features['snippets'] = true if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['snippets'] = true if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil? Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil?
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil? Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab.default_projects_features['visibility_level'] = Settings.__send__(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['domain_whitelist'] ||= [] Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project gitea] Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project gitea]
Settings.gitlab['trusted_proxies'] ||= [] Settings.gitlab['trusted_proxies'] ||= []
...@@ -291,7 +291,7 @@ Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['share ...@@ -291,7 +291,7 @@ Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['share
Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil?
Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil?
Settings.gitlab_ci['builds_path'] = Settings.absolute(Settings.gitlab_ci['builds_path'] || "builds/") Settings.gitlab_ci['builds_path'] = Settings.absolute(Settings.gitlab_ci['builds_path'] || "builds/")
Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) Settings.gitlab_ci['url'] ||= Settings.__send__(:build_gitlab_ci_url)
# #
# Reply by email # Reply by email
...@@ -330,7 +330,7 @@ Settings.pages['https'] = false if Settings.pages['https'].nil? ...@@ -330,7 +330,7 @@ Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com" Settings.pages['host'] ||= "example.com"
Settings.pages['port'] ||= Settings.pages.https ? 443 : 80 Settings.pages['port'] ||= Settings.pages.https ? 443 : 80
Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http" Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http"
Settings.pages['url'] ||= Settings.send(:build_pages_url) Settings.pages['url'] ||= Settings.__send__(:build_pages_url)
Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present? Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present?
Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present? Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present?
...@@ -430,6 +430,14 @@ Settings.cron_jobs['remove_unreferenced_lfs_objects_worker']['job_class'] = 'Rem ...@@ -430,6 +430,14 @@ Settings.cron_jobs['remove_unreferenced_lfs_objects_worker']['job_class'] = 'Rem
Settings.cron_jobs['stuck_import_jobs_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_import_jobs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['stuck_import_jobs_worker']['cron'] ||= '15 * * * *' Settings.cron_jobs['stuck_import_jobs_worker']['cron'] ||= '15 * * * *'
Settings.cron_jobs['stuck_import_jobs_worker']['job_class'] = 'StuckImportJobsWorker' Settings.cron_jobs['stuck_import_jobs_worker']['job_class'] = 'StuckImportJobsWorker'
Settings.cron_jobs['gitlab_usage_ping_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['gitlab_usage_ping_worker']['cron'] ||= Settings.__send__(:cron_random_weekly_time)
Settings.cron_jobs['gitlab_usage_ping_worker']['job_class'] = 'GitlabUsagePingWorker'
# Every day at 00:30
Settings.cron_jobs['schedule_update_user_activity_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['schedule_update_user_activity_worker']['cron'] ||= '30 0 * * *'
Settings.cron_jobs['schedule_update_user_activity_worker']['job_class'] = 'ScheduleUpdateUserActivityWorker'
Settings.cron_jobs['clear_shared_runners_minutes_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['clear_shared_runners_minutes_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['clear_shared_runners_minutes_worker']['cron'] ||= '0 0 1 * *' Settings.cron_jobs['clear_shared_runners_minutes_worker']['cron'] ||= '0 0 1 * *'
...@@ -453,7 +461,7 @@ Settings.gitlab_shell['ssh_host'] ||= Settings.gitlab.ssh_host ...@@ -453,7 +461,7 @@ Settings.gitlab_shell['ssh_host'] ||= Settings.gitlab.ssh_host
Settings.gitlab_shell['ssh_port'] ||= 22 Settings.gitlab_shell['ssh_port'] ||= 22
Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user Settings.gitlab_shell['ssh_user'] ||= Settings.gitlab.user
Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user Settings.gitlab_shell['owner_group'] ||= Settings.gitlab.user
Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.send(:build_gitlab_shell_ssh_path_prefix) Settings.gitlab_shell['ssh_path_prefix'] ||= Settings.__send__(:build_gitlab_shell_ssh_path_prefix)
# #
# Repositories # Repositories
......
...@@ -100,11 +100,15 @@ namespace :admin do ...@@ -100,11 +100,15 @@ namespace :admin do
resource :application_settings, only: [:show, :update] do resource :application_settings, only: [:show, :update] do
resources :services, only: [:index, :edit, :update] resources :services, only: [:index, :edit, :update]
<<<<<<< HEAD
## EE-specific ## EE-specific
get :usage_data get :usage_data
## EE-specific ## EE-specific
=======
get :usage_data
>>>>>>> ce/master
put :reset_runners_token put :reset_runners_token
put :reset_health_check_token put :reset_health_check_token
put :clear_repository_check_states put :clear_repository_check_states
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
- [pages, 1] - [pages, 1]
- [system_hook_push, 1] - [system_hook_push, 1]
- [update_user_activity, 1] - [update_user_activity, 1]
<<<<<<< HEAD
# EE specific queues # EE specific queues
- [geo, 1] - [geo, 1]
- [project_mirror, 1] - [project_mirror, 1]
...@@ -64,3 +65,5 @@ ...@@ -64,3 +65,5 @@
- [elastic_indexer, 1] - [elastic_indexer, 1]
- [elastic_commit_indexer, 1] - [elastic_commit_indexer, 1]
- [export_csv, 1] - [export_csv, 1]
=======
>>>>>>> ce/master
...@@ -21,7 +21,10 @@ var config = { ...@@ -21,7 +21,10 @@ var config = {
entry: { entry: {
blob: './blob_edit/blob_bundle.js', blob: './blob_edit/blob_bundle.js',
boards: './boards/boards_bundle.js', boards: './boards/boards_bundle.js',
<<<<<<< HEAD
burndown_chart: './burndown_chart/index.js', burndown_chart: './burndown_chart/index.js',
=======
>>>>>>> ce/master
common: './commons/index.js', common: './commons/index.js',
common_vue: ['vue', './vue_shared/common_vue.js'], common_vue: ['vue', './vue_shared/common_vue.js'],
common_d3: ['d3'], common_d3: ['d3'],
...@@ -129,8 +132,11 @@ var config = { ...@@ -129,8 +132,11 @@ var config = {
'notebook_viewer', 'notebook_viewer',
'pdf_viewer', 'pdf_viewer',
'pipelines', 'pipelines',
<<<<<<< HEAD
'mr_widget_ee', 'mr_widget_ee',
'issue_show' 'issue_show'
=======
>>>>>>> ce/master
], ],
minChunks: function(module, count) { minChunks: function(module, count) {
return module.resource && (/vue_shared/).test(module.resource); return module.resource && (/vue_shared/).test(module.resource);
......
class AddUsagePingToApplicationSettings < ActiveRecord::Migration class AddUsagePingToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
<<<<<<< HEAD
=======
DOWNTIME = false
>>>>>>> ce/master
def change def change
add_column :application_settings, :usage_ping_enabled, :boolean, default: true, null: false add_column :application_settings, :usage_ping_enabled, :boolean, default: true, null: false
end end
......
class CreateUserActivities < ActiveRecord::Migration class CreateUserActivities < ActiveRecord::Migration
<<<<<<< HEAD
# Set this constant to true if this migration requires downtime. # Set this constant to true if this migration requires downtime.
DOWNTIME = true DOWNTIME = true
...@@ -23,5 +24,11 @@ class CreateUserActivities < ActiveRecord::Migration ...@@ -23,5 +24,11 @@ class CreateUserActivities < ActiveRecord::Migration
t.belongs_to :user, index: { unique: true }, foreign_key: { on_delete: :cascade } t.belongs_to :user, index: { unique: true }, foreign_key: { on_delete: :cascade }
t.datetime :last_activity_at, null: false t.datetime :last_activity_at, null: false
end end
=======
DOWNTIME = false
# This migration is a no-op. It just exists to match EE.
def change
>>>>>>> ce/master
end end
end end
class DeleteOrphanNotificationSettings < ActiveRecord::Migration
DOWNTIME = false
def up
execute("DELETE FROM notification_settings WHERE EXISTS (SELECT true FROM (#{orphan_notification_settings}) AS ns WHERE ns.id = notification_settings.id)")
end
def down
# This is a no-op method to make the migration reversible.
# If someone is trying to rollback for other reasons, we should not throw an Exception.
# raise ActiveRecord::IrreversibleMigration
end
def orphan_notification_settings
<<-SQL
SELECT notification_settings.id
FROM notification_settings
LEFT OUTER JOIN namespaces
ON namespaces.id = notification_settings.source_id
WHERE notification_settings.source_type = 'Namespace'
AND namespaces.id IS NULL
SQL
end
end
class AddIndexToSystemNoteMetadata < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
# MySQL automatically creates an index on a foreign-key constraint; PostgreSQL does not
add_concurrent_index :system_note_metadata, :note_id, unique: true if Gitlab::Database.postgresql?
end
def down
remove_concurrent_index :system_note_metadata, :note_id, unique: true if Gitlab::Database.postgresql?
end
end
<<<<<<< HEAD
# See http://doc.gitlab.com/ce/development/migration_style_guide.html # See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab. # for more information on how to write migrations for GitLab.
=======
>>>>>>> ce/master
class DropUserActivitiesTable < ActiveRecord::Migration class DropUserActivitiesTable < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
DOWNTIME = false DOWNTIME = false
<<<<<<< HEAD
# When using the methods "add_concurrent_index" or "add_column_with_default" # When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an # you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this # existing transaction. When using "add_concurrent_index" make sure that this
...@@ -30,5 +34,9 @@ class DropUserActivitiesTable < ActiveRecord::Migration ...@@ -30,5 +34,9 @@ class DropUserActivitiesTable < ActiveRecord::Migration
add_index "user_activities", ["user_id"], name: "index_user_activities_on_user_id", unique: true, using: :btree add_index "user_activities", ["user_id"], name: "index_user_activities_on_user_id", unique: true, using: :btree
end end
=======
# This migration is a no-op. It just exists to match EE.
def change
>>>>>>> ce/master
end end
end end
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
<<<<<<< HEAD
ActiveRecord::Schema.define(version: 20170419065104) do ActiveRecord::Schema.define(version: 20170419065104) do
=======
ActiveRecord::Schema.define(version: 20170419001229) do
>>>>>>> ce/master
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -131,6 +135,7 @@ ActiveRecord::Schema.define(version: 20170419065104) do ...@@ -131,6 +135,7 @@ ActiveRecord::Schema.define(version: 20170419065104) do
t.integer "geo_status_timeout", default: 10 t.integer "geo_status_timeout", default: 10
t.string "uuid" t.string "uuid"
t.decimal "polling_interval_multiplier", default: 1.0, null: false t.decimal "polling_interval_multiplier", default: 1.0, null: false
<<<<<<< HEAD
t.boolean "elasticsearch_experimental_indexer" t.boolean "elasticsearch_experimental_indexer"
end end
...@@ -139,6 +144,10 @@ ActiveRecord::Schema.define(version: 20170419065104) do ...@@ -139,6 +144,10 @@ ActiveRecord::Schema.define(version: 20170419065104) do
t.integer "user_id", null: false t.integer "user_id", null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
=======
t.boolean "usage_ping_enabled", default: true, null: false
t.string "uuid"
>>>>>>> ce/master
end end
create_table "approver_groups", force: :cascade do |t| create_table "approver_groups", force: :cascade do |t|
...@@ -1343,6 +1352,8 @@ ActiveRecord::Schema.define(version: 20170419065104) do ...@@ -1343,6 +1352,8 @@ ActiveRecord::Schema.define(version: 20170419065104) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
add_index "system_note_metadata", ["note_id"], name: "index_system_note_metadata_on_note_id", unique: true, using: :btree
create_table "taggings", force: :cascade do |t| create_table "taggings", force: :cascade do |t|
t.integer "tag_id" t.integer "tag_id"
t.integer "taggable_id" t.integer "taggable_id"
......
...@@ -80,6 +80,7 @@ All technical content published by GitLab lives in the documentation, including: ...@@ -80,6 +80,7 @@ All technical content published by GitLab lives in the documentation, including:
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs. - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation. - [Update](update/README.md) Update guides to upgrade your installation.
- [User cohorts](user/admin_area/user_cohorts.md) View user activity over time.
- [Web terminals](administration/integration/terminal.md) Provide terminal access to environments from within GitLab. - [Web terminals](administration/integration/terminal.md) Provide terminal access to environments from within GitLab.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
- [Downgrade back to CE](downgrade_ee_to_ce/README.md) Follow this guide if you need to downgrade from EE to CE. - [Downgrade back to CE](downgrade_ee_to_ce/README.md) Follow this guide if you need to downgrade from EE to CE.
......
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