diff options
Diffstat (limited to 'app')
70 files changed, 703 insertions, 256 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 04b90035bcb..1c6336073e9 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -1,45 +1,33 @@ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ -import { s__ } from './locale'; import projectSelect from './project_select'; -import IssuableIndex from './issuable_index'; import Milestone from './milestone'; import IssuableForm from './issuable_form'; import LabelsSelect from './labels_select'; import MilestoneSelect from './milestone_select'; -import NewBranchForm from './new_branch_form'; import NotificationsForm from './notifications_form'; import notificationsDropdown from './notifications_dropdown'; import groupAvatar from './group_avatar'; import GroupLabelSubscription from './group_label_subscription'; import LineHighlighter from './line_highlighter'; -import NewCommitForm from './new_commit_form'; import Project from './project'; import projectAvatar from './project_avatar'; import MergeRequest from './merge_request'; import Compare from './compare'; -import initCompareAutocomplete from './compare_autocomplete'; -import ProjectFindFile from './project_find_file'; import ProjectNew from './project_new'; -import projectImport from './project_import'; import Labels from './labels'; import LabelManager from './label_manager'; import Sidebar from './right_sidebar'; import IssuableTemplateSelectors from './templates/issuable_template_selectors'; import Flash from './flash'; -import CommitsList from './commits'; -import Issue from './issue'; import BindInOut from './behaviors/bind_in_out'; import SecretValues from './behaviors/secret_values'; import Group from './group'; import ProjectsList from './projects_list'; -import setupProjectEdit from './project_edit'; -import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; import UserCallout from './user_callout'; import ShortcutsWiki from './shortcuts_wiki'; import BlobViewer from './blob/viewer/index'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import UsersSelect from './users_select'; -import RefSelectDropdown from './ref_select_dropdown'; import GfmAutoComplete from './gfm_auto_complete'; import Star from './star'; import TreeView from './tree'; @@ -50,25 +38,20 @@ import PerformanceBar from './performance_bar'; import initNotes from './init_notes'; import initIssuableSidebar from './init_issuable_sidebar'; import initProjectVisibilitySelector from './project_visibility'; -import GpgBadges from './gpg_badges'; -import initChangesDropdown from './init_changes_dropdown'; import NewGroupChild from './groups/new_group_child'; import { ajaxGet, convertPermissionToBoolean } from './lib/utils/common_utils'; import GlFieldErrors from './gl_field_errors'; import GLForm from './gl_form'; import Shortcuts from './shortcuts'; import ShortcutsNavigation from './shortcuts_navigation'; -import ShortcutsFindFile from './shortcuts_find_file'; import ShortcutsIssuable from './shortcuts_issuable'; import U2FAuthenticate from './u2f/authenticate'; import Members from './members'; import memberExpirationDate from './member_expiration_date'; -import DueDateSelectors from './due_date_select'; import Diff from './diff'; import ProjectLabelSubscription from './project_label_subscription'; import SearchAutocomplete from './search_autocomplete'; import Activities from './activities'; -import { fetchCommitMergeRequests } from './commit_merge_requests'; (function() { var Dispatcher; @@ -122,22 +105,22 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; shortcut_handler = true; break; case 'projects:merge_requests:index': + import('./pages/projects/merge_requests/index') + .then(callDefault) + .catch(fail); + shortcut_handler = true; + break; case 'projects:issues:index': - if (filteredSearchEnabled) { - const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests'); - filteredSearchManager.setup(); - } - const pagePrefix = page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_'; - new IssuableIndex(pagePrefix); - - shortcut_handler = new ShortcutsNavigation(); - new UsersSelect(); + import('./pages/projects/issues/index') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:issues:show': - new Issue(); - shortcut_handler = new ShortcutsIssuable(); - new ZenMode(); - initIssuableSidebar(); + import('./pages/projects/issues/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'dashboard:milestones:index': import('./pages/dashboard/milestones/index') @@ -194,27 +177,43 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; .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': - new ZenMode(); - new DueDateSelectors(); - new GLForm($('.milestone-form'), true); + 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': - new ZenMode(); - new DueDateSelectors(); - new GLForm($('.milestone-form'), false); + import('./pages/groups/milestones/edit') + .then(callDefault) + .catch(fail); break; case 'projects:compare:show': - new Diff(); - const paddingTop = 16; - initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop); + 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': - new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); + import('./pages/projects/branches/new') + .then(callDefault) + .catch(fail); break; case 'projects:branches:index': import('./pages/projects/branches/index') @@ -222,13 +221,16 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; .catch(fail); break; case 'projects:issues:new': + import('./pages/projects/issues/new') + .then(callDefault) + .catch(fail); + shortcut_handler = true; + break; case 'projects:issues:edit': - shortcut_handler = new ShortcutsNavigation(); - new GLForm($('.issue-form'), true); - new IssuableForm($('.issue-form')); - new LabelsSelect(); - new MilestoneSelect(); - new IssuableTemplateSelectors(); + import('./pages/projects/issues/edit') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:merge_requests:creations:new': const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); @@ -256,9 +258,9 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; new AutoWidthDropdownSelect($('.js-target-branch-select')).init(); break; case 'projects:tags:new': - new ZenMode(); - new GLForm($('.tag-form'), true); - new RefSelectDropdown($('.js-branch-select')); + import('./pages/projects/tags/new') + .then(callDefault) + .catch(fail); break; case 'projects:snippets:show': initNotes(); @@ -272,11 +274,24 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; new ZenMode(); 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': - new GLForm($('.snippet-form'), false); - new ZenMode(); + import('./pages/snippets/edit') + .then(callDefault) + .catch(fail); break; case 'projects:releases:edit': new ZenMode(); @@ -302,23 +317,15 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; .catch(fail); break; case 'projects:commit:show': - new Diff(); - new ZenMode(); - shortcut_handler = new ShortcutsNavigation(); - new MiniPipelineGraph({ - container: '.js-commit-pipeline-graph', - }).bindEvents(); - initNotes(); - const stickyBarPaddingTop = 16; - initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop); - $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); - fetchCommitMergeRequests(); + import('./pages/projects/commit/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:commit:pipelines': - new MiniPipelineGraph({ - container: '.js-commit-pipeline-graph', - }).bindEvents(); - $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath); + import('./pages/projects/commit/pipelines') + .then(callDefault) + .catch(fail); break; case 'projects:activity': import('./pages/projects/activity') @@ -327,9 +334,10 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; shortcut_handler = true; break; case 'projects:commits:show': - CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); - shortcut_handler = new ShortcutsNavigation(); - GpgBadges.fetch(); + import('./pages/projects/commits/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:show': shortcut_handler = new ShortcutsNavigation(); @@ -347,12 +355,14 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; }); break; case 'projects:edit': - setupProjectEdit(); - // Initialize expandable settings panels - initSettingsPanels(); + import('./pages/projects/edit') + .then(callDefault) + .catch(fail); break; case 'projects:imports:show': - projectImport(); + import('./pages/projects/imports/show') + .then(callDefault) + .catch(fail); break; case 'projects:pipelines:new': case 'projects:pipelines:create': @@ -412,22 +422,15 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; groupAvatar(); break; case 'projects:tree:show': - shortcut_handler = new ShortcutsNavigation(); - new TreeView(); - new BlobViewer(); - new NewCommitForm($('.js-create-dir-form')); - $('#tree-slider').waitForImages(function() { - ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath); - }); + import('./pages/projects/tree/show') + .then(callDefault) + .catch(fail); + shortcut_handler = true; break; case 'projects:find_file:show': - const findElement = document.querySelector('.js-file-finder'); - const projectFindFile = new ProjectFindFile($(".file-finder-holder"), { - url: findElement.dataset.fileFindUrl, - treeUrl: findElement.dataset.findTreeUrl, - blobUrlTemplate: findElement.dataset.blobUrlTemplate, - }); - new ShortcutsFindFile(projectFindFile); + import('./pages/projects/find_file/show') + .then(callDefault) + .catch(fail); shortcut_handler = true; break; case 'projects:blob:show': @@ -481,7 +484,7 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; shortcut_handler = true; break; case 'projects:forks:new': - import(/* webpackChunkName: 'project_fork' */ './project_fork') + import('./pages/projects/forks/new') .then(callDefault) .catch(fail); break; @@ -538,7 +541,9 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; import('./pages/admin/conversational_development_index/show').then(m => m.default()).catch(fail); break; case 'snippets:show': - import('./pages/snippets/show').then(m => m.default()).catch(fail); + import('./pages/snippets/show') + .then(callDefault) + .catch(fail); break; case 'import:fogbugz:new_user_map': import('./pages/import/fogbugz/new_user_map').then(m => m.default()).catch(fail); @@ -554,20 +559,14 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; .catch(fail); break; case 'projects:clusters:show': - import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle') - .then(cluster => new cluster.default()) // eslint-disable-line new-cap - .catch((err) => { - Flash(s__('ClusterIntegration|Problem setting up the cluster')); - throw err; - }); + import('./pages/projects/clusters/show') + .then(callDefault) + .catch(fail); break; case 'projects:clusters:index': - import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index') - .then(clusterIndex => clusterIndex.default()) - .catch((err) => { - Flash(s__('ClusterIntegration|Problem setting up the clusters list')); - throw err; - }); + import('./pages/projects/clusters/index') + .then(callDefault) + .catch(fail); break; } switch (path[0]) { @@ -647,7 +646,9 @@ import { fetchCommitMergeRequests } from './commit_merge_requests'; projectAvatar(); switch (path[1]) { case 'compare': - initCompareAutocomplete(); + import('./pages/projects/compare') + .then(callDefault) + .catch(fail); break; case 'edit': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue index 321a4872ccc..357bc9aab17 100644 --- a/app/assets/javascripts/jobs/components/header.vue +++ b/app/assets/javascripts/jobs/components/header.vue @@ -76,6 +76,7 @@ <loading-icon v-if="isLoading" size="2" + class="prepend-top-default append-bottom-default" /> </div> </template> diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js index cb3cdea8111..e26bf437efc 100644 --- a/app/assets/javascripts/merge_request.js +++ b/app/assets/javascripts/merge_request.js @@ -110,7 +110,7 @@ MergeRequest.prototype.initCommitMessageListeners = function() { }); }; -MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) { +MergeRequest.updateStatusText = function(classToRemove, classToAdd, newStatusText) { $('.detail-page-header .status-box') .removeClass(classToRemove) .addClass(classToAdd) @@ -118,14 +118,14 @@ MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, ne .text(newStatusText); }; -MergeRequest.prototype.decreaseCounter = function(by = 1) { - const $el = $('.nav-links .js-merge-counter'); +MergeRequest.decreaseCounter = function(by = 1) { + const $el = $('.js-merge-counter'); const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0); $el.text(addDelimiter(count)); }; -MergeRequest.prototype.hideCloseButton = function() { +MergeRequest.hideCloseButton = function() { const el = document.querySelector('.merge-request .js-issuable-actions'); const closeDropdownItem = el.querySelector('li.close-item'); if (closeDropdownItem) { diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js new file mode 100644 index 00000000000..5c99c90e24d --- /dev/null +++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(false); diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js new file mode 100644 index 00000000000..5c99c90e24d --- /dev/null +++ b/app/assets/javascripts/pages/groups/milestones/new/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(false); diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js new file mode 100644 index 00000000000..ae5e033e97e --- /dev/null +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -0,0 +1,3 @@ +import NewBranchForm from '~/new_branch_form'; + +export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js new file mode 100644 index 00000000000..d531ab81dc7 --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/index/index.js @@ -0,0 +1,5 @@ +import ClustersIndex from '~/clusters/clusters_index'; + +export default () => { + 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 new file mode 100644 index 00000000000..0458c02a66f --- /dev/null +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -0,0 +1,5 @@ +import ClustersBundle from '~/clusters/clusters_bundle'; + +export default () => { + 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 new file mode 100644 index 00000000000..523ad567021 --- /dev/null +++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js @@ -0,0 +1,8 @@ +import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; + +export default () => { + 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 new file mode 100644 index 00000000000..5ac38e6f278 --- /dev/null +++ b/app/assets/javascripts/pages/projects/commit/show/index.js @@ -0,0 +1,22 @@ +/* eslint-disable no-new */ +import Diff from '~/diff'; +import ZenMode from '~/zen_mode'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown'; +import initNotes from '~/init_notes'; +import initChangesDropdown from '~/init_changes_dropdown'; +import { fetchCommitMergeRequests } from '~/commit_merge_requests'; + +export default () => { + new Diff(); + new ZenMode(); + new ShortcutsNavigation(); + new MiniPipelineGraph({ + container: '.js-commit-pipeline-graph', + }).bindEvents(); + initNotes(); + const stickyBarPaddingTop = 16; + 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 new file mode 100644 index 00000000000..90b5882a24f --- /dev/null +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -0,0 +1,9 @@ +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); + new ShortcutsNavigation(); // eslint-disable-line no-new + GpgBadges.fetch(); +}; diff --git a/app/assets/javascripts/pages/projects/compare/index.js b/app/assets/javascripts/pages/projects/compare/index.js new file mode 100644 index 00000000000..890062eeee6 --- /dev/null +++ b/app/assets/javascripts/pages/projects/compare/index.js @@ -0,0 +1,5 @@ +import initCompareAutocomplete from '~/compare_autocomplete'; + +export default () => { + initCompareAutocomplete(); +}; diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js new file mode 100644 index 00000000000..6b8d4503568 --- /dev/null +++ b/app/assets/javascripts/pages/projects/compare/show/index.js @@ -0,0 +1,8 @@ +import Diff from '~/diff'; +import initChangesDropdown from '~/init_changes_dropdown'; + +export default () => { + 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 new file mode 100644 index 00000000000..7f662ef6b6a --- /dev/null +++ b/app/assets/javascripts/pages/projects/edit/index.js @@ -0,0 +1,8 @@ +import initSettingsPanels from '~/settings_panels'; +import setupProjectEdit from '~/project_edit'; + +export default () => { + setupProjectEdit(); + // Initialize expandable settings panels + initSettingsPanels(); +}; diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js new file mode 100644 index 00000000000..42bde0ff779 --- /dev/null +++ b/app/assets/javascripts/pages/projects/find_file/show/index.js @@ -0,0 +1,12 @@ +import ProjectFindFile from '~/project_find_file'; +import ShortcutsFindFile from '~/shortcuts_find_file'; + +export default () => { + const findElement = document.querySelector('.js-file-finder'); + const projectFindFile = new ProjectFindFile($('.file-finder-holder'), { + url: findElement.dataset.fileFindUrl, + treeUrl: findElement.dataset.findTreeUrl, + blobUrlTemplate: findElement.dataset.blobUrlTemplate, + }); + new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js new file mode 100644 index 00000000000..7825eb01949 --- /dev/null +++ b/app/assets/javascripts/pages/projects/forks/new/index.js @@ -0,0 +1,5 @@ +import ProjectFork from '~/project_fork'; + +export default () => { + new ProjectFork(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js new file mode 100644 index 00000000000..378f7b3f38b --- /dev/null +++ b/app/assets/javascripts/pages/projects/imports/show/index.js @@ -0,0 +1,5 @@ +import ProjectImport from '~/project_import'; + +export default () => { + new ProjectImport(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js new file mode 100644 index 00000000000..7f27f379d8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/edit/index.js @@ -0,0 +1,5 @@ +import initForm from '../form'; + +export default () => { + initForm(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js new file mode 100644 index 00000000000..5c7daf84738 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/form.js @@ -0,0 +1,16 @@ +/* eslint-disable no-new */ +import GLForm from '~/gl_form'; +import IssuableForm from '~/issuable_form'; +import LabelsSelect from '~/labels_select'; +import MilestoneSelect from '~/milestone_select'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import IssuableTemplateSelectors from '~/templates/issuable_template_selectors'; + +export default () => { + new ShortcutsNavigation(); + new GLForm($('.issue-form'), true); + new IssuableForm($('.issue-form')); + new LabelsSelect(); + new MilestoneSelect(); + new IssuableTemplateSelectors(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js new file mode 100644 index 00000000000..fd395a45f00 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/index/index.js @@ -0,0 +1,18 @@ + +/* eslint-disable no-new */ + +import IssuableIndex from '~/issuable_index'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import UsersSelect from '~/users_select'; + +export default () => { + const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + if (filteredSearchEnabled) { + const filteredSearchManager = new gl.FilteredSearchManager('issues'); + filteredSearchManager.setup(); + } + new IssuableIndex('issue_'); + + new ShortcutsNavigation(); + new UsersSelect(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js new file mode 100644 index 00000000000..7f27f379d8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/new/index.js @@ -0,0 +1,5 @@ +import initForm from '../form'; + +export default () => { + initForm(); +}; diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js new file mode 100644 index 00000000000..48ed8fb2243 --- /dev/null +++ b/app/assets/javascripts/pages/projects/issues/show/index.js @@ -0,0 +1,13 @@ + +/* eslint-disable no-new */ +import initIssuableSidebar from '~/init_issuable_sidebar'; +import Issue from '~/issue'; +import ShortcutsIssuable from '~/shortcuts_issuable'; +import ZenMode from '~/zen_mode'; + +export default () => { + new Issue(); + new ShortcutsIssuable(); + new ZenMode(); + initIssuableSidebar(); +}; diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js new file mode 100644 index 00000000000..a52bea03aa2 --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -0,0 +1,16 @@ +import IssuableIndex from '~/issuable_index'; +import ShortcutsNavigation from '~/shortcuts_navigation'; +import UsersSelect from '~/users_select'; + +export default () => { + const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search'); + + if (filteredSearchEnabled) { + const filteredSearchManager = new gl.FilteredSearchManager('merge_requests'); + filteredSearchManager.setup(); + } + + new IssuableIndex('merge_request_'); // eslint-disable-line no-new + new ShortcutsNavigation(); // eslint-disable-line no-new + new UsersSelect(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js new file mode 100644 index 00000000000..10e3979a36e --- /dev/null +++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(); diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js new file mode 100644 index 00000000000..10e3979a36e --- /dev/null +++ b/app/assets/javascripts/pages/projects/milestones/new/index.js @@ -0,0 +1,3 @@ +import initForm from '../../../../shared/milestones/form'; + +export default () => initForm(); diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js new file mode 100644 index 00000000000..dacc2875c8c --- /dev/null +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -0,0 +1,9 @@ +import RefSelectDropdown from '../../../../ref_select_dropdown'; +import ZenMode from '../../../../zen_mode'; +import GLForm from '../../../../gl_form'; + +export default () => { + 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 new file mode 100644 index 00000000000..28a0160f47d --- /dev/null +++ b/app/assets/javascripts/pages/projects/tree/show/index.js @@ -0,0 +1,15 @@ +import TreeView from '../../../../tree'; +import ShortcutsNavigation from '../../../../shortcuts_navigation'; +import BlobViewer from '../../../../blob/viewer'; +import NewCommitForm from '../../../../new_commit_form'; +import { ajaxGet } from '../../../../lib/utils/common_utils'; + +export default () => { + new ShortcutsNavigation(); // eslint-disable-line no-new + new TreeView(); // eslint-disable-line no-new + new BlobViewer(); // eslint-disable-line no-new + new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new + $('#tree-slider').waitForImages(() => + ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); +}; + diff --git a/app/assets/javascripts/pages/snippets/edit/index.js b/app/assets/javascripts/pages/snippets/edit/index.js new file mode 100644 index 00000000000..9c664b5f1ff --- /dev/null +++ b/app/assets/javascripts/pages/snippets/edit/index.js @@ -0,0 +1,3 @@ +import form from '../form'; + +export default form; diff --git a/app/assets/javascripts/pages/snippets/form.js b/app/assets/javascripts/pages/snippets/form.js new file mode 100644 index 00000000000..f996d3cd74e --- /dev/null +++ b/app/assets/javascripts/pages/snippets/form.js @@ -0,0 +1,7 @@ +import GLForm from '~/gl_form'; +import ZenMode from '~/zen_mode'; + +export default () => { + new GLForm($('.snippet-form'), false); // eslint-disable-line no-new + new ZenMode(); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/pages/snippets/new/index.js b/app/assets/javascripts/pages/snippets/new/index.js new file mode 100644 index 00000000000..9c664b5f1ff --- /dev/null +++ b/app/assets/javascripts/pages/snippets/new/index.js @@ -0,0 +1,3 @@ +import form from '../form'; + +export default form; diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue index 942acc8c412..e08c2092680 100644 --- a/app/assets/javascripts/pipelines/components/header_component.vue +++ b/app/assets/javascripts/pipelines/components/header_component.vue @@ -92,6 +92,7 @@ <loading-icon v-if="isLoading" size="2" + class="prepend-top-default append-bottom-default" /> </div> </template> diff --git a/app/assets/javascripts/shared/milestones/form.js b/app/assets/javascripts/shared/milestones/form.js new file mode 100644 index 00000000000..db466f722c4 --- /dev/null +++ b/app/assets/javascripts/shared/milestones/form.js @@ -0,0 +1,9 @@ +import ZenMode from '../../zen_mode'; +import DueDateSelectors from '../../due_date_select'; +import GLForm from '../../gl_form'; + +export default (initGFM = true) => { + new ZenMode(); // eslint-disable-line no-new + new DueDateSelectors(); // eslint-disable-line no-new + new GLForm($('.milestone-form'), initGFM); // eslint-disable-line no-new +}; diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue index 7226076a8fc..d69d100a26c 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue @@ -1,12 +1,22 @@ <script> - /* eslint-disable vue/require-default-prop */ - import { __ } from '../../../locale'; + import { __ } from '~/locale'; + import icon from '~/vue_shared/components/icon.vue'; + import toggleButton from '~/vue_shared/components/toggle_button.vue'; + import tooltip from '~/vue_shared/directives/tooltip'; import eventHub from '../../event_hub'; - import loadingButton from '../../../vue_shared/components/loading_button.vue'; + + const ICON_ON = 'notifications'; + const ICON_OFF = 'notifications-off'; + const LABEL_ON = __('Notifications on'); + const LABEL_OFF = __('Notifications off'); export default { + directives: { + tooltip, + }, components: { - loadingButton, + icon, + toggleButton, }, props: { loading: { @@ -17,22 +27,23 @@ subscribed: { type: Boolean, required: false, + default: null, }, id: { type: Number, required: false, + default: null, }, }, computed: { - buttonLabel() { - let label; - if (this.subscribed === false) { - label = __('Subscribe'); - } else if (this.subscribed === true) { - label = __('Unsubscribe'); - } - - return label; + showLoadingState() { + return this.subscribed === null; + }, + notificationIcon() { + return this.subscribed ? ICON_ON : ICON_OFF; + }, + notificationTooltip() { + return this.subscribed ? LABEL_ON : LABEL_OFF; }, }, methods: { @@ -46,21 +57,29 @@ <template> <div> <div class="sidebar-collapsed-icon"> - <i - class="fa fa-rss" - aria-hidden="true" + <span + v-tooltip + :title="notificationTooltip" + data-container="body" + data-placement="left" > - </i> + <icon + :name="notificationIcon" + :size="16" + aria-hidden="true" + class="sidebar-item-icon is-active" + /> + </span> </div> <span class="issuable-header-text hide-collapsed pull-left"> {{ __('Notifications') }} </span> - <loading-button - ref="loadingButton" - class="btn btn-default pull-right hide-collapsed js-issuable-subscribe-button" - :loading="loading" - :label="buttonLabel" - @click="toggleSubscription" + <toggle-button + ref="toggleButton" + class="pull-right hide-collapsed js-issuable-subscribe-button" + :is-loading="showLoadingState" + :value="subscribed" + @change="toggleSubscription" /> </div> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js index e82fb979162..60f42c46ffe 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js @@ -1,6 +1,7 @@ import successSvg from 'icons/_icon_status_success.svg'; import warningSvg from 'icons/_icon_status_warning.svg'; import simplePoll from '~/lib/utils/simple_poll'; +import MergeRequest from '../../../merge_request'; import Flash from '../../../flash'; import statusIcon from '../mr_widget_status_icon'; import eventHub from '../../event_hub'; @@ -165,11 +166,9 @@ export default { // If state is merged we should update the widget and stop the polling eventHub.$emit('MRWidgetUpdateRequested'); eventHub.$emit('FetchActionsContent'); - if (window.mergeRequest) { - window.mergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged'); - window.mergeRequest.hideCloseButton(); - window.mergeRequest.decreaseCounter(); - } + MergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged'); + MergeRequest.hideCloseButton(); + MergeRequest.decreaseCounter(); stopPolling(); // If user checked remove source branch and we didn't remove the branch yet diff --git a/app/assets/javascripts/vue_shared/components/modal.vue b/app/assets/javascripts/vue_shared/components/modal.vue index c103c45c7dd..8227428d8ba 100644 --- a/app/assets/javascripts/vue_shared/components/modal.vue +++ b/app/assets/javascripts/vue_shared/components/modal.vue @@ -122,7 +122,7 @@ > <button type="button" - class="btn pull-left" + class="btn" :class="btnCancelKindClass" @click="emitCancel($event)" data-dismiss="modal" @@ -132,7 +132,7 @@ <button v-if="primaryButtonLabel" type="button" - class="btn pull-right js-primary-button" + class="btn js-primary-button" :disabled="submitDisabled" :class="btnKindClass" @click="emitSubmit($event)" diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue new file mode 100644 index 00000000000..86f06c8d266 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -0,0 +1,127 @@ +<script> +import tooltip from '~/vue_shared/directives/tooltip'; + +export default { + directives: { + tooltip, + }, + props: { + cssClass: { + type: String, + required: false, + default: '', + }, + successLabel: { + type: String, + required: true, + }, + failureLabel: { + type: String, + required: true, + }, + neutralLabel: { + type: String, + required: true, + }, + successCount: { + type: Number, + required: true, + }, + failureCount: { + type: Number, + required: true, + }, + totalCount: { + type: Number, + required: true, + }, + }, + computed: { + neutralCount() { + return this.totalCount - this.successCount - this.failureCount; + }, + successPercent() { + return this.getPercent(this.successCount); + }, + successBarStyle() { + return this.barStyle(this.successPercent); + }, + successTooltip() { + return this.getTooltip(this.successLabel, this.successCount); + }, + failurePercent() { + return this.getPercent(this.failureCount); + }, + failureBarStyle() { + return this.barStyle(this.failurePercent); + }, + failureTooltip() { + return this.getTooltip(this.failureLabel, this.failureCount); + }, + neutralPercent() { + return this.getPercent(this.neutralCount); + }, + neutralBarStyle() { + return this.barStyle(this.neutralPercent); + }, + neutralTooltip() { + return this.getTooltip(this.neutralLabel, this.neutralCount); + }, + }, + methods: { + getPercent(count) { + return Math.ceil((count / this.totalCount) * 100); + }, + barStyle(percent) { + return `width: ${percent}%;`; + }, + getTooltip(label, count) { + return `${label}: ${count}`; + }, + }, +}; +</script> + +<template> + <div + class="stacked-progress-bar" + :class="cssClass" + > + <span + v-if="!totalCount" + class="status-unavailable" + > + {{ __("Not available") }} + </span> + <span + v-tooltip + v-if="successPercent" + class="status-green" + data-placement="bottom" + :title="successTooltip" + :style="successBarStyle" + > + {{ successPercent }}% + </span> + <span + v-tooltip + v-if="neutralPercent" + class="status-neutral" + data-placement="bottom" + :title="neutralTooltip" + :style="neutralBarStyle" + > + {{ neutralPercent }}% + </span> + <span + v-tooltip + v-if="failurePercent" + class="status-red" + data-placement="bottom" + :title="failureTooltip" + :style="failureBarStyle" + > + {{ failurePercent }}% + </span> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue index 2b12718ae96..09031d3ffa1 100644 --- a/app/assets/javascripts/vue_shared/components/toggle_button.vue +++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue @@ -23,11 +23,12 @@ name: { type: String, required: false, - default: '', + default: null, }, value: { type: Boolean, - required: true, + required: false, + default: null, }, disabledInput: { type: Boolean, @@ -61,6 +62,7 @@ <template> <label class="toggle-wrapper"> <input + v-if="name" type="hidden" :name="name" :value="value" diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 43b16d3cf7d..cff47ea76ec 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -59,3 +59,4 @@ @import "framework/snippets"; @import "framework/memory_graph"; @import "framework/responsive_tables"; +@import "framework/stacked-progress-bar"; diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 1be66d0ab21..924472f2d7e 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -1,4 +1,5 @@ .modal-header { + background-color: $modal-body-bg; padding: #{3 * $grid-size} #{2 * $grid-size}; .page-title { @@ -8,6 +9,7 @@ .modal-body { background-color: $modal-body-bg; + min-height: $modal-body-height; position: relative; padding: #{3 * $grid-size} #{2 * $grid-size}; @@ -20,6 +22,30 @@ } } +.modal-footer { + display: flex; + flex-direction: row; + + .btn + .btn { + margin-left: $grid-size; + } + + @media (max-width: $screen-xs-max) { + flex-direction: column; + + .btn + .btn { + margin-left: 0; + margin-top: $grid-size; + } + } + + @media (min-width: $screen-sm-min) { + .btn:first-of-type { + margin-left: auto; + } + } +} + body.modal-open { overflow: hidden; } @@ -32,12 +58,6 @@ body.modal-open { } } -@media (min-width: $screen-md-min) { - .modal-dialog { - width: 860px; - } -} - @media (min-width: $screen-lg-min) { .modal-full { width: 98%; diff --git a/app/assets/stylesheets/framework/stacked-progress-bar.scss b/app/assets/stylesheets/framework/stacked-progress-bar.scss new file mode 100644 index 00000000000..4869cda73e5 --- /dev/null +++ b/app/assets/stylesheets/framework/stacked-progress-bar.scss @@ -0,0 +1,54 @@ +.stacked-progress-bar { + display: flex; + height: 16px; + border-radius: 10px; + overflow: hidden; + background-color: $theme-gray-100; + + .status-unavailable, + .status-green, + .status-neutral, + .status-red, { + height: 100%; + min-width: 25px; + padding: 0 5px; + font-size: $tooltip-font-size; + font-weight: normal; + color: $white-light; + line-height: 16px; + + &:hover { + cursor: pointer; + } + } + + .status-unavailable { + padding: 0 10px; + color: $theme-gray-700; + } + + .status-green { + background-color: $green-500; + + &:hover { + background-color: $green-600; + } + } + + .status-neutral { + background-color: $theme-gray-200; + color: $gl-gray-dark; + + &:hover { + background-color: $theme-gray-300; + } + } + + .status-red { + background-color: $red-500; + + &:hover { + background-color: $red-600; + } + } +} diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index a23131e0818..d04e555769b 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -194,6 +194,6 @@ $modal-body-bg: $white-light; //** Modal footer border color // $modal-footer-border-color: $modal-header-border-color -// $modal-lg: 900px -// $modal-md: 600px +$modal-lg: 860px; +$modal-md: 540px; // $modal-sm: 300px diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index ef1520f1f63..da18ddf78d3 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -733,3 +733,8 @@ $popup-box-shadow-color: rgba(90, 90, 90, 0.05); Multi file editor */ $border-color-settings: #e1e1e1; + +/* +Modals +*/ +$modal-body-height: 134px; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index ae9a8b0182c..759719a72da 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -162,10 +162,6 @@ border: 0; } - span { - display: inline-block; - } - .select2-container span { margin-top: 0; } diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss index c7297a34ad8..7d40c61da26 100644 --- a/app/assets/stylesheets/pages/xterm.scss +++ b/app/assets/stylesheets/pages/xterm.scss @@ -3,22 +3,21 @@ // see also: https://gist.github.com/jasonm23/2868981 $black: #000; - $red: #cd0000; - $green: #00cd00; - $yellow: #cdcd00; - $blue: #00e; // according to wikipedia, this is the xterm standard - //$blue: #1e90ff; // this is used by all the terminals I tried (when configured with the xterm color profile) - $magenta: #cd00cd; - $cyan: #00cdcd; - $white: #e5e5e5; + $red: #ea1010; + $green: #009900; + $yellow: #999900; + $blue: #0073e6; + $magenta: #d411d4; + $cyan: #009999; + $white: #ccc; $l-black: #373b41; - $l-red: #c66; - $l-green: #b5bd68; - $l-yellow: #f0c674; - $l-blue: #81a2be; - $l-magenta: #b294bb; - $l-cyan: #8abeb7; - $l-white: $gray-darkest; + $l-red: #ff6161; + $l-green: #00d600; + $l-yellow: #bdbd00; + $l-blue: #5797ff; + $l-magenta: #d96dd9; + $l-cyan: #00bdbd; + $l-white: #fff; /* * xterm colors diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 3d2926d5d75..0df80fa700f 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -43,11 +43,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap end def diffs - @diffs = if @merge_request.can_be_created - @merge_request.diffs(diff_options) - else - [] - end + @diffs = @merge_request.diffs(diff_options) if @merge_request.can_be_created @diff_notes_disabled = true diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 5e3b2e5581c..a6e1de6ffdc 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,6 +1,8 @@ module BlobHelper def highlight(blob_name, blob_content, repository: nil, plain: false) + plain ||= blob_content.length > Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository) + raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>) end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 6012dbba1b9..df67fb243ad 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -8,6 +8,7 @@ module Ci MissingDependenciesError = Class.new(StandardError) + belongs_to :project, inverse_of: :builds belongs_to :runner belongs_to :trigger_request belongs_to :erased_by, class_name: 'User' diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d4690da3be6..d7153d7b816 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -7,7 +7,7 @@ module Ci include Presentable include Gitlab::OptimisticLocking - belongs_to :project + belongs_to :project, inverse_of: :pipelines belongs_to :user belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline' belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule' diff --git a/app/models/commit.rb b/app/models/commit.rb index 21904c87f01..2d2d89af030 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -372,19 +372,19 @@ class Commit # uri_type('doc/README.md') # => :blob # uri_type('doc/logo.png') # => :raw # uri_type('doc/api') # => :tree - # uri_type('not/found') # => :nil + # uri_type('not/found') # => nil # # Returns a symbol def uri_type(path) - entry = @raw.rugged_tree_entry(path) + entry = @raw.tree_entry(path) + return unless entry + if entry[:type] == :blob blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]), @project) blob.image? || blob.video? ? :raw : :blob else entry[:type] end - rescue Rugged::TreeError - nil end def raw_diffs(*args) diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index 67ecf470f7e..703a72c355c 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -3,6 +3,7 @@ module ShaAttribute module ClassMethods def sha_attribute(name) + return if ENV['STATIC_VERIFICATION'] return unless table_exists? column = columns.find { |c| c.name == name.to_s } diff --git a/app/models/project.rb b/app/models/project.rb index 7ab7df4fdcd..d011b614c69 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -199,13 +199,13 @@ class Project < ActiveRecord::Base has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :commit_statuses - has_many :pipelines, class_name: 'Ci::Pipeline' + has_many :pipelines, class_name: 'Ci::Pipeline', inverse_of: :project # Ci::Build objects store data on the file system such as artifact files and # build traces. Currently there's no efficient way of removing this data in # bulk that doesn't involve loading the rows into memory. As a result we're # still using `dependent: :destroy` here. - has_many :builds, class_name: 'Ci::Build', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :builds, class_name: 'Ci::Build', inverse_of: :project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :build_trace_section_names, class_name: 'Ci::BuildTraceSectionName' has_many :runner_projects, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' diff --git a/app/models/push_event.rb b/app/models/push_event.rb index 83ce9014094..90c085c888e 100644 --- a/app/models/push_event.rb +++ b/app/models/push_event.rb @@ -46,10 +46,11 @@ class PushEvent < Event # Returns PushEvent instances for which no merge requests have been created. def self.without_existing_merge_requests - existing_mrs = MergeRequest.except(:order) + existing_mrs = MergeRequest.except(:order, :where) .select(1) .where('merge_requests.source_project_id = events.project_id') .where('merge_requests.source_branch = push_event_payloads.ref') + .where(state: :opened) # For reasons unknown the use of #eager_load will result in the # "push_event_payload" association not being set. Because of this we're diff --git a/app/models/repository.rb b/app/models/repository.rb index 8e9f33c174c..b4bc0f87458 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -831,13 +831,12 @@ class Repository end def can_be_merged?(source_sha, target_branch) - our_commit = rugged.branches[target_branch].target - their_commit = rugged.lookup(source_sha) - - if our_commit && their_commit - !rugged.merge_commits(our_commit, their_commit).conflicts? - else - false + raw_repository.gitaly_migrate(:can_be_merged) do |is_enabled| + if is_enabled + gitaly_can_be_merged?(source_sha, find_branch(target_branch).target) + else + rugged_can_be_merged?(source_sha, target_branch) + end end end @@ -933,15 +932,17 @@ class Repository return [] if empty? || query.blank? offset = 2 - args = %W(grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) + args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) run_git(args).first.scrub.split(/^--$/) end def search_files_by_name(query, ref) - return [] if empty? || query.blank? + safe_query = Regexp.escape(query.sub(/^\/*/, "")) + + return [] if empty? || safe_query.blank? - args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)}) + args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query}) run_git(args).first.lines.map(&:strip) end @@ -1132,6 +1133,14 @@ class Repository Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki)) end + def gitaly_can_be_merged?(their_commit, our_commit) + !raw_repository.gitaly_conflicts_client(our_commit, their_commit).conflicts? + end + + def rugged_can_be_merged?(their_commit, our_commit) + !rugged.merge_commits(our_commit, their_commit).conflicts? + end + def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) ref ||= root_ref diff --git a/app/models/user.rb b/app/models/user.rb index 4484ee9ff4c..09aa5a7b318 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -53,7 +53,10 @@ class User < ActiveRecord::Base serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, - :validatable, :omniauthable, :confirmable, :registerable + :validatable, :omniauthable, :confirmable, :registerable + + BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \ + "administrator if you think this is an error.".freeze # Override Devise::Models::Trackable#update_tracked_fields! # to limit database writes to at most once every hour @@ -217,8 +220,7 @@ class User < ActiveRecord::Base end def inactive_message - "Your account has been blocked. Please contact your GitLab " \ - "administrator if you think this is an error." + BLOCKED_MESSAGE end end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 690918b4a00..af6d77ef5e8 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -41,8 +41,11 @@ class SystemHooksService when User data.merge!(user_data(model)) - if event == :rename + case event + when :rename data[:old_username] = model.username_was + when :failed_login + data[:state] = model.state end when ProjectMember data.merge!(project_member_data(model)) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 30a5aab13bf..06b23cd7076 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -68,21 +68,14 @@ module SystemNoteService # # Returns the created Note object def change_issue_assignees(issue, project, author, old_assignees) - body = - if issue.assignees.any? && old_assignees.any? - unassigned_users = old_assignees - issue.assignees - added_users = issue.assignees.to_a - old_assignees - - text_parts = [] - text_parts << "assigned to #{added_users.map(&:to_reference).to_sentence}" if added_users.any? - text_parts << "unassigned #{unassigned_users.map(&:to_reference).to_sentence}" if unassigned_users.any? - - text_parts.join(' and ') - elsif old_assignees.any? - "removed assignee" - elsif issue.assignees.any? - "assigned to #{issue.assignees.map(&:to_reference).to_sentence}" - end + unassigned_users = old_assignees - issue.assignees + added_users = issue.assignees.to_a - old_assignees + + text_parts = [] + text_parts << "assigned to #{added_users.map(&:to_reference).to_sentence}" if added_users.any? + text_parts << "unassigned #{unassigned_users.map(&:to_reference).to_sentence}" if unassigned_users.any? + + body = text_parts.join(' and ') create_note(NoteSummary.new(issue, project, author, body, action: 'assignee')) end diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml index ecdf76ef5c5..7a3f3667ac1 100644 --- a/app/views/dashboard/_activity_head.html.haml +++ b/app/views/dashboard/_activity_head.html.haml @@ -2,7 +2,7 @@ %ul.nav-links %li{ class: active_when(params[:filter].nil?) }> = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do - Your Projects + Your projects %li{ class: active_when(params[:filter] == 'starred') }> = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do - Starred Projects + Starred projects diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml index 7330f4cb523..a9488df07bd 100644 --- a/app/views/dashboard/_snippets_head.html.haml +++ b/app/views/dashboard/_snippets_head.html.haml @@ -2,10 +2,10 @@ %ul.nav-links = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do - Your Snippets + Your snippets = nav_link(page: explore_snippets_path) do = link_to explore_snippets_path, title: 'Explore snippets', data: {placement: 'right'} do - Explore Snippets + Explore snippets - if current_user .nav-controls.hidden-xs diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 0c27b09f7b1..96aae06a9df 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -1,5 +1,5 @@ -- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute -- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute +- issues_count = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute.count +- merge_requests_count = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute.count .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar-inner-scroll @@ -39,14 +39,14 @@ = sprite_icon('issues') %span.nav-item-name Issues - %span.badge.count= number_with_delimiter(issues.count) + %span.badge.count= number_with_delimiter(issues_count) %ul.sidebar-sub-level-items = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do = link_to issues_group_path(@group) do %strong.fly-out-top-item-name #{ _('Issues') } - %span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues.count) + %span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count) %li.divider.fly-out-top-item = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do = link_to issues_group_path(@group), title: 'List' do @@ -69,13 +69,13 @@ = sprite_icon('git-merge') %span.nav-item-name Merge Requests - %span.badge.count= number_with_delimiter(merge_requests.count) + %span.badge.count= number_with_delimiter(merge_requests_count) %ul.sidebar-sub-level-items.is-fly-out-only = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do = link_to merge_requests_group_path(@group) do %strong.fly-out-top-item-name #{ _('Merge Requests') } - %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests.count) + %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count) = nav_link(path: 'group_members#index') do = link_to group_group_members_path(@group) do .nav-icon-container diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml index 86ebec0179c..e44506ec9c9 100644 --- a/app/views/profiles/gpg_keys/index.html.haml +++ b/app/views/profiles/gpg_keys/index.html.haml @@ -3,12 +3,12 @@ = render 'profiles/head' .row.prepend-top-default - .col-lg-3.profile-settings-sidebar + .col-lg-4.profile-settings-sidebar %h4.prepend-top-0 = page_title %p GPG keys allow you to verify signed commits. - .col-lg-9 + .col-lg-8 %h5.prepend-top-0 Add a GPG key %p.profile-settings-content diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 03ab1bb59e4..5d48a35dc4c 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -1,5 +1,5 @@ #modal-create-new-dir.modal - .modal-dialog + .modal-dialog.modal-lg .modal-content .modal-header %a.close{ href: "#", "data-dismiss" => "modal" } × diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 05b7dfe2872..21b6aa4bad9 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -1,5 +1,5 @@ #modal-upload-blob.modal - .modal-dialog + .modal-dialog.modal-lg .modal-content .modal-header %a.close{ href: "#", "data-dismiss" => "modal" } × diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 8e8c911185a..a94d9c14722 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -10,37 +10,28 @@ - if can_create_issue %li - = link_to new_project_issue_path(@project) do - #{ _('New issue') } - + = link_to _('New issue'), new_project_issue_path(@project) - if merge_project %li - = link_to project_new_merge_request_path(merge_project) do - #{ _('New merge request') } - + = link_to _('New merge request'), project_new_merge_request_path(merge_project) - if can_create_snippet %li - = link_to new_project_snippet_path(@project) do - #{ _('New snippet') } + = link_to _('New snippet'), new_project_snippet_path(@project) - if can_create_issue || merge_project || can_create_snippet %li.divider - if can?(current_user, :push_code, @project) %li - = link_to project_new_blob_path(@project, @project.default_branch || 'master') do - #{ _('New file') } + = link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') - unless @project.empty_repo? %li - = link_to new_project_branch_path(@project) do - #{ _('New branch') } + = link_to _('New branch'), new_project_branch_path(@project) %li - = link_to new_project_tag_path(@project) do - #{ _('New tag') } + = link_to _('New tag'), new_project_tag_path(@project) - elsif current_user && current_user.already_forked?(@project) %li - = link_to project_new_blob_path(@project, @project.default_branch || 'master') do - #{ _('New file') } + = link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master') - elsif can?(current_user, :fork_project, @project) %li - continue_params = { to: project_new_blob_path(@project, @project.default_branch || 'master'), @@ -48,5 +39,4 @@ notice_now: edit_in_new_fork_notice_now } - fork_path = project_forks_path(@project, namespace_key: current_user.namespace.id, continue: continue_params) - = link_to fork_path, method: :post do - #{ _('New file') } + = link_to _('New file'), fork_path, method: :post diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index 111cbcda266..21a4702a2a9 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -31,11 +31,11 @@ - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do - = custom_icon('icon_fork') + = sprite_icon('fork', size: 12) %span Fork - else = link_to new_project_fork_path(@project), title: "Fork project", class: 'btn btn-new' do - = custom_icon('icon_fork') + = sprite_icon('fork', size: 12) %span Fork diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml index c66313bdbf3..311934d9c33 100644 --- a/app/views/projects/jobs/_empty_state.html.haml +++ b/app/views/projects/jobs/_empty_state.html.haml @@ -1,7 +1,7 @@ - illustration = local_assigns.fetch(:illustration) - illustration_size = local_assigns.fetch(:illustration_size) - title = local_assigns.fetch(:title) -- content = local_assigns.fetch(:content) +- content = local_assigns.fetch(:content, nil) - action = local_assigns.fetch(:action, nil) .row.empty-state @@ -11,7 +11,8 @@ .col-xs-12 .text-content %h4.text-center= title - %p= content + - if content + %p= content - if action .text-center = action diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index 8b05440fc78..1e6d6f67e66 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -93,14 +93,13 @@ illustration: 'illustrations/manual_action.svg', illustration_size: 'svg-394', title: _('This job requires a manual action'), - content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.'), - action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), class: 'btn btn-primary', title: _('Trigger this manual action') ) + content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), + action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) - else = render 'empty_state', illustration: 'illustrations/job_not_triggered.svg', illustration_size: 'svg-306', - title: _('This job has not been triggered yet'), - content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered.') + title: _('This job has not been triggered yet') = render "sidebar" diff --git a/app/views/projects/merge_requests/creations/_diffs.html.haml b/app/views/projects/merge_requests/creations/_diffs.html.haml index 627fc4e9671..5b70e894b39 100644 --- a/app/views/projects/merge_requests/creations/_diffs.html.haml +++ b/app/views/projects/merge_requests/creations/_diffs.html.haml @@ -1 +1,5 @@ -= render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false +- if @merge_request.can_be_created + = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false +- else + .nothing-here-block + This merge request cannot be created. diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index a4e820628f3..67607e4e9c6 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -23,6 +23,3 @@ %h4.underlined-title Available shared Runners : #{@shared_runners_count} %ul.bordered-list.available-shared-runners = render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner - - if @shared_runners_count > 10 - .light - and #{@shared_runners_count - 10} more... diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 63aa4e29ec9..2a75b46d376 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -52,7 +52,7 @@ = render_project_pipeline_status(project.pipeline_status) - if forks %span.prepend-left-10 - = sprite_icon('fork') + = sprite_icon('fork', size: 12) = number_with_delimiter(project.forks_count) - if stars %span.prepend-left-10 diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index d1c57b82681..07584fab7c8 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -17,10 +17,7 @@ class RepositoryForkWorker project.repository_storage_path, project.disk_path) raise "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result - project.repository.after_import - raise "Project #{project_id} had an invalid repository after fork" unless project.valid_repo? - - project.import_finish + project.after_import end private |