Commit 9fc393c2 authored by Filipa Lacerda's avatar Filipa Lacerda

[ci skip] Merge branch 'master' into 42643-persist-external-ip-of-ingress-controller-gke

* master: (114 commits)
  Fix Error 500 when viewing a commit with a GPG signature in Geo
  Remember assignee when moving an issue
  Add changelog entry
  Allow oxford commas and spaces before commas in MR issue closing pattern.
  Don't cache a nil repository root ref to prevent caching issues
  Add back database changes for Ci::Build
  Revert "Merge branch 'expired-ci-artifacts' into 'master'"
  Fix order dependencies in some specs
  migrate admin:users:* to static bundle
  correct for missing break statement in dispatcher.js
  alias create and update actions to new and edit
  migrate projects:merge_requests:edit to static bundle
  migrate projects:merge_requests:creations:diffs to static bundle
  migrate projects:merge_requests:creations:new to static bundle
  migrate projects:issues:new and projects:issues:edit to static bundle
  migrate projects:branches:index to static bundle
  migrate projects:branches:new to static bundle
  migrate projects:compare:show to static bundle
  migrate projects:environments:metrics to static bundle
  migrate projects:milestones:* and groups:milestones:* to static bundle
  ...
parents b284ce04 5526458b
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
"import/no-commonjs": "error", "import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }], "no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error", "promise/catch-or-return": "error",
"no-underscore-dangle": ["error", { "allow": ["__"]}], "no-underscore-dangle": ["error", { "allow": ["__", "_links"]}],
"vue/html-self-closing": ["error", { "vue/html-self-closing": ["error", {
"html": { "html": {
"void": "always", "void": "always",
......
...@@ -2,6 +2,25 @@ ...@@ -2,6 +2,25 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.4.4 (2018-02-16)
### Security (1 change)
- Update nokogiri to 1.8.2. !16807
### Fixed (9 changes)
- Fix 500 error when loading a merge request with an invalid comment. !16795
- Cleanup new branch/merge request form in issues. !16854
- Fix GitLab import leaving group_id on ProjectLabel. !16877
- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881
- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
- Validate user namespace before saving so that errors persist on model.
- LDAP Person no longer throws exception on invalid entry.
- Fix JIRA not working when a trailing slash is included.
## 10.4.3 (2018-02-05) ## 10.4.3 (2018-02-05)
### Security (4 changes) ### Security (4 changes)
......
...@@ -174,7 +174,7 @@ Assigning a team label makes sure issues get the attention of the appropriate ...@@ -174,7 +174,7 @@ Assigning a team label makes sure issues get the attention of the appropriate
people. people.
The current team labels are ~Build, ~"CI/CD", ~Discussion, ~Documentation, ~Edge, The current team labels are ~Build, ~"CI/CD", ~Discussion, ~Documentation, ~Edge,
~Geo, ~Gitaly, ~Platform, ~Monitoring, ~Release, and ~"UX". ~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products" and ~"UX".
The descriptions on the [labels page][labels-page] explain what falls under the The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team. responsibility of each team.
......
...@@ -401,6 +401,7 @@ gem 'sys-filesystem', '~> 1.1.6' ...@@ -401,6 +401,7 @@ gem 'sys-filesystem', '~> 1.1.6'
# SSH host key support # SSH host key support
gem 'net-ssh', '~> 4.1.0' gem 'net-ssh', '~> 4.1.0'
gem 'sshkey', '~> 1.9.0'
# Required for ED25519 SSH host key support # Required for ED25519 SSH host key support
group :ed25519 do group :ed25519 do
......
...@@ -895,6 +895,7 @@ GEM ...@@ -895,6 +895,7 @@ GEM
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.3.13) sqlite3 (1.3.13)
sshkey (1.9.0)
stackprof (0.2.10) stackprof (0.2.10)
state_machines (0.4.0) state_machines (0.4.0)
state_machines-activemodel (0.4.0) state_machines-activemodel (0.4.0)
...@@ -1192,6 +1193,7 @@ DEPENDENCIES ...@@ -1192,6 +1193,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4) spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0) spring-commands-spinach (~> 1.1.0)
sprockets (~> 3.7.0) sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10) stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0) state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6) sys-filesystem (~> 1.1.6)
......
...@@ -50,10 +50,8 @@ class AwardsHandler { ...@@ -50,10 +50,8 @@ class AwardsHandler {
this.registerEventListener('on', $('html'), 'click', (e) => { this.registerEventListener('on', $('html'), 'click', (e) => {
const $target = $(e.target); const $target = $(e.target);
if (!$target.closest('.emoji-menu-content').length) {
$('.js-awards-block.current').removeClass('current');
}
if (!$target.closest('.emoji-menu').length) { if (!$target.closest('.emoji-menu').length) {
$('.js-awards-block.current').removeClass('current');
if ($('.emoji-menu').is(':visible')) { if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active'); $('.js-add-award.is-active').removeClass('is-active');
this.hideMenuElement($('.emoji-menu')); this.hideMenuElement($('.emoji-menu'));
......
...@@ -75,6 +75,7 @@ export default class AjaxVariableList { ...@@ -75,6 +75,7 @@ export default class AjaxVariableList {
if (res.status === statusCodes.OK && res.data) { if (res.status === statusCodes.OK && res.data) {
this.updateRowsWithPersistedVariables(res.data.variables); this.updateRowsWithPersistedVariables(res.data.variables);
this.variableList.hideValues();
} else if (res.status === statusCodes.BAD_REQUEST) { } else if (res.status === statusCodes.BAD_REQUEST) {
// Validation failed // Validation failed
this.errorBox.innerHTML = generateErrorBoxContent(res.data); this.errorBox.innerHTML = generateErrorBoxContent(res.data);
......
...@@ -178,6 +178,10 @@ export default class VariableList { ...@@ -178,6 +178,10 @@ export default class VariableList {
this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled);
} }
hideValues() {
this.secretValues.updateDom(false);
}
getAllData() { getAllData() {
// Ignore the last empty row because we don't want to try persist // Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems. // a blank variable and run into validation problems.
......
/* eslint-disable func-names, wrap-iife, consistent-return,
no-return-assign, no-param-reassign, one-var-declaration-per-line, no-unused-vars,
prefer-template, object-shorthand, prefer-arrow-callback */
import { pluralize } from './lib/utils/text_utility'; import { pluralize } from './lib/utils/text_utility';
import { localTimeAgo } from './lib/utils/datetime_utility'; import { localTimeAgo } from './lib/utils/datetime_utility';
import Pager from './pager'; import Pager from './pager';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
export default (function () { export default class CommitsList {
const CommitsList = {}; constructor(limit = 0) {
this.timer = null;
CommitsList.timer = null;
CommitsList.init = function (limit) {
this.$contentList = $('.content_list'); this.$contentList = $('.content_list');
$('body').on('click', '.day-commits-table li.commit', function (e) { Pager.init(parseInt(limit, 10), false, false, this.processCommits.bind(this));
if (e.target.nodeName !== 'A') {
location.href = $(this).attr('url');
e.stopPropagation();
return false;
}
});
Pager.init(parseInt(limit, 10), false, false, this.processCommits);
this.content = $('#commits-list'); this.content = $('#commits-list');
this.searchField = $('#commits-search'); this.searchField = $('#commits-search');
this.lastSearch = this.searchField.val(); this.lastSearch = this.searchField.val();
return this.initSearch(); this.initSearch();
}; }
CommitsList.initSearch = function () { initSearch() {
this.timer = null; this.timer = null;
return this.searchField.keyup((function (_this) { this.searchField.on('keyup', () => {
return function () { clearTimeout(this.timer);
clearTimeout(_this.timer); this.timer = setTimeout(this.filterResults.bind(this), 500);
return _this.timer = setTimeout(_this.filterResults, 500); });
}; }
})(this));
};
CommitsList.filterResults = function () { filterResults() {
const form = $('.commits-search-form'); const form = $('.commits-search-form');
const search = CommitsList.searchField.val(); const search = this.searchField.val();
if (search === CommitsList.lastSearch) return Promise.resolve(); if (search === this.lastSearch) return Promise.resolve();
const commitsUrl = form.attr('action') + '?' + form.serialize(); const commitsUrl = `${form.attr('action')}?${form.serialize()}`;
CommitsList.content.fadeTo('fast', 0.5); this.content.fadeTo('fast', 0.5);
const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, { const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, {
[obj.name]: obj.value, [obj.name]: obj.value,
}), {}); }), {});
...@@ -55,9 +39,9 @@ export default (function () { ...@@ -55,9 +39,9 @@ export default (function () {
params, params,
}) })
.then(({ data }) => { .then(({ data }) => {
CommitsList.lastSearch = search; this.lastSearch = search;
CommitsList.content.html(data.html); this.content.html(data.html);
CommitsList.content.fadeTo('fast', 1.0); this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved // Change url so if user reload a page - search results are saved
history.replaceState({ history.replaceState({
...@@ -65,16 +49,16 @@ export default (function () { ...@@ -65,16 +49,16 @@ export default (function () {
}, document.title, commitsUrl); }, document.title, commitsUrl);
}) })
.catch(() => { .catch(() => {
CommitsList.content.fadeTo('fast', 1.0); this.content.fadeTo('fast', 1.0);
CommitsList.lastSearch = null; this.lastSearch = null;
}); });
}; }
// Prepare loaded data. // Prepare loaded data.
CommitsList.processCommits = (data) => { processCommits(data) {
let processedData = data; let processedData = data;
const $processedData = $(processedData); const $processedData = $(processedData);
const $commitsHeadersLast = CommitsList.$contentList.find('li.js-commit-header').last(); const $commitsHeadersLast = this.$contentList.find('li.js-commit-header').last();
const lastShownDay = $commitsHeadersLast.data('day'); const lastShownDay = $commitsHeadersLast.data('day');
const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first(); const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first();
const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day'); const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day');
...@@ -97,7 +81,5 @@ export default (function () { ...@@ -97,7 +81,5 @@ export default (function () {
localTimeAgo($processedData.find('.js-timeago')); localTimeAgo($processedData.find('.js-timeago'));
return processedData; return processedData;
}; }
}
return CommitsList;
})();
...@@ -43,169 +43,14 @@ var Dispatcher; ...@@ -43,169 +43,14 @@ var Dispatcher;
}); });
switch (page) { switch (page) {
case 'projects:environments:metrics':
import('./pages/projects/environments/metrics')
.then(callDefault)
.catch(fail);
break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
case 'projects:issues:index': case 'projects:issues:index':
case 'projects:issues:show': case 'projects:issues:show':
shortcut_handler = true;
break;
case 'projects:milestones:index':
import('./pages/projects/milestones/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:show':
import('./pages/projects/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:show':
import('./pages/groups/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:milestones:show':
import('./pages/dashboard/milestones/show')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:issues':
import('./pages/dashboard/issues')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:merge_requests':
import('./pages/dashboard/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'groups:issues':
import('./pages/groups/issues')
.then(callDefault)
.catch(fail);
break;
case 'groups:merge_requests':
import('./pages/groups/merge_requests')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:todos:index':
import('./pages/dashboard/todos/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:jobs:index':
import('./pages/admin/jobs/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:projects:index':
import('./pages/admin/projects/index/index')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:index':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'admin:users:show':
import('./pages/admin/users/shared')
.then(callDefault)
.catch(fail);
break;
case 'dashboard:projects:index':
case 'dashboard:projects:starred':
import('./pages/dashboard/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:projects:index':
case 'explore:projects:trending':
case 'explore:projects:starred':
import('./pages/explore/projects')
.then(callDefault)
.catch(fail);
break;
case 'explore:groups:index':
import('./pages/explore/groups')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:new':
case 'projects:milestones:create':
import('./pages/projects/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:milestones:edit':
case 'projects:milestones:update':
import('./pages/projects/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:new':
case 'groups:milestones:create':
import('./pages/groups/milestones/new')
.then(callDefault)
.catch(fail);
break;
case 'groups:milestones:edit':
case 'groups:milestones:update':
import('./pages/groups/milestones/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:compare:show':
import('./pages/projects/compare/show')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:new':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:create':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:index':
import('./pages/projects/branches/index')
.then(callDefault)
.catch(fail);
break;
case 'projects:issues:new': case 'projects:issues:new':
import('./pages/projects/issues/new')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:issues:edit': case 'projects:issues:edit':
import('./pages/projects/issues/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:new':
import('./pages/projects/merge_requests/creations/new')
.then(callDefault)
.catch(fail);
case 'projects:merge_requests:creations:diffs': case 'projects:merge_requests:creations:diffs':
import('./pages/projects/merge_requests/creations/diffs')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
import('./pages/projects/merge_requests/edit')
.then(callDefault)
.catch(fail);
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:tags:new': case 'projects:tags:new':
...@@ -224,6 +69,11 @@ var Dispatcher; ...@@ -224,6 +69,11 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'projects:services:edit':
import('./pages/projects/services/edit')
.then(callDefault)
.catch(fail);
break;
case 'projects:snippets:edit': case 'projects:snippets:edit':
case 'projects:snippets:update': case 'projects:snippets:update':
import('./pages/projects/snippets/edit') import('./pages/projects/snippets/edit')
...@@ -462,11 +312,6 @@ var Dispatcher; ...@@ -462,11 +312,6 @@ var Dispatcher;
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'users:show':
import('./pages/users/show')
.then(callDefault)
.catch(fail);
break;
case 'admin:conversational_development_index:show': case 'admin:conversational_development_index:show':
import('./pages/admin/conversational_development_index/show') import('./pages/admin/conversational_development_index/show')
.then(callDefault) .then(callDefault)
......
...@@ -6,6 +6,11 @@ monacoContext.require.config({ ...@@ -6,6 +6,11 @@ monacoContext.require.config({
}, },
}); });
// ignore CDN config and use local assets path for service worker which cannot be cross-domain
const relativeRootPath = (gon && gon.relative_url_root) || '';
const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext; window.__monaco_context__ = monacoContext;
export default monacoContext.require; export default monacoContext.require;
...@@ -59,8 +59,10 @@ class ImporterStatus { ...@@ -59,8 +59,10 @@ class ImporterStatus {
.catch(() => flash(__('An error occurred while importing project'))); .catch(() => flash(__('An error occurred while importing project')));
} }
setAutoUpdate() { autoUpdate() {
return setInterval(() => $.get(this.jobsUrl, data => $.each(data, (i, job) => { return axios.get(this.jobsUrl)
.then(({ data = [] }) => {
data.forEach((job) => {
const jobItem = $(`#project_${job.id}`); const jobItem = $(`#project_${job.id}`);
const statusField = jobItem.find('.job-status'); const statusField = jobItem.find('.job-status');
...@@ -81,7 +83,12 @@ class ImporterStatus { ...@@ -81,7 +83,12 @@ class ImporterStatus {
statusField.html(job.import_status); statusField.html(job.import_status);
break; break;
} }
})), 4000); });
});
}
setAutoUpdate() {
setInterval(this.autoUpdate.bind(this), 4000);
} }
} }
......
/* eslint-disable no-new */
import IntegrationSettingsForm from './integration_settings_form';
$(() => {
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init();
});
<script> <script>
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash'; import createFlash from '~/flash';
import modal from '~/vue_shared/components/modal.vue'; import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default { export default {
components: { components: {
modal, GlModal,
}, },
props: { props: {
url: { url: {
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
}, },
computed: { computed: {
text() { text() {
return s__('AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.'); return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.');
}, },
}, },
methods: { methods: {
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
redirectTo(response.request.responseURL); redirectTo(response.request.responseURL);
}) })
.catch((error) => { .catch((error) => {
Flash(s__('AdminArea|Stopping jobs failed')); createFlash(s__('AdminArea|Stopping jobs failed'));
throw error; throw error;
}); });
}, },
...@@ -37,11 +37,13 @@ ...@@ -37,11 +37,13 @@
</script> </script>
<template> <template>
<modal <gl-modal
id="stop-jobs-modal" id="stop-jobs-modal"
:title="s__('AdminArea|Stop all jobs?')" :header-title-text="s__('AdminArea|Stop all jobs?')"
:text="text" footer-primary-button-variant="danger"
kind="danger" :footer-primary-button-text="s__('AdminArea|Stop jobs')"
:primary-button-label="s__('AdminArea|Stop jobs')" @submit="onSubmit"
@submit="onSubmit" /> >
{{ text }}
</gl-modal>
</template> </template>
import Vue from 'vue'; import Vue from 'vue';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import stopJobsModal from './components/stop_jobs_modal.vue'; import stopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate); Vue.use(Translate);
export default () => { document.addEventListener('DOMContentLoaded', () => {
const stopJobsButton = document.getElementById('stop-jobs-button'); const stopJobsButton = document.getElementById('stop-jobs-button');
if (stopJobsButton) {
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el: '#stop-jobs-modal', el: '#stop-jobs-modal',
...@@ -26,4 +24,5 @@ export default () => { ...@@ -26,4 +24,5 @@ export default () => {
}); });
}, },
}); });
}; }
});
...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; ...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteProjectModal from './components/delete_project_modal.vue'; import deleteProjectModal from './components/delete_project_modal.vue';
export default () => { document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate); Vue.use(Translate);
const deleteProjectModalEl = document.getElementById('delete-project-modal'); const deleteProjectModalEl = document.getElementById('delete-project-modal');
...@@ -34,4 +34,4 @@ export default () => { ...@@ -34,4 +34,4 @@ export default () => {
deleteModal.projectName = buttonProps.projectName; deleteModal.projectName = buttonProps.projectName;
} }
}); });
}; });
...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; ...@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteUserModal from './components/delete_user_modal.vue'; import deleteUserModal from './components/delete_user_modal.vue';
export default () => { document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate); Vue.use(Translate);
const deleteUserModalEl = document.getElementById('delete-user-modal'); const deleteUserModalEl = document.getElementById('delete-user-modal');
...@@ -40,4 +40,4 @@ export default () => { ...@@ -40,4 +40,4 @@ export default () => {
deleteModal.username = buttonProps.username; deleteModal.username = buttonProps.username;
} }
}); });
}; });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initLegacyFilters from '~/init_legacy_filters';
export default () => { document.addEventListener('DOMContentLoaded', () => {
projectSelect(); projectSelect();
initLegacyFilters(); initLegacyFilters();
}; });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initLegacyFilters from '~/init_legacy_filters';
export default () => { document.addEventListener('DOMContentLoaded', () => {
projectSelect(); projectSelect();
initLegacyFilters(); initLegacyFilters();
}; });
import Milestone from '~/milestone'; import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar'; import Sidebar from '~/right_sidebar';
export default () => { document.addEventListener('DOMContentLoaded', () => {
new Milestone(); // eslint-disable-line no-new new Milestone(); // eslint-disable-line no-new
new Sidebar(); // eslint-disable-line no-new new Sidebar(); // eslint-disable-line no-new
}; });
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
export default () => new ProjectsList(); document.addEventListener('DOMContentLoaded', () => new ProjectsList());
import Todos from './todos'; import Todos from './todos';
export default () => new Todos(); document.addEventListener('DOMContentLoaded', () => new Todos());
...@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list'; ...@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list';
import Landing from '~/landing'; import Landing from '~/landing';
import initGroupsList from '../../../groups'; import initGroupsList from '../../../groups';
export default function () { document.addEventListener('DOMContentLoaded', () => {
new GroupsList(); // eslint-disable-line no-new new GroupsList(); // eslint-disable-line no-new
initGroupsList(); initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing'); const landingElement = document.querySelector('.js-explore-groups-landing');
...@@ -13,4 +13,4 @@ export default function () { ...@@ -13,4 +13,4 @@ export default function () {
'explore_groups_landing_dismissed', 'explore_groups_landing_dismissed',
); );
exploreGroupsLanding.toggle(); exploreGroupsLanding.toggle();
} });
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
export default () => new ProjectsList(); document.addEventListener('DOMContentLoaded', () => new ProjectsList());
...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; ...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.ISSUES, page: FILTERED_SEARCH.ISSUES,
}); });
projectSelect(); projectSelect();
}; });
...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; ...@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants'; import { FILTERED_SEARCH } from '~/pages/constants';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({ initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS, page: FILTERED_SEARCH.MERGE_REQUESTS,
}); });
projectSelect(); projectSelect();
}; });
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(false); document.addEventListener('DOMContentLoaded', () => initForm(false));
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(false); document.addEventListener('DOMContentLoaded', () => initForm(false));
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
export default initMilestonesShow; document.addEventListener('DOMContentLoaded', initMilestonesShow);
import AjaxLoadingSpinner from '~/ajax_loading_spinner'; import AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal'; import DeleteModal from '~/branches/branches_delete_modal';
export default () => { document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init(); AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new new DeleteModal(); // eslint-disable-line no-new
}; });
import NewBranchForm from '~/new_branch_form'; import NewBranchForm from '~/new_branch_form';
export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); document.addEventListener('DOMContentLoaded', () => (
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
));
...@@ -3,7 +3,7 @@ import GpgBadges from '~/gpg_badges'; ...@@ -3,7 +3,7 @@ import GpgBadges from '~/gpg_badges';
import ShortcutsNavigation from '~/shortcuts_navigation'; import ShortcutsNavigation from '~/shortcuts_navigation';
export default () => { export default () => {
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new
GpgBadges.fetch(); GpgBadges.fetch();
}; };
import Diff from '~/diff'; import Diff from '~/diff';
import initChangesDropdown from '~/init_changes_dropdown'; import initChangesDropdown from '~/init_changes_dropdown';
export default () => { document.addEventListener('DOMContentLoaded', () => {
new Diff(); // eslint-disable-line no-new new Diff(); // eslint-disable-line no-new
const paddingTop = 16; const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
}; });
import monitoringBundle from '~/monitoring/monitoring_bundle'; import monitoringBundle from '~/monitoring/monitoring_bundle';
export default monitoringBundle; document.addEventListener('DOMContentLoaded', monitoringBundle);
import initForm from '../form'; import initForm from '../form';
export default () => { document.addEventListener('DOMContentLoaded', initForm);
initForm();
};
import initForm from '../form'; import initForm from '../form';
export default () => { document.addEventListener('DOMContentLoaded', initForm);
initForm();
};
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest; document.addEventListener('DOMContentLoaded', initMergeRequest);
import Compare from '~/compare'; import Compare from '~/compare';
import MergeRequest from '~/merge_request'; import MergeRequest from '~/merge_request';
export default () => { document.addEventListener('DOMContentLoaded', () => {
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) { if (mrNewCompareNode) {
new Compare({ // eslint-disable-line no-new new Compare({ // eslint-disable-line no-new
...@@ -15,4 +15,4 @@ export default () => { ...@@ -15,4 +15,4 @@ export default () => {
action: mrNewSubmitNode.dataset.mrSubmitAction, action: mrNewSubmitNode.dataset.mrSubmitAction,
}); });
} }
}; });
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
export default initMergeRequest; document.addEventListener('DOMContentLoaded', initMergeRequest);
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(); document.addEventListener('DOMContentLoaded', () => initForm());
import milestones from '~/pages/milestones/shared'; import milestones from '~/pages/milestones/shared';
export default milestones; document.addEventListener('DOMContentLoaded', milestones);
import initForm from '../../../../shared/milestones/form'; import initForm from '../../../../shared/milestones/form';
export default () => initForm(); document.addEventListener('DOMContentLoaded', () => initForm());
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import milestones from '~/pages/milestones/shared'; import milestones from '~/pages/milestones/shared';
export default () => { document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow(); initMilestonesShow();
milestones(); milestones();
}; });
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
import Vue from 'vue'; import Vue from 'vue';
import PipelineSchedulesCallout from './components/pipeline_schedules_callout.vue'; import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({ document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipeline-schedules-callout', el: '#pipeline-schedules-callout',
......
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
<script> <script>
import Vue from 'vue'; import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import Translate from '../../vue_shared/translate'; import Translate from '../../../../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg'; import illustrationSvg from '../icons/intro_illustration.svg';
Vue.use(Translate); Vue.use(Translate);
......
import Vue from 'vue'; import Vue from 'vue';
import Translate from '../vue_shared/translate'; import Translate from '../../../../vue_shared/translate';
import GlFieldErrors from '../gl_field_errors'; import GlFieldErrors from '../../../../gl_field_errors';
import intervalPatternInput from './components/interval_pattern_input.vue'; import intervalPatternInput from './components/interval_pattern_input.vue';
import TimezoneDropdown from './components/timezone_dropdown'; import TimezoneDropdown from './components/timezone_dropdown';
import TargetBranchDropdown from './components/target_branch_dropdown'; import TargetBranchDropdown from './components/target_branch_dropdown';
import setupNativeFormVariableList from '../ci_variable_list/native_form_variable_list'; import setupNativeFormVariableList from '../../../../ci_variable_list/native_form_variable_list';
Vue.use(Translate); Vue.use(Translate);
...@@ -27,7 +27,7 @@ function initIntervalPatternInput() { ...@@ -27,7 +27,7 @@ function initIntervalPatternInput() {
}); });
} }
document.addEventListener('DOMContentLoaded', () => { export default () => {
/* Most of the form is written in haml, but for fields with more complex behaviors, /* Most of the form is written in haml, but for fields with more complex behaviors,
* you should mount individual Vue components here. If at some point components need * you should mount individual Vue components here. If at some point components need
* to share state, it may make sense to refactor the whole form to Vue */ * to share state, it may make sense to refactor the whole form to Vue */
...@@ -46,4 +46,4 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -46,4 +46,4 @@ document.addEventListener('DOMContentLoaded', () => {
container: $('.js-ci-variable-list-section'), container: $('.js-ci-variable-list-section'),
formField: 'schedule', formField: 'schedule',
}); });
}); };
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
import Chart from 'vendor/Chart';
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
const buildChart = (chartScope) => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
new Chart(ctx).Line(data, options);
};
document.addEventListener('DOMContentLoaded', () => {
const chartTimesData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const data = {
labels: chartTimesData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartTimesData.values,
}],
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options);
chartsData.forEach(scope => buildChart(scope));
});
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
export default () => {
const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init();
if (prometheusSettingsWrapper) {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
}
};
import UserCallout from '~/user_callout';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import UserTabs from './user_tabs'; import UserTabs from './user_tabs';
...@@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => {
const page = $('body').attr('data-page'); const page = $('body').attr('data-page');
const action = page.split(':')[1]; const action = page.split(':')[1];
initUserProfile(action); initUserProfile(action);
new UserCallout(); // eslint-disable-line no-new
}); });
import UserCallout from '~/user_callout';
export default () => new UserCallout();
import axios from '../lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import Activities from '../activities'; import Activities from '~/activities';
import { localTimeAgo } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import flash from '~/flash';
import ActivityCalendar from './activity_calendar'; import ActivityCalendar from './activity_calendar';
import { localTimeAgo } from '../lib/utils/datetime_utility';
import { __ } from '../locale';
import flash from '../flash';
/** /**
* UserTabs * UserTabs
......
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
const buildChart = (chartScope) => {
const data = {
labels: chartScope.labels,
datasets: [{
fillColor: '#707070',
strokeColor: '#707070',
pointColor: '#707070',
pointStrokeColor: '#EEE',
data: chartScope.totalValues,
},
{
fillColor: '#1aaa55',
strokeColor: '#1aaa55',
pointColor: '#1aaa55',
pointStrokeColor: '#fff',
data: chartScope.successValues,
},
],
};
const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Line(data, options);
};
chartData.forEach(scope => buildChart(scope));
});
import Chart from 'vendor/Chart';
document.addEventListener('DOMContentLoaded', () => {
const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
const data = {
labels: chartData.labels,
datasets: [{
fillColor: 'rgba(220,220,220,0.5)',
strokeColor: 'rgba(220,220,220,1)',
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data: chartData.values,
}],
};
const ctx = $('#build_timesChart').get(0).getContext('2d');
const options = {
scaleOverlay: true,
responsive: true,
maintainAspectRatio: false,
};
if (window.innerWidth < 768) {
// Scale fonts if window width lower than 768px (iPad portrait)
options.scaleFontSize = 8;
}
new Chart(ctx).Bar(data, options);
});
import PrometheusMetrics from './prometheus_metrics';
$(() => {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
});
...@@ -44,7 +44,10 @@ ...@@ -44,7 +44,10 @@
type="button" type="button"
class="btn btn-xs btn-default" class="btn btn-xs btn-default"
> >
<loading-icon v-if="isRefreshing" /> <loading-icon
v-if="isRefreshing"
:inline="true"
/>
{{ s__("mrWidget|Refresh") }} {{ s__("mrWidget|Refresh") }}
</button> </button>
</div> </div>
......
...@@ -152,6 +152,7 @@ export default { ...@@ -152,6 +152,7 @@ export default {
}, },
handleNotification(data) { handleNotification(data) {
if (data.ci_status === this.mr.ciStatus) return; if (data.ci_status === this.mr.ciStatus) return;
if (!data.pipeline) return;
const label = data.pipeline.details.status.label; const label = data.pipeline.details.status.label;
const title = `Pipeline ${label}`; const title = `Pipeline ${label}`;
......
<script>
const buttonVariants = [
'danger',
'primary',
'success',
'warning',
];
export default {
name: 'GlModal',
props: {
id: {
type: String,
required: false,
default: null,
},
headerTitleText: {
type: String,
required: false,
default: '',
},
footerPrimaryButtonVariant: {
type: String,
required: false,
default: 'primary',
validator: value => buttonVariants.indexOf(value) !== -1,
},
footerPrimaryButtonText: {
type: String,
required: false,
default: '',
},
},
methods: {
emitCancel(event) {
this.$emit('cancel', event);
},
emitSubmit(event) {
this.$emit('submit', event);
},
},
};
</script>
<template>
<div
:id="id"
class="modal fade"
tabindex="-1"
role="dialog"
>
<div
class="modal-dialog"
role="document"
>
<div class="modal-content">
<div class="modal-header">
<slot name="header">
<button
type="button"
class="close"
data-dismiss="modal"
:aria-label="s__('Modal|Close')"
@click="emitCancel($event)"
>
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">
<slot name="title">
{{ headerTitleText }}
</slot>
</h4>
</slot>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button
type="button"
class="btn"
data-dismiss="modal"
@click="emitCancel($event)"
>
{{ s__('Modal|Cancel') }}
</button>
<button
type="button"
class="btn"
:class="`btn-${footerPrimaryButtonVariant}`"
data-dismiss="modal"
@click="emitSubmit($event)"
>
{{ footerPrimaryButtonText }}
</button>
</slot>
</div>
</div>
</div>
</div>
</template>
...@@ -255,8 +255,6 @@ ul.controls { ...@@ -255,8 +255,6 @@ ul.controls {
} }
.author_link { .author_link {
display: inline-block;
.avatar-inline { .avatar-inline {
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
......
...@@ -7,17 +7,24 @@ module WebpackHelper ...@@ -7,17 +7,24 @@ module WebpackHelper
def webpack_controller_bundle_tags def webpack_controller_bundle_tags
bundles = [] bundles = []
segments = [*controller.controller_path.split('/'), controller.action_name].compact
until segments.empty? action = case controller.action_name
when 'create' then 'new'
when 'update' then 'edit'
else controller.action_name
end
route = [*controller.controller_path.split('/'), action].compact
until route.empty?
begin begin
asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js') asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js')
bundles.unshift(*asset_paths) bundles.unshift(*asset_paths)
rescue Webpack::Rails::Manifest::EntryPointMissingError rescue Webpack::Rails::Manifest::EntryPointMissingError
# no bundle exists for this path # no bundle exists for this path
end end
segments.pop route.pop
end end
javascript_include_tag(*bundles) javascript_include_tag(*bundles)
......
...@@ -41,41 +41,12 @@ module Ci ...@@ -41,41 +41,12 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) } scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) } scope :ignore_failures, ->() { where(allow_failure: false) }
# This convoluted mess is because we need to handle two cases of
# artifact files during the migration. And a simple OR clause
# makes it impossible to optimize.
# Instead we want to use UNION ALL and do two carefully
# constructed disjoint queries. But Rails cannot handle UNION or
# UNION ALL queries so we do the query in a subquery and wrap it
# in an otherwise redundant WHERE IN query (IN is fine for
# non-null columns).
# This should all be ripped out when the migration is finished and
# replaced with just the new storage to avoid the extra work.
scope :with_artifacts, ->() do scope :with_artifacts, ->() do
old = Ci::Build.select(:id).where(%q[artifacts_file <> '']) where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)], '', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id'))
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id'))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
end end
scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_artifacts_not_expired, ->() do scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND (artifacts_expire_at IS NULL OR artifacts_expire_at > ?)], Time.now)
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)],
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND (expire_at IS NULL OR expire_at > ?)', Time.now))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
end
scope :with_expired_artifacts, ->() do
old = Ci::Build.select(:id).where(%q[artifacts_file <> '' AND artifacts_expire_at < ?], Time.now)
new = Ci::Build.select(:id).where(%q[(artifacts_file IS NULL OR artifacts_file = '') AND EXISTS (?)],
Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id AND expire_at < ?', Time.now))
where('ci_builds.id IN (? UNION ALL ?)', old, new)
end
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) } scope :manual_actions, ->() { where(when: :manual, status: COMPLETED_STATUSES + [:manual]) }
scope :ref_protected, -> { where(protected: true) } scope :ref_protected, -> { where(protected: true) }
......
...@@ -9,6 +9,7 @@ class Identity < ActiveRecord::Base ...@@ -9,6 +9,7 @@ class Identity < ActiveRecord::Base
validates :user_id, uniqueness: { scope: :provider } validates :user_id, uniqueness: { scope: :provider }
before_save :ensure_normalized_extern_uid, if: :extern_uid_changed? before_save :ensure_normalized_extern_uid, if: :extern_uid_changed?
after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider?
scope :with_provider, ->(provider) { where(provider: provider) } scope :with_provider, ->(provider) { where(provider: provider) }
scope :with_extern_uid, ->(provider, extern_uid) do scope :with_extern_uid, ->(provider, extern_uid) do
...@@ -34,4 +35,12 @@ class Identity < ActiveRecord::Base ...@@ -34,4 +35,12 @@ class Identity < ActiveRecord::Base
self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid) self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid)
end end
def user_synced_attributes_metadata_from_provider?
user.user_synced_attributes_metadata&.provider == provider
end
def clear_user_synced_attributes
user.user_synced_attributes_metadata&.destroy
end
end end
...@@ -33,9 +33,8 @@ class Key < ActiveRecord::Base ...@@ -33,9 +33,8 @@ class Key < ActiveRecord::Base
after_destroy :refresh_user_cache after_destroy :refresh_user_cache
def key=(value) def key=(value)
value&.delete!("\n\r") write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil)
value.strip! unless value.blank?
write_attribute(:key, value)
@public_key = nil @public_key = nil
end end
...@@ -97,7 +96,7 @@ class Key < ActiveRecord::Base ...@@ -97,7 +96,7 @@ class Key < ActiveRecord::Base
def generate_fingerprint def generate_fingerprint
self.fingerprint = nil self.fingerprint = nil
return unless self.key.present? return unless public_key.valid?
self.fingerprint = public_key.fingerprint self.fingerprint = public_key.fingerprint
end end
......
...@@ -492,12 +492,8 @@ class Repository ...@@ -492,12 +492,8 @@ class Repository
end end
def root_ref def root_ref
if raw_repository # When the repo does not exist, or there is no root ref, we raise this error so no data is cached.
raw_repository.root_ref raw_repository&.root_ref or raise Gitlab::Git::Repository::NoRepository # rubocop:disable Style/AndOr
else
# When the repo does not exist we raise this error so no data is cached.
raise Gitlab::Git::Repository::NoRepository
end
end end
cache_method :root_ref cache_method :root_ref
...@@ -593,7 +589,15 @@ class Repository ...@@ -593,7 +589,15 @@ class Repository
def license_key def license_key
return unless exists? return unless exists?
# The licensee gem creates a Rugged object from the path:
# https://github.com/benbalter/licensee/blob/v8.7.0/lib/licensee/projects/git_project.rb
begin
Licensee.license(path).try(:key) Licensee.license(path).try(:key)
# Normally we would rescue Rugged::Error, but that is banned by lint-rugged
# and we need to migrate this endpoint to Gitaly:
# https://gitlab.com/gitlab-org/gitaly/issues/1026
rescue
end
end end
cache_method :license_key cache_method :license_key
......
...@@ -249,7 +249,7 @@ class User < ActiveRecord::Base ...@@ -249,7 +249,7 @@ class User < ActiveRecord::Base
def find_for_database_authentication(warden_conditions) def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup conditions = warden_conditions.dup
if login = conditions.delete(:login) if login = conditions.delete(:login)
where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase) where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase.strip)
else else
find_by(conditions) find_by(conditions)
end end
......
...@@ -19,19 +19,10 @@ module Issues ...@@ -19,19 +19,10 @@ module Issues
# on rewriting notes (unfolding references) # on rewriting notes (unfolding references)
# #
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
# New issue tasks
#
@new_issue = create_new_issue @new_issue = create_new_issue
rewrite_notes update_new_issue
rewrite_issue_award_emoji update_old_issue
add_note_moved_from
# Old issue tasks
#
add_note_moved_to
close_issue
mark_as_moved
end end
notify_participants notify_participants
...@@ -41,11 +32,24 @@ module Issues ...@@ -41,11 +32,24 @@ module Issues
private private
def update_new_issue
rewrite_notes
rewrite_issue_award_emoji
add_note_moved_from
end
def update_old_issue
add_note_moved_to
close_issue
mark_as_moved
end
def create_new_issue def create_new_issue
new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
milestone_id: cloneable_milestone_id, milestone_id: cloneable_milestone_id,
project: @new_project, author: @old_issue.author, project: @new_project, author: @old_issue.author,
description: rewrite_content(@old_issue.description) } description: rewrite_content(@old_issue.description),
assignee_ids: @old_issue.assignee_ids }
new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params) new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute CreateService.new(@new_project, @current_user, new_params).execute
......
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
- build_path_proc = ->(scope) { admin_jobs_path(scope: scope) } - build_path_proc = ->(scope) { admin_jobs_path(scope: scope) }
= render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope = render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
#stop-jobs-modal #stop-jobs-modal
.nav-controls
%button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal', %button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal',
target: '#stop-jobs-modal', target: '#stop-jobs-modal',
url: cancel_all_admin_jobs_path } } url: cancel_all_admin_jobs_path } }
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
- if @project.avatar? - if @project.avatar?
%hr %hr
= link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted" = link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted"
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings"
%section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) } %section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) }
.settings-header .settings-header
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'schedule_form'
= form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "form-horizontal js-pipeline-schedule-form" } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "form-horizontal js-pipeline-schedule-form" } do |f|
= form_errors(@schedule) = form_errors(@schedule)
.form-group .form-group
......
- breadcrumb_title _("Schedules") - breadcrumb_title _("Schedules")
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'schedules_index'
- @no_container = true - @no_container = true
- page_title _("Pipeline Schedules") - page_title _("Pipeline Schedules")
......
- @no_container = true - @no_container = true
- breadcrumb_title "CI / CD Charts" - breadcrumb_title "CI / CD Charts"
- page_title _("Charts"), _("Pipelines") - page_title _("Charts"), _("Pipelines")
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('common_d3')
= page_specific_javascript_bundle_tag('graphs')
%div{ class: container_class } %div{ class: container_class }
.sub-header-block .sub-header-block
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_times')
%div %div
%p.light %p.light
= _("Commit duration in minutes for last 30 commits") = _("Commit duration in minutes for last 30 commits")
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('pipelines_charts')
%h4= _("Pipelines charts") %h4= _("Pipelines charts")
%p %p
&nbsp; &nbsp;
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('integrations')
.row.prepend-top-default.append-bottom-default .row.prepend-top-default.append-bottom-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
- content_for :page_specific_javascripts do
= webpack_bundle_tag('prometheus_metrics')
.row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring .row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); $(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide(); $('.save-project-loader').hide();
$('.project-edit-container').show(); $('.project-edit-container').show();
$('.edit-project .btn-save').enable(); $('.edit-project .js-btn-save-general-project-settings').enable();
...@@ -23,11 +23,11 @@ ...@@ -23,11 +23,11 @@
- if show_archive_options - if show_archive_options
%li.divider %li.divider
%li.js-filter-archived-projects %li.js-filter-archived-projects
= link_to group_children_path(@group, archived: nil), class: ("is-active" unless params[:archived].present?) do = link_to filter_groups_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
Hide archived projects Hide archived projects
%li.js-filter-archived-projects %li.js-filter-archived-projects
= link_to group_children_path(@group, archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do = link_to filter_groups_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
Show archived projects Show archived projects
%li.js-filter-archived-projects %li.js-filter-archived-projects
= link_to group_children_path(@group, archived: 'only'), class: ("is-active" if params[:archived] == 'only') do = link_to filter_groups_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
Show archived projects only Show archived projects only
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
- page_description @user.bio - page_description @user.bio
- header_title @user.name, user_path(@user) - header_title @user.name, user_path(@user)
- @no_container = true - @no_container = true
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_d3'
= webpack_bundle_tag 'users'
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
......
---
title: Update issue closing pattern to allow variations in punctuation
merge_request: 17198
author: Vicky Chijwani
type: changed
--- ---
title: Fix JIRA not working when a trailing slash is included title: Prevent MR Widget error when no CI configured
merge_request: merge_request:
author: author:
type: fixed type: fixed
---
title: Fix Teleporting Emoji
merge_request: 16963
author: Jared Deckard <jared.deckard@gmail.com>
type: fixed
---
title: "Fix user avatar's vertical align on the issues and merge requests pages"
merge_request: 17072
author: Laszlo Karpati
type: fixed
---
title: Sanitize extra blank spaces used when uploading a SSH key
merge_request: 40552
author:
type: fixed
title: Fix 404 when listing archived projects in a group where all projects have been archived
merge_request: 17077
author: Ashley Dumaine
type: fixed
---
title: API endpoint for importing a project export
merge_request: 17025
author:
type: added
--- ---
title: LDAP Person no longer throws exception on invalid entry title: Remember assignee when moving an issue
merge_request: merge_request:
author: author:
type: fixed type: fixed
---
title: Fix 500 error when loading a merge request with an invalid comment
merge_request: 16795
author:
type: fixed
---
title: Update nokogiri to 1.8.2
merge_request: 16807
author:
type: security
---
title: Fix monaco editor features which were incompatable with GitLab CDN settings
merge_request: 17021
author:
type: fixed
---
title: Fix GitLab import leaving group_id on ProjectLabel
merge_request: 16877
author:
type: fixed
---
title: Hide CI secret variable values after saving
merge_request: 17044
author:
type: changed
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