Commit ac92554d authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into backport-ee-changes-for-build-minutes

parents b7f4553e 8c9a06c3
......@@ -35,7 +35,6 @@ stages:
.dedicated-runner: &dedicated-runner
tags:
- gitlab-org
- 2gb
.knapsack-state: &knapsack-state
services: []
......
......@@ -2,6 +2,136 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 8.16.0 (2017-02-22)
- Add LDAP Rake task to rename a provider. !2181
- Validate label's title length. !5767 (Tomáš Kukrál)
- Allow to add deploy keys with write-access. !5807 (Ali Ibrahim)
- Allow to use + symbol in filenames. !6644 (blackst0ne)
- Search bar redesign first iteration. !7345
- Fix date inconsistency on due date picker. !7422 (Giuliano Varriale)
- Add email confirmation field to registration form. !7432
- Updated project visibility settings UX. !7645
- Go to a project order. !7737 (Jacopo Beschi @jacopo-beschi)
- Support slash comand `/merge` for merging merge requests. !7746 (Jarka Kadlecova)
- Add more storage statistics. !7754 (Markus Koller)
- Add support for PlantUML diagrams in AsciiDoc documents. !7810 (Horacio Sanson)
- Remove extra orphaned rows when removing stray namespaces. !7841
- Added lighter count badge background-color for on white backgrounds. !7873
- Fixes issue boards list colored top border visual glitch. !7898 (Pier Paolo Ramon)
- change 'gray' color theme name to 'black' to match the actual color. !7908 (BM5k)
- Remove trailing whitespace when generating changelog entry. !7948
- Remove checking branches state in issue new branch button. !8023
- Log LDAP blocking/unblocking events to application log. !8042 (Markus Koller)
- ensure permalinks scroll to correct position on multiple clicks. !8046
- Allow to use ENV variables in redis config. !8073 (Semyon Pupkov)
- fix button layout issue on branches page. !8074
- Reduce DB-load for build-queues by storing last_update in Redis. !8084
- Record and show last used date of SSH Keys. !8113 (Vincent Wong)
- Resolves overflow in compare branch and tags dropdown. !8118
- Replace wording for slash command confirmation message. !8123
- remove build_user. !8162 (Arsenev Vladislav)
- Prevent empty pagination when list is not empty. !8172
- Make successful pipeline emails off for watchers. !8176
- Improve copy in Issue Tracker empty state. !8202
- Adds CSS class to status icon on MR widget to prevent non-colored icon. !8219
- Improve visibility of "Resolve conflicts" and "Merge locally" actions. !8229
- Add Gitaly to the architecture documentation. !8264 (Pablo Carranza <pablo@gitlab.com>)
- Sort numbers in build names more intelligently. !8277
- Show nested groups tab on group page. !8308
- Rename users with namespace ending with .git. !8309
- Rename filename to file path in tooltip of file header in merge request diff. !8314
- About GitLab link in sidebar that links to help page. !8316
- Merged the 'Groups' and 'Projects' tabs when viewing user profiles. !8323 (James Gregory)
- re-enable change username button after failure. !8332
- Darkened hr border color in descriptions because of update of bootstrap. !8333
- display merge request discussion tab for empty branches. !8347
- Fix double spaced CI log. !8349 (Jared Deckard <jared.deckard@gmail.com>)
- Refactored note edit form to improve frontend performance on MR and Issues pages, especially pages with has a lot of discussions in it. !8356
- Make CTRL+Enter submits a new merge request. !8360 (Saad Shahd)
- Fixes too short input for placeholder message in commit listing page. !8367
- Fix typo: seach to search. !8370
- Adds label to Environments "Date Created". !8376 (Saad Shahd)
- Convert project setting text into protected branch path link. !8377 (Ken Ding)
- Precompile all JavaScript fixtures. !8384
- Use original casing for build action text. !8387
- Scroll to bottom on build completion if autoscroll was active. !8391
- Properly handle failed reCAPTCHA on user registration. !8403
- Changed alerts to be responsive, centered text on smaller viewports. !8424 (Connor Smallman)
- Pass Gitaly resource path to gitlab-workhorse if Gitaly is enabled. !8440
- Fixes and Improves CSS and HTML problems in mini pipeline graph and builds dropdown. !8443
- Don't instrument 405 Grape calls. !8445
- Change CI template linter textarea with Ace Editor. !8452 (Didem Acet)
- Removes unneeded `window` declaration in environments related code. !8456
- API: fix query response for `/projects/:id/issues?milestone="No%20Milestone"`. !8457 (Panagiotis Atmatzidis, David Eisner)
- Fix broken url on group avatar. !8464 (hogewest)
- Fixes buttons not being accessible via the keyboard when creating new group. !8469
- Restore backup correctly when "BACKUP" environment variable is passed. !8477
- Add new endpoints for Time Tracking. !8483
- Fix Compare page throws 500 error when any branch/reference is not selected. !8492 (Martin Cabrera)
- Treat environments matching `production/*` as Production. !8500
- Hide build artifacts keep button if operation is not allowed. !8501
- Update the gitlab-markup gem to the version 1.5.1. !8509
- Remove Lock Icon on Protected Tag. !8513 (Sergey Nikitin)
- Use cached values to compute total issues count in milestone index pages. !8518
- Speed up dashboard milestone index by scoping IssuesFinder to user authorized projects. !8524
- Copy <some text> to clipboard. !8535
- Check for env[Grape::Env::GRAPE_ROUTING_ARGS] instead of endpoint.route. !8544
- Fixes builds dropdown making request when clicked to be closed. !8545
- Fixes pipeline status cell is too wide by adding missing classes in table head cells. !8549
- Mutate the attribute instead of issuing a write operation to the DB in `ProjectFeaturesCompatibility` concern. !8552
- Fix links to commits pages on pipelines list page. !8558
- Ensure updating project settings shows a flash message on success. !8579 (Sandish Chen)
- Fixes big pipeline and small pipeline width problems and tooltips text being outside the tooltip. !8593
- Autoresize markdown preview. !8607 (Didem Acet)
- Link external build badge to its target URL. !8611
- Adjust ProjectStatistic#repository_size with values saved as MB. !8616
- Correct User-agent placement in robots.txt. !8623 (Eric Sabelhaus)
- Record used SSH keys only once per day. !8655
- Do not generate pipeline branch/tag path if not present. !8658
- Fix Merge When Pipeline Succeeds immediate merge bug. !8685
- Fix blame 500 error on invalid path. !25761 (Jeff Stubler)
- Added animations to issue boards interactions.
- Check if user can read project before being assigned to issue.
- Show 'too many changes' message for created merge requests when they are too large.
- Fix redirect after update file when user has forked project.
- Parse JIRA issue references even if Issue Tracker is disabled.
- Made download artifacts button accessible via keyboard by changing it from an anchor tag to an actual button. (Ryan Harris)
- Make play button on Pipelines page accessible via keyboard. (Ryan Harris)
- Decreases font-size on login page.
- Fixed merge request tabs dont move when opening collapsed sidebar.
- Display project avatars on Admin Area and Projects pages for mobile views. (Ryan Harris)
- Fix participants margins to fit on one line.
- 26352 Change Profile settings to User / Settings.
- Fix Commits API to accept a Project path upon POST.
- Expire related caches after changing HEAD. (Minqi Pan)
- Add various hover animations throughout the application.
- Re-order update steps in the 8.14 -> 8.15 upgrade guide.
- Move award emoji's out of the discussion tab for merge requests.
- Synchronize all project authorization refreshing work to prevent race conditions.
- Remove the project_authorizations.id column.
- Combined the settings options project members and groups into a single one called members.
- Change earlier to task_status_short to avoid titlebar line wraps.
- 25701 standardize text colors.
- Handle HTTP errors in environment list.
- Re-add Google Cloud Storage as a backup strategy.
- Change status colors of runners to better defaults.
- Added number_with_delimiter to counter on milestone panels. (Ryan Harris)
- Query external CI statuses in the background.
- Allow group and project paths when transferring projects via the API.
- Don't validate environment urls on .gitlab-ci.yml.
- Fix a Grape deprecation, use `#request_method` instead of `#route_method`.
- Fill missing authorized projects rows.
- Allow API query to find projects with dots in their name. (Bruno Melli)
- Fix import/export wrong user mapping.
- Removed bottom padding from merge manually from CLI because of repositioning award emoji's.
- Fix project queued for deletion re-creation tooltip.
- Fix search group/project filtering to show results.
- Fix 500 error when POSTing to Users API with optional confirm param.
- 26504 Fix styling of MR jump to discussion button.
- Add margin to markdown math blocks.
- Add hover state to MR comment reply button.
## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176
......
......@@ -21,7 +21,7 @@ gem 'rugged', '~> 0.24.0'
# Authentication libraries
gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0'
gem 'omniauth', '~> 1.3.1'
gem 'omniauth', '~> 1.3.2'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-cas3', '~> 1.1.2'
......
......@@ -449,7 +449,7 @@ GEM
octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.4)
omniauth (1.3.1)
omniauth (1.3.2)
hashie (>= 1.2, < 4)
rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1)
......@@ -925,7 +925,7 @@ DEPENDENCIES
oauth2 (~> 1.2.0)
octokit (~> 4.6.2)
oj (~> 2.17.4)
omniauth (~> 1.3.1)
omniauth (~> 1.3.2)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0)
omniauth-azure-oauth2 (~> 0.0.6)
......
......@@ -113,4 +113,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
[These people](https://twitter.com/gitlab/favorites) seem to like it.
[These people](https://twitter.com/gitlab/likes) seem to like it.
8.16.0-pre
8.17.0-pre
......@@ -62,6 +62,7 @@ var DropDown = function(list) {
this.list = list;
this.items = [];
this.getItems();
this.initTemplateString();
this.addEvents();
this.initialState = list.innerHTML;
};
......@@ -72,6 +73,17 @@ Object.assign(DropDown.prototype, {
return this.items;
},
initTemplateString: function() {
var items = this.items || this.getItems();
var templateString = '';
if(items.length > 0) {
templateString = items[items.length - 1].outerHTML;
}
this.templateString = templateString;
return this.templateString;
},
clickEvent: function(e) {
// climb up the tree to find the LI
var selected = utils.closest(e.target, 'LI');
......@@ -111,30 +123,21 @@ Object.assign(DropDown.prototype, {
addData: function(data) {
this.data = (this.data || []).concat(data);
this.render(data);
this.render(this.data);
},
// call render manually on data;
render: function(data){
// debugger
// empty the list first
var sampleItem;
var templateString = this.templateString;
var newChildren = [];
var toAppend;
for(var i = 0; i < this.items.length; i++) {
var item = this.items[i];
sampleItem = item;
if(item.parentNode && item.parentNode.dataset.hasOwnProperty('dynamic')) {
item.parentNode.removeChild(item);
}
}
newChildren = this.data.map(function(dat){
var html = utils.t(sampleItem.outerHTML, dat);
newChildren = (data ||[]).map(function(dat){
var html = utils.t(templateString, dat);
var template = document.createElement('div');
template.innerHTML = html;
// console.log(template.content)
// Help set the image src template
var imageTags = template.querySelectorAll('img[data-src]');
......@@ -156,7 +159,7 @@ Object.assign(DropDown.prototype, {
if(toAppend) {
toAppend.innerHTML = newChildren.join('');
} else {
this.list.innerHTML = newChildren.join('');
this.list.innerHTML = newChildren.join('');
}
},
......@@ -173,10 +176,7 @@ Object.assign(DropDown.prototype, {
},
destroy: function() {
if (!this.hidden) {
this.hide();
}
this.hide();
this.list.removeEventListener('click', this.clickWrapper);
}
});
......@@ -278,7 +278,7 @@ require('./window')(function(w){
self.hooks[i].list.hide();
}
}.bind(this);
w.addEventListener('click', this.windowClickedWrapper);
document.addEventListener('click', this.windowClickedWrapper);
},
removeEvents: function(){
......@@ -307,7 +307,7 @@ require('./window')(function(w){
if(!list){
list = document.querySelector(hook.dataset[utils.toDataCamelCase(DATA_TRIGGER)]);
}
if(hook) {
if(hook.tagName === 'A' || hook.tagName === 'BUTTON') {
this.hooks.push(new HookButton(hook, list, plugins, config));
......@@ -462,6 +462,8 @@ Object.assign(HookInput.prototype, {
var self = this;
this.mousedown = function mousedown(e) {
if(self.hasRemovedEvents) return;
var mouseEvent = new CustomEvent('mousedown.dl', {
detail: {
hook: self,
......@@ -474,6 +476,8 @@ Object.assign(HookInput.prototype, {
}
this.input = function input(e) {
if(self.hasRemovedEvents) return;
var inputEvent = new CustomEvent('input.dl', {
detail: {
hook: self,
......@@ -487,10 +491,14 @@ Object.assign(HookInput.prototype, {
}
this.keyup = function keyup(e) {
if(self.hasRemovedEvents) return;
keyEvent(e, 'keyup.dl');
}
this.keydown = function keydown(e) {
if(self.hasRemovedEvents) return;
keyEvent(e, 'keydown.dl');
}
......@@ -520,7 +528,8 @@ Object.assign(HookInput.prototype, {
this.trigger.addEventListener('keydown', this.keydown);
},
removeEvents: function(){
removeEvents: function() {
this.hasRemovedEvents = true;
this.trigger.removeEventListener('mousedown', this.mousedown);
this.trigger.removeEventListener('input', this.input);
this.trigger.removeEventListener('keyup', this.keyup);
......@@ -578,7 +587,7 @@ require('./window')(function(w){
var listItems = removeHighlight(list);
if(currentIndex>0){
if(!listItems[currentIndex-1]){
currentIndex = currentIndex-1;
currentIndex = currentIndex-1;
}
listItems[currentIndex-1].classList.add('dropdown-active');
}
......@@ -630,7 +639,7 @@ require('./window')(function(w){
return;
}
if(currentKey === 'ArrowUp') {
isUpArrow = true;
isUpArrow = true;
}
if(currentKey === 'ArrowDown') {
isDownArrow = true;
......@@ -668,16 +677,16 @@ var camelize = function(str) {
};
var closest = function(thisTag, stopTag) {
while(thisTag.tagName !== stopTag && thisTag.tagName !== 'HTML'){
while(thisTag && thisTag.tagName !== stopTag && thisTag.tagName !== 'HTML'){
thisTag = thisTag.parentNode;
}
return thisTag;
};
var isDropDownParts = function(target) {
if(target.tagName === 'HTML') { return false; }
if(!target || target.tagName === 'HTML') { return false; }
return (
target.hasAttribute(DATA_TRIGGER) ||
target.hasAttribute(DATA_TRIGGER) ||
target.hasAttribute(DATA_DROPDOWN)
);
};
......
/* global CustomEvent */
/* eslint-disable no-global-assign */
// Custom event support for IE
CustomEvent = function CustomEvent(event, parameters) {
const params = parameters || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
CustomEvent.prototype = window.Event.prototype;
......@@ -9,7 +9,7 @@
this.config = {
droplabFilter: {
template: 'hint',
filterFunction: gl.DropdownUtils.filterHint,
filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
},
};
}
......@@ -20,6 +20,9 @@
if (selected.tagName === 'LI') {
if (selected.hasAttribute('data-value')) {
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();
......
......@@ -15,7 +15,7 @@
loadingTemplate: this.loadingTemplate,
},
droplabFilter: {
filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol),
filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
},
};
}
......
......@@ -37,7 +37,7 @@
}
getSearchInput() {
const query = this.input.value.trim();
const query = gl.DropdownUtils.getSearchInput(this.input);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
return lastToken.value || '';
......
......@@ -20,17 +20,15 @@
return escapedText;
}
static filterWithSymbol(filterSymbol, item, query) {
static filterWithSymbol(filterSymbol, input, item) {
const updatedItem = item;
const query = gl.DropdownUtils.getSearchInput(input);
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(query);
if (lastToken !== searchToken) {
const title = updatedItem.title.toLowerCase();
let value = lastToken.value.toLowerCase();
if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
value = value.slice(1);
}
value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1));
// Eg. filterSymbol = ~ for labels
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
......@@ -44,8 +42,9 @@
return updatedItem;
}
static filterHint(item, query) {
static filterHint(input, item) {
const updatedItem = item;
const query = gl.DropdownUtils.getSearchInput(input);
let { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
lastToken = lastToken.key || lastToken || '';
......@@ -72,6 +71,48 @@
// Return boolean based on whether it was set
return dataValue !== null;
}
static getSearchInput(filteredSearchInput) {
const inputValue = filteredSearchInput.value;
const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
return inputValue.slice(0, right);
}
static getInputSelectionPosition(input) {
const selectionStart = input.selectionStart;
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// This helps with matching the beginning & end of a token:key
inputValue = inputValue.replace(/"(.*?)"/g, str => str.replace(/\s/g, '_'));
// Get the right position for the word selected
// Regex matches first space
let right = inputValue.slice(selectionStart).search(/\s/);
if (right >= 0) {
right += selectionStart;
} else if (right < 0) {
right = inputValue.length;
}
// Get the left position for the word selected
// Regex matches last non-whitespace character
let left = inputValue.slice(0, right).search(/\S+$/);
if (selectionStart === 0) {
left = 0;
} else if (selectionStart === inputValue.length && left < 0) {
left = inputValue.length;
} else if (left < 0) {
left = selectionStart;
}
return {
left,
right,
};
}
}
window.gl = window.gl || {};
......
......@@ -39,6 +39,7 @@
}
this.dismissDropdown();
this.dispatchInputEvent();
}
}
......@@ -78,7 +79,16 @@
dispatchInputEvent() {
// Propogate input change to FilteredSearchDropdownManager
// so that it can determine which dropdowns to open
this.input.dispatchEvent(new Event('input'));
this.input.dispatchEvent(new CustomEvent('input', {
bubbles: true,
cancelable: true,
}));
}
dispatchFormSubmitEvent() {
// dispatchEvent() is necessary as form.submit() does not
// trigger event handlers
this.input.form.dispatchEvent(new Event('submit'));
}
hideDropdown() {
......
......@@ -57,28 +57,33 @@
static addWordToInput(tokenName, tokenValue = '') {
const input = document.querySelector('.filtered-search');
const inputValue = input.value;
const word = `${tokenName}:${tokenValue}`;
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(input.value);
const lastSearchToken = searchToken.split(' ').last();
const lastInputCharacter = input.value[input.value.length - 1];
const lastInputTrimmedCharacter = input.value.trim()[input.value.trim().length - 1];
// Remove the typed tokenName
if (word.indexOf(lastSearchToken) === 0 && searchToken !== '') {
// Remove spaces after the colon
if (lastInputCharacter === ' ' && lastInputTrimmedCharacter === ':') {
input.value = input.value.trim();
}
input.value = input.value.slice(0, -1 * lastSearchToken.length);
} else if (lastInputCharacter !== ' ' || (lastToken && lastToken.value[lastToken.value.length - 1] === ' ')) {
// Remove the existing tokenValue
const lastTokenString = `${lastToken.key}:${lastToken.symbol}${lastToken.value}`;
input.value = input.value.slice(0, -1 * lastTokenString.length);
// Get the string to replace
let newCaretPosition = input.selectionStart;
const { left, right } = gl.DropdownUtils.getInputSelectionPosition(input);
input.value = `${inputValue.substr(0, left)}${word}${inputValue.substr(right)}`;
// If we have added a tokenValue at the end of the input,
// add a space and set selection to the end
if (right >= inputValue.length && tokenValue !== '') {
input.value += ' ';
newCaretPosition = input.value.length;
}
input.value += word;
gl.FilteredSearchDropdownManager.updateInputCaretPosition(newCaretPosition, input);
}
static updateInputCaretPosition(selectionStart, input) {
// Reset the position
// Sometimes can end up at end of input
input.setSelectionRange(selectionStart, selectionStart);
const { right } = gl.DropdownUtils.getInputSelectionPosition(input);
input.setSelectionRange(right, right);
}
updateCurrentDropdownOffset() {
......@@ -90,9 +95,18 @@
this.font = window.getComputedStyle(this.filteredSearchInput).font;
}
const input = this.filteredSearchInput;
const inputText = input.value.slice(0, input.selectionStart);
const filterIconPadding = 27;
const offset = gl.text
.getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding;
let offset = gl.text.getTextWidth(inputText, this.font) + filterIconPadding;
const currentDropdownWidth = this.mapping[key].element.clientWidth === 0 ? 200 :
this.mapping[key].element.clientWidth;
const offsetMaxWidth = this.filteredSearchInput.clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
this.mapping[key].reference.setOffset(offset);
}
......@@ -148,9 +162,9 @@
setDropdown() {
const { lastToken, searchToken } = this.tokenizer
.processTokens(this.filteredSearchInput.value);
.processTokens(gl.DropdownUtils.getSearchInput(this.filteredSearchInput));
if (this.filteredSearchInput.value.split('').last() === ' ') {
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
}
......
......@@ -25,24 +25,32 @@
}
bindEvents() {
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this);
this.checkForEnterWrapper = this.checkForEnter.bind(this);
this.clearSearchWrapper = this.clearSearch.bind(this);
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
this.tokenChange = this.tokenChange.bind(this);
this.filteredSearchInput.form.addEventListener('submit', this.handleFormSubmit);
this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.addEventListener('click', this.tokenChange);
this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
this.clearSearchButton.addEventListener('click', this.clearSearchWrapper);
}
unbindEvents() {
this.filteredSearchInput.form.removeEventListener('submit', this.handleFormSubmit);
this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
this.filteredSearchInput.removeEventListener('click', this.tokenChange);
this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
this.clearSearchButton.removeEventListener('click', this.clearSearchWrapper);
}
......@@ -83,8 +91,14 @@
this.dropdownManager.resetDropdowns();
}
handleFormSubmit(e) {
e.preventDefault();
this.search();
}
loadSearchParamsFromURL() {
const params = gl.utils.getUrlParamsArray();
const usernameParams = this.getUsernameParams();
const inputValues = [];
params.forEach((p) => {
......@@ -115,6 +129,16 @@
}
inputValues.push(`${sanitizedKey}:${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`);
} else if (!match && keyParam === 'assignee_id') {
const id = parseInt(value, 10);
if (usernameParams[id]) {
inputValues.push(`assignee:@${usernameParams[id]}`);
}
} else if (!match && keyParam === 'author_id') {
const id = parseInt(value, 10);
if (usernameParams[id]) {
inputValues.push(`author:@${usernameParams[id]}`);
}
} else if (!match && keyParam === 'search') {
inputValues.push(sanitizedValue);
}
......@@ -164,6 +188,27 @@
Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`);
}
getUsernameParams() {
const usernamesById = {};
try {
const attribute = this.filteredSearchInput.getAttribute('data-username-params');
JSON.parse(attribute).forEach((user) => {
usernamesById[user.id] = user.username;
});
} catch (e) {
// do nothing
}
return usernamesById;
}
tokenChange() {
const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
const currentDropdownRef = dropdown.reference;
this.setDropdownWrapper();
currentDropdownRef.dispatchInputEvent();
}
}
window.gl = window.gl || {};
......
......@@ -2,12 +2,12 @@
(function() {
this.GroupAvatar = (function() {
function GroupAvatar() {
$('.js-choose-group-avatar-button').bind("click", function() {
$('.js-choose-group-avatar-button').on("click", function() {
var form;
form = $(this).closest("form");
return form.find(".js-group-avatar-input").click();
});
$('.js-group-avatar-input').bind("change", function() {
$('.js-group-avatar-input').on("change", function() {
var filename, form;
form = $(this).closest("form");
filename = $(this).val().replace(/^.*[\\\/]/, '');
......
......@@ -126,7 +126,9 @@
MergeRequestWidget.prototype.getMergeStatus = function() {
return $.get(this.opts.merge_check_url, function(data) {
return $('.mr-state-widget').replaceWith(data);
var $html = $(data);
$('.mr-widget-body').replaceWith($html.find('.mr-widget-body'));
$('.mr-widget-footer').replaceWith($html.find('.mr-widget-footer'));
});
};
......
......@@ -8,31 +8,42 @@
* temporarily.
* */
if ($('.accept-mr-form').length) {
$('.accept-mr-form').on('ajax:send', () => {
$('.accept-mr-form :input').disable();
});
$(document)
.off('ajax:send', '.accept-mr-form')
.on('ajax:send', '.accept-mr-form', () => {
$('.accept-mr-form :input').disable();
});
$('.accept_merge_request').on('click', () => {
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
});
$(document)
.off('click', '.accept_merge_request')
.on('click', '.accept_merge_request', () => {
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
});
$('.merge_when_build_succeeds').on('click', () => {
$('#merge_when_build_succeeds').val('1');
});
$(document)
.off('click', '.merge_when_build_succeeds')
.on('click', '.merge_when_build_succeeds', () => {
$('#merge_when_build_succeeds').val('1');
});
$('.js-merge-dropdown a').on('click', (e) => {
e.preventDefault();
$(this).closest('form').submit();
});
} else if ($('.rebase-in-progress').length) {
$(document)
.off('click', '.js-merge-dropdown a')
.on('click', '.js-merge-dropdown a', (e) => {
e.preventDefault();
$(e.target).closest('form').submit();
});
if ($('.rebase-in-progress').length) {
merge_request_widget.rebaseInProgress();
} else if ($('.rebase-mr-form').length) {
$('.rebase-mr-form').on('ajax:send', () => {
$(document)
.off('ajax:send', '.rebase-mr-form')
.on('ajax:send', '.rebase-mr-form', () => {
$('.rebase-mr-form :input').disable();
});
$('.js-rebase-button').on('click', () => {
$(document)
.off('click', '.js-rebase-button')
.on('click', '.js-rebase-button', () => {
$('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress");
});
} else {
......
/* global Vue, Flash, gl */
/* eslint-disable no-param-reassign, no-bitwise */
/* eslint-disable no-param-reassign */
((gl) => {
gl.VueStage = Vue.extend({
......@@ -9,7 +9,20 @@
spinner: '<span class="fa fa-spinner fa-spin"></span>',
};
},
props: ['stage', 'svgs', 'match'],
props: {
stage: {
type: Object,
required: true,
},
svgs: {
type: DOMStringMap,
required: true,
},
match: {
type: Function,
required: true,
},
},
methods: {
fetchBuilds(e) {
const areaExpanded = e.currentTarget.attributes['aria-expanded'];
......@@ -24,6 +37,18 @@
return flash;
});
},
keepGraph(e) {
const { target } = e;
if (target.className.indexOf('js-ci-action-icon') >= 0) return null;
if (
target.parentElement &&
(target.parentElement.className.indexOf('js-ci-action-icon') >= 0)
) return null;
return e.stopPropagation();
},
},
computed: {
buildsOrSpinner() {
......@@ -64,7 +89,7 @@
<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
<div class="arrow-up"></div>
<div
@click=''
@click='keepGraph($event)'
:class="dropdownClass"
class="js-builds-dropdown-list scrollable-menu"
v-html="buildsOrSpinner"
......
......@@ -15,6 +15,7 @@
}
.ci-status-icon-pending,
.ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings {
color: $gl-warning;
......
......@@ -46,10 +46,6 @@
font-weight: bold;
}
.fa-clipboard {
color: $dropdown-title-btn-color;
}
.commit-info {
&.branches {
margin-left: 8px;
......
......@@ -377,6 +377,10 @@
display: inline-block;
padding: 5px;
&:nth-of-type(7n) {
padding-right: 0;
}
.author_link {
display: block;
}
......
......@@ -195,10 +195,10 @@ ul.notes {
}
.note-body {
overflow: auto;
overflow-x: auto;
overflow-y: hidden;
.note-text {
overflow: auto;
word-wrap: break-word;
@include md-typography;
// Reset ul style types since we're nested inside a ul already
......
......@@ -18,7 +18,6 @@
.file-finder-input:hover,
.issuable-search-form:hover,
.search-text-input:hover,
textarea:hover,
.form-control:hover {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
......
......@@ -19,7 +19,8 @@
overflow: visible;
}
&.ci-failed {
&.ci-failed,
&.ci-failed_with_warnings {
color: $gl-danger;
border-color: $gl-danger;
......
......@@ -14,12 +14,8 @@ class ConfirmationsController < Devise::ConfirmationsController
if signed_in?(resource_name)
after_sign_in_path_for(resource)
else
sign_in(resource)
if signed_in?(resource_name)
after_sign_in_path_for(resource)
else
new_session_path(resource_name)
end
flash[:notice] += " Please sign in."
new_session_path(resource_name)
end
end
end
......@@ -33,6 +33,18 @@ class Projects::IssuesController < Projects::ApplicationController
@labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute
end
@users = []
if params[:assignee_id].present?
assignee = User.find_by_id(params[:assignee_id])
@users.push(assignee) if assignee
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users.push(author) if author
end
respond_to do |format|
format.html
format.atom { render layout: false }
......
......@@ -45,6 +45,8 @@ class SearchController < ApplicationController
end
@search_objects = @search_results.objects(@scope, params[:page])
check_single_commit_result
end
def autocomplete
......@@ -59,4 +61,16 @@ class SearchController < ApplicationController
render json: search_autocomplete_opts(term).to_json
end
private
def check_single_commit_result
if @search_results.single_commit_result?
only_commit = @search_results.objects('commits').first
query = params[:search].strip.downcase
found_by_commit_sha = Commit.valid_hash?(query) && only_commit.sha.start_with?(query)
redirect_to namespace_project_commit_path(@project.namespace, @project, only_commit) if found_by_commit_sha
end
end
end
module ServicesHelper
def service_event_description(event)
case event
when "push"
when "push", "push_events"
"Event will be triggered by a push to the repository"
when "tag_push"
when "tag_push", "tag_push_events"
"Event will be triggered when a new tag is pushed to the repository"
when "note"
when "note", "note_events"
"Event will be triggered when someone adds a comment"
when "issue"
when "issue", "issue_events"
"Event will be triggered when an issue is created/updated/closed"
when "confidential_issue"
when "confidential_issue", "confidential_issue_events"
"Event will be triggered when a confidential issue is created/updated/closed"
when "merge_request"
when "merge_request", "merge_request_events"
"Event will be triggered when a merge request is created/updated/merged"
when "build"
when "build", "build_events"
"Event will be triggered when a build status changes"
when "wiki_page"
when "wiki_page", "wiki_page_events"
"Event will be triggered when a wiki page is created/updated"
when "commit"
when "commit", "commit_events"
"Event will be triggered when a commit is created/updated"
end
end
......@@ -26,4 +26,6 @@ module ServicesHelper
event = event.pluralize if %w[merge_request issue confidential_issue].include?(event)
"#{event}_events"
end
extend self
end
......@@ -107,15 +107,11 @@ class Notify < BaseMailer
def mail_thread(model, headers = {})
add_project_headers
add_unsubscription_headers_and_links
headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers['X-GitLab-Reply-Key'] = reply_key
if !@labels_url && @sent_notification && @sent_notification.unsubscribable?
headers['List-Unsubscribe'] = "<#{unsubscribe_sent_notification_url(@sent_notification, force: true)}>"
@sent_notification_url = unsubscribe_sent_notification_url(@sent_notification)
end
if Gitlab::IncomingEmail.enabled?
address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace
......@@ -171,4 +167,16 @@ class Notify < BaseMailer
headers['X-GitLab-Project-Id'] = @project.id
headers['X-GitLab-Project-Path'] = @project.path_with_namespace
end
def add_unsubscription_headers_and_links
return unless !@labels_url && @sent_notification && @sent_notification.unsubscribable?
list_unsubscribe_methods = [unsubscribe_sent_notification_url(@sent_notification, force: true)]
if Gitlab::IncomingEmail.enabled? && Gitlab::IncomingEmail.supports_wildcard?
list_unsubscribe_methods << "mailto:#{Gitlab::IncomingEmail.unsubscribe_address(reply_key)}"
end
headers['List-Unsubscribe'] = list_unsubscribe_methods.map { |e| "<#{e}>" }.join(',')
@sent_notification_url = unsubscribe_sent_notification_url(@sent_notification)
end
end
......@@ -128,16 +128,21 @@ module Ci
end
def stages
# TODO, this needs refactoring, see gitlab-ce#26481.
stages_query = statuses
.group('stage').select(:stage).order('max(stage_idx)')
status_sql = statuses.latest.where('stage=sg.stage').status_sql
stages_query = statuses.group('stage').select(:stage)
.order('max(stage_idx)')
warnings_sql = statuses.latest.select('COUNT(*) > 0')
.where('stage=sg.stage').failed_but_allowed.to_sql
stages_with_statuses = CommitStatus.from(stages_query, :sg).
pluck('sg.stage', status_sql)
stages_with_statuses = CommitStatus.from(stages_query, :sg)
.pluck('sg.stage', status_sql, "(#{warnings_sql})")
stages_with_statuses.map do |stage|
Ci::Stage.new(self, name: stage.first, status: stage.last)
Ci::Stage.new(self, Hash[%i[name status warnings].zip(stage)])
end
end
......
......@@ -126,9 +126,11 @@ module Ci
end
def tick_runner_queue
new_update = SecureRandom.hex
Gitlab::Redis.with { |redis| redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME) }
new_update
SecureRandom.hex.tap do |new_update|
Gitlab::Redis.with do |redis|
redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME)
end
end
end
def ensure_runner_queue_value
......
......@@ -8,10 +8,11 @@ module Ci
delegate :project, to: :pipeline
def initialize(pipeline, name:, status: nil)
def initialize(pipeline, name:, status: nil, warnings: nil)
@pipeline = pipeline
@name = name
@status = status
@warnings = warnings
end
def to_param
......@@ -39,5 +40,17 @@ module Ci
def builds
@builds ||= pipeline.builds.where(stage: name)
end
def success?
status.to_s == 'success'
end
def has_warnings?
if @warnings.nil?
statuses.latest.failed_but_allowed.any?
else
@warnings
end
end
end
end
......@@ -21,6 +21,9 @@ class Commit
DIFF_HARD_LIMIT_FILES = 1000
DIFF_HARD_LIMIT_LINES = 50000
# The SHA can be between 7 and 40 hex characters.
COMMIT_SHA_PATTERN = '\h{7,40}'
class << self
def decorate(commits, project)
commits.map do |commit|
......@@ -52,6 +55,10 @@ class Commit
def from_hash(hash, project)
new(Gitlab::Git::Commit.new(hash), project)
end
def valid_hash?(key)
!!(/\A#{COMMIT_SHA_PATTERN}\z/ =~ key)
end
end
attr_accessor :raw
......@@ -77,8 +84,6 @@ class Commit
# Pattern used to extract commit references from text
#
# The SHA can be between 7 and 40 hex characters.
#
# This pattern supports cross-project references.
def self.reference_pattern
@reference_pattern ||= %r{
......@@ -88,7 +93,7 @@ class Commit
end
def self.link_reference_pattern
@link_reference_pattern ||= super("commit", /(?<commit>\h{7,40})/)
@link_reference_pattern ||= super("commit", /(?<commit>#{COMMIT_SHA_PATTERN})/)
end
def to_reference(from_project = nil, full: false)
......
module HasStatus
extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped]
STARTED_STATUSES = %w[running success failed skipped]
ACTIVE_STATUSES = %w[pending running]
......
......@@ -11,7 +11,7 @@ module Taskable
INCOMPLETE = 'incomplete'.freeze
ITEM_PATTERN = /
^
(?:\s*[-+*]|(?:\d+\.))? # optional list prefix
\s*(?:[-+*]|(?:\d+\.))? # optional list prefix
\s* # optional whitespace prefix
(\[\s\]|\[[xX]\]) # checkbox
(\s.+) # followed by whitespace and some text.
......
......@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base
include AfterCommitQueue
include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user
before_validation :generate_fingerprint
......@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base
end
def update_last_used_at
UseKeyWorker.perform_async(self.id)
lease = Gitlab::ExclusiveLease.new("key_update_last_used_at:#{id}", timeout: LAST_USED_AT_REFRESH_TIME)
return unless lease.try_obtain
UseKeyWorker.perform_async(id)
end
def add_to_shell
......
......@@ -131,6 +131,8 @@ class Namespace < ActiveRecord::Base
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
remove_exports!
# If repositories moved successfully we need to
# send update instructions to users.
# However we cannot allow rollback since we moved namespace dir
......@@ -219,6 +221,8 @@ class Namespace < ActiveRecord::Base
GitlabShellWorker.perform_in(5.minutes, :rm_namespace, repository_storage_path, new_path)
end
end
remove_exports!
end
def refresh_access_of_projects_invited_groups
......@@ -231,4 +235,20 @@ class Namespace < ActiveRecord::Base
def full_path_changed?
path_changed? || parent_id_changed?
end
def remove_exports!
Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
end
def export_path
File.join(Gitlab::ImportExport.storage_path, full_path_was)
end
def full_path_was
if parent
parent.full_path + '/' + path_was
else
path_was
end
end
end
......@@ -121,8 +121,6 @@ class Project < ActiveRecord::Base
# Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
# Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues, dependent: :destroy
has_many :labels, dependent: :destroy, class_name: 'ProjectLabel'
has_many :services, dependent: :destroy
......
......@@ -25,7 +25,7 @@ You can create a Personal Access Token here:
http://app.asana.com/-/account_api'
end
def to_param
def self.to_param
'asana'
end
......@@ -44,7 +44,7 @@ http://app.asana.com/-/account_api'
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -12,7 +12,7 @@ class AssemblaService < Service
'Project Management Software (Source Commits Endpoint)'
end
def to_param
def self.to_param
'assembla'
end
......@@ -23,7 +23,7 @@ class AssemblaService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -40,7 +40,7 @@ class BambooService < CiService
'You must set up automatic revision labeling and a repository trigger in Bamboo.'
end
def to_param
def self.to_param
'bamboo'
end
......@@ -56,10 +56,6 @@ class BambooService < CiService
]
end
def supported_events
%w(push)
end
def build_page(sha, ref)
with_reactive_cache(sha, ref) {|cached| cached[:build_page] }
end
......
......@@ -19,7 +19,7 @@ class BugzillaService < IssueTrackerService
end
end
def to_param
def self.to_param
'bugzilla'
end
end
......@@ -24,10 +24,6 @@ class BuildkiteService < CiService
hook.save
end
def supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
......@@ -54,7 +50,7 @@ class BuildkiteService < CiService
'Continuous integration and deployments'
end
def to_param
def self.to_param
'buildkite'
end
......
......@@ -19,11 +19,11 @@ class BuildsEmailService < Service
'Email the builds status to a list of recipients.'
end
def to_param
def self.to_param
'builds_email'
end
def supported_events
def self.supported_events
%w(build)
end
......
......@@ -12,7 +12,7 @@ class CampfireService < Service
'Simple web-based real-time group chat'
end
def to_param
def self.to_param
'campfire'
end
......@@ -24,7 +24,7 @@ class CampfireService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -25,7 +25,7 @@ class ChatNotificationService < Service
valid?
end
def supported_events
def self.supported_events
%w[push issue confidential_issue merge_request note tag_push
build pipeline wiki_page]
end
......@@ -82,19 +82,19 @@ class ChatNotificationService < Service
def get_message(object_kind, data)
case object_kind
when "push", "tag_push"
PushMessage.new(data)
ChatMessage::PushMessage.new(data)
when "issue"
IssueMessage.new(data) unless is_update?(data)
ChatMessage::IssueMessage.new(data) unless is_update?(data)
when "merge_request"
MergeMessage.new(data) unless is_update?(data)
ChatMessage::MergeMessage.new(data) unless is_update?(data)
when "note"
NoteMessage.new(data)
ChatMessage::NoteMessage.new(data)
when "build"
BuildMessage.new(data) if should_build_be_notified?(data)
ChatMessage::BuildMessage.new(data) if should_build_be_notified?(data)
when "pipeline"
PipelineMessage.new(data) if should_pipeline_be_notified?(data)
ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page"
WikiPageMessage.new(data)
ChatMessage::WikiPageMessage.new(data)
end
end
......
......@@ -13,8 +13,8 @@ class ChatSlashCommandsService < Service
ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end
def supported_events
[]
def self.supported_events
%w()
end
def can_test?
......
......@@ -8,7 +8,7 @@ class CiService < Service
self.respond_to?(:token) && self.token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -23,7 +23,7 @@ class CustomIssueTrackerService < IssueTrackerService
end
end
def to_param
def self.to_param
'custom_issue_tracker'
end
......
......@@ -5,8 +5,8 @@
class DeploymentService < Service
default_value_for :category, 'deployment'
def supported_events
[]
def self.supported_events
%w()
end
def predefined_variables
......
......@@ -32,7 +32,7 @@ class DroneCiService < CiService
true
end
def supported_events
def self.supported_events
%w(push merge_request tag_push)
end
......@@ -87,7 +87,7 @@ class DroneCiService < CiService
'Drone is a Continuous Integration platform built on Docker, written in Go'
end
def to_param
def self.to_param
'drone_ci'
end
......
......@@ -12,11 +12,11 @@ class EmailsOnPushService < Service
'Email the commits and diff of each push to a list of recipients.'
end
def to_param
def self.to_param
'emails_on_push'
end
def supported_events
def self.supported_events
%w(push tag_push)
end
......
......@@ -13,7 +13,7 @@ class ExternalWikiService < Service
'Replaces the link to the internal wiki with a link to an external wiki.'
end
def to_param
def self.to_param
'external_wiki'
end
......@@ -29,4 +29,8 @@ class ExternalWikiService < Service
nil
end
end
def self.supported_events
%w()
end
end
......@@ -12,7 +12,7 @@ class FlowdockService < Service
'Flowdock is a collaboration web app for technical teams.'
end
def to_param
def self.to_param
'flowdock'
end
......@@ -22,7 +22,7 @@ class FlowdockService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -12,7 +12,7 @@ class GemnasiumService < Service
'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.'
end
def to_param
def self.to_param
'gemnasium'
end
......@@ -23,7 +23,7 @@ class GemnasiumService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -7,7 +7,7 @@ class GitlabIssueTrackerService < IssueTrackerService
default_value_for :default, true
def to_param
def self.to_param
'gitlab'
end
......
......@@ -27,7 +27,7 @@ class HipchatService < Service
'Private group chat and IM'
end
def to_param
def self.to_param
'hipchat'
end
......@@ -45,7 +45,7 @@ class HipchatService < Service
]
end
def supported_events
def self.supported_events
%w(push issue confidential_issue merge_request note tag_push build)
end
......
......@@ -17,11 +17,11 @@ class IrkerService < Service
'gateway.'
end
def to_param
def self.to_param
'irker'
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -57,7 +57,7 @@ class IssueTrackerService < Service
end
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -12,7 +12,7 @@ class JiraService < IssueTrackerService
# This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service
# configuration screen.
def supported_events
def self.supported_events
%w(commit merge_request)
end
......@@ -81,7 +81,7 @@ class JiraService < IssueTrackerService
end
end
def to_param
def self.to_param
'jira'
end
......
......@@ -52,7 +52,7 @@ class KubernetesService < DeploymentService
'deployments with `app=$CI_ENVIRONMENT_SLUG`'
end
def to_param
def self.to_param
'kubernetes'
end
......
......@@ -7,7 +7,7 @@ class MattermostService < ChatNotificationService
'Receive event notifications in Mattermost'
end
def to_param
def self.to_param
'mattermost'
end
......@@ -36,6 +36,6 @@ class MattermostService < ChatNotificationService
end
def default_channel_placeholder
"#town-square"
"town-square"
end
end
......@@ -15,7 +15,7 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
"Perform common operations on GitLab in Mattermost"
end
def to_param
def self.to_param
'mattermost_slash_commands'
end
......
......@@ -15,11 +15,11 @@ class PipelinesEmailService < Service
'Email the pipelines status to a list of recipients.'
end
def to_param
def self.to_param
'pipelines_email'
end
def supported_events
def self.supported_events
%w[pipeline]
end
......
......@@ -14,7 +14,7 @@ class PivotaltrackerService < Service
'Project Management Software (Source Commits Endpoint)'
end
def to_param
def self.to_param
'pivotaltracker'
end
......@@ -34,7 +34,7 @@ class PivotaltrackerService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -13,7 +13,7 @@ class PushoverService < Service
'Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.'
end
def to_param
def self.to_param
'pushover'
end
......@@ -61,7 +61,7 @@ class PushoverService < Service
]
end
def supported_events
def self.supported_events
%w(push)
end
......
......@@ -19,7 +19,7 @@ class RedmineService < IssueTrackerService
end
end
def to_param
def self.to_param
'redmine'
end
end
......@@ -7,7 +7,7 @@ class SlackService < ChatNotificationService
'Receive event notifications in Slack'
end
def to_param
def self.to_param
'slack'
end
......
......@@ -9,7 +9,7 @@ class SlackSlashCommandsService < ChatSlashCommandsService
"Perform common operations on GitLab in Slack"
end
def to_param
def self.to_param
'slack_slash_commands'
end
......
......@@ -43,14 +43,10 @@ class TeamcityService < CiService
'requests build, that setting is in the vsc root advanced settings.'
end
def to_param
def self.to_param
'teamcity'
end
def supported_events
%w(push)
end
def fields
[
{ type: 'text', name: 'teamcity_url',
......
......@@ -76,6 +76,11 @@ class Service < ActiveRecord::Base
def to_param
# implement inside child
self.class.to_param
end
def self.to_param
raise NotImplementedError
end
def fields
......@@ -92,7 +97,11 @@ class Service < ActiveRecord::Base
end
def event_names
supported_events.map { |event| "#{event}_events" }
self.class.event_names
end
def self.event_names
self.supported_events.map { |event| "#{event}_events" }
end
def event_field(event)
......@@ -104,6 +113,10 @@ class Service < ActiveRecord::Base
end
def supported_events
self.class.supported_events
end
def self.supported_events
%w(push tag_push issue confidential_issue merge_request wiki_page)
end
......
......@@ -40,10 +40,12 @@ class PipelineEntity < Grape::Entity
end
expose :path do |pipeline|
namespace_project_tree_path(
pipeline.project.namespace,
pipeline.project,
id: pipeline.ref)
if pipeline.ref
namespace_project_tree_path(
pipeline.project.namespace,
pipeline.project,
id: pipeline.ref)
end
end
expose :tag?, as: :tag
......
......@@ -6,6 +6,14 @@ module Ci
runner.tick_runner_queue
end
end
return unless build.project.shared_runners_enabled?
Ci::Runner.shared.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
end
end
end
......@@ -38,15 +38,13 @@ module MergeRequests
private
def merge_requests_for(branch)
origin_merge_requests = @project.origin_merge_requests
.opened.where(source_branch: branch).to_a
fork_merge_requests = @project.fork_merge_requests
.opened.where(source_branch: branch).to_a
(origin_merge_requests + fork_merge_requests)
.uniq.select(&:source_project)
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
def merge_requests_for(source_branch, mr_states: [:opened])
MergeRequest
.with_state(mr_states)
.where(source_branch: source_branch, source_project_id: @project.id)
.preload(:source_project) # we don't need a #includes since we're just preloading for the #select
.select(&:source_project)
end
def pipeline_merge_requests(pipeline)
......
......@@ -42,7 +42,7 @@ module MergeRequests
commit_ids.include?(merge_request.diff_head_sha)
end
merge_requests.uniq.select(&:source_project).each do |merge_request|
filter_merge_requests(merge_requests).each do |merge_request|
MergeRequests::PostMergeService.
new(merge_request.target_project, @current_user).
execute(merge_request)
......@@ -58,10 +58,13 @@ module MergeRequests
def reload_merge_requests
merge_requests = @project.merge_requests.opened.
by_source_or_target_branch(@branch_name).to_a
merge_requests += fork_merge_requests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
# Fork merge requests
merge_requests += MergeRequest.opened
.where(source_branch: @branch_name, source_project: @project)
.where.not(target_project: @project).to_a
filter_merge_requests(merge_requests).each do |merge_request|
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff
else
......@@ -175,16 +178,7 @@ module MergeRequests
end
def merge_requests_for_source_branch
@source_merge_requests ||= begin
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
merge_requests += fork_merge_requests
filter_merge_requests(merge_requests)
end
end
def fork_merge_requests
@fork_merge_requests ||= @project.fork_merge_requests.opened.
where(source_branch: @branch_name).to_a
@source_merge_requests ||= merge_requests_for(@branch_name)
end
def branch_added?
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
%h4
Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
to be merged automatically when the pipeline succeeds.
......
%button.choose-btn.btn.btn-sm.js-choose-group-avatar-button
%button.choose-btn.btn.btn-sm.js-choose-group-avatar-button{ type: 'button' }
%i.fa.fa-paperclip
%span Choose File ...
&nbsp;
......
......@@ -11,13 +11,13 @@
class: "check_all_issues left"
.issues-other-filters.filtered-search-container
.filtered-search-input-container
%input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id }
%input.form-control.filtered-search{ placeholder: 'Search or filter results...', 'data-id' => 'filtered-search', 'data-project-id' => @project.id, 'data-username-params' => @users.to_json(only: [:id, :username]) }
= icon('filter')
%button.clear-search.hidden{ type: 'button' }
= icon('times')
#js-dropdown-hint.dropdown-menu.hint-dropdown
%ul{ 'data-dropdown' => true }
%li.filter-dropdown-item{ 'data-value' => '' }
%li.filter-dropdown-item{ 'data-action' => 'submit' }
%button.btn.btn-link
= icon('search')
%span
......@@ -47,6 +47,10 @@
%li.filter-dropdown-item{ 'data-value' => 'none' }
%button.btn.btn-link
No Assignee
- if current_user
%li.filter-dropdown-item{ 'data-value' => current_user.to_reference }
%button.btn.btn-link
Assigned to me
%li.divider
%ul.filter-dropdown{ 'data-dynamic' => true, 'data-dropdown' => true }
%li.filter-dropdown-item
......@@ -121,7 +125,13 @@
new MilestoneSelect();
new IssueStatusSelect();
new SubscriptionSelect();
$('form.filter-form').on('submit', function (event) {
event.preventDefault();
Turbolinks.visit(this.action + '&' + $(this).serialize());
$(document).off('page:restore').on('page:restore', function (event) {
if (gl.FilteredSearchManager) {
new gl.FilteredSearchManager();
}
Issuable.init();
new gl.IssuableBulkActions({
prefixId: 'issue_',
});
});
---
title: Go to a project order
merge_request: 7737
author: Jacopo Beschi @jacopo-beschi
---
title: Fix double spaced CI log
merge_request: 8349
author: Jared Deckard <jared.deckard@gmail.com>
---
title: Allow group and project paths when transferring projects via the API
merge_request:
author:
---
title: Prevent empty pagination when list is not empty
merge_request: 8172
author:
---
title: Improve visibility of "Resolve conflicts" and "Merge locally" actions
merge_request: 8229
author:
---
title: Reduce DB-load for build-queues by storing last_update in Redis
merge_request: 8084
author:
---
title: Remove Lock Icon on Protected Tag
merge_request: 8513
author: Sergey Nikitin
---
title: Handle unsubscribe from email notifications via replying to reply+%{key}+unsubscribe@ address
merge_request: 6597
author:
---
title: Allow creating protected branches when user can merge to such branch
merge_request: 8458
author:
---
title: Adds service trigger events to api
merge_request: 8324
author:
---
title: Updated project visibility settings UX
merge_request: 7645
author:
---
title: Treat environments matching `production/*` as Production
merge_request: 8500
author:
---
title: 'Allows to search within project by commit hash'
merge_request:
author: YarNayar
---
title: ensure permalinks scroll to correct position on multiple clicks
merge_request: 8046
author:
---
title: Support slash comand `/merge` for merging merge requests.
merge_request: 7746
author: Jarka Kadlecova
---
title: Fix nested tasks in ordered list
merge_request: 8626
author:
---
title: Added number_with_delimiter to counter on milestone panels
merge_request:
author: Ryan Harris
---
title: Adds label to Environments "Date Created"
merge_request: 8376
author: Saad Shahd
---
title: Change status colors of runners to better defaults
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.
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.
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