diff options
Diffstat (limited to 'app')
366 files changed, 1670 insertions, 1731 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index 1f34c6b50c2..464611f66f0 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -9,7 +9,7 @@ const Api = { projectsPath: '/api/:version/projects.json', projectPath: '/api/:version/projects/:id', projectLabelsPath: '/:namespace_path/:project_path/labels', - groupLabelsPath: '/groups/:namespace_path/labels', + groupLabelsPath: '/groups/:namespace_path/-/labels', licensePath: '/api/:version/templates/licenses/:key', gitignorePath: '/api/:version/templates/gitignores/:key', gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key', @@ -32,7 +32,7 @@ const Api = { }, // Return groups list. Filtered by query - groups(query, options, callback) { + groups(query, options, callback = $.noop) { const url = Api.buildUrl(Api.groupsPath); return axios.get(url, { params: Object.assign({ diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 87109a802e5..9456edebccb 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -50,10 +50,8 @@ class AwardsHandler { this.registerEventListener('on', $('html'), 'click', (e) => { const $target = $(e.target); - if (!$target.closest('.emoji-menu-content').length) { - $('.js-awards-block.current').removeClass('current'); - } if (!$target.closest('.emoji-menu').length) { + $('.js-awards-block.current').removeClass('current'); if ($('.emoji-menu').is(':visible')) { $('.js-add-award.is-active').removeClass('is-active'); this.hideMenuElement($('.emoji-menu')); @@ -314,7 +312,7 @@ class AwardsHandler { } getAwardUrl() { - return this.getVotesBlock().data('award-url'); + return this.getVotesBlock().data('awardUrl'); } checkMutuality(votesBlock, emoji) { diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js index cdea625fc8c..b669b63d23c 100644 --- a/app/assets/javascripts/behaviors/copy_to_clipboard.js +++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js @@ -2,7 +2,7 @@ import Clipboard from 'clipboard'; function showTooltip(target, title) { const $target = $(target); - const originalTitle = $target.data('original-title'); + const originalTitle = $target.data('originalTitle'); if (!$target.data('hideTooltip')) { $target diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js index 2cf8f4fa935..312edc0cd69 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js +++ b/app/assets/javascripts/behaviors/quick_submit.js @@ -43,7 +43,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => { const $form = $(e.target).closest('form'); const $submitButton = $form.find('input[type=submit], button[type=submit]').first(); - if (!$submitButton.attr('disabled')) { + if (!$submitButton.prop('disabled')) { $submitButton.trigger('click', [e]); if (!isInIssuePage()) { diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js index 035a7e5c431..e10cb2e3dc4 100644 --- a/app/assets/javascripts/behaviors/requires_input.js +++ b/app/assets/javascripts/behaviors/requires_input.js @@ -40,7 +40,7 @@ $.fn.requiresInput = function requiresInput() { // based on the option selected function hideOrShowHelpBlock(form) { const selected = $('.js-select-namespace option:selected'); - if (selected.length && selected.data('options-parent') === 'groups') { + if (selected.length && selected.data('optionsParent') === 'groups') { form.find('.help-block').hide(); } else if (selected.length) { form.find('.help-block').show(); diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js index 6b06344f5ba..931ed042dfd 100644 --- a/app/assets/javascripts/blob_edit/blob_bundle.js +++ b/app/assets/javascripts/blob_edit/blob_bundle.js @@ -4,16 +4,16 @@ import NewCommitForm from '../new_commit_form'; import EditBlob from './edit_blob'; import BlobFileDropzone from '../blob/blob_file_dropzone'; -$(() => { +export default () => { const editBlobForm = $('.js-edit-blob-form'); const uploadBlobForm = $('.js-upload-blob-form'); const deleteBlobForm = $('.js-delete-blob-form'); if (editBlobForm.length) { - const urlRoot = editBlobForm.data('relative-url-root'); - const assetsPath = editBlobForm.data('assets-prefix'); - const blobLanguage = editBlobForm.data('blob-language'); - const currentAction = $('.js-file-title').data('current-action'); + const urlRoot = editBlobForm.data('relativeUrlRoot'); + const assetsPath = editBlobForm.data('assetsPrefix'); + const blobLanguage = editBlobForm.data('blobLanguage'); + const currentAction = $('.js-file-title').data('currentAction'); new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction); new NewCommitForm(editBlobForm); @@ -34,4 +34,4 @@ $(() => { if (deleteBlobForm.length) { new NewCommitForm(deleteBlobForm); } -}); +}; diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js index a25f7fb3dcd..d4f6adaccbc 100644 --- a/app/assets/javascripts/blob_edit/edit_blob.js +++ b/app/assets/javascripts/blob_edit/edit_blob.js @@ -59,7 +59,7 @@ export default class EditBlob { if (paneId === '#preview') { this.$toggleButton.hide(); - axios.post(currentLink.data('preview-url'), { + axios.post(currentLink.data('previewUrl'), { content: this.editor.getValue(), }) .then(({ data }) => { diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js index cf0bb5f5376..362ef43e6f7 100644 --- a/app/assets/javascripts/boards/components/new_list_dropdown.js +++ b/app/assets/javascripts/boards/components/new_list_dropdown.js @@ -25,7 +25,7 @@ $(document).off('created.label').on('created.label', (e, label) => { gl.issueBoards.newListDropdownInit = () => { $('.js-new-board-list').each(function () { const $this = $(this); - new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path')); + new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath')); $this.glDropdown({ data(term, callback) { diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js index 184665f395c..0df1f7a6f82 100644 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ b/app/assets/javascripts/boards/filtered_search_boards.js @@ -1,7 +1,8 @@ /* eslint-disable class-methods-use-this */ import FilteredSearchContainer from '../filtered_search/container'; +import FilteredSearchManager from '../filtered_search/filtered_search_manager'; -export default class FilteredSearchBoards extends gl.FilteredSearchManager { +export default class FilteredSearchBoards extends FilteredSearchManager { constructor(store, updateUrl = false, cantEdit = []) { super('boards'); diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js index 76f93e5c6bd..b33adff609f 100644 --- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js @@ -75,6 +75,7 @@ export default class AjaxVariableList { if (res.status === statusCodes.OK && res.data) { this.updateRowsWithPersistedVariables(res.data.variables); + this.variableList.hideValues(); } else if (res.status === statusCodes.BAD_REQUEST) { // Validation failed this.errorBox.innerHTML = generateErrorBoxContent(res.data); diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js index 3467e88119b..745f3404295 100644 --- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js +++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js @@ -178,6 +178,10 @@ export default class VariableList { this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); } + hideValues() { + this.secretValues.updateDom(false); + } + getAllData() { // Ignore the last empty row because we don't want to try persist // a blank variable and run into validation problems. diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index da0e8063ccb..ce19069f103 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -7,7 +7,6 @@ mixins: [ pipelinesMixin, ], - props: { endpoint: { type: String, diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 4b2f75fffde..2be63bd8c76 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -1,52 +1,36 @@ -/* 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 { localTimeAgo } from './lib/utils/datetime_utility'; import Pager from './pager'; import axios from './lib/utils/axios_utils'; -export default (function () { - const CommitsList = {}; - - CommitsList.timer = null; +export default class CommitsList { + constructor(limit = 0) { + this.timer = null; - CommitsList.init = function (limit) { this.$contentList = $('.content_list'); - $('body').on('click', '.day-commits-table li.commit', function (e) { - if (e.target.nodeName !== 'A') { - location.href = $(this).attr('url'); - e.stopPropagation(); - return false; - } - }); - - Pager.init(parseInt(limit, 10), false, false, this.processCommits); + Pager.init(parseInt(limit, 10), false, false, this.processCommits.bind(this)); this.content = $('#commits-list'); this.searchField = $('#commits-search'); this.lastSearch = this.searchField.val(); - return this.initSearch(); - }; + this.initSearch(); + } - CommitsList.initSearch = function () { + initSearch() { this.timer = null; - return this.searchField.keyup((function (_this) { - return function () { - clearTimeout(_this.timer); - return _this.timer = setTimeout(_this.filterResults, 500); - }; - })(this)); - }; + this.searchField.on('keyup', () => { + clearTimeout(this.timer); + this.timer = setTimeout(this.filterResults.bind(this), 500); + }); + } - CommitsList.filterResults = function () { + filterResults() { const form = $('.commits-search-form'); - const search = CommitsList.searchField.val(); - if (search === CommitsList.lastSearch) return Promise.resolve(); - const commitsUrl = form.attr('action') + '?' + form.serialize(); - CommitsList.content.fadeTo('fast', 0.5); + const search = this.searchField.val(); + if (search === this.lastSearch) return Promise.resolve(); + const commitsUrl = `${form.attr('action')}?${form.serialize()}`; + this.content.fadeTo('fast', 0.5); const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, { [obj.name]: obj.value, }), {}); @@ -55,9 +39,9 @@ export default (function () { params, }) .then(({ data }) => { - CommitsList.lastSearch = search; - CommitsList.content.html(data.html); - CommitsList.content.fadeTo('fast', 1.0); + this.lastSearch = search; + this.content.html(data.html); + this.content.fadeTo('fast', 1.0); // Change url so if user reload a page - search results are saved history.replaceState({ @@ -65,16 +49,16 @@ export default (function () { }, document.title, commitsUrl); }) .catch(() => { - CommitsList.content.fadeTo('fast', 1.0); - CommitsList.lastSearch = null; + this.content.fadeTo('fast', 1.0); + this.lastSearch = null; }); - }; + } // Prepare loaded data. - CommitsList.processCommits = (data) => { + processCommits(data) { let processedData = data; 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 $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first(); const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day'); @@ -97,7 +81,5 @@ export default (function () { localTimeAgo($processedData.find('.js-timeago')); return processedData; - }; - - return CommitsList; -})(); + } +} diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js index c11b7d5f340..db96da4ccba 100644 --- a/app/assets/javascripts/commons/bootstrap.js +++ b/app/assets/javascripts/commons/bootstrap.js @@ -13,6 +13,6 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; // custom jQuery functions $.fn.extend({ - disable() { return $(this).attr('disabled', 'disabled').addClass('disabled'); }, - enable() { return $(this).removeAttr('disabled').removeClass('disabled'); }, + disable() { return $(this).prop('disabled', true).addClass('disabled'); }, + enable() { return $(this).prop('disabled', false).removeClass('disabled'); }, }); diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index e2a008e8904..d5a35ed81a6 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -13,7 +13,7 @@ export default class Compare { $dropdown = $(dropdown); return $dropdown.glDropdown({ selectable: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), filterable: true, id: function(obj, $el) { return $el.data('id'); diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js index 59899e97be1..fa341918fc1 100644 --- a/app/assets/javascripts/compare_autocomplete.js +++ b/app/assets/javascripts/compare_autocomplete.js @@ -9,7 +9,7 @@ export default function initCompareAutocomplete() { $dropdown = $(this); selected = $dropdown.data('selected'); const $dropdownContainer = $dropdown.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer); + const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer); $dropdown.glDropdown({ data: function(term, callback) { @@ -25,7 +25,7 @@ export default function initCompareAutocomplete() { selectable: true, filterable: true, filterRemote: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), filterInput: 'input[type="search"]', renderRow: function(ref) { var link; diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue index 39b699a6395..34aa04083e6 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -37,7 +37,7 @@ > <div class="item-details"> <!-- FIXME: Pass an alt attribute here for accessibility --> - <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/> + <user-avatar-image :img-src="mergeRequest.author.avatarUrl" /> <h5 class="item-title merge-merquest-title"> <a :href="mergeRequest.url"> {{ mergeRequest.title }} diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js index 3ab8f3ab7ad..3df082e8c0c 100644 --- a/app/assets/javascripts/diff.js +++ b/app/assets/javascripts/diff.js @@ -68,7 +68,7 @@ export default class Diff { } const file = $target.parents('.diff-file'); - const link = file.data('blob-diff-path'); + const link = file.data('blobDiffPath'); const view = file.data('view'); const params = { since, to, bottom, offset, unfold, view }; @@ -121,7 +121,7 @@ export default class Diff { } // eslint-disable-next-line class-methods-use-this diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); + return $('.inline-parallel-buttons a.active').data('viewType'); } // eslint-disable-next-line class-methods-use-this lineNumbers(line) { diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js index e0422057090..679057e787c 100644 --- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js +++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js @@ -15,7 +15,7 @@ import './components/resolve_discussion_btn'; import './components/diff_note_avatars'; import './components/new_issue_for_discussion'; -$(() => { +export default () => { const projectPathHolder = document.querySelector('.merge-request') || document.querySelector('.commit-box'); const projectPath = projectPathHolder.dataset.projectPath; const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn'; @@ -71,8 +71,8 @@ $(() => { el: '#resolve-count-app', components: { 'resolve-count': ResolveCount - } + }, }); $(window).trigger('resize.nav'); -}); +}; diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f8082c74943..80ee41d3bbb 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -43,340 +43,24 @@ var Dispatcher; }); switch (page) { - case 'projects:environments:metrics': - import('./pages/projects/environments/metrics') - .then(callDefault) - .catch(fail); - break; case 'projects:merge_requests:index': case 'projects:issues:index': 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': - import('./pages/projects/issues/new') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:issues:edit': - import('./pages/projects/issues/edit') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:merge_requests:creations:new': - import('./pages/projects/merge_requests/creations/new') - .then(callDefault) - .catch(fail); 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': - import('./pages/projects/merge_requests/edit') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'projects:tags:new': - import('./pages/projects/tags/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:show': - import('./pages/projects/snippets/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:new': - case 'projects:snippets:create': - import('./pages/projects/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:snippets:edit': - case 'projects:snippets:update': - import('./pages/projects/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'snippets:new': - import('./pages/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'snippets:edit': - import('./pages/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'snippets:create': - import('./pages/snippets/new') - .then(callDefault) - .catch(fail); - break; - case 'snippets:update': - import('./pages/snippets/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:releases:edit': - import('./pages/projects/releases/edit') - .then(callDefault) - .catch(fail); - break; case 'projects:merge_requests:show': - import('./pages/projects/merge_requests/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'dashboard:activity': - import('./pages/dashboard/activity') - .then(callDefault) - .catch(fail); - break; case 'projects:commit:show': - import('./pages/projects/commit/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; - case 'projects:commit:pipelines': - import('./pages/projects/commit/pipelines') - .then(callDefault) - .catch(fail); - break; case 'projects:activity': - import('./pages/projects/activity') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:commits:show': - import('./pages/projects/commits/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:show': - shortcut_handler = true; - break; - case 'projects:edit': - import('./pages/projects/edit') - .then(callDefault) - .catch(fail); - break; - case 'projects:imports:show': - import('./pages/projects/imports/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:pipelines:new': - case 'projects:pipelines:create': - import('./pages/projects/pipelines/new') - .then(callDefault) - .catch(fail); - break; - case 'projects:pipelines:builds': - case 'projects:pipelines:failures': - case 'projects:pipelines:show': - import('./pages/projects/pipelines/builds') - .then(callDefault) - .catch(fail); - break; - case 'groups:activity': - import('./pages/groups/activity') - .then(callDefault) - .catch(fail); - break; case 'groups:show': - shortcut_handler = true; - break; - case 'groups:group_members:index': - import('./pages/groups/group_members/index') - .then(callDefault) - .catch(fail); - break; - case 'projects:project_members:index': - import('./pages/projects/project_members') - .then(callDefault) - .catch(fail); - break; - case 'groups:create': - case 'groups:new': - import('./pages/groups/new') - .then(callDefault) - .catch(fail); - break; - case 'groups:edit': - import('./pages/groups/edit') - .then(callDefault) - .catch(fail); - break; - case 'admin:groups:create': - case 'admin:groups:new': - import('./pages/admin/groups/new') - .then(callDefault) - .catch(fail); - break; - case 'admin:groups:edit': - import('./pages/admin/groups/edit') - .then(callDefault) - .catch(fail); - break; case 'projects:tree:show': - import('./pages/projects/tree/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:find_file:show': - import('./pages/projects/find_file/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:blob:show': - import('./pages/projects/blob/show') - .then(callDefault) - .catch(fail); - shortcut_handler = true; - break; case 'projects:blame:show': - import('./pages/projects/blame/show') - .then(callDefault) - .catch(fail); shortcut_handler = true; break; case 'groups:labels:new': @@ -431,11 +115,6 @@ var Dispatcher; .catch(fail); shortcut_handler = true; break; - case 'help:index': - import('./pages/help') - .then(callDefault) - .catch(fail); - break; case 'search:show': import('./pages/search/show') .then(callDefault) @@ -462,21 +141,11 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'users:show': - import('./pages/users/show') - .then(callDefault) - .catch(fail); - break; case 'admin:conversational_development_index:show': import('./pages/admin/conversational_development_index/show') .then(callDefault) .catch(fail); break; - case 'snippets:show': - import('./pages/snippets/show') - .then(callDefault) - .catch(fail); - break; case 'import:fogbugz:new_user_map': import('./pages/import/fogbugz/new_user_map') .then(callDefault) @@ -492,18 +161,6 @@ var Dispatcher; .then(callDefault) .catch(fail); break; - case 'projects:clusters:show': - case 'projects:clusters:update': - case 'projects:clusters:destroy': - import('./pages/projects/clusters/show') - .then(callDefault) - .catch(fail); - break; - case 'projects:clusters:index': - import('./pages/projects/clusters/index') - .then(callDefault) - .catch(fail); - break; case 'dashboard:groups:index': import('./pages/dashboard/groups/index') .then(callDefault) @@ -511,20 +168,7 @@ var Dispatcher; break; } switch (path[0]) { - case 'sessions': - import('./pages/sessions') - .then(callDefault) - .catch(fail); - break; - case 'omniauth_callbacks': - import('./pages/omniauth_callbacks') - .then(callDefault) - .catch(fail); - break; case 'admin': - import('./pages/admin') - .then(callDefault) - .catch(fail); switch (path[1]) { case 'broadcast_messages': import('./pages/admin/broadcast_messages') diff --git a/app/assets/javascripts/docs/docs_bundle.js b/app/assets/javascripts/docs/docs_bundle.js index a32bd6d0fc7..897439f56b0 100644 --- a/app/assets/javascripts/docs/docs_bundle.js +++ b/app/assets/javascripts/docs/docs_bundle.js @@ -4,10 +4,7 @@ function addMousetrapClick(el, key) { el.addEventListener('click', () => Mousetrap.trigger(key)); } -function domContentLoaded() { +export default () => { addMousetrapClick(document.querySelector('.js-trigger-shortcut'), '?'); addMousetrapClick(document.querySelector('.js-trigger-search-bar'), 's'); -} - -document.addEventListener('DOMContentLoaded', domContentLoaded); - +}; diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index bd4c58b7cb1..417258e0092 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -17,9 +17,9 @@ class DueDateSelect { this.$value = $block.find('.value'); this.$valueContent = $block.find('.value-content'); this.$sidebarValue = $('.js-due-date-sidebar-value', $block); - this.fieldName = $dropdown.data('field-name'); - this.abilityName = $dropdown.data('ability-name'); - this.issueUpdateURL = $dropdown.data('issue-update'); + this.fieldName = $dropdown.data('fieldName'); + this.abilityName = $dropdown.data('abilityName'); + this.issueUpdateURL = $dropdown.data('issueUpdate'); this.rawSelectedDate = null; this.displayedDate = null; diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js index 90020344748..6a4874e1ab8 100644 --- a/app/assets/javascripts/files_comment_button.js +++ b/app/assets/javascripts/files_comment_button.js @@ -25,7 +25,7 @@ export default { if (!this.userCanCreateNote) { // data-can-create-note is an empty string when true, otherwise undefined - this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === ''; + this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === ''; } this.isParallelView = Cookies.get('diff_view') === 'parallel'; diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js index c51d4b056af..b693084e434 100644 --- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js +++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js @@ -1,4 +1,5 @@ import eventHub from '../event_hub'; +import FilteredSearchTokenizer from '../filtered_search_tokenizer'; export default { name: 'RecentSearchesDropdownContent', @@ -23,7 +24,7 @@ export default { processedItems() { return this.items.map((item) => { const { tokens, searchToken } - = gl.FilteredSearchTokenizer.processTokens(item, this.allowedKeys); + = FilteredSearchTokenizer.processTokens(item, this.allowedKeys); const resultantTokens = tokens.map(token => ({ prefix: `${token.key}:`, diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js index a6cc079d720..5ddd0e5e690 100644 --- a/app/assets/javascripts/filtered_search/dropdown_emoji.js +++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js @@ -1,9 +1,10 @@ import Flash from '../flash'; import Ajax from '../droplab/plugins/ajax'; import Filter from '../droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; -class DropdownEmoji extends gl.FilteredSearchDropdown { +export default class DropdownEmoji extends FilteredSearchDropdown { constructor(options = {}) { super(options); this.config = { @@ -49,7 +50,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { itemClicked(e) { super.itemClicked(e, (selected) => { const name = selected.querySelector('.js-data-value').innerText.trim(); - return gl.DropdownUtils.getEscapedText(name); + return DropdownUtils.getEscapedText(name); }); } @@ -76,6 +77,3 @@ class DropdownEmoji extends gl.FilteredSearchDropdown { .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownEmoji = DropdownEmoji; diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 23040cd9eb8..184b34b7b5e 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -1,14 +1,17 @@ import Filter from '~/droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; -class DropdownHint extends gl.FilteredSearchDropdown { +export default class DropdownHint extends FilteredSearchDropdown { constructor(options = {}) { const { input, tokenKeys } = options; super(options); this.config = { Filter: { template: 'hint', - filterFunction: gl.DropdownUtils.filterHint.bind(null, { + filterFunction: DropdownUtils.filterHint.bind(null, { input, allowedKeys: tokenKeys.getKeys(), }), @@ -45,10 +48,10 @@ class DropdownHint extends gl.FilteredSearchDropdown { }); if (searchTerms.length > 0) { - gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); + FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); } - gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); + FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); } this.dismissDropdown(); this.dispatchInputEvent(); @@ -73,6 +76,3 @@ class DropdownHint extends gl.FilteredSearchDropdown { this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownHint = DropdownHint; diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js index 788fb1dc614..2ffda7e2037 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js @@ -1,9 +1,10 @@ import Flash from '../flash'; import Ajax from '../droplab/plugins/ajax'; import Filter from '../droplab/plugins/filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; +import DropdownUtils from './dropdown_utils'; -class DropdownNonUser extends gl.FilteredSearchDropdown { +export default class DropdownNonUser extends FilteredSearchDropdown { constructor(options = {}) { const { input, endpoint, symbol, preprocessing } = options; super(options); @@ -21,7 +22,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { }, }, Filter: { - filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input), + filterFunction: DropdownUtils.filterWithSymbol.bind(null, this.symbol, input), template: 'title', }, }; @@ -30,7 +31,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { itemClicked(e) { super.itemClicked(e, (selected) => { const title = selected.querySelector('.js-data-value').innerText.trim(); - return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`; + return `${this.symbol}${DropdownUtils.getEscapedText(title)}`; }); } @@ -45,6 +46,3 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownNonUser = DropdownNonUser; diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js index a9e2b65def0..22421fc4868 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js +++ b/app/assets/javascripts/filtered_search/dropdown_user.js @@ -1,9 +1,11 @@ import Flash from '../flash'; import AjaxFilter from '../droplab/plugins/ajax_filter'; -import './filtered_search_dropdown'; +import FilteredSearchDropdown from './filtered_search_dropdown'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; +import DropdownUtils from './dropdown_utils'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; -class DropdownUser extends gl.FilteredSearchDropdown { +export default class DropdownUser extends FilteredSearchDropdown { constructor(options = {}) { const { tokenKeys } = options; super(options); @@ -56,8 +58,8 @@ class DropdownUser extends gl.FilteredSearchDropdown { } getSearchInput() { - const query = gl.DropdownUtils.getSearchInput(this.input); - const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get()); + const query = DropdownUtils.getSearchInput(this.input); + const { lastToken } = FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get()); let value = lastToken || ''; @@ -78,6 +80,3 @@ class DropdownUser extends gl.FilteredSearchDropdown { this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init(); } } - -window.gl = window.gl || {}; -gl.DropdownUser = DropdownUser; diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index cf8a9b0402b..9bc36c1f9b6 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -1,7 +1,10 @@ import _ from 'underscore'; import FilteredSearchContainer from './container'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; -class DropdownUtils { +export default class DropdownUtils { static getEscapedText(text) { let escapedText = text; const hasSpace = text.indexOf(' ') !== -1; @@ -24,7 +27,7 @@ class DropdownUtils { static filterWithSymbol(filterSymbol, input, item) { const updatedItem = item; - const searchInput = gl.DropdownUtils.getSearchInput(input); + const searchInput = DropdownUtils.getSearchInput(input); const title = updatedItem.title.toLowerCase(); let value = searchInput.toLowerCase(); @@ -114,9 +117,9 @@ class DropdownUtils { static filterHint(config, item) { const { input, allowedKeys } = config; const updatedItem = item; - const searchInput = gl.DropdownUtils.getSearchQuery(input); + const searchInput = DropdownUtils.getSearchQuery(input); const { lastToken, tokens } = - gl.FilteredSearchTokenizer.processTokens(searchInput, allowedKeys); + FilteredSearchTokenizer.processTokens(searchInput, allowedKeys); const lastKey = lastToken.key || lastToken || ''; const allowMultiple = item.type === 'array'; const itemInExistingTokens = tokens.some(t => t.key === item.hint); @@ -140,7 +143,7 @@ class DropdownUtils { const dataValue = selected.getAttribute('data-value'); if (dataValue) { - gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true); + FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true); } // Return boolean based on whether it was set @@ -190,7 +193,7 @@ class DropdownUtils { } } else if (token.classList.contains('input-token')) { const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); const input = FilteredSearchContainer.container.querySelector('.filtered-search'); const inputValue = input && input.value; @@ -211,7 +214,7 @@ class DropdownUtils { static getSearchInput(filteredSearchInput) { const inputValue = filteredSearchInput.value; - const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput); + const { right } = DropdownUtils.getInputSelectionPosition(filteredSearchInput); return inputValue.slice(0, right); } @@ -252,6 +255,3 @@ class DropdownUtils { }; } } - -window.gl = window.gl || {}; -gl.DropdownUtils = DropdownUtils; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js index 9e9a9ef74be..fb4ae1d17dd 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js @@ -1,6 +1,9 @@ +import DropdownUtils from './dropdown_utils'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; + const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; -class FilteredSearchDropdown { +export default class FilteredSearchDropdown { constructor({ droplab, dropdown, input, filter }) { this.droplab = droplab; this.hookId = input && input.id; @@ -30,11 +33,11 @@ class FilteredSearchDropdown { const { selected } = e.detail; if (selected.tagName === 'LI' && selected.innerHTML) { - const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected); + const dataValueSet = DropdownUtils.setDataValueIfSelected(this.filter, selected); if (!dataValueSet) { const value = getValueFunction(selected); - gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); + FilteredSearchDropdownManager.addWordToInput(this.filter, value, true); } this.resetFilters(); @@ -108,6 +111,9 @@ class FilteredSearchDropdown { if (hook) { const data = hook.list.data || []; + + if (!data) return; + const results = data.map((o) => { const updated = o; updated.droplab_hidden = false; @@ -117,6 +123,3 @@ class FilteredSearchDropdown { } } } - -window.gl = window.gl || {}; -gl.FilteredSearchDropdown = FilteredSearchDropdown; diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index b2add862051..c64553a1b92 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -1,13 +1,20 @@ import _ from 'underscore'; import DropLab from '~/droplab/drop_lab'; import FilteredSearchContainer from './container'; - -class FilteredSearchDropdownManager { +import FilteredSearchTokenKeys from './filtered_search_token_keys'; +import DropdownUtils from './dropdown_utils'; +import DropdownHint from './dropdown_hint'; +import DropdownEmoji from './dropdown_emoji'; +import DropdownNonUser from './dropdown_non_user'; +import DropdownUser from './dropdown_user'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; + +export default class FilteredSearchDropdownManager { constructor(baseEndpoint = '', tokenizer, page, isGroup, filteredSearchTokenKeys) { this.container = FilteredSearchContainer.container; this.baseEndpoint = baseEndpoint.replace(/\/$/, ''); this.tokenizer = tokenizer; - this.filteredSearchTokenKeys = filteredSearchTokenKeys; + this.filteredSearchTokenKeys = filteredSearchTokenKeys || FilteredSearchTokenKeys; this.filteredSearchInput = this.container.querySelector('.filtered-search'); this.page = page; @@ -33,24 +40,24 @@ class FilteredSearchDropdownManager { const allowedMappings = { hint: { reference: null, - gl: 'DropdownHint', + gl: DropdownHint, element: this.container.querySelector('#js-dropdown-hint'), }, }; const availableMappings = { author: { reference: null, - gl: 'DropdownUser', + gl: DropdownUser, element: this.container.querySelector('#js-dropdown-author'), }, assignee: { reference: null, - gl: 'DropdownUser', + gl: DropdownUser, element: this.container.querySelector('#js-dropdown-assignee'), }, milestone: { reference: null, - gl: 'DropdownNonUser', + gl: DropdownNonUser, extraArguments: { endpoint: `${this.baseEndpoint}/milestones.json`, symbol: '%', @@ -59,17 +66,17 @@ class FilteredSearchDropdownManager { }, label: { reference: null, - gl: 'DropdownNonUser', + gl: DropdownNonUser, extraArguments: { endpoint: `${this.baseEndpoint}/labels.json`, symbol: '~', - preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing, + preprocessing: DropdownUtils.duplicateLabelPreprocessing, }, element: this.container.querySelector('#js-dropdown-label'), }, 'my-reaction': { reference: null, - gl: 'DropdownEmoji', + gl: DropdownEmoji, element: this.container.querySelector('#js-dropdown-my-reaction'), }, }; @@ -86,11 +93,11 @@ class FilteredSearchDropdownManager { static addWordToInput(tokenName, tokenValue = '', clicked = false) { const input = FilteredSearchContainer.container.querySelector('.filtered-search'); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue); input.value = ''; if (clicked) { - gl.FilteredSearchVisualTokens.moveInputToTheRight(); + FilteredSearchVisualTokens.moveInputToTheRight(); } } @@ -131,9 +138,9 @@ class FilteredSearchDropdownManager { const extraArguments = mappingKey.extraArguments || {}; const glArguments = Object.assign({}, defaultArguments, extraArguments); - // Passing glArguments to `new gl[glClass](<arguments>)` + // Passing glArguments to `new glClass(<arguments>)` mappingKey.reference = - new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))(); + new (Function.prototype.bind.apply(glClass, [null, glArguments]))(); } if (firstLoad) { @@ -171,7 +178,7 @@ class FilteredSearchDropdownManager { } setDropdown() { - const query = gl.DropdownUtils.getSearchQuery(true); + const query = DropdownUtils.getSearchQuery(true); const { lastToken, searchToken } = this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys()); @@ -216,6 +223,3 @@ class FilteredSearchDropdownManager { this.droplab.destroy(); } } - -window.gl = window.gl || {}; -gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager; diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index 532a5fe1090..e294b629bd0 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -1,15 +1,23 @@ import _ from 'underscore'; +import { + getParameterByName, + getUrlParamsArray, +} from '~/lib/utils/common_utils'; import { visitUrl } from '../lib/utils/url_utility'; import Flash from '../flash'; import FilteredSearchContainer from './container'; -import RecentSearchesRoot from './recent_searches_root'; import FilteredSearchTokenKeys from './filtered_search_token_keys'; +import RecentSearchesRoot from './recent_searches_root'; import RecentSearchesStore from './stores/recent_searches_store'; import RecentSearchesService from './services/recent_searches_service'; import eventHub from './event_hub'; import { addClassIfElementExists } from '../lib/utils/dom_utils'; +import FilteredSearchTokenizer from './filtered_search_tokenizer'; +import FilteredSearchDropdownManager from './filtered_search_dropdown_manager'; +import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; +import DropdownUtils from './dropdown_utils'; -class FilteredSearchManager { +export default class FilteredSearchManager { constructor({ page, filteredSearchTokenKeys = FilteredSearchTokenKeys, @@ -66,8 +74,8 @@ class FilteredSearchManager { }); if (this.filteredSearchInput) { - this.tokenizer = gl.FilteredSearchTokenizer; - this.dropdownManager = new gl.FilteredSearchDropdownManager( + this.tokenizer = FilteredSearchTokenizer; + this.dropdownManager = new FilteredSearchDropdownManager( this.filteredSearchInput.getAttribute('data-base-endpoint') || '', this.tokenizer, this.page, @@ -85,7 +93,6 @@ class FilteredSearchManager { this.bindEvents(); this.loadSearchParamsFromURL(); this.dropdownManager.setDropdown(); - this.cleanupWrapper = this.cleanup.bind(this); document.addEventListener('beforeunload', this.cleanupWrapper); } @@ -197,8 +204,8 @@ class FilteredSearchManager { // 8 = Backspace Key // 46 = Delete Key if (e.keyCode === 8 || e.keyCode === 46) { - const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); - const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken); + const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(lastVisualToken); const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue); if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) { @@ -206,8 +213,8 @@ class FilteredSearchManager { if (backspaceCount === 2) { backspaceCount = 0; - this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial(); - gl.FilteredSearchVisualTokens.removeLastTokenPartial(); + this.filteredSearchInput.value = FilteredSearchVisualTokens.getLastTokenPartial(); + FilteredSearchVisualTokens.removeLastTokenPartial(); } } @@ -275,7 +282,7 @@ class FilteredSearchManager { e.stopImmediatePropagation(); const button = e.target.closest('.selectable'); - gl.FilteredSearchVisualTokens.selectToken(button, true); + FilteredSearchVisualTokens.selectToken(button, true); this.removeSelectedToken(); } } @@ -287,7 +294,7 @@ class FilteredSearchManager { const isElementTokensContainer = e.target.classList.contains('tokens-container'); if ((!isElementInFilteredSearch && !isElementInFilterDropdown) || isElementTokensContainer) { - gl.FilteredSearchVisualTokens.moveInputToTheRight(); + FilteredSearchVisualTokens.moveInputToTheRight(); this.dropdownManager.resetDropdowns(); } } @@ -300,13 +307,13 @@ class FilteredSearchManager { if (token && canEdit) { e.preventDefault(); e.stopPropagation(); - gl.FilteredSearchVisualTokens.editToken(token); + FilteredSearchVisualTokens.editToken(token); this.tokenChange(); } } toggleClearSearchButton() { - const query = gl.DropdownUtils.getSearchQuery(); + const query = DropdownUtils.getSearchQuery(); const hidden = 'hidden'; const hasHidden = this.clearSearchButton.classList.contains(hidden); @@ -318,7 +325,7 @@ class FilteredSearchManager { } handleInputPlaceholder() { - const query = gl.DropdownUtils.getSearchQuery(); + const query = DropdownUtils.getSearchQuery(); const placeholder = 'Search or filter results...'; const currentPlaceholder = this.filteredSearchInput.placeholder; @@ -338,7 +345,7 @@ class FilteredSearchManager { } removeSelectedToken() { - gl.FilteredSearchVisualTokens.removeSelectedToken(); + FilteredSearchVisualTokens.removeSelectedToken(); this.handleInputPlaceholder(); this.toggleClearSearchButton(); this.dropdownManager.updateCurrentDropdownOffset(); @@ -358,7 +365,7 @@ class FilteredSearchManager { let canClearToken = t.classList.contains('js-visual-token'); if (canClearToken) { - const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(t); + const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(t); canClearToken = this.canEdit && this.canEdit(tokenName, tokenValue); } @@ -386,12 +393,12 @@ class FilteredSearchManager { const { tokens, searchToken } = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys()); const { isLastVisualTokenValid } - = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (isLastVisualTokenValid) { tokens.forEach((t) => { input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, ''); - gl.FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`); + FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`); }); const fragments = searchToken.split(':'); @@ -404,10 +411,10 @@ class FilteredSearchManager { const searchTerms = inputValues.join(' '); input.value = input.value.replace(searchTerms, ''); - gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms); + FilteredSearchVisualTokens.addSearchVisualToken(searchTerms); } - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenKey); + FilteredSearchVisualTokens.addFilterVisualToken(tokenKey); input.value = input.value.replace(`${tokenKey}:`, ''); } } else { @@ -415,7 +422,7 @@ class FilteredSearchManager { const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g; if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') { - gl.FilteredSearchVisualTokens.addFilterVisualToken(searchToken); + FilteredSearchVisualTokens.addFilterVisualToken(searchToken); // Trim the last space as seen in the if statement above input.value = input.value.replace(searchToken, '').trim(); @@ -431,7 +438,7 @@ class FilteredSearchManager { saveCurrentSearchQuery() { // Don't save before we have fetched the already saved searches this.fetchingRecentSearchesPromise.then(() => { - const searchQuery = gl.DropdownUtils.getSearchQuery(); + const searchQuery = DropdownUtils.getSearchQuery(); if (searchQuery.length > 0) { const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery); this.recentSearchesService.save(resultantSearches); @@ -447,7 +454,7 @@ class FilteredSearchManager { } loadSearchParamsFromURL() { - const urlParams = gl.utils.getUrlParamsArray(); + const urlParams = getUrlParamsArray(); const params = this.getAllParams(urlParams); const usernameParams = this.getUsernameParams(); let hasFilteredSearch = false; @@ -463,7 +470,7 @@ class FilteredSearchManager { if (condition) { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(condition.tokenKey); - gl.FilteredSearchVisualTokens.addFilterVisualToken( + FilteredSearchVisualTokens.addFilterVisualToken( condition.tokenKey, condition.value, canEdit, @@ -492,7 +499,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue); - gl.FilteredSearchVisualTokens.addFilterVisualToken( + FilteredSearchVisualTokens.addFilterVisualToken( sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`, canEdit, @@ -503,7 +510,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'assignee'; const canEdit = this.canEdit && this.canEdit(tokenName); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); } } else if (!match && keyParam === 'author_id') { const id = parseInt(value, 10); @@ -511,7 +518,7 @@ class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'author'; const canEdit = this.canEdit && this.canEdit(tokenName); - gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); } } else if (!match && keyParam === 'search') { hasFilteredSearch = true; @@ -543,13 +550,13 @@ class FilteredSearchManager { search(state = null) { const paths = []; - const searchQuery = gl.DropdownUtils.getSearchQuery(); + const searchQuery = DropdownUtils.getSearchQuery(); this.saveCurrentSearchQuery(); const { tokens, searchToken } = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys()); - const currentState = state || gl.utils.getParameterByName('state') || 'opened'; + const currentState = state || getParameterByName('state') || 'opened'; paths.push(`state=${currentState}`); tokens.forEach((token) => { @@ -628,6 +635,3 @@ class FilteredSearchManager { return true; } } - -window.gl = window.gl || {}; -gl.FilteredSearchManager = FilteredSearchManager; diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js index f2e66503e5e..d75610f6d68 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js +++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js @@ -1,6 +1,6 @@ import './filtered_search_token_keys'; -class FilteredSearchTokenizer { +export default class FilteredSearchTokenizer { static processTokens(input, allowedKeys) { // Regex extracts `(token):(symbol)(value)` // Values that start with a double quote must end in a double quote (same for single) @@ -50,6 +50,3 @@ class FilteredSearchTokenizer { }; } } - -window.gl = window.gl || {}; -gl.FilteredSearchTokenizer = FilteredSearchTokenizer; diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index 2e859d2de3a..a19bb882410 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -3,8 +3,9 @@ import AjaxCache from '../lib/utils/ajax_cache'; import Flash from '../flash'; import FilteredSearchContainer from './container'; import UsersCache from '../lib/utils/users_cache'; +import DropdownUtils from './dropdown_utils'; -class FilteredSearchVisualTokens { +export default class FilteredSearchVisualTokens { static getLastVisualTokenBeforeInput() { const inputLi = FilteredSearchContainer.container.querySelector('.input-token'); const lastVisualToken = inputLi && inputLi.previousElementSibling; @@ -74,7 +75,7 @@ class FilteredSearchVisualTokens { let processed = labels; if (!labels.preprocessed) { - processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels); + processed = DropdownUtils.duplicateLabelPreprocessing(labels); AjaxCache.override(labelsEndpoint, processed); processed.preprocessed = true; } @@ -90,7 +91,7 @@ class FilteredSearchVisualTokens { return AjaxCache.retrieve(labelsEndpoint) .then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint)) .then((labels) => { - const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue); + const matchingLabel = (labels || []).find(label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue); if (!matchingLabel) { return; @@ -259,11 +260,11 @@ class FilteredSearchVisualTokens { static tokenizeInput() { const input = FilteredSearchContainer.container.querySelector('.filtered-search'); const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (input.value) { if (isLastVisualTokenValid) { - gl.FilteredSearchVisualTokens.addSearchVisualToken(input.value); + FilteredSearchVisualTokens.addSearchVisualToken(input.value); } else { FilteredSearchVisualTokens.addValueToPreviousVisualTokenElement(input.value); } @@ -324,12 +325,12 @@ class FilteredSearchVisualTokens { if (!tokenContainer.lastElementChild.isEqualNode(inputLi)) { const { isLastVisualTokenValid } = - gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); + FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); if (!isLastVisualTokenValid) { - const lastPartial = gl.FilteredSearchVisualTokens.getLastTokenPartial(); - gl.FilteredSearchVisualTokens.removeLastTokenPartial(); - gl.FilteredSearchVisualTokens.addSearchVisualToken(lastPartial); + const lastPartial = FilteredSearchVisualTokens.getLastTokenPartial(); + FilteredSearchVisualTokens.removeLastTokenPartial(); + FilteredSearchVisualTokens.addSearchVisualToken(lastPartial); } tokenContainer.removeChild(inputLi); @@ -337,6 +338,3 @@ class FilteredSearchVisualTokens { } } } - -window.gl = window.gl || {}; -gl.FilteredSearchVisualTokens = FilteredSearchVisualTokens; diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 15df7a7f989..6cf78bab6ad 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -485,7 +485,7 @@ GitLabDropdown = (function() { $target = $(e.target); if ($target && !$target.hasClass('dropdown-menu-close') && !$target.hasClass('dropdown-menu-close-icon') && - !$target.data('is-link')) { + !$target.data('isLink')) { e.stopPropagation(); return false; } else { @@ -607,7 +607,20 @@ GitLabDropdown = (function() { }; GitLabDropdown.prototype.renderItem = function(data, group, index) { - var field, fieldName, html, selected, text, url, value; + var field, fieldName, html, selected, text, url, value, rowHidden; + + if (!this.options.renderRow) { + value = this.options.id ? this.options.id(data) : data.id; + + if (value) { + value = value.toString().replace(/'/g, '\\\''); + } + } + + // Hide element + if (this.options.hideRow && this.options.hideRow(value)) { + rowHidden = true; + } if (group == null) { group = false; } @@ -616,6 +629,7 @@ GitLabDropdown = (function() { index = false; } html = document.createElement('li'); + if (data === 'divider' || data === 'separator') { html.className = data; return html; @@ -631,11 +645,9 @@ GitLabDropdown = (function() { html = this.options.renderRow.call(this.options, data, this); } else { if (!selected) { - value = this.options.id ? this.options.id(data) : data.id; fieldName = this.options.fieldName; if (value) { - value = value.toString().replace(/'/g, '\\\''); field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`); if (field.length) { selected = true; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index d200044b79f..2d40856e038 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -12,7 +12,7 @@ export default class GLForm { this.destroy(); // Setup the form this.setupForm(); - this.form.data('gl-form', this); + this.form.data('glForm', this); } destroy() { @@ -21,7 +21,7 @@ export default class GLForm { if (this.autoComplete) { this.autoComplete.destroy(); } - this.form.data('gl-form', null); + this.form.data('glForm', null); } setupForm() { diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js index b33165f9402..6bf21f4f27d 100644 --- a/app/assets/javascripts/gpg_badges.js +++ b/app/assets/javascripts/gpg_badges.js @@ -11,7 +11,7 @@ export default class GpgBadges { badges.html('<i class="fa fa-spinner fa-spin"></i>'); const params = parseQueryStringIntoObject(form.serialize()); - return axios.get(form.data('signatures-path'), { params }) + return axios.get(form.data('signaturesPath'), { params }) .then(({ data }) => { data.signatures.forEach((signature) => { badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js deleted file mode 100644 index 534bc535bb6..00000000000 --- a/app/assets/javascripts/graphs/graphs_bundle.js +++ /dev/null @@ -1,4 +0,0 @@ -import Chart from 'vendor/Chart'; - -// export to global scope -window.Chart = Chart; diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue index 08d0bf6e344..4d86ac8023c 100644 --- a/app/assets/javascripts/groups/components/item_stats_value.vue +++ b/app/assets/javascripts/groups/components/item_stats_value.vue @@ -30,11 +30,11 @@ default: 'bottom', }, /** - * value could either be number or string - * as `memberCount` is always passed as string - * while `subgroupCount` & `projectCount` - * are always number - */ + * value could either be number or string + * as `memberCount` is always passed as string + * while `subgroupCount` & `projectCount` + * are always number + */ value: { type: [Number, String], required: false, diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js index 65a2395fe29..12fc5f9b5c9 100644 --- a/app/assets/javascripts/groups_select.js +++ b/app/assets/javascripts/groups_select.js @@ -7,8 +7,8 @@ export default function groupsSelect() { window.GROUP_SELECT_PER_PAGE = 20; $('.ajax-groups-select').each(function setAjaxGroupsSelect2() { const $select = $(this); - const allAvailable = $select.data('all-available'); - const skipGroups = $select.data('skip-groups') || []; + const allAvailable = $select.data('allAvailable'); + const skipGroups = $select.data('skipGroups') || []; $select.select2({ placeholder: 'Search for a group', multiple: $select.hasClass('multiselect'), diff --git a/app/assets/javascripts/help/help.js b/app/assets/javascripts/help/help.js index 4a22ebf187d..d02477b19a2 100644 --- a/app/assets/javascripts/help/help.js +++ b/app/assets/javascripts/help/help.js @@ -1,6 +1,8 @@ // We will render the icons list here -if ($('#user-content-gitlab-icons').length > 0) { - const $iconsHeader = $('#user-content-gitlab-icons'); - const $iconsList = $('<div id="iconsList">ICONS</div>'); - $($iconsList).insertAfter($iconsHeader.parent()); -} +export default () => { + if ($('#user-content-gitlab-icons').length > 0) { + const $iconsHeader = $('#user-content-gitlab-icons'); + const $iconsList = $('<div id="iconsList">ICONS</div>'); + $($iconsList).insertAfter($iconsHeader.parent()); + } +}; diff --git a/app/assets/javascripts/how_to_merge.js b/app/assets/javascripts/how_to_merge.js index 19f4a946f73..12e6f24595a 100644 --- a/app/assets/javascripts/how_to_merge.js +++ b/app/assets/javascripts/how_to_merge.js @@ -1,12 +1,13 @@ -document.addEventListener('DOMContentLoaded', () => { - const modal = $('#modal_merge_info').modal({ - modal: true, - show: false, - }); - $('.how_to_merge_link').on('click', () => { - modal.show(); - }); - $('.modal-header .close').on('click', () => { - modal.hide(); - }); -}); +export default () => { + const modal = $('#modal_merge_info'); + + if (modal) { + modal.modal({ + modal: true, + show: false, + }); + + $('.how_to_merge_link').on('click', modal.show); + $('.modal-header .close').on('click', modal.hide); + } +}; diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js index af83a1ec0b4..142a220097b 100644 --- a/app/assets/javascripts/ide/monaco_loader.js +++ b/app/assets/javascripts/ide/monaco_loader.js @@ -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 window.__monaco_context__ = monacoContext; export default monacoContext.require; diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index 134a503864e..35094f8e73b 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -59,29 +59,36 @@ class ImporterStatus { .catch(() => flash(__('An error occurred while importing project'))); } - setAutoUpdate() { - return setInterval(() => $.get(this.jobsUrl, data => $.each(data, (i, job) => { - const jobItem = $(`#project_${job.id}`); - const statusField = jobItem.find('.job-status'); + autoUpdate() { + return axios.get(this.jobsUrl) + .then(({ data = [] }) => { + data.forEach((job) => { + const jobItem = $(`#project_${job.id}`); + const statusField = jobItem.find('.job-status'); + + const spinner = '<i class="fa fa-spinner fa-spin"></i>'; - const spinner = '<i class="fa fa-spinner fa-spin"></i>'; + switch (job.import_status) { + case 'finished': + jobItem.removeClass('active').addClass('success'); + statusField.html('<span><i class="fa fa-check"></i> done</span>'); + break; + case 'scheduled': + statusField.html(`${spinner} scheduled`); + break; + case 'started': + statusField.html(`${spinner} started`); + break; + default: + statusField.html(job.import_status); + break; + } + }); + }); + } - switch (job.import_status) { - case 'finished': - jobItem.removeClass('active').addClass('success'); - statusField.html('<span><i class="fa fa-check"></i> done</span>'); - break; - case 'scheduled': - statusField.html(`${spinner} scheduled`); - break; - case 'started': - statusField.html(`${spinner} started`); - break; - default: - statusField.html(job.import_status); - break; - } - })), 4000); + setAutoUpdate() { + setInterval(this.autoUpdate.bind(this), 4000); } } diff --git a/app/assets/javascripts/integrations/index.js b/app/assets/javascripts/integrations/index.js deleted file mode 100644 index 10fe6bac0e8..00000000000 --- a/app/assets/javascripts/integrations/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable no-new */ -import IntegrationSettingsForm from './integration_settings_form'; - -$(() => { - const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form'); - integrationSettingsForm.init(); -}); diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js index 3f27cfc2f6d..2848fe003cb 100644 --- a/app/assets/javascripts/integrations/integration_settings_form.js +++ b/app/assets/javascripts/integrations/integration_settings_form.js @@ -6,8 +6,8 @@ export default class IntegrationSettingsForm { this.$form = $(formSelector); // Form Metadata - this.canTestService = this.$form.data('can-test'); - this.testEndPoint = this.$form.data('test-url'); + this.canTestService = this.$form.data('canTest'); + this.testEndPoint = this.$form.data('testUrl'); // Form Child Elements this.$serviceToggle = this.$form.find('#service_active'); diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue index 9afa9dea126..1338be0ec4b 100644 --- a/app/assets/javascripts/issue_show/components/description.vue +++ b/app/assets/javascripts/issue_show/components/description.vue @@ -78,6 +78,7 @@ taskListUpdateSuccess(data) { try { this.checkForSpam(data); + this.closeRecaptcha(); } catch (error) { if (error && error.name === 'SpamError') this.openRecaptcha(); } diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js index 03546f61d1f..71c0f894389 100644 --- a/app/assets/javascripts/issue_status_select.js +++ b/app/assets/javascripts/issue_status_select.js @@ -1,6 +1,6 @@ export default function issueStatusSelect() { $('.js-issue-status').each((i, el) => { - const fieldName = $(el).data('field-name'); + const fieldName = $(el).data('fieldName'); return $(el).glDropdown({ selectable: true, fieldName, diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js index db53b04de0e..85a88ae409b 100644 --- a/app/assets/javascripts/jobs/job_details_bundle.js +++ b/app/assets/javascripts/jobs/job_details_bundle.js @@ -3,7 +3,7 @@ import JobMediator from './job_details_mediator'; import jobHeader from './components/header.vue'; import detailsBlock from './components/sidebar_details_block.vue'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { const dataset = document.getElementById('js-job-details-vue').dataset; const mediator = new JobMediator({ endpoint: dataset.endpoint }); @@ -55,4 +55,4 @@ document.addEventListener('DOMContentLoaded', () => { }); }, }); -}); +}; diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 5ecf81ad11d..5de48aa49a9 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -25,19 +25,19 @@ export default class LabelsSelect { $dropdown = $(dropdown); $dropdownContainer = $dropdown.closest('.labels-filter'); $toggleText = $dropdown.find('.dropdown-toggle-text'); - namespacePath = $dropdown.data('namespace-path'); - projectPath = $dropdown.data('project-path'); + namespacePath = $dropdown.data('namespacePath'); + projectPath = $dropdown.data('projectPath'); labelUrl = $dropdown.data('labels'); issueUpdateURL = $dropdown.data('issueUpdate'); selectedLabel = $dropdown.data('selected'); if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) { selectedLabel = selectedLabel.split(','); } - showNo = $dropdown.data('show-no'); - showAny = $dropdown.data('show-any'); + showNo = $dropdown.data('showNo'); + showAny = $dropdown.data('showAny'); showMenuAbove = $dropdown.data('showMenuAbove'); - defaultLabel = $dropdown.data('default-label'); - abilityName = $dropdown.data('ability-name'); + defaultLabel = $dropdown.data('defaultLabel'); + abilityName = $dropdown.data('abilityName'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); $form = $dropdown.closest('form, .js-issuable-update'); @@ -45,11 +45,11 @@ export default class LabelsSelect { $sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip'); $value = $block.find('.value'); $loading = $block.find('.block-loading').fadeOut(); - fieldName = $dropdown.data('field-name'); + fieldName = $dropdown.data('fieldName'); useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown'); propertyName = useId ? 'id' : 'title'; initialSelected = $selectbox - .find('input[name="' + $dropdown.data('field-name') + '"]') + .find('input[name="' + $dropdown.data('fieldName') + '"]') .map(function () { return this.value; }).get(); @@ -268,7 +268,7 @@ export default class LabelsSelect { return defaultLabel; } }, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), id: function(label) { if (label.id <= 0) return label.title; @@ -316,9 +316,9 @@ export default class LabelsSelect { }, multiSelect: $dropdown.hasClass('js-multiselect'), vue: $dropdown.hasClass('js-issue-board-sidebar'), - clicked: function(options) { - const { $el, e, isMarking } = options; - const label = options.selectedObj; + clicked: function (clickEvent) { + const { $el, e, isMarking } = clickEvent; + const label = clickEvent.selectedObj; var isIssueIndex, isMRIndex, page, boardsModel; var fadeOutLoader = () => { diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index ab3cc29146a..1b4900827b8 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -4,7 +4,7 @@ import initFlyOutNav from './fly_out_nav'; function hideEndFade($scrollingTabs) { $scrollingTabs.each(function scrollTabsLoop() { const $this = $(this); - $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth')); + $this.siblings('.fade-right').toggleClass('scrolling', Math.round($this.width()) < $this.prop('scrollWidth')); }); } diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 2dc9cf0cc29..5dc98b4a920 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -138,7 +138,7 @@ textUtils.init = function(form) { return $('.js-md', form).off('click').on('click', function() { var $this; $this = $(this); - return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend')); + return self.updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend')); }); }; diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index fbd381d8ff7..e5c1fce3db9 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -83,7 +83,7 @@ LineHighlighter.prototype.clickHandler = function(event) { var current, lineNumber, range; event.preventDefault(); this.clearHighlight(); - lineNumber = $(event.target).closest('a').data('line-number'); + lineNumber = $(event.target).closest('a').data('lineNumber'); current = this.hashToRange(this._hash); if (!(current[0] && event.shiftKey)) { // If there's no current selection, or there is but Shift wasn't held, diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index b99cb257ce3..dc9e5bb03f4 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -61,7 +61,7 @@ gl.lazyLoader = new LazyLoader({ observerNode: '#content-body', }); -$(() => { +document.addEventListener('DOMContentLoaded', () => { const $body = $('body'); const $document = $(document); const $window = $(window); @@ -220,7 +220,7 @@ $(() => { $document.on('click', '.js-confirm-danger', (e) => { const btn = $(e.target); const form = btn.closest('form'); - const text = btn.data('confirm-danger-message'); + const text = btn.data('confirmDangerMessage'); e.preventDefault(); // eslint-disable-next-line no-new diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js index 52315e969d1..330ebed5f73 100644 --- a/app/assets/javascripts/members.js +++ b/app/assets/javascripts/members.js @@ -19,7 +19,7 @@ export default class Members { isSelectable(selected, $el) { return !$el.hasClass('is-active'); }, - fieldName: $btn.data('field-name'), + fieldName: $btn.data('fieldName'), id(selected, $el) { return $el.data('id'); }, @@ -51,7 +51,7 @@ export default class Members { } // eslint-disable-next-line class-methods-use-this getMemberListItems($el) { - const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); + const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('elId')}`); return { $memberListItem, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 3e97a8c758d..41971e92ec0 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -361,7 +361,7 @@ export default class MergeRequestTabs { } diffViewType() { - return $('.inline-parallel-buttons a.active').data('view-type'); + return $('.inline-parallel-buttons a.active').data('viewType'); } isDiffAction(action) { diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index 6581be606eb..2841ecb558b 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -24,19 +24,19 @@ export default class MilestoneSelect { $els.each((i, dropdown) => { let collapsedSidebarLabelTemplate, milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; const $dropdown = $(dropdown); - const projectId = $dropdown.data('project-id'); + const projectId = $dropdown.data('projectId'); const milestonesUrl = $dropdown.data('milestones'); const issueUpdateURL = $dropdown.data('issueUpdate'); - const showNo = $dropdown.data('show-no'); - const showAny = $dropdown.data('show-any'); + const showNo = $dropdown.data('showNo'); + const showAny = $dropdown.data('showAny'); const showMenuAbove = $dropdown.data('showMenuAbove'); - const showUpcoming = $dropdown.data('show-upcoming'); - const showStarted = $dropdown.data('show-started'); - const useId = $dropdown.data('use-id'); - const defaultLabel = $dropdown.data('default-label'); - const defaultNo = $dropdown.data('default-no'); - const issuableId = $dropdown.data('issuable-id'); - const abilityName = $dropdown.data('ability-name'); + const showUpcoming = $dropdown.data('showUpcoming'); + const showStarted = $dropdown.data('showStarted'); + const useId = $dropdown.data('useId'); + const defaultLabel = $dropdown.data('defaultLabel'); + const defaultNo = $dropdown.data('defaultNo'); + const issuableId = $dropdown.data('issuableId'); + const abilityName = $dropdown.data('abilityName'); const $selectBox = $dropdown.closest('.selectbox'); const $block = $selectBox.closest('.block'); const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); @@ -114,7 +114,7 @@ export default class MilestoneSelect { } }, defaultLabel: defaultLabel, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), text: milestone => _.escape(milestone.title), id: (milestone) => { if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) { @@ -166,7 +166,7 @@ export default class MilestoneSelect { } if (boardsStore) { - boardsStore[$dropdown.data('field-name')] = selected.name; + boardsStore[$dropdown.data('fieldName')] = selected.name; e.preventDefault(); } else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) { return Issuable.filterResults($dropdown.closest('form')); diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 8efb8ac5320..f17b432cffd 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -219,7 +219,7 @@ export default class Notes { } editNote = $textarea.closest('.note'); if (editNote.length) { - originalText = $textarea.closest('form').data('original-note'); + originalText = $textarea.closest('form').data('originalNote'); newText = $textarea.val(); if (originalText !== newText) { if (!confirm('Are you sure you want to cancel editing this comment?')) { @@ -609,9 +609,9 @@ export default class Notes { */ addDiscussionNote($form, note, isNewDiffComment) { if ($form.attr('data-resolve-all') != null) { - var projectPath = $form.data('project-path'); - var discussionId = $form.data('discussion-id'); - var mergeRequestId = $form.data('noteable-iid'); + var projectPath = $form.data('projectPath'); + var discussionId = $form.data('discussionId'); + var mergeRequestId = $form.data('noteableIid'); if (ResolveService != null) { ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId); @@ -751,7 +751,7 @@ export default class Notes { form.removeClass('current-note-edit-form'); form.find('.js-finish-edit-warning').hide(); // Replace markdown textarea text with original note text. - return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note')); + return form.find('.js-note-text').val(form.find('form.edit-note').data('originalNote')); } /** @@ -776,7 +776,7 @@ export default class Notes { var $note, $notes; $note = $(el); $notes = $note.closest('.discussion-notes'); - const discussionId = $('.notes', $notes).data('discussion-id'); + const discussionId = $('.notes', $notes).data('discussionId'); if (typeof gl.diffNotesCompileComponents !== 'undefined') { if (gl.diffNoteApps[noteElId]) { @@ -897,7 +897,7 @@ export default class Notes { // DiffNote form.find('#note_position').val(dataHolder.attr('data-position')); - form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancel-text')); + form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancelText')); form.find('.js-note-target-close').remove(); form.find('.js-note-new-discussion').remove(); this.setupNoteForm(form); @@ -1037,7 +1037,7 @@ export default class Notes { removeDiscussionNoteForm(form) { var glForm, row; row = form.closest('tr'); - glForm = form.data('gl-form'); + glForm = form.data('glForm'); glForm.destroy(); form.find('.js-note-text').data('autosave').reset(); // show the reply button (will only work for replies) @@ -1122,8 +1122,8 @@ export default class Notes { return discardbtn.show(); } } else { - reopentext = reopenbtn.data('original-text'); - closetext = closebtn.data('original-text'); + reopentext = reopenbtn.data('originalText'); + closetext = closebtn.data('originalText'); if (reopenbtn.text() !== reopentext) { reopenbtn.text(reopentext); } @@ -1150,9 +1150,9 @@ export default class Notes { var $originalContentEl = $note.find('.original-note-content'); var originalContent = $originalContentEl.text().trim(); - var postUrl = $originalContentEl.data('post-url'); - var targetId = $originalContentEl.data('target-id'); - var targetType = $originalContentEl.data('target-type'); + var postUrl = $originalContentEl.data('postUrl'); + var targetId = $originalContentEl.data('targetId'); + var targetType = $originalContentEl.data('targetType'); this.glForm = new GLForm($editForm.find('form'), this.enableGFM); @@ -1513,9 +1513,9 @@ export default class Notes { // If comment intends to resolve discussion, do the same. if (isDiscussionResolve) { $form - .attr('data-discussion-id', $submitBtn.data('discussion-id')) + .attr('data-discussion-id', $submitBtn.data('discussionId')) .attr('data-resolve-all', 'true') - .attr('data-project-path', $submitBtn.data('project-path')); + .attr('data-project-path', $submitBtn.data('projectPath')); } // Show final note element on UI @@ -1587,7 +1587,7 @@ export default class Notes { this.addNoteError($form); }); - return $closeBtn.text($closeBtn.data('original-text')); + return $closeBtn.text($closeBtn.data('originalText')); } /** @@ -1642,7 +1642,7 @@ export default class Notes { this.updateNoteError(); }); - return $closeBtn.text($closeBtn.data('original-text')); + return $closeBtn.text($closeBtn.data('originalText')); } } diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js index 9570d1c00aa..479a512ed65 100644 --- a/app/assets/javascripts/notifications_dropdown.js +++ b/app/assets/javascripts/notifications_dropdown.js @@ -3,11 +3,11 @@ import Flash from './flash'; export default function notificationsDropdown() { $(document).on('click', '.update-notification', function updateNotificationCallback(e) { e.preventDefault(); - if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') { + if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') { return; } - const notificationLevel = $(this).data('notification-level'); + const notificationLevel = $(this).data('notificationLevel'); const form = $(this).parents('.notification-form:first'); form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner'); diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js index fd3105b1960..7e85bce0d73 100644 --- a/app/assets/javascripts/pager.js +++ b/app/assets/javascripts/pager.js @@ -56,7 +56,7 @@ export default { }, initLoadMore() { - $(document).unbind('scroll'); + $(document).off('scroll'); $(document).endlessScroll({ bottomPixels: ENDLESS_SCROLL_BOTTOM_PX, fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS, diff --git a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js index d87e6304a24..66702ec4ca0 100644 --- a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js +++ b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js @@ -15,21 +15,21 @@ export default class AbuseReports { const $messageCellElement = $(this); const reportMessage = $messageCellElement.text(); if (reportMessage.length > MAX_MESSAGE_LENGTH) { - $messageCellElement.data('original-message', reportMessage); - $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.data('originalMessage', reportMessage); + $messageCellElement.data('messageTruncated', 'true'); $messageCellElement.text(truncate(reportMessage, MAX_MESSAGE_LENGTH)); } } toggleMessageTruncation() { const $messageCellElement = $(this); - const originalMessage = $messageCellElement.data('original-message'); + const originalMessage = $messageCellElement.data('originalMessage'); if (!originalMessage) return; - if ($messageCellElement.data('message-truncated') === 'true') { - $messageCellElement.data('message-truncated', 'false'); + if ($messageCellElement.data('messageTruncated') === 'true') { + $messageCellElement.data('messageTruncated', 'false'); $messageCellElement.text(originalMessage); } else { - $messageCellElement.data('message-truncated', 'true'); + $messageCellElement.data('messageTruncated', 'true'); $messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`); } } diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js index 135c15c346b..45e05f111a7 100644 --- a/app/assets/javascripts/pages/admin/admin.js +++ b/app/assets/javascripts/pages/admin/admin.js @@ -16,9 +16,9 @@ export default function adminInit() { $('input#user_force_random_password').on('change', function randomPasswordClick() { const $elems = $('#user_password, #user_password_confirmation'); if ($(this).attr('checked')) { - $elems.val('').attr('disabled', true); + $elems.val('').prop('disabled', true); } else { - $elems.removeAttr('disabled'); + $elems.prop('disabled', false); } }); diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js index 885acfac6d0..b68ce5d32d8 100644 --- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js +++ b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js @@ -14,7 +14,7 @@ export default function initBroadcastMessagesForm() { $('div.broadcast-message-preview').css('color', previewColor); }); - const previewPath = $('textarea#broadcast_message_message').data('preview-path'); + const previewPath = $('textarea#broadcast_message_message').data('previewPath'); $('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() { const message = $(this).val(); diff --git a/app/assets/javascripts/pages/admin/groups/edit/index.js b/app/assets/javascripts/pages/admin/groups/edit/index.js index ff9ef8d2449..d3d125a1859 100644 --- a/app/assets/javascripts/pages/admin/groups/edit/index.js +++ b/app/assets/javascripts/pages/admin/groups/edit/index.js @@ -1,3 +1,3 @@ -import groupAvatar from '../../../../group_avatar'; +import groupAvatar from '~/group_avatar'; -export default () => groupAvatar(); +document.addEventListener('DOMContentLoaded', groupAvatar); diff --git a/app/assets/javascripts/pages/admin/groups/new/index.js b/app/assets/javascripts/pages/admin/groups/new/index.js index fb5c46e4729..21f1ce222ac 100644 --- a/app/assets/javascripts/pages/admin/groups/new/index.js +++ b/app/assets/javascripts/pages/admin/groups/new/index.js @@ -2,8 +2,8 @@ import BindInOut from '../../../../behaviors/bind_in_out'; import Group from '../../../../group'; import groupAvatar from '../../../../group_avatar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { BindInOut.initAll(); new Group(); // eslint-disable-line no-new groupAvatar(); -}; +}); diff --git a/app/assets/javascripts/pages/admin/index.js b/app/assets/javascripts/pages/admin/index.js index 8b843037d85..e50b61f09e2 100644 --- a/app/assets/javascripts/pages/admin/index.js +++ b/app/assets/javascripts/pages/admin/index.js @@ -1,3 +1,3 @@ import initAdmin from './admin'; -export default () => initAdmin(); +document.addEventListener('DOMContentLoaded', initAdmin); diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue index 555725cbe12..ba1d8e4d8db 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue +++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue @@ -1,13 +1,13 @@ <script> import axios from '~/lib/utils/axios_utils'; - import Flash from '~/flash'; - import modal from '~/vue_shared/components/modal.vue'; - import { s__ } from '~/locale'; + import createFlash from '~/flash'; + import GlModal from '~/vue_shared/components/gl_modal.vue'; import { redirectTo } from '~/lib/utils/url_utility'; + import { s__ } from '~/locale'; export default { components: { - modal, + GlModal, }, props: { url: { @@ -17,7 +17,7 @@ }, computed: { 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: { @@ -28,7 +28,7 @@ redirectTo(response.request.responseURL); }) .catch((error) => { - Flash(s__('AdminArea|Stopping jobs failed')); + createFlash(s__('AdminArea|Stopping jobs failed')); throw error; }); }, @@ -37,11 +37,13 @@ </script> <template> - <modal + <gl-modal id="stop-jobs-modal" - :title="s__('AdminArea|Stop all jobs?')" - :text="text" - kind="danger" - :primary-button-label="s__('AdminArea|Stop jobs')" - @submit="onSubmit" /> + :header-title-text="s__('AdminArea|Stop all jobs?')" + footer-primary-button-variant="danger" + :footer-primary-button-text="s__('AdminArea|Stop jobs')" + @submit="onSubmit" + > + {{ text }} + </gl-modal> </template> diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js index 0e004bd9174..5a4f8c6e745 100644 --- a/app/assets/javascripts/pages/admin/jobs/index/index.js +++ b/app/assets/javascripts/pages/admin/jobs/index/index.js @@ -1,29 +1,28 @@ import Vue from 'vue'; - import Translate from '~/vue_shared/translate'; - import stopJobsModal from './components/stop_jobs_modal.vue'; Vue.use(Translate); -export default () => { +document.addEventListener('DOMContentLoaded', () => { const stopJobsButton = document.getElementById('stop-jobs-button'); - - // eslint-disable-next-line no-new - new Vue({ - el: '#stop-jobs-modal', - components: { - stopJobsModal, - }, - mounted() { - stopJobsButton.classList.remove('disabled'); - }, - render(createElement) { - return createElement('stop-jobs-modal', { - props: { - url: stopJobsButton.dataset.url, - }, - }); - }, - }); -}; + if (stopJobsButton) { + // eslint-disable-next-line no-new + new Vue({ + el: '#stop-jobs-modal', + components: { + stopJobsModal, + }, + mounted() { + stopJobsButton.classList.remove('disabled'); + }, + render(createElement) { + return createElement('stop-jobs-modal', { + props: { + url: stopJobsButton.dataset.url, + }, + }); + }, + }); + } +}); diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js index a87b27090a8..3c597a1093e 100644 --- a/app/assets/javascripts/pages/admin/projects/index/index.js +++ b/app/assets/javascripts/pages/admin/projects/index/index.js @@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; import deleteProjectModal from './components/delete_project_modal.vue'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { Vue.use(Translate); const deleteProjectModalEl = document.getElementById('delete-project-modal'); @@ -34,4 +34,4 @@ export default () => { deleteModal.projectName = buttonProps.projectName; } }); -}; +}); diff --git a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue index 7b5e333011e..7b5e333011e 100644 --- a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue +++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue diff --git a/app/assets/javascripts/pages/admin/users/shared/index.js b/app/assets/javascripts/pages/admin/users/index.js index d2a0f82fa2b..4f5d6b55031 100644 --- a/app/assets/javascripts/pages/admin/users/shared/index.js +++ b/app/assets/javascripts/pages/admin/users/index.js @@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf'; import deleteUserModal from './components/delete_user_modal.vue'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { Vue.use(Translate); const deleteUserModalEl = document.getElementById('delete-user-modal'); @@ -40,4 +40,4 @@ export default () => { deleteModal.username = buttonProps.username; } }); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/activity/index.js b/app/assets/javascripts/pages/dashboard/activity/index.js index 95faf1f1e98..1b887cad496 100644 --- a/app/assets/javascripts/pages/dashboard/activity/index.js +++ b/app/assets/javascripts/pages/dashboard/activity/index.js @@ -1,3 +1,3 @@ import Activities from '~/activities'; -export default () => new Activities(); +document.addEventListener('DOMContentLoaded', () => new Activities()); diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js index 8a2aae706c0..9f235ed6a98 100644 --- a/app/assets/javascripts/pages/dashboard/groups/index/index.js +++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js @@ -1,5 +1,3 @@ -import initGroupsList from '../../../../groups'; +import initGroupsList from '~/groups'; -export default () => { - initGroupsList(); -}; +export default initGroupsList; diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/issues/index.js +++ b/app/assets/javascripts/pages/dashboard/issues/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js index b7353669e65..c4901dd1cb6 100644 --- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js +++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js @@ -1,7 +1,7 @@ import projectSelect from '~/project_select'; import initLegacyFilters from '~/init_legacy_filters'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { projectSelect(); initLegacyFilters(); -}; +}); diff --git a/app/assets/javascripts/pages/dashboard/milestones/show/index.js b/app/assets/javascripts/pages/dashboard/milestones/show/index.js index 2e7a08a369c..397149aaa9e 100644 --- a/app/assets/javascripts/pages/dashboard/milestones/show/index.js +++ b/app/assets/javascripts/pages/dashboard/milestones/show/index.js @@ -1,7 +1,9 @@ import Milestone from '~/milestone'; import Sidebar from '~/right_sidebar'; +import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Milestone(); // eslint-disable-line no-new new Sidebar(); // eslint-disable-line no-new -}; + new MountMilestoneSidebar(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/dashboard/projects/index.js +++ b/app/assets/javascripts/pages/dashboard/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); diff --git a/app/assets/javascripts/pages/dashboard/todos/index/index.js b/app/assets/javascripts/pages/dashboard/todos/index/index.js index 77c23685943..9d2c2f2994f 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/index.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/index.js @@ -1,3 +1,3 @@ import Todos from './todos'; -export default () => new Todos(); +document.addEventListener('DOMContentLoaded', () => new Todos()); diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js index e59c38b8bc4..3c7edbdd7c7 100644 --- a/app/assets/javascripts/pages/explore/groups/index.js +++ b/app/assets/javascripts/pages/explore/groups/index.js @@ -2,7 +2,7 @@ import GroupsList from '~/groups_list'; import Landing from '~/landing'; import initGroupsList from '../../../groups'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new GroupsList(); // eslint-disable-line no-new initGroupsList(); const landingElement = document.querySelector('.js-explore-groups-landing'); @@ -13,4 +13,4 @@ export default function () { 'explore_groups_landing_dismissed', ); exploreGroupsLanding.toggle(); -} +}); diff --git a/app/assets/javascripts/pages/explore/projects/index.js b/app/assets/javascripts/pages/explore/projects/index.js index c88cbf1a6ba..0c585e162cb 100644 --- a/app/assets/javascripts/pages/explore/projects/index.js +++ b/app/assets/javascripts/pages/explore/projects/index.js @@ -1,3 +1,3 @@ import ProjectsList from '~/projects_list'; -export default () => new ProjectsList(); +document.addEventListener('DOMContentLoaded', () => new ProjectsList()); diff --git a/app/assets/javascripts/pages/groups/activity/index.js b/app/assets/javascripts/pages/groups/activity/index.js index 95faf1f1e98..1b887cad496 100644 --- a/app/assets/javascripts/pages/groups/activity/index.js +++ b/app/assets/javascripts/pages/groups/activity/index.js @@ -1,3 +1,3 @@ import Activities from '~/activities'; -export default () => new Activities(); +document.addEventListener('DOMContentLoaded', () => new Activities()); diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js index 1aeec55a4be..d44874c8741 100644 --- a/app/assets/javascripts/pages/groups/edit/index.js +++ b/app/assets/javascripts/pages/groups/edit/index.js @@ -1,7 +1,7 @@ import groupAvatar from '~/group_avatar'; import TransferDropdown from '~/groups/transfer_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { groupAvatar(); new TransferDropdown(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/groups/group_members/index/index.js b/app/assets/javascripts/pages/groups/group_members/index/index.js index 29319b97ae2..c22a164cd4e 100644 --- a/app/assets/javascripts/pages/groups/group_members/index/index.js +++ b/app/assets/javascripts/pages/groups/group_members/index/index.js @@ -4,8 +4,8 @@ import memberExpirationDate from '~/member_expiration_date'; import Members from '~/members'; import UsersSelect from '~/users_select'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { memberExpirationDate(); new Members(); new UsersSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js index fbdfabd1e95..d149b307e7f 100644 --- a/app/assets/javascripts/pages/groups/issues/index.js +++ b/app/assets/javascripts/pages/groups/issues/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.ISSUES, }); projectSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index f6d284bf9ef..a5cc1f34b63 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -2,9 +2,9 @@ import projectSelect from '~/project_select'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { FILTERED_SEARCH } from '~/pages/constants'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, }); projectSelect(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/edit/index.js +++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js index 5c99c90e24d..ddd10fe5062 100644 --- a/app/assets/javascripts/pages/groups/milestones/new/index.js +++ b/app/assets/javascripts/pages/groups/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(false); +document.addEventListener('DOMContentLoaded', () => initForm(false)); diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js index c9a18353f2e..88f40b5278e 100644 --- a/app/assets/javascripts/pages/groups/milestones/show/index.js +++ b/app/assets/javascripts/pages/groups/milestones/show/index.js @@ -1,3 +1,3 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; -export default initMilestonesShow; +document.addEventListener('DOMContentLoaded', initMilestonesShow); diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js index 7850b90d3d2..b2f275dc5ea 100644 --- a/app/assets/javascripts/pages/groups/new/index.js +++ b/app/assets/javascripts/pages/groups/new/index.js @@ -2,8 +2,8 @@ import BindInOut from '~/behaviors/bind_in_out'; import Group from '~/group'; import groupAvatar from '~/group_avatar'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { BindInOut.initAll(); new Group(); // eslint-disable-line no-new groupAvatar(); -}; +}); diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js index 5c763986da3..d7b35d2b26b 100644 --- a/app/assets/javascripts/pages/groups/show/index.js +++ b/app/assets/javascripts/pages/groups/show/index.js @@ -5,7 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown'; import NotificationsForm from '~/notifications_form'; import ProjectsList from '~/projects_list'; import ShortcutsNavigation from '~/shortcuts_navigation'; -import initGroupsList from '../../../groups'; +import initGroupsList from '~/groups'; document.addEventListener('DOMContentLoaded', () => { const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); diff --git a/app/assets/javascripts/pages/help/index.js b/app/assets/javascripts/pages/help/index.js deleted file mode 100644 index 4cf8afc4b7e..00000000000 --- a/app/assets/javascripts/pages/help/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import VersionCheckImage from '../../version_check_image'; - -export default () => VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); diff --git a/app/assets/javascripts/pages/help/index/index.js b/app/assets/javascripts/pages/help/index/index.js new file mode 100644 index 00000000000..05c81fc618b --- /dev/null +++ b/app/assets/javascripts/pages/help/index/index.js @@ -0,0 +1,7 @@ +import VersionCheckImage from '~/version_check_image'; +import docs from '~/docs/docs_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + docs(); + VersionCheckImage.bindErrorEvent($('img.js-version-status-badge')); +}); diff --git a/app/assets/javascripts/pages/help/show/index.js b/app/assets/javascripts/pages/help/show/index.js new file mode 100644 index 00000000000..ec426a850b6 --- /dev/null +++ b/app/assets/javascripts/pages/help/show/index.js @@ -0,0 +1,3 @@ +import initHelp from '~/help/help'; + +document.addEventListener('DOMContentLoaded', initHelp); diff --git a/app/assets/javascripts/pages/help/ui/index.js b/app/assets/javascripts/pages/help/ui/index.js new file mode 100644 index 00000000000..709ca2f3828 --- /dev/null +++ b/app/assets/javascripts/pages/help/ui/index.js @@ -0,0 +1,3 @@ +import initUIKit from '~/ui_development_kit'; + +document.addEventListener('DOMContentLoaded', initUIKit); diff --git a/app/assets/javascripts/pages/import/gitlab_projects/new/index.js b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js new file mode 100644 index 00000000000..bb86f72b95b --- /dev/null +++ b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js @@ -0,0 +1,3 @@ +import initGitLabImportProject from '~/projects/project_import_gitlab_project'; + +document.addEventListener('DOMContentLoaded', initGitLabImportProject); diff --git a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js index 7aa5be0d5b9..b2a896a3265 100644 --- a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js +++ b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js @@ -2,8 +2,10 @@ import Milestone from '~/milestone'; import Sidebar from '~/right_sidebar'; +import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar'; export default () => { new Milestone(); new Sidebar(); + new MountMilestoneSidebar(); }; diff --git a/app/assets/javascripts/pages/omniauth_callbacks/index.js b/app/assets/javascripts/pages/omniauth_callbacks/index.js index 54f4e56359a..c2c069d1ca8 100644 --- a/app/assets/javascripts/pages/omniauth_callbacks/index.js +++ b/app/assets/javascripts/pages/omniauth_callbacks/index.js @@ -1,5 +1,3 @@ import initU2F from '../../shared/sessions/u2f'; -export default () => { - initU2F(); -}; +document.addEventListener('DOMContentLoaded', initU2F); diff --git a/app/assets/javascripts/pages/profiles/accounts/show/index.js b/app/assets/javascripts/pages/profiles/accounts/show/index.js new file mode 100644 index 00000000000..96c3d725780 --- /dev/null +++ b/app/assets/javascripts/pages/profiles/accounts/show/index.js @@ -0,0 +1,3 @@ +import initProfileAccount from '~/profile/account'; + +document.addEventListener('DOMContentLoaded', initProfileAccount); diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js index 7af95127fd5..5543ad82428 100644 --- a/app/assets/javascripts/pages/projects/activity/index.js +++ b/app/assets/javascripts/pages/projects/activity/index.js @@ -1,7 +1,7 @@ import Activities from '~/activities'; import ShortcutsNavigation from '~/shortcuts_navigation'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new Activities(); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new -} +}); diff --git a/app/assets/javascripts/pages/projects/blame/show/index.js b/app/assets/javascripts/pages/projects/blame/show/index.js index 480357a309c..80d0bff92fa 100644 --- a/app/assets/javascripts/pages/projects/blame/show/index.js +++ b/app/assets/javascripts/pages/projects/blame/show/index.js @@ -1,3 +1,3 @@ import initBlob from '~/pages/projects/init_blob'; -export default initBlob; +document.addEventListener('DOMContentLoaded', initBlob); diff --git a/app/assets/javascripts/pages/projects/blob/edit/index.js b/app/assets/javascripts/pages/projects/blob/edit/index.js new file mode 100644 index 00000000000..189053f3ed7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/blob/edit/index.js @@ -0,0 +1,3 @@ +import initBlobBundle from '~/blob_edit/blob_bundle'; + +document.addEventListener('DOMContentLoaded', initBlobBundle); diff --git a/app/assets/javascripts/pages/projects/blob/new/index.js b/app/assets/javascripts/pages/projects/blob/new/index.js new file mode 100644 index 00000000000..189053f3ed7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/blob/new/index.js @@ -0,0 +1,3 @@ +import initBlobBundle from '~/blob_edit/blob_bundle'; + +document.addEventListener('DOMContentLoaded', initBlobBundle); diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js index a3eeb1cefb6..26cbb279d4a 100644 --- a/app/assets/javascripts/pages/projects/blob/show/index.js +++ b/app/assets/javascripts/pages/projects/blob/show/index.js @@ -1,7 +1,7 @@ import BlobViewer from '~/blob/viewer/index'; import initBlob from '~/pages/projects/init_blob'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new BlobViewer(); // eslint-disable-line no-new initBlob(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js index cee0f19bf2a..8fa266a37ce 100644 --- a/app/assets/javascripts/pages/projects/branches/index/index.js +++ b/app/assets/javascripts/pages/projects/branches/index/index.js @@ -1,7 +1,7 @@ import AjaxLoadingSpinner from '~/ajax_loading_spinner'; import DeleteModal from '~/branches/branches_delete_modal'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { AjaxLoadingSpinner.init(); new DeleteModal(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index ae5e033e97e..d32d5c6cb29 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,3 +1,5 @@ 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)) +)); diff --git a/app/assets/javascripts/pages/projects/clusters/destroy/index.js b/app/assets/javascripts/pages/projects/clusters/destroy/index.js new file mode 100644 index 00000000000..8001d2dd1da --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/destroy/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + new ClustersBundle(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js index d531ab81dc7..e4b8baede58 100644 --- a/app/assets/javascripts/pages/projects/clusters/index/index.js +++ b/app/assets/javascripts/pages/projects/clusters/index/index.js @@ -1,5 +1,5 @@ import ClustersIndex from '~/clusters/clusters_index'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ClustersIndex(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js index 0458c02a66f..8001d2dd1da 100644 --- a/app/assets/javascripts/pages/projects/clusters/show/index.js +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -1,5 +1,5 @@ import ClustersBundle from '~/clusters/clusters_bundle'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ClustersBundle(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/clusters/update/index.js b/app/assets/javascripts/pages/projects/clusters/update/index.js new file mode 100644 index 00000000000..8001d2dd1da --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/update/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +document.addEventListener('DOMContentLoaded', () => { + new ClustersBundle(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js index 523ad567021..7889704a324 100644 --- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -1,8 +1,8 @@ import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new MiniPipelineGraph({ container: '.js-commit-pipeline-graph', }).bindEvents(); $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); -}; +}); diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js index 5ac38e6f278..460a54ab504 100644 --- a/app/assets/javascripts/pages/projects/commit/show/index.js +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -7,7 +7,7 @@ import initNotes from '~/init_notes'; import initChangesDropdown from '~/init_changes_dropdown'; import { fetchCommitMergeRequests } from '~/commit_merge_requests'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); new ZenMode(); new ShortcutsNavigation(); @@ -19,4 +19,4 @@ export default () => { initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop); $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); fetchCommitMergeRequests(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js index 90b5882a24f..3682020579b 100644 --- a/app/assets/javascripts/pages/projects/commits/show/index.js +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -2,8 +2,8 @@ import CommitsList from '~/commits'; import GpgBadges from '~/gpg_badges'; import ShortcutsNavigation from '~/shortcuts_navigation'; -export default () => { - CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); +document.addEventListener('DOMContentLoaded', () => { + new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new GpgBadges.fetch(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js index 6b8d4503568..2b4fd3c47c0 100644 --- a/app/assets/javascripts/pages/projects/compare/show/index.js +++ b/app/assets/javascripts/pages/projects/compare/show/index.js @@ -1,8 +1,8 @@ import Diff from '~/diff'; import initChangesDropdown from '~/init_changes_dropdown'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); // eslint-disable-line no-new const paddingTop = 16; initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); -}; +}); diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js index 9edf36d66b1..064de22dfd6 100644 --- a/app/assets/javascripts/pages/projects/edit/index.js +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -4,11 +4,11 @@ import ProjectNew from '../shared/project_new'; import projectAvatar from '../shared/project_avatar'; import initProjectPermissionsSettings from '../shared/permissions'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ProjectNew(); // eslint-disable-line no-new setupProjectEdit(); // Initialize expandable settings panels initSettingsPanels(); projectAvatar(); initProjectPermissionsSettings(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js index f4760cb2720..0b644780ad4 100644 --- a/app/assets/javascripts/pages/projects/environments/metrics/index.js +++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js @@ -1,3 +1,3 @@ import monitoringBundle from '~/monitoring/monitoring_bundle'; -export default monitoringBundle; +document.addEventListener('DOMContentLoaded', monitoringBundle); diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js index 42bde0ff779..23d857d69ec 100644 --- a/app/assets/javascripts/pages/projects/find_file/show/index.js +++ b/app/assets/javascripts/pages/projects/find_file/show/index.js @@ -1,7 +1,7 @@ import ProjectFindFile from '~/project_find_file'; import ShortcutsFindFile from '~/shortcuts_find_file'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { const findElement = document.querySelector('.js-file-finder'); const projectFindFile = new ProjectFindFile($('.file-finder-holder'), { url: findElement.dataset.fileFindUrl, @@ -9,4 +9,4 @@ export default () => { blobUrlTemplate: findElement.dataset.blobUrlTemplate, }); new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js index ec6eab34989..42df19c2968 100644 --- a/app/assets/javascripts/graphs/graphs_charts.js +++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js @@ -1,4 +1,4 @@ -import Chart from 'vendor/Chart'; +import Chart from 'chart.js'; import _ from 'underscore'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/pages/projects/graphs/show/index.js index b670e907a5c..f516ff20995 100644 --- a/app/assets/javascripts/graphs/graphs_show.js +++ b/app/assets/javascripts/pages/projects/graphs/show/index.js @@ -1,6 +1,6 @@ -import flash from '../flash'; -import { __ } from '../locale'; -import axios from '../lib/utils/axios_utils'; +import flash from '~/flash'; +import { __ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; import ContributorsStatGraph from './stat_graph_contributors'; document.addEventListener('DOMContentLoaded', () => { diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js index 151a4ce012c..9ac0b4c07e5 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js @@ -1,9 +1,9 @@ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ import _ from 'underscore'; +import { n__, s__, createDateTimeFormat, sprintf } from '~/locale'; import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; import ContributorsStatGraphUtil from './stat_graph_contributors_util'; -import { n__, s__, createDateTimeFormat, sprintf } from '../locale'; export default (function() { function ContributorsStatGraph() { diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js index 9a4012232a0..6ffaa277a0a 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js @@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis'; import { area } from 'd3-shape'; import { brushX } from 'd3-brush'; import { timeParse } from 'd3-time-format'; -import { dateTickFormat } from '../lib/utils/tick_formats'; +import { dateTickFormat } from '~/lib/utils/tick_formats'; const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse }; diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js index 77135ad1f0e..77135ad1f0e 100644 --- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js index 378f7b3f38b..d5f92baf054 100644 --- a/app/assets/javascripts/pages/projects/imports/show/index.js +++ b/app/assets/javascripts/pages/projects/imports/show/index.js @@ -1,5 +1,5 @@ import ProjectImport from '~/project_import'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ProjectImport(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js index 26f0ad46114..82143fa875a 100644 --- a/app/assets/javascripts/pages/projects/init_blob.js +++ b/app/assets/javascripts/pages/projects/init_blob.js @@ -3,6 +3,7 @@ import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater'; import ShortcutsNavigation from '~/shortcuts_navigation'; import ShortcutsBlob from '~/shortcuts_blob'; import BlobForkSuggestion from '~/blob/blob_fork_suggestion'; +import initBlobBundle from '~/blob_edit/blob_bundle'; export default () => { new LineHighlighter(); // eslint-disable-line no-new @@ -30,4 +31,6 @@ export default () => { suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'), actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'), }).init(); + + initBlobBundle(); }; diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/edit/index.js +++ b/app/assets/javascripts/pages/projects/issues/edit/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js index 7f27f379d8c..ffc84dc106b 100644 --- a/app/assets/javascripts/pages/projects/issues/new/index.js +++ b/app/assets/javascripts/pages/projects/issues/new/index.js @@ -1,5 +1,3 @@ import initForm from '../form'; -export default () => { - initForm(); -}; +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js index da312c1f1b7..db064e3f801 100644 --- a/app/assets/javascripts/pages/projects/issues/show/index.js +++ b/app/assets/javascripts/pages/projects/issues/show/index.js @@ -1,13 +1,12 @@ -/* eslint-disable no-new */ - import initIssuableSidebar from '~/init_issuable_sidebar'; import Issue from '~/issue'; import ShortcutsIssuable from '~/shortcuts_issuable'; import ZenMode from '~/zen_mode'; +import '~/notes/index'; document.addEventListener('DOMContentLoaded', () => { - new Issue(); - new ShortcutsIssuable(); - new ZenMode(); + new Issue(); // eslint-disable-line no-new + new ShortcutsIssuable(); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new initIssuableSidebar(); }); diff --git a/app/assets/javascripts/pages/projects/jobs/show/index.js b/app/assets/javascripts/pages/projects/jobs/show/index.js new file mode 100644 index 00000000000..3626f3ffec6 --- /dev/null +++ b/app/assets/javascripts/pages/projects/jobs/show/index.js @@ -0,0 +1,3 @@ +import initJobDetails from '~/jobs/job_details_bundle'; + +document.addEventListener('DOMContentLoaded', initJobDetails); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js index ccd0b54c5ed..1d5aec4001d 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js @@ -1,7 +1,7 @@ import Compare from '~/compare'; import MergeRequest from '~/merge_request'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); if (mrNewCompareNode) { new Compare({ // eslint-disable-line no-new @@ -15,4 +15,4 @@ export default () => { action: mrNewSubmitNode.dataset.mrSubmitAction, }); } -}; +}); diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js index 734d01ae6f2..febfecebbd2 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js @@ -1,3 +1,3 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; -export default initMergeRequest; +document.addEventListener('DOMContentLoaded', initMergeRequest); diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js index c3463c266e3..07f3e579c97 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js @@ -2,16 +2,19 @@ import MergeRequest from '~/merge_request'; import ZenMode from '~/zen_mode'; import initNotes from '~/init_notes'; import initIssuableSidebar from '~/init_issuable_sidebar'; +import initDiffNotes from '~/diff_notes/diff_notes_bundle'; import ShortcutsIssuable from '~/shortcuts_issuable'; import Diff from '~/diff'; import { handleLocationHash } from '~/lib/utils/common_utils'; +import howToMerge from '~/how_to_merge'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new Diff(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new - initIssuableSidebar(); // eslint-disable-line no-new - initNotes(); // eslint-disable-line no-new + initIssuableSidebar(); + initNotes(); + initDiffNotes(); const mrShowNode = document.querySelector('.merge-request'); @@ -21,4 +24,5 @@ export default () => { new ShortcutsIssuable(true); // eslint-disable-line no-new handleLocationHash(); -}; + howToMerge(); +}); diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/edit/index.js +++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); diff --git a/app/assets/javascripts/pages/projects/milestones/index/index.js b/app/assets/javascripts/pages/projects/milestones/index/index.js index 8fb4d83d8a3..38789365a67 100644 --- a/app/assets/javascripts/pages/projects/milestones/index/index.js +++ b/app/assets/javascripts/pages/projects/milestones/index/index.js @@ -1,3 +1,3 @@ import milestones from '~/pages/milestones/shared'; -export default milestones; +document.addEventListener('DOMContentLoaded', milestones); diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js index 10e3979a36e..9a4ebf9890d 100644 --- a/app/assets/javascripts/pages/projects/milestones/new/index.js +++ b/app/assets/javascripts/pages/projects/milestones/new/index.js @@ -1,3 +1,3 @@ import initForm from '../../../../shared/milestones/form'; -export default () => initForm(); +document.addEventListener('DOMContentLoaded', () => initForm()); diff --git a/app/assets/javascripts/pages/projects/milestones/show/index.js b/app/assets/javascripts/pages/projects/milestones/show/index.js index 35b5c9c2ced..84a52421598 100644 --- a/app/assets/javascripts/pages/projects/milestones/show/index.js +++ b/app/assets/javascripts/pages/projects/milestones/show/index.js @@ -1,7 +1,7 @@ import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show'; import milestones from '~/pages/milestones/shared'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { initMilestonesShow(); milestones(); -}; +}); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js new file mode 100644 index 00000000000..d65be6bc69e --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js @@ -0,0 +1,3 @@ +import initForm from '../shared/init_form'; + +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js new file mode 100644 index 00000000000..d65be6bc69e --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js @@ -0,0 +1,3 @@ +import initForm from '../shared/init_form'; + +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js index a6c945e22b0..544360dcd51 100644 --- a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js @@ -1,5 +1,5 @@ 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({ el: '#pipeline-schedules-callout', diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js new file mode 100644 index 00000000000..d65be6bc69e --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js @@ -0,0 +1,3 @@ +import initForm from '../shared/init_form'; + +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index 2d18fa2044b..2d18fa2044b 100644 --- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue index aa04a0ac47a..77508e62cef 100644 --- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue @@ -1,7 +1,7 @@ <script> import Vue from 'vue'; import Cookies from 'js-cookie'; - import Translate from '../../vue_shared/translate'; + import Translate from '../../../../../vue_shared/translate'; import illustrationSvg from '../icons/intro_illustration.svg'; Vue.use(Translate); diff --git a/app/assets/javascripts/pipeline_schedules/components/target_branch_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js index 0c3926d76b5..0c3926d76b5 100644 --- a/app/assets/javascripts/pipeline_schedules/components/target_branch_dropdown.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js diff --git a/app/assets/javascripts/pipeline_schedules/components/timezone_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js index 95ed9c7dc21..95ed9c7dc21 100644 --- a/app/assets/javascripts/pipeline_schedules/components/timezone_dropdown.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js diff --git a/app/assets/javascripts/pipeline_schedules/icons/intro_illustration.svg b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg index 26d1ff97b3e..26d1ff97b3e 100644 --- a/app/assets/javascripts/pipeline_schedules/icons/intro_illustration.svg +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js index 0b1a81bae13..cfd30d6053f 100644 --- a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js @@ -1,10 +1,10 @@ import Vue from 'vue'; -import Translate from '../vue_shared/translate'; -import GlFieldErrors from '../gl_field_errors'; +import Translate from '../../../../vue_shared/translate'; +import GlFieldErrors from '../../../../gl_field_errors'; import intervalPatternInput from './components/interval_pattern_input.vue'; import TimezoneDropdown from './components/timezone_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); @@ -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, * 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 */ @@ -46,4 +46,4 @@ document.addEventListener('DOMContentLoaded', () => { container: $('.js-ci-variable-list-section'), formField: 'schedule', }); -}); +}; diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js new file mode 100644 index 00000000000..d65be6bc69e --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js @@ -0,0 +1,3 @@ +import initForm from '../shared/init_form'; + +document.addEventListener('DOMContentLoaded', initForm); diff --git a/app/assets/javascripts/pages/projects/pipelines/builds/index.js b/app/assets/javascripts/pages/projects/pipelines/builds/index.js index 060a78b427e..fbe9824c34b 100644 --- a/app/assets/javascripts/pages/projects/pipelines/builds/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/builds/index.js @@ -1,16 +1,3 @@ -import Pipelines from '../../../../pipelines'; +import initPipelines from '../init_pipelines'; -export default () => { - const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; - const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; - - new Pipelines({ // eslint-disable-line no-new - initTabs: true, - pipelineStatusUrl, - tabsOptions: { - action: controllerAction, - defaultAction: 'pipelines', - parentEl: '.pipelines-tabs', - }, - }); -}; +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js new file mode 100644 index 00000000000..bb92f4e1459 --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js @@ -0,0 +1,56 @@ +import Chart from 'chart.js'; + +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)); +}); diff --git a/app/assets/javascripts/pages/projects/pipelines/failures/index.js b/app/assets/javascripts/pages/projects/pipelines/failures/index.js new file mode 100644 index 00000000000..fbe9824c34b --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/failures/index.js @@ -0,0 +1,3 @@ +import initPipelines from '../init_pipelines'; + +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js new file mode 100644 index 00000000000..94dfeb96e8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js @@ -0,0 +1,16 @@ +import Pipelines from '~/pipelines'; + +export default () => { + const { controllerAction } = document.querySelector('.js-pipeline-container').dataset; + const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`; + + new Pipelines({ // eslint-disable-line no-new + initTabs: true, + pipelineStatusUrl, + tabsOptions: { + action: controllerAction, + defaultAction: 'pipelines', + parentEl: '.pipelines-tabs', + }, + }); +}; diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js index c54cc62bf05..da20bd995e9 100644 --- a/app/assets/javascripts/pages/projects/pipelines/new/index.js +++ b/app/assets/javascripts/pages/projects/pipelines/new/index.js @@ -1,5 +1,5 @@ -import NewBranchForm from '../../../../new_branch_form'; +import NewBranchForm from '~/new_branch_form'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js new file mode 100644 index 00000000000..fbe9824c34b --- /dev/null +++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js @@ -0,0 +1,3 @@ +import initPipelines from '../init_pipelines'; + +document.addEventListener('DOMContentLoaded', initPipelines); diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index 863dac0d20e..d23ad9a92f4 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -50,7 +50,7 @@ export default class Project { Project.projectSelectDropdown(); } - static projectSelectDropdown () { + static projectSelectDropdown() { projectSelect(); $('.project-item-select').on('click', e => Project.changeProject($(e.currentTarget).val())); } @@ -71,7 +71,7 @@ export default class Project { selected = $dropdown.data('selected'); return $dropdown.glDropdown({ data(term, callback) { - axios.get($dropdown.data('refs-url'), { + axios.get($dropdown.data('refsUrl'), { params: { ref: $dropdown.data('ref'), search: term, @@ -84,8 +84,8 @@ export default class Project { filterable: true, filterRemote: true, filterByText: true, - inputFieldName: $dropdown.data('input-field-name'), - fieldName: $dropdown.data('field-name'), + inputFieldName: $dropdown.data('inputFieldName'), + fieldName: $dropdown.data('fieldName'), renderRow: function(ref) { var li = refListItem.cloneNode(false); diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index f4643e7dba0..adbe744290a 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -3,10 +3,10 @@ import UsersSelect from '../../../users_select'; import groupsSelect from '../../../groups_select'; import Members from '../../../members'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { memberExpirationDate('.js-access-expiration-date-groups'); groupsSelect(); memberExpirationDate(); new Members(); // eslint-disable-line no-new new UsersSelect(); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/releases/edit/index.js b/app/assets/javascripts/pages/projects/releases/edit/index.js index 3d997cdfff0..0bf53a8de09 100644 --- a/app/assets/javascripts/pages/projects/releases/edit/index.js +++ b/app/assets/javascripts/pages/projects/releases/edit/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.release-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.release-form'))); diff --git a/app/assets/javascripts/pages/projects/services/edit/index.js b/app/assets/javascripts/pages/projects/services/edit/index.js new file mode 100644 index 00000000000..ba4b271f09e --- /dev/null +++ b/app/assets/javascripts/pages/projects/services/edit/index.js @@ -0,0 +1,13 @@ +import IntegrationSettingsForm from '~/integrations/integration_settings_form'; +import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics'; + +document.addEventListener('DOMContentLoaded', () => { + 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(); + } +}); diff --git a/app/assets/javascripts/pages/projects/snippets/edit/index.js b/app/assets/javascripts/pages/projects/snippets/edit/index.js index 9edb16dc73b..caf9ee9b398 100644 --- a/app/assets/javascripts/pages/projects/snippets/edit/index.js +++ b/app/assets/javascripts/pages/projects/snippets/edit/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.snippet-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form'))); diff --git a/app/assets/javascripts/pages/projects/snippets/new/index.js b/app/assets/javascripts/pages/projects/snippets/new/index.js index 9edb16dc73b..caf9ee9b398 100644 --- a/app/assets/javascripts/pages/projects/snippets/new/index.js +++ b/app/assets/javascripts/pages/projects/snippets/new/index.js @@ -1,3 +1,3 @@ import initForm from '~/pages/projects/init_form'; -export default initForm($('.snippet-form')); +document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form'))); diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js index a3cf75c385b..a134599cb04 100644 --- a/app/assets/javascripts/pages/projects/snippets/show/index.js +++ b/app/assets/javascripts/pages/projects/snippets/show/index.js @@ -3,9 +3,9 @@ import ZenMode from '~/zen_mode'; import LineHighlighter from '../../../../line_highlighter'; import BlobViewer from '../../../../blob/viewer'; -export default function () { +document.addEventListener('DOMContentLoaded', () => { new LineHighlighter(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new initNotes(); new ZenMode(); // eslint-disable-line no-new -} +}); diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js index dacc2875c8c..191c98b36bb 100644 --- a/app/assets/javascripts/pages/projects/tags/new/index.js +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -2,8 +2,8 @@ import RefSelectDropdown from '../../../../ref_select_dropdown'; import ZenMode from '../../../../zen_mode'; import GLForm from '../../../../gl_form'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ZenMode(); // eslint-disable-line no-new new GLForm($('.tag-form'), true); // eslint-disable-line no-new new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new -}; +}); diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js index cba57058380..ed7d3f1747c 100644 --- a/app/assets/javascripts/pages/projects/tree/show/index.js +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import initBlob from '~/blob_edit/blob_bundle'; import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue'; import TreeView from '../../../../tree'; import ShortcutsNavigation from '../../../../shortcuts_navigation'; @@ -6,7 +7,7 @@ import BlobViewer from '../../../../blob/viewer'; import NewCommitForm from '../../../../new_commit_form'; import { ajaxGet } from '../../../../lib/utils/common_utils'; -export default () => { +document.addEventListener('DOMContentLoaded', () => { new ShortcutsNavigation(); // eslint-disable-line no-new new TreeView(); // eslint-disable-line no-new new BlobViewer(); // eslint-disable-line no-new @@ -14,6 +15,7 @@ export default () => { $('#tree-slider').waitForImages(() => ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); + initBlob(); const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status'); const statusLink = document.querySelector('.commit-actions .ci-status-link'); if (statusLink != null) { @@ -33,5 +35,4 @@ export default () => { }, }); } -}; - +}); diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js index 250f9d992ab..de8d4168d71 100644 --- a/app/assets/javascripts/pages/search/init_filtered_search.js +++ b/app/assets/javascripts/pages/search/init_filtered_search.js @@ -1,7 +1,9 @@ +import FilteredSearchManager from '~/filtered_search/filtered_search_manager'; + export default ({ page }) => { - const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search'); if (filteredSearchEnabled) { - const filteredSearchManager = new gl.FilteredSearchManager({ page }); + const filteredSearchManager = new FilteredSearchManager({ page }); filteredSearchManager.setup(); } }; diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js index dc621bc87c0..cf44e291199 100644 --- a/app/assets/javascripts/pages/search/show/search.js +++ b/app/assets/javascripts/pages/search/show/search.js @@ -9,7 +9,7 @@ export default class Search { this.searchInput = '.js-search-input'; this.searchClear = '.js-search-clear'; - this.groupId = $groupDropdown.data('group-id'); + this.groupId = $groupDropdown.data('groupId'); this.eventListeners(); $groupDropdown.glDropdown({ @@ -36,7 +36,7 @@ export default class Search { return obj.full_name; }, toggleLabel(obj) { - return `${($groupDropdown.data('default-label'))} ${obj.full_name}`; + return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`; }, clicked: () => Search.submitSearch(), }); @@ -69,7 +69,7 @@ export default class Search { return obj.name_with_namespace; }, toggleLabel(obj) { - return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`; + return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`; }, clicked: () => Search.submitSearch(), }); diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js index 54f4e56359a..c2c069d1ca8 100644 --- a/app/assets/javascripts/pages/sessions/index.js +++ b/app/assets/javascripts/pages/sessions/index.js @@ -1,5 +1,3 @@ import initU2F from '../../shared/sessions/u2f'; -export default () => { - initU2F(); -}; +document.addEventListener('DOMContentLoaded', initU2F); diff --git a/app/assets/javascripts/pages/snippets/edit/index.js b/app/assets/javascripts/pages/snippets/edit/index.js index 9c664b5f1ff..2ee38b64ca1 100644 --- a/app/assets/javascripts/pages/snippets/edit/index.js +++ b/app/assets/javascripts/pages/snippets/edit/index.js @@ -1,3 +1,3 @@ import form from '../form'; -export default form; +document.addEventListener('DOMContentLoaded', form); diff --git a/app/assets/javascripts/pages/snippets/new/index.js b/app/assets/javascripts/pages/snippets/new/index.js index 9c664b5f1ff..2ee38b64ca1 100644 --- a/app/assets/javascripts/pages/snippets/new/index.js +++ b/app/assets/javascripts/pages/snippets/new/index.js @@ -1,3 +1,3 @@ import form from '../form'; -export default form; +document.addEventListener('DOMContentLoaded', form); diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js index 04c9562bfbb..f548b9fad65 100644 --- a/app/assets/javascripts/pages/snippets/show/index.js +++ b/app/assets/javascripts/pages/snippets/show/index.js @@ -1,12 +1,11 @@ -/* eslint-disable no-new */ import LineHighlighter from '../../../line_highlighter'; import BlobViewer from '../../../blob/viewer'; import ZenMode from '../../../zen_mode'; import initNotes from '../../../init_notes'; -export default () => { - new LineHighlighter(); - new BlobViewer(); +document.addEventListener('DOMContentLoaded', () => { + new LineHighlighter(); // eslint-disable-line no-new + new BlobViewer(); // eslint-disable-line no-new initNotes(); - new ZenMode(); -}; + new ZenMode(); // eslint-disable-line no-new +}); diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js index 57306322aa4..57306322aa4 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/pages/users/activity_calendar.js diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/pages/users/index.js index 9fd8452a2b6..899dcd42e37 100644 --- a/app/assets/javascripts/users/index.js +++ b/app/assets/javascripts/pages/users/index.js @@ -1,3 +1,4 @@ +import UserCallout from '~/user_callout'; import Cookies from 'js-cookie'; import UserTabs from './user_tabs'; @@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => { const page = $('body').attr('data-page'); const action = page.split(':')[1]; initUserProfile(action); + new UserCallout(); // eslint-disable-line no-new }); diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js deleted file mode 100644 index f18f98b4e9a..00000000000 --- a/app/assets/javascripts/pages/users/show/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import UserCallout from '~/user_callout'; - -export default () => new UserCallout(); diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index e13b9839a20..c1217623467 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -1,9 +1,9 @@ -import axios from '../lib/utils/axios_utils'; -import Activities from '../activities'; +import axios from '~/lib/utils/axios_utils'; +import Activities from '~/activities'; +import { localTimeAgo } from '~/lib/utils/datetime_utility'; +import { __ } from '~/locale'; +import flash from '~/flash'; import ActivityCalendar from './activity_calendar'; -import { localTimeAgo } from '../lib/utils/datetime_utility'; -import { __ } from '../locale'; -import flash from '../flash'; /** * UserTabs diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue index a5f22c4ec80..0cdffbde05b 100644 --- a/app/assets/javascripts/pipelines/components/async_button.vue +++ b/app/assets/javascripts/pipelines/components/async_button.vue @@ -31,10 +31,14 @@ type: String, required: true, }, - id: { + pipelineId: { type: Number, required: true, }, + type: { + type: String, + required: true, + }, }, data() { return { @@ -46,17 +50,27 @@ return `btn ${this.cssClass}`; }, }, + created() { + // We're using eventHub to listen to the modal here instead of + // using props because it would would make the parent components + // much more complex to keep track of the loading state of each button + eventHub.$on('postAction', this.setLoading); + }, + beforeDestroy() { + eventHub.$off('postAction', this.setLoading); + }, methods: { onClick() { - eventHub.$emit('actionConfirmationModal', { - id: this.id, - callback: this.makeRequest, + eventHub.$emit('openConfirmationModal', { + pipelineId: this.pipelineId, + endpoint: this.endpoint, + type: this.type, }); }, - makeRequest() { - this.isLoading = true; - - eventHub.$emit('postAction', this.endpoint); + setLoading(endpoint) { + if (endpoint === this.endpoint) { + this.isLoading = true; + } }, }, }; diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index a1f58580318..ab84711d4a2 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -52,7 +52,7 @@ </script> <template> <div class="build-content middle-block js-pipeline-graph"> - <div class="pipeline-visualization pipeline-graph"> + <div class="pipeline-visualization pipeline-graph pipeline-tab-content"> <div class="text-center"> <loading-icon v-if="isLoading" diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index e027f08ff5c..7adcf4017b8 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -7,7 +7,6 @@ jobComponent, dropdownJobComponent, }, - props: { title: { type: String, diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue index 62fe479fdf4..c9028952ddd 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue @@ -1,7 +1,8 @@ <script> + import modal from '~/vue_shared/components/modal.vue'; + import { s__, sprintf } from '~/locale'; import pipelinesTableRowComponent from './pipelines_table_row.vue'; - import stopConfirmationModal from './stop_confirmation_modal.vue'; - import retryConfirmationModal from './retry_confirmation_modal.vue'; + import eventHub from '../event_hub'; /** * Pipelines Table Component. @@ -11,8 +12,7 @@ export default { components: { pipelinesTableRowComponent, - stopConfirmationModal, - retryConfirmationModal, + modal, }, props: { pipelines: { @@ -33,6 +33,52 @@ required: true, }, }, + data() { + return { + pipelineId: '', + endpoint: '', + type: '', + }; + }, + computed: { + modalTitle() { + return this.type === 'stop' ? + sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), { + pipelineId: `'${this.pipelineId}'`, + }, false) : + sprintf(s__('Pipeline|Retry pipeline #%{pipelineId}?'), { + pipelineId: `'${this.pipelineId}'`, + }, false); + }, + modalText() { + return this.type === 'stop' ? + sprintf(s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), { + pipelineId: `<strong>#${this.pipelineId}</strong>`, + }, false) : + sprintf(s__('Pipeline|You’re about to retry pipeline %{pipelineId}.'), { + pipelineId: `<strong>#${this.pipelineId}</strong>`, + }, false); + }, + primaryButtonLabel() { + return this.type === 'stop' ? s__('Pipeline|Stop pipeline') : s__('Pipeline|Retry pipeline'); + }, + }, + created() { + eventHub.$on('openConfirmationModal', this.setModalData); + }, + beforeDestroy() { + eventHub.$off('openConfirmationModal', this.setModalData); + }, + methods: { + setModalData(data) { + this.pipelineId = data.pipelineId; + this.endpoint = data.endpoint; + this.type = data.type; + }, + onSubmit() { + eventHub.$emit('postAction', this.endpoint); + }, + }, }; </script> <template> @@ -74,7 +120,20 @@ :auto-devops-help-path="autoDevopsHelpPath" :view-type="viewType" /> - <stop-confirmation-modal /> - <retry-confirmation-modal /> + <modal + id="confirmation-modal" + :title="modalTitle" + :text="modalText" + kind="danger" + :primary-button-label="primaryButtonLabel" + @submit="onSubmit" + > + <template + slot="body" + slot-scope="props" + > + <p v-html="props.text"></p> + </template> + </modal> </div> </template> diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 0e3a10ed7f4..2ba59051773 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -223,7 +223,8 @@ <div class="table-section section-10 commit-link"> <div class="table-mobile-header" - role="rowheader"> + role="rowheader" + > Status </div> <div class="table-mobile-content"> @@ -305,9 +306,10 @@ css-class="js-pipelines-retry-button btn-default btn-retry" title="Retry" icon="repeat" - :id="pipeline.id" + :pipeline-id="pipeline.id" data-toggle="modal" - data-target="#retry-confirmation-modal" + data-target="#confirmation-modal" + type="retry" /> <async-button-component @@ -316,9 +318,10 @@ css-class="js-pipelines-cancel-button btn-remove" title="Cancel" icon="close" - :id="pipeline.id" + :pipeline-id="pipeline.id" data-toggle="modal" - data-target="#stop-confirmation-modal" + data-target="#confirmation-modal" + type="stop" /> </div> </div> diff --git a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue deleted file mode 100644 index e2ac08d67bc..00000000000 --- a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> - import modal from '~/vue_shared/components/modal.vue'; - import { s__, sprintf } from '~/locale'; - import eventHub from '../event_hub'; - - export default { - components: { - modal, - }, - data() { - return { - id: '', - callback: () => {}, - }; - }, - computed: { - title() { - return sprintf(s__('Pipeline|Retry pipeline #%{id}?'), { - id: `'${this.id}'`, - }, false); - }, - text() { - return sprintf(s__('Pipeline|You’re about to retry pipeline %{id}.'), { - id: `<strong>#${this.id}</strong>`, - }, false); - }, - primaryButtonLabel() { - return s__('Pipeline|Retry pipeline'); - }, - }, - created() { - eventHub.$on('actionConfirmationModal', this.updateModal); - }, - beforeDestroy() { - eventHub.$off('actionConfirmationModal', this.updateModal); - }, - methods: { - updateModal(action) { - this.id = action.id; - this.callback = action.callback; - }, - onSubmit() { - this.callback(); - }, - }, - }; -</script> - -<template> - <modal - id="retry-confirmation-modal" - :title="title" - :text="text" - kind="danger" - :primary-button-label="primaryButtonLabel" - @submit="onSubmit" - > - <template - slot="body" - slot-scope="props" - > - <p v-html="props.text"></p> - </template> - </modal> -</template> diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 58806aa114a..ecf2b10486e 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -50,9 +50,7 @@ computed: { dropdownClass() { - return this.dropdownContent.length > 0 ? - 'js-builds-dropdown-container' : - 'js-builds-dropdown-loading'; + return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading'; }, triggerButtonClass() { diff --git a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue deleted file mode 100644 index d737d567787..00000000000 --- a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> - import modal from '~/vue_shared/components/modal.vue'; - import { s__, sprintf } from '~/locale'; - import eventHub from '../event_hub'; - - export default { - components: { - modal, - }, - data() { - return { - id: '', - callback: () => {}, - }; - }, - computed: { - title() { - return sprintf(s__('Pipeline|Stop pipeline #%{id}?'), { - id: `'${this.id}'`, - }, false); - }, - text() { - return sprintf(s__('Pipeline|You’re about to stop pipeline %{id}.'), { - id: `<strong>#${this.id}</strong>`, - }, false); - }, - primaryButtonLabel() { - return s__('Pipeline|Stop pipeline'); - }, - }, - created() { - eventHub.$on('actionConfirmationModal', this.updateModal); - }, - beforeDestroy() { - eventHub.$off('actionConfirmationModal', this.updateModal); - }, - methods: { - updateModal(action) { - this.id = action.id; - this.callback = action.callback; - }, - onSubmit() { - this.callback(); - }, - }, - }; -</script> - -<template> - <modal - id="stop-confirmation-modal" - :title="title" - :text="text" - kind="danger" - :primary-button-label="primaryButtonLabel" - @submit="onSubmit" - > - <template - slot="body" - slot-scope="props" - > - <p v-html="props.text"></p> - </template> - </modal> -</template> diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js index d88d280cb3f..705a60b3ba2 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js +++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js @@ -1,10 +1,14 @@ import Vue from 'vue'; -import Flash from '../flash'; -import PipelinesMediator from './pipeline_details_mediatior'; +import Flash from '~/flash'; +import Translate from '~/vue_shared/translate'; +import { __ } from '~/locale'; +import PipelinesMediator from './pipeline_details_mediator'; import pipelineGraph from './components/graph/graph_component.vue'; import pipelineHeader from './components/header_component.vue'; import eventHub from './event_hub'; +Vue.use(Translate); + document.addEventListener('DOMContentLoaded', () => { const dataset = document.querySelector('.js-pipeline-details-vue').dataset; @@ -54,7 +58,7 @@ document.addEventListener('DOMContentLoaded', () => { postAction(action) { this.mediator.service.postAction(action.path) .then(() => this.mediator.refreshPipeline()) - .catch(() => new Flash('An error occurred while making the request.')); + .catch(() => Flash(__('An error occurred while making the request.'))); }, }, render(createElement) { diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js index 823ccd849f4..10f238fe73b 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js +++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js @@ -1,6 +1,7 @@ import Visibility from 'visibilityjs'; import Flash from '../flash'; import Poll from '../lib/utils/poll'; +import { __ } from '../locale'; import PipelineStore from './stores/pipeline_store'; import PipelineService from './services/pipeline_service'; @@ -47,7 +48,7 @@ export default class pipelinesMediator { errorCallback() { this.state.isLoading = false; - return new Flash('An error occurred while fetching the pipeline.'); + Flash(__('An error occurred while fetching the pipeline.')); } refreshPipeline() { diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js deleted file mode 100644 index 821aa7e229f..00000000000 --- a/app/assets/javascripts/pipelines/pipelines_charts.js +++ /dev/null @@ -1,38 +0,0 @@ -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)); -}); diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js deleted file mode 100644 index b5e7a0e53d9..00000000000 --- a/app/assets/javascripts/pipelines/pipelines_times.js +++ /dev/null @@ -1,27 +0,0 @@ -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); -}); diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js index a93bc935dd0..84049a1f0b7 100644 --- a/app/assets/javascripts/profile/account/index.js +++ b/app/assets/javascripts/profile/account/index.js @@ -1,29 +1,29 @@ import Vue from 'vue'; - import Translate from '~/vue_shared/translate'; - import deleteAccountModal from './components/delete_account_modal.vue'; -Vue.use(Translate); +export default () => { + Vue.use(Translate); -const deleteAccountButton = document.getElementById('delete-account-button'); -const deleteAccountModalEl = document.getElementById('delete-account-modal'); -// eslint-disable-next-line no-new -new Vue({ - el: deleteAccountModalEl, - components: { - deleteAccountModal, - }, - mounted() { - deleteAccountButton.classList.remove('disabled'); - }, - render(createElement) { - return createElement('delete-account-modal', { - props: { - actionUrl: deleteAccountModalEl.dataset.actionUrl, - confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword, - username: deleteAccountModalEl.dataset.username, - }, - }); - }, -}); + const deleteAccountButton = document.getElementById('delete-account-button'); + const deleteAccountModalEl = document.getElementById('delete-account-modal'); + // eslint-disable-next-line no-new + new Vue({ + el: deleteAccountModalEl, + components: { + deleteAccountModal, + }, + mounted() { + deleteAccountButton.classList.remove('disabled'); + }, + render(createElement) { + return createElement('delete-account-modal', { + props: { + actionUrl: deleteAccountModalEl.dataset.actionUrl, + confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword, + username: deleteAccountModalEl.dataset.username, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js index e116ee23601..930f0fb381e 100644 --- a/app/assets/javascripts/profile/profile.js +++ b/app/assets/javascripts/profile/profile.js @@ -33,9 +33,6 @@ import flash from '../flash'; $('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie); $('#user_notification_email').on('change', this.submitForm); $('#user_notified_of_own_activity').on('change', this.submitForm); - $('.update-username').on('ajax:before', this.beforeUpdateUsername); - $('.update-username').on('ajax:complete', this.afterUpdateUsername); - $('.update-notifications').on('ajax:success', this.onUpdateNotifs); this.form.on('submit', this.onSubmitForm); } @@ -48,21 +45,6 @@ import flash from '../flash'; return this.saveForm(); } - beforeUpdateUsername() { - $('.loading-username', this).removeClass('hidden'); - } - - afterUpdateUsername() { - $('.loading-username', this).addClass('hidden'); - $('button[type=submit]', this).enable(); - } - - onUpdateNotifs(e, data) { - return data.saved ? - flash(__('Notification settings saved'), 'notice') : - flash(__('Failed to save new settings')); - } - saveForm() { const self = this; const formData = new FormData(this.form[0]); diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 586d188350f..4fd639cce8e 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -73,7 +73,7 @@ export default class ProjectFindFile { // find file } - // files pathes load + // files pathes load load(url) { axios.get(url) .then(({ data }) => { @@ -85,7 +85,7 @@ export default class ProjectFindFile { .catch(() => flash(__('An error occurred while loading filenames'))); } - // render result + // render result renderList(filePaths, searchText) { var blobItemUrl, filePath, html, i, j, len, matches, results; this.element.find(".tree-table > tbody").empty(); diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js index 07a49d1506c..412aca7bfed 100644 --- a/app/assets/javascripts/project_select.js +++ b/app/assets/javascripts/project_select.js @@ -5,13 +5,13 @@ import ProjectSelectComboButton from './project_select_combo_button'; export default function projectSelect() { $('.ajax-project-select').each(function(i, select) { var placeholder; - const simpleFilter = $(select).data('simple-filter') || false; - this.groupId = $(select).data('group-id'); - this.includeGroups = $(select).data('include-groups'); - this.allProjects = $(select).data('all-projects') || false; - this.orderBy = $(select).data('order-by') || 'id'; - this.withIssuesEnabled = $(select).data('with-issues-enabled'); - this.withMergeRequestsEnabled = $(select).data('with-merge-requests-enabled'); + const simpleFilter = $(select).data('simpleFilter') || false; + this.groupId = $(select).data('groupId'); + this.includeGroups = $(select).data('includeGroups'); + this.allProjects = $(select).data('allProjects') || false; + this.orderBy = $(select).data('orderBy') || 'id'; + this.withIssuesEnabled = $(select).data('withIssuesEnabled'); + this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled'); placeholder = "Search for project"; if (this.includeGroups) { diff --git a/app/assets/javascripts/projects/project_import_gitlab_project.js b/app/assets/javascripts/projects/project_import_gitlab_project.js index cec6f0dd5a3..d2c7d77bb2d 100644 --- a/app/assets/javascripts/projects/project_import_gitlab_project.js +++ b/app/assets/javascripts/projects/project_import_gitlab_project.js @@ -1,14 +1,8 @@ import { getParameterValues } from '../lib/utils/url_utility'; -const bindEvents = () => { +export default () => { const path = getParameterValues('path')[0]; // get the path url and append it in the inputS $('.js-path-name').val(path); }; - -document.addEventListener('DOMContentLoaded', bindEvents); - -export default { - bindEvents, -}; diff --git a/app/assets/javascripts/prometheus_metrics/index.js b/app/assets/javascripts/prometheus_metrics/index.js deleted file mode 100644 index a0c43c5abe1..00000000000 --- a/app/assets/javascripts/prometheus_metrics/index.js +++ /dev/null @@ -1,6 +0,0 @@ -import PrometheusMetrics from './prometheus_metrics'; - -$(() => { - const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring'); - prometheusMetrics.loadActiveMetrics(); -}); diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js index 59ad5b45855..e8126ac573d 100644 --- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js +++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js @@ -19,7 +19,7 @@ export default class PrometheusMetrics { this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count'); this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list'); - this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('active-metrics'); + this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('activeMetrics'); this.$panelToggle.on('click', e => this.handlePanelToggle(e)); } diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js index 38b1406a99f..40a873833e1 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js @@ -9,8 +9,8 @@ export default class ProtectedBranchAccessDropdown { $dropdown.glDropdown({ data, selectable: true, - inputId: $dropdown.data('input-id'), - fieldName: $dropdown.data('field-name'), + inputId: $dropdown.data('inputId'), + fieldName: $dropdown.data('fieldName'), toggleLabel(item, $el) { if ($el.is('.is-active')) { return item.text; diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 2948baeab11..8fc87633e18 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -59,7 +59,7 @@ export default class ProtectedBranchCreate { ); this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val()); - this.$form.find('input[type="submit"]').attr('disabled', completedForm); + this.$form.find('input[type="submit"]').prop('disabled', completedForm); } static getProtectedBranches(term, callback) { diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index b51b3e9a6ff..54560d08ad7 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -41,11 +41,11 @@ export default class ProtectedBranchEdit { axios.patch(this.$wrap.data('url'), { protected_branch: { merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('access-level-id'), + id: this.$allowedToMergeDropdown.data('accessLevelId'), access_level: $allowedToMergeInput.val(), }], push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('access-level-id'), + id: this.$allowedToPushDropdown.data('accessLevelId'), access_level: $allowedToPushInput.val(), }], }, diff --git a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js index d4c9a91a74a..b803da798d5 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js +++ b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js @@ -9,8 +9,8 @@ export default class ProtectedTagAccessDropdown { this.options.$dropdown.glDropdown({ data: this.options.data, selectable: true, - inputId: this.options.$dropdown.data('input-id'), - fieldName: this.options.$dropdown.data('field-name'), + inputId: this.options.$dropdown.data('inputId'), + fieldName: this.options.$dropdown.data('fieldName'), toggleLabel(item, $el) { if ($el.is('.is-active')) { return item.text; diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js index d1e4a75c17b..2f94ffe2507 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_create.js +++ b/app/assets/javascripts/protected_tags/protected_tag_create.js @@ -39,7 +39,7 @@ export default class ProtectedTagCreate { const $tagInput = this.$form.find('input[name="protected_tag[name]"]'); const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes'); - this.$form.find('input[type="submit"]').attr('disabled', !($tagInput.val() && $allowedToCreateInput.length)); + this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length)); } static getProtectedTags(term, callback) { diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js index 21a258cf93c..8687b2a4044 100644 --- a/app/assets/javascripts/protected_tags/protected_tag_edit.js +++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js @@ -31,7 +31,7 @@ export default class ProtectedTagEdit { axios.patch(this.$wrap.data('url'), { protected_tag: { create_access_levels_attributes: [{ - id: this.$allowedToCreateDropdownButton.data('access-level-id'), + id: this.$allowedToCreateDropdownButton.data('accessLevelId'), access_level: $allowedToCreateInput.val(), }], }, diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js index 65e4101352c..56c25a35e6d 100644 --- a/app/assets/javascripts/ref_select_dropdown.js +++ b/app/assets/javascripts/ref_select_dropdown.js @@ -6,7 +6,7 @@ class RefSelectDropdown { filterable: true, filterByText: true, remote: false, - fieldName: $dropdownButton.data('field-name'), + fieldName: $dropdownButton.data('fieldName'), filterInput: 'input[type="search"]', selectable: true, isSelectable(branch, $el) { @@ -24,7 +24,7 @@ class RefSelectDropdown { }); const $dropdownContainer = $dropdownButton.closest('.dropdown'); - const $fieldInput = $(`input[name="${$dropdownButton.data('field-name')}"]`, $dropdownContainer); + const $fieldInput = $(`input[name="${$dropdownButton.data('fieldName')}"]`, $dropdownContainer); const $filterInput = $('input[type="search"]', $dropdownContainer); $filterInput.on('keyup', (e) => { diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js index 5482c55f8bb..05a623ca6d9 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/render_gfm.js @@ -1,6 +1,7 @@ import renderMath from './render_math'; import renderMermaid from './render_mermaid'; import syntaxHighlight from './syntax_highlight'; + // Render Gitlab flavoured Markdown // // Delegates to syntax highlight and render math & mermaid diagrams. diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 01c3be5411f..8d3cc849f81 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -76,8 +76,8 @@ Sidebar.prototype.toggleTodo = function(e) { $('.js-issuable-todo').disable().addClass('is-loading'); axios[ajaxType](url, { - issuable_id: $this.data('issuable-id'), - issuable_type: $this.data('issuable-type'), + issuable_id: $this.data('issuableId'), + issuable_type: $this.data('issuableType'), }).then(({ data }) => { this.todoUpdateDone(data); }).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`)); @@ -96,18 +96,18 @@ Sidebar.prototype.todoUpdateDone = function(data) { $el.removeClass('is-loading') .enable() - .attr('aria-label', $el.data(`${attrPrefix}-text`)) + .attr('aria-label', $el.data(`${attrPrefix}Text`)) .attr('data-delete-path', deletePath) - .attr('title', $el.data(`${attrPrefix}-text`)); + .attr('title', $el.data(`${attrPrefix}Text`)); if ($el.hasClass('has-tooltip')) { $el.tooltip('fixTitle'); } - if ($el.data(`${attrPrefix}-icon`)) { - $elText.html($el.data(`${attrPrefix}-icon`)); + if ($el.data(`${attrPrefix}Icon`)) { + $elText.html($el.data(`${attrPrefix}Icon`)); } else { - $elText.text($el.data(`${attrPrefix}-text`)); + $elText.text($el.data(`${attrPrefix}Text`)); } }); }; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 8f4a8704c3b..fdfa4f28aba 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -1,5 +1,6 @@ /* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */ import axios from './lib/utils/axios_utils'; +import DropdownUtils from './filtered_search/dropdown_utils'; import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils'; /** @@ -25,32 +26,32 @@ function setSearchOptions() { if ($projectOptionsDataEl.length) { gl.projectOptions = gl.projectOptions || {}; - var projectPath = $projectOptionsDataEl.data('project-path'); + var projectPath = $projectOptionsDataEl.data('projectPath'); gl.projectOptions[projectPath] = { name: $projectOptionsDataEl.data('name'), - issuesPath: $projectOptionsDataEl.data('issues-path'), - issuesDisabled: $projectOptionsDataEl.data('issues-disabled'), - mrPath: $projectOptionsDataEl.data('mr-path'), + issuesPath: $projectOptionsDataEl.data('issuesPath'), + issuesDisabled: $projectOptionsDataEl.data('issuesDisabled'), + mrPath: $projectOptionsDataEl.data('mrPath'), }; } if ($groupOptionsDataEl.length) { gl.groupOptions = gl.groupOptions || {}; - var groupPath = $groupOptionsDataEl.data('group-path'); + var groupPath = $groupOptionsDataEl.data('groupPath'); gl.groupOptions[groupPath] = { name: $groupOptionsDataEl.data('name'), - issuesPath: $groupOptionsDataEl.data('issues-path'), - mrPath: $groupOptionsDataEl.data('mr-path'), + issuesPath: $groupOptionsDataEl.data('issuesPath'), + mrPath: $groupOptionsDataEl.data('mrPath'), }; } if ($dashboardOptionsDataEl.length) { gl.dashboardOptions = { - issuesPath: $dashboardOptionsDataEl.data('issues-path'), - mrPath: $dashboardOptionsDataEl.data('mr-path'), + issuesPath: $dashboardOptionsDataEl.data('issuesPath'), + mrPath: $dashboardOptionsDataEl.data('mrPath'), }; } } @@ -61,9 +62,9 @@ export default class SearchAutocomplete { this.bindEventContext(); this.wrap = wrap || $('.search'); this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts'); - this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path'); - this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || ''); - this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || ''); + this.autocompletePath = autocompletePath || this.optsEl.data('autocompletePath'); + this.projectId = projectId || (this.optsEl.data('autocompleteProjectId') || ''); + this.projectRef = projectRef || (this.optsEl.data('autocompleteProjectRef') || ''); this.dropdown = this.wrap.find('.dropdown'); this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle'); this.dropdownContent = this.dropdown.find('.dropdown-content'); diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index 9d22b9d77be..0686910fc7e 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -1,5 +1,5 @@ <script> - import Flash from '../../../flash'; + import Flash from '~/flash'; import editForm from './edit_form.vue'; import issuableMixin from '../../../vue_shared/mixins/issuable'; import Icon from '../../../vue_shared/components/icon.vue'; @@ -53,8 +53,7 @@ discussion_locked: locked, }) .then(() => location.reload()) - .catch(() => Flash(this.__(`Something went wrong trying to - change the locked state of this ${this.issuableDisplayName}`))); + .catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`))); }, }, }; diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js index 977dd83a7ea..b10e2cc60ef 100644 --- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js +++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js @@ -50,7 +50,7 @@ class SidebarMoveIssue { const selectedProjectId = options.isMarking ? project.id : 0; this.mediator.setMoveToProjectId(selectedProjectId); - this.$confirmButton.attr('disabled', !isValidProjectId(selectedProjectId)); + this.$confirmButton.prop('disabled', !isValidProjectId(selectedProjectId)); }, }); } diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js new file mode 100644 index 00000000000..b15ad0e5586 --- /dev/null +++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js @@ -0,0 +1,27 @@ +import Vue from 'vue'; +import timeTracker from './components/time_tracking/time_tracker.vue'; + +export default class SidebarMilestone { + constructor() { + const el = document.getElementById('issuable-time-tracker'); + + if (!el) return; + + // eslint-disable-next-line no-new + new Vue({ + el, + components: { + timeTracker, + }, + render: createElement => createElement('timeTracker', { + props: { + time_estimate: parseInt(el.dataset.timeEstimate, 10), + time_spent: parseInt(el.dataset.timeSpent, 10), + human_time_estimate: el.dataset.humanTimeEstimate, + human_time_spent: el.dataset.humanTimeSpent, + rootPath: '/', + }, + }), + }); + } +} diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 48dd91bdf06..6142ce6c6a3 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -18,7 +18,7 @@ export default class SingleFileDiff { this.toggleDiff = this.toggleDiff.bind(this); this.content = $('.diff-content', this.file); this.$toggleIcon = $('.diff-toggle-caret', this.file); - this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path'); + this.diffForPath = this.content.find('[data-diff-for-path]').data('diffForPath'); this.isOpen = !this.diffForPath; if (this.diffForPath) { this.collapsedContent = this.content; @@ -88,6 +88,8 @@ export default class SingleFileDiff { if (cb) cb(); }) - .catch(createFlash(__('An error occurred while retrieving diff'))); + .catch(() => { + createFlash(__('An error occurred while retrieving diff')); + }); } } diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js index d5606e153f6..3deb629d5f2 100644 --- a/app/assets/javascripts/star.js +++ b/app/assets/javascripts/star.js @@ -1,17 +1,20 @@ import Flash from './flash'; import { __, s__ } from './locale'; import { spriteIcon } from './lib/utils/common_utils'; +import axios from './lib/utils/axios_utils'; export default class Star { constructor() { - $('.project-home-panel .toggle-star') - .on('ajax:success', function handleSuccess(e, data) { - const $this = $(this); - const $starSpan = $this.find('span'); - const $startIcon = $this.find('svg'); + $('.project-home-panel .toggle-star').on('click', function toggleStarClickCallback() { + const $this = $(this); + const $starSpan = $this.find('span'); + const $startIcon = $this.find('svg'); - function toggleStar(isStarred) { + axios.post($this.data('endpoint')) + .then(({ data }) => { + const isStarred = $starSpan.hasClass('starred'); $this.parent().find('.star-count').text(data.star_count); + if (isStarred) { $starSpan.removeClass('starred').text(s__('StarProject|Star')); $startIcon.remove(); @@ -21,12 +24,8 @@ export default class Star { $startIcon.remove(); $this.prepend(spriteIcon('star')); } - } - - toggleStar($starSpan.hasClass('starred')); - }) - .on('ajax:error', () => { - Flash('Star toggle failed. Try again later.', 'alert'); - }); + }) + .catch(() => Flash('Star toggle failed. Try again later.')); + }); } } diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js index 1ab4c2229ca..3ed064f87a9 100644 --- a/app/assets/javascripts/subscription_select.js +++ b/app/assets/javascripts/subscription_select.js @@ -1,6 +1,6 @@ export default function subscriptionSelect() { $('.js-subscription-event').each((i, element) => { - const fieldName = $(element).data('field-name'); + const fieldName = $(element).data('fieldName'); return $(element).glDropdown({ selectable: true, diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js index 129a551cbcd..8fa78b636f8 100644 --- a/app/assets/javascripts/task_list.js +++ b/app/assets/javascripts/task_list.js @@ -40,7 +40,7 @@ export default class TaskList { [this.fieldName]: $target.val(), }; - return axios.patch($target.data('update-url') || $('form.js-issuable-update').attr('action'), patchData) + return axios.patch($target.data('updateUrl') || $('form.js-issuable-update').attr('action'), patchData) .then(({ data }) => this.onSuccess(data)) .catch(err => this.onError(err)); } diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js index 4cc1c96b870..b5b64f44a11 100644 --- a/app/assets/javascripts/templates/issuable_template_selector.js +++ b/app/assets/javascripts/templates/issuable_template_selector.js @@ -6,9 +6,9 @@ import TemplateSelector from '../blob/template_selector'; export default class IssuableTemplateSelector extends TemplateSelector { constructor(...args) { super(...args); - this.projectPath = this.dropdown.data('project-path'); - this.namespacePath = this.dropdown.data('namespace-path'); - this.issuableType = this.$dropdownContainer.data('issuable-type'); + this.projectPath = this.dropdown.data('projectPath'); + this.namespacePath = this.dropdown.data('namespacePath'); + this.issuableType = this.$dropdownContainer.data('issuableType'); this.titleInput = $(`#${this.issuableType}_title`); const initialQuery = { diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js index f503076715c..78dda172ee6 100644 --- a/app/assets/javascripts/ui_development_kit.js +++ b/app/assets/javascripts/ui_development_kit.js @@ -1,6 +1,6 @@ import Api from './api'; -document.addEventListener('DOMContentLoaded', () => { +export default () => { $('#js-project-dropdown').glDropdown({ data: (term, callback) => { Api.projects(term, { @@ -19,4 +19,4 @@ document.addEventListener('DOMContentLoaded', () => { id: data => data.id, isSelected: data => (data.id === 2), }); -}); +}; diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js index a45b22f3084..a783122d500 100644 --- a/app/assets/javascripts/user_callout.js +++ b/app/assets/javascripts/user_callout.js @@ -22,7 +22,7 @@ export default class UserCallout { const $currentTarget = $(e.currentTarget); if (this.options.setCalloutPerProject) { - Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('project-path') }); + Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('projectPath') }); } else { Cookies.set(this.cookieName, 'true', { expires: 365 }); } diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index eaed81cf79e..8958534689c 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -34,23 +34,23 @@ function UsersSelect(currentUser, els, options = {}) { var options = {}; var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove; $dropdown = $(dropdown); - options.projectId = $dropdown.data('project-id'); - options.groupId = $dropdown.data('group-id'); - options.showCurrentUser = $dropdown.data('current-user'); - options.todoFilter = $dropdown.data('todo-filter'); - options.todoStateFilter = $dropdown.data('todo-state-filter'); - options.perPage = $dropdown.data('per-page'); - showNullUser = $dropdown.data('null-user'); - defaultNullUser = $dropdown.data('null-user-default'); + options.projectId = $dropdown.data('projectId'); + options.groupId = $dropdown.data('groupId'); + options.showCurrentUser = $dropdown.data('currentUser'); + options.todoFilter = $dropdown.data('todoFilter'); + options.todoStateFilter = $dropdown.data('todoStateFilter'); + options.perPage = $dropdown.data('perPage'); + showNullUser = $dropdown.data('nullUser'); + defaultNullUser = $dropdown.data('nullUserDefault'); showMenuAbove = $dropdown.data('showMenuAbove'); - showAnyUser = $dropdown.data('any-user'); - firstUser = $dropdown.data('first-user'); - options.authorId = $dropdown.data('author-id'); - defaultLabel = $dropdown.data('default-label'); + showAnyUser = $dropdown.data('anyUser'); + firstUser = $dropdown.data('firstUser'); + options.authorId = $dropdown.data('authorId'); + defaultLabel = $dropdown.data('defaultLabel'); issueURL = $dropdown.data('issueUpdate'); $selectbox = $dropdown.closest('.selectbox'); $block = $selectbox.closest('.block'); - abilityName = $dropdown.data('ability-name'); + abilityName = $dropdown.data('abilityName'); $value = $block.find('.value'); $collapsedSidebar = $block.find('.sidebar-collapsed-user'); $loading = $block.find('.block-loading').fadeOut(); @@ -63,7 +63,7 @@ function UsersSelect(currentUser, els, options = {}) { const assignYourself = function () { const unassignedSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=0]`); + .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`); if (unassignedSelected) { unassignedSelected.remove(); @@ -72,7 +72,7 @@ function UsersSelect(currentUser, els, options = {}) { // Save current selected user to the DOM const input = document.createElement('input'); input.type = 'hidden'; - input.name = $dropdown.data('field-name'); + input.name = $dropdown.data('fieldName'); const currentUserInfo = $dropdown.data('currentUserInfo'); @@ -96,7 +96,7 @@ function UsersSelect(currentUser, els, options = {}) { const getSelectedUserInputs = function() { return $selectbox - .find(`input[name="${$dropdown.data('field-name')}"]`); + .find(`input[name="${$dropdown.data('fieldName')}"]`); }; const getSelected = function() { @@ -106,14 +106,14 @@ function UsersSelect(currentUser, els, options = {}) { }; const checkMaxSelect = function() { - const maxSelect = $dropdown.data('max-select'); + const maxSelect = $dropdown.data('maxSelect'); if (maxSelect) { const selected = getSelected(); if (selected.length > maxSelect) { const firstSelectedId = selected[0]; const firstSelected = $dropdown.closest('.selectbox') - .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`); + .find(`input[name='${$dropdown.data('fieldName')}'][value=${firstSelectedId}]`); firstSelected.remove(); emitSidebarEvent('sidebar.removeAssignee', { @@ -158,7 +158,7 @@ function UsersSelect(currentUser, els, options = {}) { const currentUserInfo = $dropdown.data('currentUserInfo'); $dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default'); } else { - const $input = $(`input[name="${$dropdown.data('field-name')}"]`); + const $input = $(`input[name="${$dropdown.data('fieldName')}"]`); $input.val(gon.current_user_id); selectedId = $input.val(); $dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default'); @@ -293,10 +293,10 @@ function UsersSelect(currentUser, els, options = {}) { const selected = getSelected().filter(i => i !== 0); if (selected.length > 0) { - if ($dropdown.data('dropdown-header')) { + if ($dropdown.data('dropdownHeader')) { showDivider += 1; users.splice(showDivider, 0, { - header: $dropdown.data('dropdown-header'), + header: $dropdown.data('dropdownHeader'), }); } @@ -327,7 +327,7 @@ function UsersSelect(currentUser, els, options = {}) { fields: ['name', 'username'] }, selectable: true, - fieldName: $dropdown.data('field-name'), + fieldName: $dropdown.data('fieldName'), toggleLabel: function(selected, el, glDropdown) { const inputValue = glDropdown.filterInput.val(); @@ -362,7 +362,7 @@ function UsersSelect(currentUser, els, options = {}) { emitSidebarEvent('sidebar.saveAssignees'); } - if (!$dropdown.data('always-show-selectbox')) { + if (!$dropdown.data('alwaysShowSelectbox')) { $selectbox.hide(); // Recalculate where .value is because vue might have changed it @@ -373,7 +373,7 @@ function UsersSelect(currentUser, els, options = {}) { } }, multiSelect: $dropdown.hasClass('js-multiselect'), - inputMeta: $dropdown.data('input-meta'), + inputMeta: $dropdown.data('inputMeta'), clicked: function(options) { const { $el, e, isMarking } = options; const user = options.selectedObj; @@ -381,7 +381,7 @@ function UsersSelect(currentUser, els, options = {}) { if ($dropdown.hasClass('js-multiselect')) { const isActive = $el.hasClass('is-active'); const previouslySelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]"); + .find("input[name='" + ($dropdown.data('fieldName')) + "'][value!=0]"); // Enables support for limiting the number of users selected // Automatically removes the first on the list if more users are selected @@ -400,7 +400,7 @@ function UsersSelect(currentUser, els, options = {}) { // Remove unassigned selection (if it was previously selected) const unassignedSelected = $dropdown.closest('.selectbox') - .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]"); + .find("input[name='" + ($dropdown.data('fieldName')) + "'][value=0]"); if (unassignedSelected) { unassignedSelected.remove(); @@ -408,7 +408,7 @@ function UsersSelect(currentUser, els, options = {}) { } else { if (previouslySelected.length === 0) { // Select unassigned because there is no more selected users - this.addInput($dropdown.data('field-name'), 0, {}); + this.addInput($dropdown.data('fieldName'), 0, {}); } // User unselected @@ -440,7 +440,7 @@ function UsersSelect(currentUser, els, options = {}) { return; } if ($el.closest('.add-issues-modal').length) { - gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id; + gl.issueBoards.ModalStore.store.filter[$dropdown.data('fieldName')] = user.id; } else if (handleClick) { e.preventDefault(); handleClick(user, isMarking); @@ -449,15 +449,15 @@ function UsersSelect(currentUser, els, options = {}) { } else if ($dropdown.hasClass('js-filter-submit')) { return $dropdown.closest('form').submit(); } else if (!$dropdown.hasClass('js-multiselect')) { - selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val(); + selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('fieldName')) + "']").val(); return assignTo(selected); } // Automatically close dropdown after assignee is selected // since CE has no multiple assignees // EE does not have a max-select - if ($dropdown.data('max-select') && - getSelected().length === $dropdown.data('max-select')) { + if ($dropdown.data('maxSelect') && + getSelected().length === $dropdown.data('maxSelect')) { // Close the dropdown $dropdown.dropdown('toggle'); } @@ -469,7 +469,7 @@ function UsersSelect(currentUser, els, options = {}) { const $el = $(e.currentTarget); const selected = getSelected(); if ($dropdown.hasClass('js-issue-board-sidebar') && selected.length === 0) { - this.addInput($dropdown.data('field-name'), 0, {}); + this.addInput($dropdown.data('fieldName'), 0, {}); } $el.find('.is-active').removeClass('is-active'); @@ -485,7 +485,7 @@ function UsersSelect(currentUser, els, options = {}) { highlightSelected(selectedId); } }, - updateLabel: $dropdown.data('dropdown-title'), + updateLabel: $dropdown.data('dropdownTitle'), renderRow: function(user) { var avatar, img, listClosingTags, listWithName, listWithUserName, username; username = user.username ? "@" + user.username : ""; @@ -533,15 +533,15 @@ function UsersSelect(currentUser, els, options = {}) { var firstUser, showAnyUser, showEmailUser, showNullUser; var options = {}; options.skipLdap = $(select).hasClass('skip_ldap'); - options.projectId = $(select).data('project-id'); - options.groupId = $(select).data('group-id'); - options.showCurrentUser = $(select).data('current-user'); - options.authorId = $(select).data('author-id'); - options.skipUsers = $(select).data('skip-users'); - showNullUser = $(select).data('null-user'); - showAnyUser = $(select).data('any-user'); - showEmailUser = $(select).data('email-user'); - firstUser = $(select).data('first-user'); + options.projectId = $(select).data('projectId'); + options.groupId = $(select).data('groupId'); + options.showCurrentUser = $(select).data('currentUser'); + options.authorId = $(select).data('authorId'); + options.skipUsers = $(select).data('skipUsers'); + showNullUser = $(select).data('nullUser'); + showAnyUser = $(select).data('anyUser'); + showEmailUser = $(select).data('emailUser'); + firstUser = $(select).data('firstUser'); return $(select).select2({ placeholder: "Search for a user", multiple: $(select).hasClass('multiselect'), diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue index 40c3cb500bb..ebaf2b972eb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue @@ -44,7 +44,10 @@ type="button" class="btn btn-xs btn-default" > - <loading-icon v-if="isRefreshing" /> + <loading-icon + v-if="isRefreshing" + :inline="true" + /> {{ s__("mrWidget|Refresh") }} </button> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js deleted file mode 100644 index cea3d97fa88..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js +++ /dev/null @@ -1,19 +0,0 @@ -import statusIcon from '../mr_widget_status_icon.vue'; - -export default { - name: 'MRWidgetNotAllowed', - components: { - statusIcon, - }, - template: ` - <div class="mr-widget-body media"> - <status-icon status="success" :show-disabled-button="true" /> - <div class="media-body space-children"> - <span class="bold"> - Ready to be merged automatically. - Ask someone with write access to this repository to merge this request - </span> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue new file mode 100644 index 00000000000..e4af50b09f8 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue @@ -0,0 +1,25 @@ +<script> + import StatusIcon from '../mr_widget_status_icon.vue'; + + export default { + name: 'MRWidgetNotAllowed', + components: { + StatusIcon, + }, + }; +</script> + +<template> + <div class="mr-widget-body media"> + <status-icon + status="success" + :show-disabled-button="true" + /> + <div class="media-body space-children"> + <span class="bold"> + {{ s__(`mrWidget|Ready to be merged automatically. +Ask someone with write access to this repository to merge this request`) }} + </span> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js deleted file mode 100644 index e66ce071ab4..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js +++ /dev/null @@ -1,18 +0,0 @@ -import statusIcon from '../mr_widget_status_icon.vue'; - -export default { - name: 'MRWidgetPipelineBlocked', - components: { - statusIcon, - }, - template: ` - <div class="mr-widget-body media"> - <status-icon status="warning" :show-disabled-button="true" /> - <div class="media-body space-children"> - <span class="bold"> - Pipeline blocked. The pipeline for this merge request requires a manual action to proceed - </span> - </div> - </div> - `, -}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue new file mode 100644 index 00000000000..6d7cc03f7ad --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue @@ -0,0 +1,24 @@ +<script> + import StatusIcon from '../mr_widget_status_icon.vue'; + + export default { + name: 'MRWidgetPipelineBlocked', + components: { + StatusIcon, + }, + }; +</script> +<template> + <div class="mr-widget-body media"> + <status-icon + status="warning" + :show-disabled-button="true" + /> + <div class="media-body space-children"> + <span class="bold"> + {{ s__(`mrWidget|Pipeline blocked. +The pipeline for this merge request requires a manual action to proceed`) }} + </span> + </div> + </div> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue index 2968af0d5cb..e9f23b0b113 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue @@ -107,7 +107,8 @@ <template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest"> <div class="accept-merge-holder clearfix -js-toggle-container accept-action media space-children"> +js-toggle-container accept-action media space-children" + > <button type="button" class="btn btn-sm btn-reopen btn-success" diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js index 5517888c3b1..edb3baa39e4 100644 --- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js +++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js @@ -25,11 +25,11 @@ export { default as ArchivedState } from './components/states/mr_widget_archived export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue'; export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue'; -export { default as NotAllowedState } from './components/states/mr_widget_not_allowed'; +export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue'; export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge'; export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch'; export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions'; -export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked'; +export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js index edf67fcd0a7..797f0f6ec0f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js @@ -96,9 +96,7 @@ export default { cb.call(null, data); } }) - .catch(() => { - new Flash('Something went wrong. Please try again.'); // eslint-disable-line - }); + .catch(() => new Flash('Something went wrong. Please try again.')); }, initPolling() { this.pollingInterval = new SmartInterval({ @@ -146,12 +144,11 @@ export default { Project.initRefSwitcher(); } }) - .catch(() => { - new Flash('Something went wrong. Please try again.'); // eslint-disable-line - }); + .catch(() => new Flash('Something went wrong. Please try again.')); }, handleNotification(data) { if (data.ci_status === this.mr.ciStatus) return; + if (!data.pipeline) return; const label = data.pipeline.details.status.label; const title = `Pipeline ${label}`; diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index ed004b3bb08..9a750ce42bd 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -4,7 +4,6 @@ import { stateKey } from './state_maps'; import { formatDate } from '../../lib/utils/datetime_utility'; export default class MergeRequestStore { - constructor(data) { this.sha = data.diff_head_sha; this.gitlabLogo = data.gitlabLogo; @@ -169,5 +168,4 @@ export default class MergeRequestStore { return timeagoInstance.format(date); } - } diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue new file mode 100644 index 00000000000..67c9181c7b1 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue @@ -0,0 +1,106 @@ +<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">×</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> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index 1f72dea1b33..a0cd0cbd200 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -6,12 +6,12 @@ import userAvatarImage from './user_avatar/user_avatar_image.vue'; /** - * Renders header component for job and pipeline page based on UI mockups - * - * Used in: - * - job show page - * - pipeline show page - */ + * Renders header component for job and pipeline page based on UI mockups + * + * Used in: + * - job show page + * - pipeline show page + */ export default { components: { ciIconBadge, @@ -118,7 +118,8 @@ <section class="header-action-buttons" - v-if="actions.length"> + v-if="actions.length" + > <template v-for="(action, i) in actions" > diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue index 6ae6b179f7f..e832d94d32f 100644 --- a/app/assets/javascripts/vue_shared/components/loading_button.vue +++ b/app/assets/javascripts/vue_shared/components/loading_button.vue @@ -1,6 +1,5 @@ <script> /* eslint-disable vue/require-default-prop */ - /* This is a re-usable vue component for rendering a button that will probably be sending off ajax requests and need to show the loading status by setting the `loading` option. diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 1371dca0c35..d2e968a8419 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -64,7 +64,7 @@ return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete); }, beforeDestroy() { - const glForm = $(this.$refs['gl-form']).data('gl-form'); + const glForm = $(this.$refs['gl-form']).data('glForm'); if (glForm) { glForm.destroy(); } diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index f65eab11a27..177d2cfc8da 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -65,7 +65,8 @@ </li> <li class="md-header-tab" - :class="{ active: previewMarkdown }"> + :class="{ active: previewMarkdown }" + > <a class="js-preview-link" href="#md-preview-holder" diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index c44c606a8b2..22fc5757447 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -13,6 +13,12 @@ props: { /** This function will take the information given by the pagination component + + Here is an example `change` method: + + change(pagenum) { + gl.utils.visitUrl(`?page=${pagenum}`); + }, */ change: { type: Function, diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index a2ea155a10e..2c30311b1c1 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -167,7 +167,7 @@ label { .input-group { .select2-container { display: table-cell; - width: 200px !important; + max-width: 180px; } .input-group-addon { diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index dcd98cb522f..7e829826eba 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -255,8 +255,6 @@ ul.controls { } .author_link { - display: inline-block; - .avatar-inline { margin-left: 0; margin-right: 0; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index dbee7073975..b40dcf93969 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -8,7 +8,7 @@ .select2-choice { background: $white-light; border-color: $input-border; - height: 35px; + height: 34px; padding: $gl-vert-padding $gl-input-padding; font-size: $gl-font-size; line-height: 1.42857143; diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss index 4869cda73e5..528ba53a48b 100644 --- a/app/assets/stylesheets/framework/stacked_progress_bar.scss +++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss @@ -10,7 +10,7 @@ .status-neutral, .status-red, { height: 100%; - min-width: 25px; + min-width: 30px; padding: 0 5px; font-size: $tooltip-font-size; font-weight: normal; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 945978d2f76..a5a8f6d2206 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -562,6 +562,7 @@ $jq-ui-default-color: #777; /* * Label */ +$label-font-size: 12px; $label-padding: 7px; $label-padding-modal: 10px; $label-gray-bg: #f8fafc; diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index da096354b5a..8871a069d5d 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -72,7 +72,6 @@ .label { color: $gl-text-color; - font-size: inherit; } iframe.twitter-share-button { diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3fe95b34e01..0cf67734237 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -102,12 +102,11 @@ .issuable-show-labels { a { - margin-right: 5px; margin-bottom: 5px; display: inline-block; .color-label { - padding: 6px 10px; + padding: 4px $grid-size; border-radius: $label-border-radius; } diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index a72e654824e..0f49d15203b 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -105,13 +105,16 @@ } .label { - padding: 8px 12px; - font-size: 14px; + padding: 4px $grid-size; + font-size: $label-font-size; + position: relative; + top: ($grid-size / 2); } } .color-label { - padding: 3px $label-padding; + padding: 0 $grid-size; + line-height: 16px; border-radius: $label-border-radius; } @@ -302,10 +305,11 @@ } .label-link { - display: inline-block; + display: inline-flex; vertical-align: top; .label { vertical-align: inherit; + font-size: $label-font-size; } } diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index ae8fa45a2d7..e5afa8fffcb 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -115,6 +115,10 @@ display: block; margin-top: 7px; + .issue-link { + display: inline-block; + } + .issuable-number { color: $gl-text-color-secondary; margin-right: 5px; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index f10908c3630..42772f13155 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -320,14 +320,17 @@ } } -// Pipeline graph -.pipeline-graph { +.pipeline-tab-content { width: 100%; background-color: $gray-light; padding: $gl-padding; + overflow: auto; +} + +// Pipeline graph +.pipeline-graph { white-space: nowrap; transition: max-height 0.3s, padding 0.3s; - overflow: auto; .stage-column-list, .builds-container > ul { diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index a79772ea37b..4b9824fab0c 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -128,7 +128,6 @@ .label { color: $gl-text-color; - font-size: inherit; } p { diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 0d7ee06deb6..f7ba305a59f 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -103,7 +103,7 @@ module IssuableCollections # @filter_params[:authorized_only] = true end - @filter_params.permit(IssuableFinder::VALID_PARAMS) + @filter_params.permit(finder_type.valid_params) end # rubocop:enable Gitlab/ModuleWithInstanceVariables @@ -146,7 +146,7 @@ module IssuableCollections def finder strong_memoize(:finder) do - issuable_finder_for(@finder_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables + issuable_finder_for(finder_type) end end diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index 3ba1235cee0..3b11a373368 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -4,7 +4,6 @@ module IssuesAction # rubocop:disable Gitlab/ModuleWithInstanceVariables def issues - @finder_type = IssuesFinder @issues = issuables_collection .non_archived .page(params[:page]) @@ -17,4 +16,11 @@ module IssuesAction end end # rubocop:enable Gitlab/ModuleWithInstanceVariables + + private + + def finder_type + (super if defined?(super)) || + (IssuesFinder if action_name == 'issues') + end end diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index a9cc13038bf..b70db99b157 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -4,8 +4,6 @@ module MergeRequestsAction # rubocop:disable Gitlab/ModuleWithInstanceVariables def merge_requests - @finder_type = MergeRequestsFinder - @merge_requests = issuables_collection.page(params[:page]) @issuable_meta_data = issuable_meta_data(@merge_requests, collection_type) @@ -14,6 +12,11 @@ module MergeRequestsAction private + def finder_type + (super if defined?(super)) || + (MergeRequestsFinder if action_name == 'merge_requests') + end + def filter_params super.merge(non_archived: true) end diff --git a/app/controllers/projects/clusters/gcp_controller.rb b/app/controllers/projects/clusters/gcp_controller.rb index 94d33b91562..0f41af7d87b 100644 --- a/app/controllers/projects/clusters/gcp_controller.rb +++ b/app/controllers/projects/clusters/gcp_controller.rb @@ -39,12 +39,12 @@ class Projects::Clusters::GcpController < Projects::ApplicationController def verify_billing case google_project_billing_status - when 'true' - return - when 'false' - flash[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" } - else + when nil flash[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.') + when false + flash[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" } + when true + return end @cluster = ::Clusters::Cluster.new(create_params) @@ -81,9 +81,7 @@ class Projects::Clusters::GcpController < Projects::ApplicationController end def google_project_billing_status - Gitlab::Redis::SharedState.with do |redis| - redis.get(CheckGcpProjectBillingWorker.redis_shared_state_key_for(token_in_session)) - end + CheckGcpProjectBillingWorker.get_billing_state(token_in_session) end def token_in_session diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 33fced99132..73806454525 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -243,9 +243,8 @@ class Projects::IssuesController < Projects::ApplicationController Issues::UpdateService.new(project, current_user, update_params) end - def set_issuables_index - @finder_type = IssuesFinder - super + def finder_type + IssuesFinder end def whitelist_query_limiting diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 8eed957d9fe..a1af125547c 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -323,9 +323,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @target_branches = @merge_request.target_project.repository.branch_names end - def set_issuables_index - @finder_type = MergeRequestsFinder - super + def finder_type + MergeRequestsFinder end def check_user_can_push_to_source_branch! diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 72573e0765d..0370edc6e20 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -279,7 +279,6 @@ class ProjectsController < Projects::ApplicationController @project_wiki = @project.wiki @wiki_home = @project_wiki.find_page('home', params[:version_id]) elsif @project.feature_available?(:issues, current_user) - @finder_type = IssuesFinder @issues = issuables_collection.page(params[:page]) @collection_type = 'Issue' @issuable_meta_data = issuable_meta_data(@issues, @collection_type) @@ -289,6 +288,10 @@ class ProjectsController < Projects::ApplicationController end end + def finder_type + IssuesFinder + end + def determine_layout if [:new, :create].include?(action_name.to_sym) 'application' diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 63e5fdb1da5..8acefd58e77 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -13,7 +13,10 @@ class RootController < Dashboard::ProjectsController before_action :redirect_logged_user, if: -> { current_user.present? } def index - super + # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37434 + Gitlab::GitalyClient.allow_n_plus_1_calls do + super + end end private diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 0fe3000ca01..384a336e2bb 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -25,32 +25,38 @@ class IssuableFinder NONE = '0'.freeze - SCALAR_PARAMS = %i[ - assignee_id - assignee_username - author_id - author_username - authorized_only - due_date - group_id - iids - label_name - milestone_title - my_reaction_emoji - non_archived - project_id - scope - search - sort - state - include_subgroups - ].freeze - ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze - - VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze - attr_accessor :current_user, :params + def self.scalar_params + @scalar_params ||= %i[ + assignee_id + assignee_username + author_id + author_username + authorized_only + group_id + iids + label_name + milestone_title + my_reaction_emoji + non_archived + project_id + scope + search + sort + state + include_subgroups + ] + end + + def self.array_params + @array_params ||= { label_name: [], iids: [], assignee_username: [] } + end + + def self.valid_params + @valid_params ||= scalar_params + [array_params] + end + def initialize(current_user, params = {}) @current_user = current_user @params = params @@ -58,6 +64,15 @@ class IssuableFinder def execute items = init_collection + items = filter_items(items) + + # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far + items = by_project(items) + + sort(items) + end + + def filter_items(items) items = by_scope(items) items = by_created_at(items) items = by_state(items) @@ -65,16 +80,11 @@ class IssuableFinder items = by_search(items) items = by_assignee(items) items = by_author(items) - items = by_due_date(items) items = by_non_archived(items) items = by_iids(items) items = by_milestone(items) items = by_label(items) - items = by_my_reaction_emoji(items) - - # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far - items = by_project(items) - sort(items) + by_my_reaction_emoji(items) end def find(*params) @@ -396,42 +406,6 @@ class IssuableFinder items end - def by_due_date(items) - if due_date? - if filter_by_no_due_date? - items = items.without_due_date - elsif filter_by_overdue? - items = items.due_before(Date.today) - elsif filter_by_due_this_week? - items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) - elsif filter_by_due_this_month? - items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) - end - end - - items - end - - def filter_by_no_due_date? - due_date? && params[:due_date] == Issue::NoDueDate.name - end - - def filter_by_overdue? - due_date? && params[:due_date] == Issue::Overdue.name - end - - def filter_by_due_this_week? - due_date? && params[:due_date] == Issue::DueThisWeek.name - end - - def filter_by_due_this_month? - due_date? && params[:due_date] == Issue::DueThisMonth.name - end - - def due_date? - params[:due_date].present? && klass.column_names.include?('due_date') - end - def label_names if labels? params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name] diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 98831f5be4a..d65c620e75a 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -16,12 +16,17 @@ # sort: string # my_reaction_emoji: string # public_only: boolean +# due_date: date or '0', '', 'overdue', 'week', or 'month' # class IssuesFinder < IssuableFinder CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER + def self.scalar_params + @scalar_params ||= super + [:due_date] + end + def klass - Issue + Issue.includes(:author) end def with_confidentiality_access_check @@ -52,6 +57,46 @@ class IssuesFinder < IssuableFinder params.fetch(:public_only, false) end + def filter_items(items) + by_due_date(super) + end + + def by_due_date(items) + if due_date? + if filter_by_no_due_date? + items = items.without_due_date + elsif filter_by_overdue? + items = items.due_before(Date.today) + elsif filter_by_due_this_week? + items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) + elsif filter_by_due_this_month? + items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) + end + end + + items + end + + def filter_by_no_due_date? + due_date? && params[:due_date] == Issue::NoDueDate.name + end + + def filter_by_overdue? + due_date? && params[:due_date] == Issue::Overdue.name + end + + def filter_by_due_this_week? + due_date? && params[:due_date] == Issue::DueThisWeek.name + end + + def filter_by_due_this_month? + due_date? && params[:due_date] == Issue::DueThisMonth.name + end + + def due_date? + params[:due_date].present? + end + def user_can_see_all_confidential_issues? return @user_can_see_all_confidential_issues if defined?(@user_can_see_all_confidential_issues) diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index af24045886e..4734d97b8c7 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -10,26 +10,59 @@ class MembersFinder def execute project_members = project.project_members project_members = project_members.non_invite unless can?(current_user, :admin_project, project) - wheres = ["members.id IN (#{project_members.select(:id).to_sql})"] if group - # We need `.where.not(user_id: nil)` here otherwise when a group has an - # invitee, it would make the following query return 0 rows since a NULL - # user_id would be present in the subquery - # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values - non_null_user_ids = project_members.where.not(user_id: nil).select(:user_id) - group_members = GroupMembersFinder.new(group).execute - group_members = group_members.where.not(user_id: non_null_user_ids) - group_members = group_members.non_invite unless can?(current_user, :admin_group, group) + group_members = group_members.non_invite - wheres << "members.id IN (#{group_members.select(:id).to_sql})" - end + union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false) - Member.where(wheres.join(' OR ')) + sql = distinct_on(union) + + Member.includes(:user).from("(#{sql}) AS #{Member.table_name}") + else + project_members + end end def can?(*args) Ability.allowed?(*args) end + + private + + def distinct_on(union) + # We're interested in a list of members without duplicates by user_id. + # We prefer project members over group members, project members should go first. + if Gitlab::Database.postgresql? + <<~SQL + SELECT DISTINCT ON (user_id, invite_email) member_union.* + FROM (#{union.to_sql}) AS member_union + ORDER BY user_id, + invite_email, + CASE + WHEN type = 'ProjectMember' THEN 1 + WHEN type = 'GroupMember' THEN 2 + ELSE 3 + END + SQL + else + # Older versions of MySQL do not support window functions (and DISTINCT ON is postgres-specific). + <<~SQL + SELECT t1.* + FROM (#{union.to_sql}) AS t1 + JOIN ( + SELECT + COALESCE(user_id, -1) AS user_id, + COALESCE(invite_email, 'NULL') AS invite_email, + MIN(CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END) AS type_number + FROM + (#{union.to_sql}) AS t3 + GROUP BY COALESCE(user_id, -1), COALESCE(invite_email, 'NULL') + ) AS t2 ON COALESCE(t1.user_id, -1) = t2.user_id + AND COALESCE(t1.invite_email, 'NULL') = t2.invite_email + AND CASE WHEN t1.type = 'ProjectMember' THEN 1 WHEN t1.type = 'GroupMember' THEN 2 ELSE 3 END = t2.type_number + SQL + end + end end diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index 12157818bcd..33ee1e975b9 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -57,7 +57,7 @@ class NotesFinder types = %w(commit issue merge_request snippet) note_relations = types.map { |t| notes_for_type(t) } note_relations.map! { |notes| search(notes) } - UnionFinder.new.find_union(note_relations, Note) + UnionFinder.new.find_union(note_relations, Note.includes(:author)) end def noteables_for_type(noteable_type) diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index 33359fa1efb..ec61fe1892e 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -56,8 +56,10 @@ class SnippetsFinder < UnionFinder end def feature_available_projects - projects = Project.public_or_visible_to_user(current_user) - .with_feature_available_for_user(:snippets, current_user).select(:id) + projects = Project.public_or_visible_to_user(current_user, use_where_in: false) do |part| + part.with_feature_available_for_user(:snippets, current_user) + end.select(:id) + arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql) table[:project_id].in(arel_query) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 479797231cc..7b71f636397 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,18 +68,32 @@ module ApplicationHelper end end - def avatar_icon(user_or_email = nil, size = nil, scale = 2, only_path: true) - user = - if user_or_email.is_a?(User) - user_or_email - else - User.find_by_any_email(user_or_email.try(:downcase)) - end + # Takes both user and email and returns the avatar_icon by + # user (preferred) or email. + def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + elsif email + avatar_icon_for_email(email, size, scale, only_path: only_path) + else + default_avatar + end + end + + def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) + user = User.find_by_any_email(email.try(:downcase)) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + else + gravatar_icon(email, size, scale) + end + end + def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true) if user user.avatar_url(size: size, only_path: only_path) || default_avatar else - gravatar_icon(user_or_email, size, scale) + gravatar_icon(nil, size, scale) end end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index be11d453898..21b6c0a8ad5 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -8,10 +8,22 @@ module AvatarsHelper })) end + def user_avatar_url_for(options = {}) + if options[:url] + options[:url] + elsif options[:user] + avatar_icon_for_user(options[:user], options[:size]) + else + avatar_icon_for_email(options[:user_email], options[:size]) + end + end + def user_avatar_without_link(options = {}) avatar_size = options[:size] || 16 user_name = options[:user].try(:name) || options[:user_name] - avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size) + + avatar_url = user_avatar_url_for(options.merge(size: avatar_size)) + has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip] data_attributes = options[:data] || {} css_class = %W[avatar s#{avatar_size}].push(*options[:css_class]) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 0f5fc2823a3..b5ca39711bc 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -160,7 +160,7 @@ module DiffHelper end def diff_file_changed_icon(diff_file) - if diff_file.deleted_file? || diff_file.renamed_file? + if diff_file.deleted_file? "file-deletion" elsif diff_file.new_file? "file-addition" diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index b78d3072186..40ca666f1bf 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -33,7 +33,7 @@ module NamespacesHelper if namespace.is_a?(Group) group_icon(namespace) else - avatar_icon(namespace.owner.email, size) + avatar_icon_for_user(namespace.owner, size) end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index fa6811dccbf..42b5eb11ef5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -19,7 +19,7 @@ module ProjectsHelper classes = %W[avatar avatar-inline s#{opts[:size]}] classes << opts[:avatar_class] if opts[:avatar_class] - avatar = avatar_icon(author, opts[:size]) + avatar = avatar_icon_for_user(author, opts[:size]) src = opts[:lazy_load] ? nil : avatar image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar) @@ -291,6 +291,10 @@ module ProjectsHelper nav_tabs << :pipelines end + if project.external_issue_tracker + nav_tabs << :external_issue_tracker + end + tab_ability_map.each do |tab, ability| if can?(current_user, ability, project) nav_tabs << tab diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb index 9d071f2d59a..8bcced70d63 100644 --- a/app/helpers/webpack_helper.rb +++ b/app/helpers/webpack_helper.rb @@ -7,17 +7,24 @@ module WebpackHelper def webpack_controller_bundle_tags 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 - 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) rescue Webpack::Rails::Manifest::EntryPointMissingError # no bundle exists for this path end - segments.pop + route.pop end javascript_include_tag(*bundles) diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb index d76c61c369f..75cf56a51f2 100644 --- a/app/mailers/emails/members.rb +++ b/app/mailers/emails/members.rb @@ -7,18 +7,11 @@ module Emails helper_method :member_source, :member end - def member_access_requested_email(member_source_type, member_id) + def member_access_requested_email(member_source_type, member_id, recipient_notification_email) @member_source_type = member_source_type @member_id = member_id - admins = member_source.members.owners_and_masters.pluck(:notification_email) - # A project in a group can have no explicit owners/masters, in that case - # we fallbacks to the group's owners/masters. - if admins.empty? && member_source.respond_to?(:group) && member_source.group - admins = member_source.group.members.owners_and_masters.pluck(:notification_email) - end - - mail(to: admins, + mail(to: recipient_notification_email, subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) end diff --git a/app/models/blob.rb b/app/models/blob.rb index 19ad110db58..71c974b4c09 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -160,7 +160,7 @@ class Blob < SimpleDelegator if stored_externally? if rich_viewer rich_viewer.binary? - elsif Linguist::Language.find_by_filename(name).any? + elsif Linguist::Language.find_by_extension(name).any? false elsif _mime_type _mime_type.binary? diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 2abe90dd181..a72a815bfe8 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -13,7 +13,7 @@ module Ci belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule' has_many :stages - has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id + has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent has_many :variables, class_name: 'Ci::PipelineVariable' diff --git a/app/models/commit.rb b/app/models/commit.rb index 2d2d89af030..add5fcf0e79 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -116,6 +116,10 @@ class Commit raw.id end + def project_id + project.id + end + def ==(other) other.is_a?(self.class) && raw == other.raw end @@ -413,6 +417,10 @@ class Commit !!(title =~ WIP_REGEX) end + def merged_merge_request?(user) + !!merged_merge_request(user) + end + private def commit_reference(from, referable_commit_id, full: false) @@ -441,10 +449,6 @@ class Commit changes end - def merged_merge_request?(user) - !!merged_merge_request(user) - end - def merged_merge_request_no_cache(user) MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index fd6703831e4..caf8afa97f9 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -94,6 +94,14 @@ module Milestoneish Gitlab::TimeTrackingFormatter.output(total_issue_time_spent) end + def total_issue_time_estimate + @total_issue_time_estimate ||= issues.sum(:time_estimate) + end + + def human_total_issue_time_estimate + Gitlab::TimeTrackingFormatter.output(total_issue_time_estimate) + end + private def count_issues_by_state(user) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 3aed071dd49..b6cf168d60e 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -1,8 +1,8 @@ class Deployment < ActiveRecord::Base include InternalId - belongs_to :project, required: true, validate: true - belongs_to :environment, required: true, validate: true + belongs_to :project, required: true + belongs_to :environment, required: true belongs_to :user belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/environment.rb b/app/models/environment.rb index 2f6eae605ee..f78c21aebe5 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -4,7 +4,7 @@ class Environment < ActiveRecord::Base NUMBERS = '0'..'9' SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a - belongs_to :project, required: true, validate: true + belongs_to :project, required: true has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent diff --git a/app/models/event.rb b/app/models/event.rb index 8a79100de5a..75538ba196c 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -96,10 +96,6 @@ class Event < ActiveRecord::Base self.inheritance_column = 'action' - # "data" will be removed in 10.0 but it may be possible that JOINs happen that - # include this column, hence we're ignoring it as well. - ignore_column :data - class << self def model_name ActiveModel::Name.new(self, nil, 'event') diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index 2aaba2e4c90..282fd7edcb7 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -39,7 +39,7 @@ class ExternalIssue end def to_reference(_from = nil, full: nil) - id + reference_link_text end def reference_link_text(from = nil) diff --git a/app/models/identity.rb b/app/models/identity.rb index b3fa7d8176a..2b433e9b988 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -9,6 +9,7 @@ class Identity < ActiveRecord::Base validates :user_id, uniqueness: { scope: :provider } 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_extern_uid, ->(provider, extern_uid) do @@ -34,4 +35,12 @@ class Identity < ActiveRecord::Base self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid) 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 diff --git a/app/models/key.rb b/app/models/key.rb index 7406c98c99e..ae5769c0627 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -33,9 +33,8 @@ class Key < ActiveRecord::Base after_destroy :refresh_user_cache def key=(value) - value&.delete!("\n\r") - value.strip! unless value.blank? - write_attribute(:key, value) + write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil) + @public_key = nil end @@ -97,7 +96,7 @@ class Key < ActiveRecord::Base def generate_fingerprint self.fingerprint = nil - return unless self.key.present? + return unless public_key.valid? self.fingerprint = public_key.fingerprint end diff --git a/app/models/member.rb b/app/models/member.rb index c47145667b5..2d17795e62d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -314,7 +314,7 @@ class Member < ActiveRecord::Base end def notification_setting - @notification_setting ||= user.notification_settings_for(source) + @notification_setting ||= user&.notification_settings_for(source) end def notifiable?(type, opts = {}) diff --git a/app/models/project.rb b/app/models/project.rb index 03640d7d18b..8cd5cd8ab67 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -262,7 +262,7 @@ class Project < ActiveRecord::Base validates :repository_storage, presence: true, inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } - validates :variables, variable_duplicates: true + validates :variables, variable_duplicates: { scope: :environment_scope } has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent @@ -317,18 +317,42 @@ class Project < ActiveRecord::Base # Returns a collection of projects that is either public or visible to the # logged in user. - def self.public_or_visible_to_user(user = nil) - if user - authorized = user - .project_authorizations - .select(1) - .where('project_authorizations.project_id = projects.id') - - levels = Gitlab::VisibilityLevel.levels_for_user(user) - - where('EXISTS (?) OR projects.visibility_level IN (?)', authorized, levels) + # + # A caller may pass in a block to modify individual parts of + # the query, e.g. to apply .with_feature_available_for_user on top of it. + # This is useful for performance as we can stick those additional filters + # at the bottom of e.g. the UNION. + # + # Optionally, turning `use_where_in` off leads to returning a + # relation using #from instead of #where. This can perform much better + # but leads to trouble when used in conjunction with AR's #merge method. + def self.public_or_visible_to_user(user = nil, use_where_in: true, &block) + # If we don't get a block passed, use identity to avoid if/else repetitions + block = ->(part) { part } unless block_given? + + return block.call(public_to_user) unless user + + # If the user is allowed to see all projects, + # we can shortcut and just return. + return block.call(all) if user.full_private_access? + + authorized = user + .project_authorizations + .select(1) + .where('project_authorizations.project_id = projects.id') + authorized_projects = block.call(where('EXISTS (?)', authorized)) + + levels = Gitlab::VisibilityLevel.levels_for_user(user) + visible_projects = block.call(where(visibility_level: levels)) + + # We use a UNION here instead of OR clauses since this results in better + # performance. + union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')]) + + if use_where_in + where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection else - public_to_user + from("(#{union.to_sql}) AS #{table_name}") end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 30eafe31454..436a870b0c4 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -10,6 +10,8 @@ class JiraService < IssueTrackerService before_update :reset_password + alias_method :project_url, :url + # 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. diff --git a/app/models/repository.rb b/app/models/repository.rb index 1cf55fd4332..299a3f32a85 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -492,12 +492,8 @@ class Repository end def root_ref - if raw_repository - raw_repository.root_ref - else - # When the repo does not exist we raise this error so no data is cached. - raise Gitlab::Git::Repository::NoRepository - end + # 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 or raise Gitlab::Git::Repository::NoRepository # rubocop:disable Style/AndOr end cache_method :root_ref @@ -593,7 +589,15 @@ class Repository def license_key return unless exists? - Licensee.license(path).try(:key) + # 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) + # 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 cache_method :license_key diff --git a/app/models/user.rb b/app/models/user.rb index 4097fe2b5dc..f5eeba27572 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -59,6 +59,8 @@ class User < ActiveRecord::Base # Override Devise::Models::Trackable#update_tracked_fields! # to limit database writes to at most once every hour def update_tracked_fields!(request) + return if Gitlab::Database.read_only? + update_tracked_fields(request) lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i) @@ -249,7 +251,7 @@ class User < ActiveRecord::Base def find_for_database_authentication(warden_conditions) conditions = warden_conditions.dup 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 find_by(conditions) end diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb index 2f511ab44b7..7140890d201 100644 --- a/app/services/issues/move_service.rb +++ b/app/services/issues/move_service.rb @@ -19,19 +19,10 @@ module Issues # on rewriting notes (unfolding references) # ActiveRecord::Base.transaction do - # New issue tasks - # @new_issue = create_new_issue - rewrite_notes - rewrite_issue_award_emoji - add_note_moved_from - - # Old issue tasks - # - add_note_moved_to - close_issue - mark_as_moved + update_new_issue + update_old_issue end notify_participants @@ -41,11 +32,24 @@ module Issues 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 new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids, milestone_id: cloneable_milestone_id, 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) CreateService.new(@new_project, @current_user, new_params).execute diff --git a/app/services/members/authorized_destroy_service.rb b/app/services/members/authorized_destroy_service.rb index de3a252d6c6..2e89f00dad8 100644 --- a/app/services/members/authorized_destroy_service.rb +++ b/app/services/members/authorized_destroy_service.rb @@ -11,6 +11,7 @@ module Members Member.transaction do unassign_issues_and_merge_requests(member) unless member.invite? + member.notification_setting&.destroy member.destroy end diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb index ab6f8ea44a9..4b186d93772 100644 --- a/app/services/merge_requests/build_service.rb +++ b/app/services/merge_requests/build_service.rb @@ -134,7 +134,7 @@ module MergeRequests end def append_closes_description - return unless issue + return unless issue&.to_reference.present? closes_issue = "Closes #{issue.to_reference}" @@ -163,7 +163,7 @@ module MergeRequests return if merge_request.title.present? if issue_iid.present? - merge_request.title = "Resolve #{issue_iid}" + merge_request.title = "Resolve #{issue.to_reference}" branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize merge_request.title += " \"#{branch_title}\"" if branch_title.present? end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 8c84ccfcc92..56e941d90ff 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -208,7 +208,12 @@ class NotificationService def new_access_request(member) return true unless member.notifiable?(:subscription) - mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later + recipients = member.source.members.owners_and_masters + if fallback_to_group_owners_masters?(recipients, member) + recipients = member.source.group.members.owners_and_masters + end + + recipients.each { |recipient| deliver_access_request_email(recipient, member) } end def decline_access_request(member) @@ -435,4 +440,14 @@ class NotificationService def notifiable_users(*args) NotificationRecipientService.notifiable_users(*args) end + + def deliver_access_request_email(recipient, member) + mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later + end + + def fallback_to_group_owners_masters?(recipients, member) + return false if recipients.present? + + member.source.respond_to?(:group) && member.source.group + end end diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb index 87d9ed7a0e6..a549cfbabea 100644 --- a/app/services/projects/create_from_template_service.rb +++ b/app/services/projects/create_from_template_service.rb @@ -5,11 +5,15 @@ module Projects end def execute - params[:file] = Gitlab::ProjectTemplate.find(params[:template_name]).file + template_name = params.delete(:template_name) + file = Gitlab::ProjectTemplate.find(template_name).file + + params[:file] = file + + GitlabProjectsImportService.new(current_user, params).execute - GitlabProjectsImportService.new(@current_user, @params).execute ensure - params[:file]&.close + file&.close end end end diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb index a3d7f5cbed5..a68ecb4abe1 100644 --- a/app/services/projects/gitlab_projects_import_service.rb +++ b/app/services/projects/gitlab_projects_import_service.rb @@ -11,12 +11,14 @@ module Projects def execute FileUtils.mkdir_p(File.dirname(import_upload_path)) + + file = params.delete(:file) FileUtils.copy_entry(file.path, import_upload_path) - Gitlab::ImportExport::ProjectCreator.new(params[:namespace_id], - current_user, - import_upload_path, - params[:path]).execute + params[:import_type] = 'gitlab_project' + params[:import_source] = import_upload_path + + ::Projects::CreateService.new(current_user, params).execute end private @@ -28,9 +30,5 @@ module Projects def tmp_filename SecureRandom.hex end - - def file - params[:file] - end end end diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb index 8a9d8892e9b..4bfa3c45303 100644 --- a/app/validators/variable_duplicates_validator.rb +++ b/app/validators/variable_duplicates_validator.rb @@ -1,13 +1,28 @@ # VariableDuplicatesValidator # -# This validtor is designed for especially the following condition +# This validator is designed for especially the following condition # - Use `accepts_nested_attributes_for :xxx` in a parent model # - Use `validates :xxx, uniqueness: { scope: :xxx_id }` in a child model class VariableDuplicatesValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - duplicates = value.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first) + if options[:scope] + scoped = value.group_by do |variable| + Array(options[:scope]).map { |attr| variable.send(attr) } # rubocop:disable GitlabSecurity/PublicSend + end + scoped.each_value { |scope| validate_duplicates(record, attribute, scope) } + else + validate_duplicates(record, attribute, value) + end + end + + private + + def validate_duplicates(record, attribute, values) + duplicates = values.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first) if duplicates.any? - record.errors.add(attribute, "Duplicate variables: #{duplicates.join(", ")}") + error_message = "have duplicate values (#{duplicates.join(", ")})" + error_message += " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend + record.errors.add(attribute, error_message) end end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 60f12030f98..938185b6eba 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -647,8 +647,11 @@ = f.label :version_check_enabled do = f.check_box :version_check_enabled Version check enabled + = link_to icon('question-circle'), help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check") .help-block - Let GitLab inform you when an update is available. + Let GitLab inform you when an update is available. When + enabled, GitLab Inc. will collect info about your hostname + and version. .form-group .col-sm-offset-2.col-sm-10 - can_be_configured = @application_setting.usage_ping_can_be_configured? diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml index a01676d82a8..4e3e2f7a475 100644 --- a/app/views/admin/jobs/index.html.haml +++ b/app/views/admin/jobs/index.html.haml @@ -7,10 +7,9 @@ - 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 - .nav-controls - - if @all_builds.running_or_pending.any? - #stop-jobs-modal - + - if @all_builds.running_or_pending.any? + #stop-jobs-modal + .nav-controls %button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal', target: '#stop-jobs-modal', url: cancel_all_admin_jobs_path } } diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 3b85586e45b..bbfeceff5b9 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -1,6 +1,6 @@ %li.flex-row .user-avatar - = image_tag avatar_icon(user), class: "avatar", alt: '' + = image_tag avatar_icon_for_user(user), class: "avatar", alt: '' .row-main-content .user-name.row-title.str-truncated-100 = link_to user.name, [:admin, user] diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 101667508a9..ec3be869797 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -10,7 +10,7 @@ = @user.name %ul.well-list %li - = image_tag avatar_icon(@user, 60), class: "avatar s60" + = image_tag avatar_icon_for_user(@user, 60), class: "avatar s60" %li %span.light Profile page: %strong diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index a039756c7e2..56ec1b3db0d 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -1,6 +1,6 @@ - if inject_u2f_api? - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('u2f') + = webpack_bundle_tag('u2f') %div = render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication' diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml index 205320ed87c..8b9fa3d6b05 100644 --- a/app/views/discussions/_discussion.html.haml +++ b/app/views/discussions/_discussion.html.haml @@ -3,7 +3,7 @@ .timeline-entry-inner .timeline-icon = link_to user_path(discussion.author) do - = image_tag avatar_icon(discussion.author), class: "avatar s40" + = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40" .timeline-content .discussion.js-toggle-container{ data: { discussion_id: discussion.id } } .discussion-header diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder index 38741fe6662..d56234e6c1a 100644 --- a/app/views/events/_event.atom.builder +++ b/app/views/events/_event.atom.builder @@ -10,7 +10,7 @@ xml.entry do # eager-loaded. This allows us to re-use the user object's Email address, # instead of having to run additional queries to figure out what Email to use # for the avatar. - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author)) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_user(event.author)) xml.author do xml.username event.author_username diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 00909982d59..f2ae7c52031 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -5,7 +5,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' - if group_issues_exists .top-area diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 694292aa7c1..046b92bd9fb 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -2,7 +2,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' - if @group_merge_requests.empty? = render 'shared/empty_states/merge_requests', project_select_button: true diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml index 63811ea1c81..bf2725dc328 100644 --- a/app/views/help/index.html.haml +++ b/app/views/help/index.html.haml @@ -1,5 +1,3 @@ -= webpack_bundle_tag 'docs' - %div - if Gitlab::CurrentSettings.help_page_text.present? = markdown_field(Gitlab::CurrentSettings.current_application_settings, :help_page_text) diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml index d6789baea28..c07c148a12a 100644 --- a/app/views/help/show.html.haml +++ b/app/views/help/show.html.haml @@ -1,5 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'help' - page_title @path.split("/").reverse.map(&:humanize) .documentation.wiki.prepend-top-default = markdown @markdown diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 445f0dffbcc..1c4d67a8d2c 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -68,7 +68,7 @@ .example .cover-block .avatar-holder - = image_tag avatar_icon('admin@example.com', 90), class: "avatar s90", alt: '' + = image_tag avatar_icon_for_email('admin@example.com', 90), class: "avatar s90", alt: '' .cover-title John Smith diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index 5d68e1e2156..df5841d1911 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -1,7 +1,5 @@ - page_title "GitLab Import" - header_title "Projects", root_path -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'project_import_gl' %h3.page-title = icon('gitlab') diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder index 0c113c08526..21cf6d0dd65 100644 --- a/app/views/issues/_issue.atom.builder +++ b/app/views/issues/_issue.atom.builder @@ -3,7 +3,7 @@ xml.entry do xml.link href: project_issue_url(issue.project, issue) xml.title truncate(issue.title, length: 80) xml.updated issue.updated_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_user(issue.author)) xml.author do xml.name issue.author_name diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index e7fc83a8d04..1d00ae928f6 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -45,7 +45,7 @@ = todos_count_format(todos_pending_count) %li.header-user.dropdown = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do - = image_tag avatar_icon(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar" + = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar" = sprite_icon('angle-down', css_class: 'caret-down') .dropdown-menu-nav.dropdown-menu-align-right %ul diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 2b98cb9de99..059571f795f 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -127,6 +127,19 @@ = link_to project_milestones_path(@project), title: 'Milestones' do %span Milestones + - if project_nav_tab? :external_issue_tracker + = nav_link do + - issue_tracker = @project.external_issue_tracker + = link_to issue_tracker.issue_tracker_path, class: 'shortcuts-external_tracker' do + .nav-icon-container + = sprite_icon('issue-external') + %span.nav-item-name + = issue_tracker.title + %ul.sidebar-sub-level-items.is-fly-out-only + = nav_link(html_options: { class: "fly-out-top-item" } ) do + = link_to issue_tracker.issue_tracker_path do + %strong.fly-out-top-item-name + = issue_tracker.title - if project_nav_tab? :merge_requests = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index 8eb3f2d5192..38dab104eb5 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -60,7 +60,7 @@ %tbody %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.author %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } @@ -76,7 +76,7 @@ %tbody %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.committer %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" } @@ -100,7 +100,7 @@ triggered by - if @pipeline.user %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" } - %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" } %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" } = @pipeline.user.name diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index bae37292d62..7b06e8afa0b 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -60,7 +60,7 @@ %tbody %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.author %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } @@ -76,7 +76,7 @@ %tbody %tr %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } - %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } - if commit.committer %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" } @@ -100,7 +100,7 @@ triggered by - if @pipeline.user %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" } - %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ + %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" } %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" } = @pipeline.user.name diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml index 83ae9129807..a8eb66ca13c 100644 --- a/app/views/profiles/_head.html.haml +++ b/app/views/profiles/_head.html.haml @@ -1,2 +1,2 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('profile') + = webpack_bundle_tag('profile') diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 79e197ad08b..0f849f6f8b7 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -102,6 +102,3 @@ %p = s_("Profiles|You don't have access to delete this user.") .append-bottom-default - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('account') diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 5c76d2d8f51..110736dc557 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -20,8 +20,8 @@ or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host} .col-lg-8 .clearfix.avatar-image.append-bottom-default - = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do - = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' + = link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do + = image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160' %h5.prepend-top-0= _("Upload new avatar") .prepend-top-5.append-bottom-10 %button.btn.js-choose-user-avatar-button{ type: 'button' }= _("Choose file...") diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 5207dac3ac2..e58cd20402c 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -6,8 +6,8 @@ - content_for :page_specific_javascripts do - if inject_u2f_api? - = page_specific_javascript_bundle_tag('u2f') - = page_specific_javascript_bundle_tag('two_factor_auth') + = webpack_bundle_tag('u2f') + = webpack_bundle_tag('two_factor_auth') .js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path } .row.prepend-top-default diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 21b6aa4bad9..b3afd16f900 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -2,7 +2,7 @@ .modal-dialog.modal-lg .modal-content .modal-header - %a.close{ href: "#", "data-dismiss" => "modal" } × + %a.close{ href: "#", "data-dismiss" => "modal" } × %h3.page-title= title .modal-body = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal', data: { method: method } do @@ -29,4 +29,4 @@ = commit_in_fork_help - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('blob') + = webpack_bundle_tag('blob') diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 626cbc9e41d..9d90251ab66 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -3,7 +3,6 @@ - page_title "Edit", @blob.path, @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('blob') %div{ class: container_class } - if @conflict diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index a4263774dfd..fa091d8f6ef 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -2,7 +2,6 @@ - page_title "New File", @path.presence, @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('blob') .editor-title-row %h3.page-title.blob-new-page-title New file diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 2ed454131af..efb8175398b 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,10 +3,6 @@ - page_title @blob.path, @ref -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'blob' - - %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml index 1e7c461f02e..15349387eb2 100644 --- a/app/views/projects/blob/viewers/_balsamiq.html.haml +++ b/app/views/projects/blob/viewers/_balsamiq.html.haml @@ -1,4 +1,4 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('balsamiq_viewer') + = webpack_bundle_tag('balsamiq_viewer') .file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_notebook.html.haml b/app/views/projects/blob/viewers/_notebook.html.haml index 8a41bc53004..d1ffaca35b9 100644 --- a/app/views/projects/blob/viewers/_notebook.html.haml +++ b/app/views/projects/blob/viewers/_notebook.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('notebook_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('notebook_viewer') .file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_pdf.html.haml b/app/views/projects/blob/viewers/_pdf.html.haml index ec2b18bd4ab..fc3f0d922b1 100644 --- a/app/views/projects/blob/viewers/_pdf.html.haml +++ b/app/views/projects/blob/viewers/_pdf.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('pdf_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('pdf_viewer') .file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/blob/viewers/_sketch.html.haml b/app/views/projects/blob/viewers/_sketch.html.haml index 775e4584f77..8fb67c819c1 100644 --- a/app/views/projects/blob/viewers/_sketch.html.haml +++ b/app/views/projects/blob/viewers/_sketch.html.haml @@ -1,6 +1,6 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('sketch_viewer') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('sketch_viewer') .file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } } .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' } diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml index 6578d826ace..e58809ec008 100644 --- a/app/views/projects/blob/viewers/_stl.html.haml +++ b/app/views/projects/blob/viewers/_stl.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('stl_viewer') + = webpack_bundle_tag('stl_viewer') .file-content.is-stl-loading .text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } } diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 0a54c736761..d8b4266143e 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do + %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } } - if current_user.starred?(@project) = sprite_icon('star') %span.starred= _('Unstar') diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 49b0b314e1d..3f699882c5f 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -8,5 +8,5 @@ } } - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('commit_pipelines') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('commit_pipelines') diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 2890e9d2b65..4058e61eb9a 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -7,8 +7,8 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('diff_notes') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('diff_notes') .container-fluid{ class: [limited_container_width, container_class] } = render "commit_box" diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder index 04914888763..50f7e7a3a33 100644 --- a/app/views/projects/commits/_commit.atom.builder +++ b/app/views/projects/commits/_commit.atom.builder @@ -3,7 +3,7 @@ xml.entry do xml.link href: project_commit_url(@project, id: commit.id) xml.title truncate(commit.title, length: 80, escape: false) xml.updated commit.committed_date.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_email(commit.author_email)) xml.author do |author| xml.name commit.author_name diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index 71d30da14a9..d98e0564da4 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,8 +1,8 @@ - @no_container = true - page_title "Cycle Analytics" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('cycle_analytics') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('cycle_analytics') #cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } } - if @cycle_analytics_no_data diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index e16d132f869..0931ceb1512 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -58,7 +58,7 @@ - if @project.avatar? %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" - = 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) } .settings-header diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index d9c9f0ed546..eca10d99908 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -2,8 +2,8 @@ - page_title "Environments" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag("environments_folder") + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag("environments_folder") #environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json), "folder-name" => @folder, diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 88f1348da47..31cf173fa9c 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -3,8 +3,8 @@ - add_to_breadcrumbs("Pipelines", project_pipelines_path(@project)) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag("common_vue") - = page_specific_javascript_bundle_tag("environments") + = webpack_bundle_tag("common_vue") + = webpack_bundle_tag("environments") #environments-list-view{ data: { environments_data: environments_list_data, "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 10812f67cbe..91b3743e9e7 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -2,7 +2,6 @@ - page_title "Metrics for environment", @environment.name - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'common_d3' .prometheus-container{ class: container_class } .top-area diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml index a073a164f11..7be4ef39117 100644 --- a/app/views/projects/environments/terminal.html.haml +++ b/app/views/projects/environments/terminal.html.haml @@ -3,7 +3,7 @@ - content_for :page_specific_javascripts do = stylesheet_link_tag "xterm/xterm" - = page_specific_javascript_bundle_tag("terminal") + = webpack_bundle_tag("terminal") %div{ class: container_class } .top-area diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml index 300a39fe257..d4b4a6203f3 100644 --- a/app/views/projects/graphs/charts.html.haml +++ b/app/views/projects/graphs/charts.html.haml @@ -1,9 +1,5 @@ - @no_container = true - page_title "Charts" -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_d3') - = webpack_bundle_tag('graphs') - = webpack_bundle_tag('graphs_charts') .repo-charts{ class: container_class } %h4.sub-header diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index cce16bc58b3..c81ee6874e3 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,9 +1,5 @@ - @no_container = true - page_title _('Contributors') -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_d3') - = webpack_bundle_tag('graphs') - = webpack_bundle_tag('graphs_show') .js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) } .sub-header-block diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 193111b4cee..fb06ba58c27 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -6,7 +6,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' = content_for :meta_tags do = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues") diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 1f28d8acff6..91f68d8c419 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -5,10 +5,6 @@ - page_description @issue.description - page_card_attributes @issue.card_attributes -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'notes' - - can_update_issue = can?(current_user, :update_issue, @issue) - can_report_spam = @issue.submittable_as_spam_by?(current_user) @@ -87,5 +83,5 @@ = render 'shared/issuable/sidebar', issuable: @issue -= page_specific_javascript_bundle_tag('common_vue') -= page_specific_javascript_bundle_tag('issue_show') += webpack_bundle_tag('common_vue') += webpack_bundle_tag('issue_show') diff --git a/app/views/projects/jobs/_user.html.haml b/app/views/projects/jobs/_user.html.haml index 83f299da651..461d503f95d 100644 --- a/app/views/projects/jobs/_user.html.haml +++ b/app/views/projects/jobs/_user.html.haml @@ -1,7 +1,7 @@ by %a{ href: user_path(@build.user) } %span.hidden-xs - = image_tag avatar_icon(@build.user, 24), class: "avatar s24" + = image_tag avatar_icon_for_user(@build.user, 24), class: "avatar s24" %strong{ data: { toggle: 'tooltip', placement: 'top', title: @build.user.to_reference } } = @build.user.name %strong.visible-xs-inline= @build.user.to_reference diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 93efa7e8e86..849c273db8c 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -112,7 +112,3 @@ .js-build-options{ data: javascript_build_options } #js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json) } } - -- content_for :page_specific_javascripts do - = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('job_details') diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml index 917ec7fdbda..54a661040ea 100644 --- a/app/views/projects/merge_requests/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/_how_to_merge.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('how_to_merge') - #modal_merge_info.modal .modal-dialog .modal-content diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml index 454bc359b6b..2a2e57027be 100644 --- a/app/views/projects/merge_requests/conflicts.html.haml +++ b/app/views/projects/merge_requests/conflicts.html.haml @@ -1,7 +1,7 @@ - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('merge_conflicts') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('merge_conflicts') = page_specific_javascript_tag('lib/ace.js') = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml index 454bc359b6b..2a2e57027be 100644 --- a/app/views/projects/merge_requests/conflicts/show.html.haml +++ b/app/views/projects/merge_requests/conflicts/show.html.haml @@ -1,7 +1,7 @@ - page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('merge_conflicts') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('merge_conflicts') = page_specific_javascript_tag('lib/ace.js') = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 640d2791dc1..720ba236434 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -8,7 +8,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' %div{ class: container_class } = render 'projects/last_push' diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 8740c6895df..e29f21b3bec 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -6,7 +6,6 @@ - page_card_attributes @merge_request.card_attributes - content_for :page_specific_javascripts do = webpack_bundle_tag('common_vue') - = webpack_bundle_tag('diff_notes') .merge-request{ data: { mr_action: j(params[:tab].presence || 'show'), url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project) } } = render "projects/merge_requests/mr_title" diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 2efb7fc719f..97be8950db0 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,7 +1,7 @@ - breadcrumb_title "Graph" - page_title "Graph", @ref - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('network') + = webpack_bundle_tag('network') = render "head" %div{ class: container_class } .project-network diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb index 7491b37310d..a0e82e891ff 100644 --- a/app/views/projects/network/show.json.erb +++ b/app/views/projects/network/show.json.erb @@ -9,7 +9,7 @@ author: { name: c.author_name, email: c.author_email, - icon: image_path(avatar_icon(c.author_email, 20)) + icon: image_path(avatar_icon_for_email(c.author_email, 20)) }, time: c.time, space: c.spaces.first, diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index ff440e99042..160e325996a 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -1,7 +1,3 @@ -- 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_errors(@schedule) .form-group diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index a8692b83b07..55d0e8bb7f9 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -21,7 +21,7 @@ = s_("PipelineSchedules|Inactive") %td - if pipeline_schedule.owner - = image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20" + = image_tag avatar_icon_for_user(pipeline_schedule.owner, 20), class: "avatar s20" = link_to user_path(pipeline_schedule.owner) do = pipeline_schedule.owner&.name %td diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index 4fbdd1dd5e4..bcb6dddba1a 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -1,9 +1,5 @@ - breadcrumb_title _("Schedules") -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'schedules_index' - - @no_container = true - page_title _("Pipeline Schedules") diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 5de17977d5a..852143ecb2a 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -3,16 +3,16 @@ .tabs-holder %ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator %li.js-pipeline-tab-link - = link_to project_pipeline_path(@project, @pipeline), data: { target: 'div#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do - Pipeline + = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do + = _("Pipeline") %li.js-builds-tab-link - = link_to builds_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do - Jobs + = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do + = _("Jobs") %span.badge.js-builds-counter= pipeline.total_size - if failed_builds.present? %li.js-failures-tab-link - = link_to failures_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do - Failed Jobs + = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do + = _("Failed Jobs") %span.badge.js-failures-counter= failed_builds.count .tab-content diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml index ba55bc23add..a86cb14960a 100644 --- a/app/views/projects/pipelines/charts.html.haml +++ b/app/views/projects/pipelines/charts.html.haml @@ -1,9 +1,6 @@ - @no_container = true - breadcrumb_title "CI / CD Charts" - 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 } .sub-header-block diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml index a5dbd1b1532..510697c2ae9 100644 --- a/app/views/projects/pipelines/charts/_pipeline_times.haml +++ b/app/views/projects/pipelines/charts/_pipeline_times.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('pipelines_times') - %div %p.light = _("Commit duration in minutes for last 30 commits") diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml index 41dc2f6cf9d..2f4b6def155 100644 --- a/app/views/projects/pipelines/charts/_pipelines.haml +++ b/app/views/projects/pipelines/charts/_pipelines.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('pipelines_charts') - %h4= _("Pipelines charts") %p diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index f8555f11aab..fdcc60f48a5 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -13,5 +13,5 @@ "ci-lint-path" => ci_lint_path, "reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } } - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('pipelines') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('pipelines') diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml index 2f30fe33a97..127a338e413 100644 --- a/app/views/projects/protected_branches/_index.html.haml +++ b/app/views/projects/protected_branches/_index.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('protected_branches') + = webpack_bundle_tag('protected_branches') - content_for :create_protected_branch do = render 'projects/protected_branches/create_protected_branch' diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml index 955220562a6..74f7f63c941 100644 --- a/app/views/projects/protected_tags/_index.html.haml +++ b/app/views/projects/protected_tags/_index.html.haml @@ -1,5 +1,5 @@ - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('protected_tags') + = webpack_bundle_tag('protected_tags') - content_for :create_protected_tag do = render 'projects/protected_tags/create_protected_tag' diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index 36ea5e013e4..744b88760bc 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -14,8 +14,8 @@ .col-lg-12 #js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } } - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('registry_list') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('registry_list') .row.prepend-top-10 .col-lg-12 diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index 170f9e259df..87895a15239 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -11,7 +11,7 @@ %div = link_to project_commits_path(@project, commit.id) do %code= commit.short_id - = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' + = image_tag avatar_icon_for_email(commit.author_email), class: "", width: 16, alt: '' = markdown(truncate(commit.title, length: 40), pipeline: :single_line, author: commit.author) %td %span.pull-right.cgray diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 0808b28a9df..17e804d682b 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -1,6 +1,3 @@ -- content_for :page_specific_javascripts do - = webpack_bundle_tag('integrations') - .row.prepend-top-default.append-bottom-default .col-lg-3 %h4.prepend-top-0 diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index b0cb5ce5e8f..5f38ecd6820 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -1,6 +1,3 @@ -- 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 .col-lg-3 %h4.prepend-top-0 diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml index 517d51993d2..3077203c2a6 100644 --- a/app/views/projects/settings/repository/show.html.haml +++ b/app/views/projects/settings/repository/show.html.haml @@ -3,8 +3,8 @@ - @content_class = "limit-container-width" unless fluid_layout - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('deploy_keys') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('deploy_keys') -# Protected branches & tags use a lot of nested partials. -# The shared parts of the views can be found in the `shared` directory. diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml index 2c05ebe52ae..1a353953838 100644 --- a/app/views/projects/update.js.haml +++ b/app/views/projects/update.js.haml @@ -6,4 +6,4 @@ $(".project-edit-errors").html("#{escape_javascript(render('errors'))}"); $('.save-project-loader').hide(); $('.project-edit-container').show(); - $('.edit-project .btn-save').enable(); + $('.edit-project .js-btn-save-general-project-settings').enable(); diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml index c4a5131c1a7..57a0b64bfd5 100644 --- a/app/views/search/results/_snippet_blob.html.haml +++ b/app/views/search/results/_snippet_blob.html.haml @@ -7,7 +7,7 @@ = snippet.title by = link_to user_snippets_path(snippet.author) do - = image_tag avatar_icon(snippet.author), class: "avatar avatar-inline s16", alt: '' + = image_tag avatar_icon_for_user(snippet.author), class: "avatar avatar-inline s16", alt: '' = snippet.author_name %span.light= time_ago_with_tooltip(snippet.created_at) %h4.snippet-title diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml index aef825691e0..65710c09a89 100644 --- a/app/views/search/results/_snippet_title.html.haml +++ b/app/views/search/results/_snippet_title.html.haml @@ -18,6 +18,6 @@ %span by = link_to user_snippets_path(snippet_title.author) do - = image_tag avatar_icon(snippet_title.author), class: "avatar avatar-inline s16", alt: '' + = image_tag avatar_icon_for_user(snippet_title.author), class: "avatar avatar-inline s16", alt: '' = snippet_title.author_name %span.light= time_ago_with_tooltip(snippet_title.created_at) diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml index ee8ad8e3999..a10fc42b82d 100644 --- a/app/views/shared/boards/_show.html.haml +++ b/app/views/shared/boards/_show.html.haml @@ -6,7 +6,6 @@ - content_for :page_specific_javascripts do = webpack_bundle_tag 'common_vue' - = webpack_bundle_tag 'filtered_search' = webpack_bundle_tag 'boards' %script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board" diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml index 1a259b679c7..8607be9cd06 100644 --- a/app/views/shared/groups/_dropdown.html.haml +++ b/app/views/shared/groups/_dropdown.html.haml @@ -23,11 +23,11 @@ - if show_archive_options %li.divider %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 %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 %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 diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 15fd01c8429..dc583d3eb3b 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -1,7 +1,7 @@ - todo = issuable_todo(issuable) - content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_vue') - = page_specific_javascript_bundle_tag('sidebar') + = webpack_bundle_tag('common_vue') + = webpack_bundle_tag('sidebar') %aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } } diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 71878e93255..ba57d922c6d 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -8,7 +8,7 @@ %li.member{ class: dom_class(member), id: dom_id(member) } %span.list-item-name - if user - = image_tag avatar_icon(user, 40), class: "avatar s40", alt: '' + = image_tag avatar_icon_for_user(user, 40), class: "avatar s40", alt: '' .user-info = link_to user.name, user_path(user), class: 'member' %span.cgray= user.to_reference @@ -36,7 +36,7 @@ Expires in #{distance_of_time_in_words_to_now(member.expires_at)} - else - = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: '' + = image_tag avatar_icon_for_email(member.invite_email, 40), class: "avatar s40", alt: '' .user-info .member= member.invite_email .cgray diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 14395bcc661..129f6ab604e 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -17,7 +17,7 @@ = confidential_icon(issuable) = link_to issuable.title, issuable_url_args, title: issuable.title .issuable-detail - = link_to [namespace, project, issuable] do + = link_to [namespace, project, issuable], class: 'issue-link' do %span.issuable-number= issuable.to_reference - labels.each do |label| @@ -28,4 +28,4 @@ - assignees.each do |assignee| = link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }), class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do - - image_tag(avatar_icon(assignee, 16), class: "avatar s16", alt: '') + - image_tag(avatar_icon_for_user(assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml index 1615871385e..fe83040c168 100644 --- a/app/views/shared/milestones/_participants_tab.html.haml +++ b/app/views/shared/milestones/_participants_tab.html.haml @@ -2,7 +2,7 @@ - users.each do |user| %li = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" + = image_tag avatar_icon_for_user(user, 32), class: "avatar s32" %strong= truncate(user.name, length: 40) %div %small.cgray= user.username diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml index 4f51455c26e..cd4188daf5b 100644 --- a/app/views/shared/milestones/_sidebar.html.haml +++ b/app/views/shared/milestones/_sidebar.html.haml @@ -1,5 +1,7 @@ - affix_offset = local_assigns.fetch(:affix_offset, "50") - project = local_assigns[:project] +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag('common_vue') %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } .issuable-sidebar.milestone-sidebar @@ -85,21 +87,12 @@ Closed: = milestone.issues_visible_to_user(current_user).closed.count - .block.time_spent - .sidebar-collapsed-icon - = custom_icon('icon_hourglass') - %span.collapsed-milestone-total-time-spent - - if milestone.human_total_issue_time_spent - = milestone.human_total_issue_time_spent - - else - = _("None") - .title.hide-collapsed - = _("Total issue time spent") - .value.hide-collapsed - - if milestone.human_total_issue_time_spent - %span.bold= milestone.human_total_issue_time_spent - - else - %span.no-value= _("No time spent") + .block + #issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate, time_spent: @milestone.total_issue_time_spent, human_time_estimate: @milestone.human_total_issue_time_estimate, human_time_spent: @milestone.human_total_issue_time_spent } } + // Fallback while content is loading + .title.hide-collapsed + = _('Time tracking') + = icon('spinner spin') .block.merge-requests .sidebar-collapsed-icon diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 98e0161f7d1..bf359774ead 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -16,7 +16,7 @@ = icon_for_system_note(note) - else %a.image-diff-avatar-link{ href: user_path(note.author) } - = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40' + = image_tag avatar_icon_for_user(note.author), alt: '', class: 'avatar s40' - if note.is_a?(DiffNote) && note.on_image? - if show_image_comment_badge && note_counter == 0 -# Only show this for the first comment in the discussion diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml index e11f778adf5..b3f865c5b47 100644 --- a/app/views/shared/notes/_notes_with_form.html.haml +++ b/app/views/shared/notes/_notes_with_form.html.haml @@ -14,7 +14,7 @@ .timeline-icon.hidden-xs.hidden-sm %a.author_link{ href: user_path(current_user) } - = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40' + = image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40' .timeline-content.timeline-content-form = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete - elsif !current_user diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 2a75b46d376..33435216c14 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -17,7 +17,7 @@ .avatar-container.s40 = link_to project_path(project), class: dom_class(project) do - if project.creator && use_creator_avatar - = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:'' + = image_tag avatar_icon_for_user(project.creator, 40), class: "avatar s40", alt:'' - else = project_icon(project, alt: '', class: 'avatar project-avatar s40') .project-details diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 43322978749..2726a4934fb 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -1,6 +1,6 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/ace.js') - = page_specific_javascript_bundle_tag('snippet') + = webpack_bundle_tag('snippet') .snippet-form-holder = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f| diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 57b445321e2..491a8a41090 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -1,7 +1,7 @@ - link_project = local_assigns.fetch(:link_project, false) %li.snippet-row - = image_tag avatar_icon(snippet.author), class: "avatar s40 hidden-xs", alt: '' + = image_tag avatar_icon_for_user(snippet.author), class: "avatar s40 hidden-xs", alt: '' .title = link_to reliable_snippet_path(snippet) do diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 90aa1be30ac..a396d1007a7 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -4,9 +4,6 @@ - page_description @user.bio - header_title @user.name, user_path(@user) - @no_container = true -- content_for :page_specific_javascripts do - = webpack_bundle_tag 'common_d3' - = webpack_bundle_tag 'users' = content_for :meta_tags do = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity") @@ -35,8 +32,8 @@ .profile-header .avatar-holder - = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do - = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: '' + = link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do + = image_tag avatar_icon_for_user(@user, 90), class: "avatar s90", alt: '' .user-info .cover-title diff --git a/app/workers/check_gcp_project_billing_worker.rb b/app/workers/check_gcp_project_billing_worker.rb index 5466ccdda59..363f81590ab 100644 --- a/app/workers/check_gcp_project_billing_worker.rb +++ b/app/workers/check_gcp_project_billing_worker.rb @@ -7,6 +7,7 @@ class CheckGcpProjectBillingWorker LEASE_TIMEOUT = 3.seconds.to_i SESSION_KEY_TIMEOUT = 5.minutes BILLING_TIMEOUT = 1.hour + BILLING_CHANGED_LABELS = { state_transition: nil }.freeze def self.get_session_token(token_key) Gitlab::Redis::SharedState.with do |redis| @@ -22,8 +23,11 @@ class CheckGcpProjectBillingWorker end end - def self.redis_shared_state_key_for(token) - "gitlab:gcp:#{Digest::SHA1.hexdigest(token)}:billing_enabled" + def self.get_billing_state(token) + Gitlab::Redis::SharedState.with do |redis| + value = redis.get(redis_shared_state_key_for(token)) + ActiveRecord::Type::Boolean.new.type_cast_from_user(value) + end end def perform(token_key) @@ -33,12 +37,9 @@ class CheckGcpProjectBillingWorker return unless token return unless try_obtain_lease_for(token) - billing_enabled_projects = CheckGcpProjectBillingService.new.execute(token) - Gitlab::Redis::SharedState.with do |redis| - redis.set(self.class.redis_shared_state_key_for(token), - !billing_enabled_projects.empty?, - ex: BILLING_TIMEOUT) - end + billing_enabled_state = !CheckGcpProjectBillingService.new.execute(token).empty? + update_billing_change_counter(self.class.get_billing_state(token), billing_enabled_state) + self.class.set_billing_state(token, billing_enabled_state) end private @@ -51,9 +52,41 @@ class CheckGcpProjectBillingWorker "gitlab:gcp:session:#{token_key}" end + def self.redis_shared_state_key_for(token) + "gitlab:gcp:#{Digest::SHA1.hexdigest(token)}:billing_enabled" + end + + def self.set_billing_state(token, value) + Gitlab::Redis::SharedState.with do |redis| + redis.set(redis_shared_state_key_for(token), value, ex: BILLING_TIMEOUT) + end + end + def try_obtain_lease_for(token) Gitlab::ExclusiveLease .new("check_gcp_project_billing_worker:#{token.hash}", timeout: LEASE_TIMEOUT) .try_obtain end + + def billing_changed_counter + @billing_changed_counter ||= Gitlab::Metrics.counter( + :gcp_billing_change_count, + "Counts the number of times a GCP project changed billing_enabled state from false to true", + BILLING_CHANGED_LABELS + ) + end + + def state_transition(previous_state, current_state) + if previous_state.nil? && !current_state + 'no_billing' + elsif previous_state.nil? && current_state + 'with_billing' + elsif !previous_state && current_state + 'billing_configured' + end + end + + def update_billing_change_counter(previous_state, current_state) + billing_changed_counter.increment(state_transition: state_transition(previous_state, current_state)) + end end diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb index 52eebe475ec..5b25d980bdb 100644 --- a/app/workers/process_commit_worker.rb +++ b/app/workers/process_commit_worker.rb @@ -23,27 +23,25 @@ class ProcessCommitWorker return unless user commit = build_commit(project, commit_hash) - author = commit.author || user process_commit_message(project, commit, user, author, default) - update_issue_metrics(commit, author) end def process_commit_message(project, commit, user, author, default = false) - closed_issues = default ? commit.closes_issues(user) : [] + # this is a GitLab generated commit message, ignore it. + return if commit.merged_merge_request?(user) - unless closed_issues.empty? - close_issues(project, user, author, commit, closed_issues) - end + closed_issues = default ? commit.closes_issues(user) : [] + close_issues(project, user, author, commit, closed_issues) if closed_issues.any? commit.create_cross_references!(author, closed_issues) end def close_issues(project, user, author, commit, issues) # We don't want to run permission related queries for every single issue, - # therefor we use IssueCollection here and skip the authorization check in + # therefore we use IssueCollection here and skip the authorization check in # Issues::CloseService#execute. IssueCollection.new(issues).updatable_by_user(user).each do |issue| Issues::CloseService.new(project, author) |