summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/dispatcher.js18
-rw-r--r--app/assets/javascripts/main.js5
-rw-r--r--app/assets/javascripts/project.js4
-rw-r--r--app/assets/javascripts/project_label_subscription.js79
-rw-r--r--app/assets/javascripts/project_new.js272
-rw-r--r--app/assets/javascripts/project_select.js134
-rw-r--r--app/assets/javascripts/project_show.js11
-rw-r--r--app/assets/javascripts/project_variables.js60
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb6
-rw-r--r--app/controllers/projects/commits_controller.rb1
-rw-r--r--app/controllers/projects/merge_requests_controller.rb3
-rw-r--r--app/controllers/projects/wikis_controller.rb8
-rw-r--r--app/helpers/ci_status_helper.rb5
-rw-r--r--app/models/ci/pipeline.rb66
-rw-r--r--app/models/commit.rb9
-rw-r--r--app/models/commit_collection.rb44
-rw-r--r--app/models/fork_network_member.rb10
-rw-r--r--app/models/identity.rb15
-rw-r--r--app/models/merge_request_diff.rb4
-rw-r--r--app/models/project_wiki.rb4
-rw-r--r--app/models/repository.rb16
-rw-r--r--app/models/user.rb3
-rw-r--r--app/models/wiki_page.rb19
-rw-r--r--app/services/projects/transfer_service.rb26
-rw-r--r--app/views/admin/runners/show.html.haml2
-rw-r--r--app/views/projects/wikis/_pages_wiki_page.html.haml2
-rw-r--r--app/views/projects/wikis/history.html.haml3
-rw-r--r--app/views/projects/wikis/show.html.haml4
28 files changed, 459 insertions, 374 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index d716218d9a4..5ff7edb6077 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -1,6 +1,6 @@
/* 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';
-/* global ProjectSelect */
+import projectSelect from './project_select';
import IssuableIndex from './issuable_index';
/* global Milestone */
import IssuableForm from './issuable_form';
@@ -26,8 +26,7 @@ import projectAvatar from './project_avatar';
/* global Compare */
/* global CompareAutocomplete */
/* global ProjectFindFile */
-/* global ProjectNew */
-/* global ProjectShow */
+import ProjectNew from './project_new';
import projectImport from './project_import';
import Labels from './labels';
import LabelManager from './label_manager';
@@ -91,6 +90,8 @@ 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 ProjectVariables from './project_variables';
(function() {
var Dispatcher;
@@ -187,7 +188,7 @@ import Diff from './diff';
initIssuableSidebar();
break;
case 'dashboard:milestones:index':
- new ProjectSelect();
+ projectSelect();
break;
case 'projects:milestones:show':
case 'groups:milestones:show':
@@ -197,7 +198,7 @@ import Diff from './diff';
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
- new ProjectSelect();
+ projectSelect();
initLegacyFilters();
break;
case 'groups:issues':
@@ -206,7 +207,7 @@ import Diff from './diff';
const filteredSearchManager = new gl.FilteredSearchManager(page === 'groups:issues' ? 'issues' : 'merge_requests');
filteredSearchManager.setup();
}
- new ProjectSelect();
+ projectSelect();
break;
case 'dashboard:todos:index':
new Todos();
@@ -484,7 +485,7 @@ import Diff from './diff';
if ($el.find('.dropdown-group-label').length) {
new GroupLabelSubscription($el);
} else {
- new gl.ProjectLabelSubscription($el);
+ new ProjectLabelSubscription($el);
}
});
break;
@@ -520,7 +521,7 @@ import Diff from './diff';
// Initialize expandable settings panels
initSettingsPanels();
case 'groups:settings:ci_cd:show':
- new gl.ProjectVariables();
+ new ProjectVariables();
break;
case 'ci:lints:create':
case 'ci:lints:show':
@@ -623,7 +624,6 @@ import Diff from './diff';
case 'show':
new Star();
new ProjectNew();
- new ProjectShow();
new NotificationsDropdown();
break;
case 'wikis':
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 0035dd23011..cef79eec273 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -71,11 +71,6 @@ import './pager';
import './preview_markdown';
import './project_find_file';
import './project_import';
-import './project_label_subscription';
-import './project_new';
-import './project_select';
-import './project_show';
-import './project_variables';
import './projects_dropdown';
import './projects_list';
import './syntax_highlight';
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index ddb78aaeea1..36b6a5ed376 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,7 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, no-var, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
-/* global ProjectSelect */
import Cookies from 'js-cookie';
+import projectSelect from './project_select';
export default class Project {
constructor() {
@@ -46,7 +46,7 @@ export default class Project {
}
static projectSelectDropdown () {
- new ProjectSelect();
+ projectSelect();
$('.project-item-select').on('click', e => Project.changeProject($(e.currentTarget).val()));
}
diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js
index 0a811627600..b65521b278f 100644
--- a/app/assets/javascripts/project_label_subscription.js
+++ b/app/assets/javascripts/project_label_subscription.js
@@ -1,55 +1,50 @@
-/* eslint-disable wrap-iife, func-names, space-before-function-paren, object-shorthand, comma-dangle, one-var, one-var-declaration-per-line, no-restricted-syntax, max-len, no-param-reassign */
+export default class ProjectLabelSubscription {
+ constructor(container) {
+ this.$container = $(container);
+ this.$buttons = this.$container.find('.js-subscribe-button');
-(function(global) {
- class ProjectLabelSubscription {
- constructor(container) {
- this.$container = $(container);
- this.$buttons = this.$container.find('.js-subscribe-button');
-
- this.$buttons.on('click', this.toggleSubscription.bind(this));
- }
+ this.$buttons.on('click', this.toggleSubscription.bind(this));
+ }
- toggleSubscription(event) {
- event.preventDefault();
+ toggleSubscription(event) {
+ event.preventDefault();
- const $btn = $(event.currentTarget);
- const $span = $btn.find('span');
- const url = $btn.attr('data-url');
- const oldStatus = $btn.attr('data-status');
+ const $btn = $(event.currentTarget);
+ const $span = $btn.find('span');
+ const url = $btn.attr('data-url');
+ const oldStatus = $btn.attr('data-status');
- $btn.addClass('disabled');
- $span.toggleClass('hidden');
+ $btn.addClass('disabled');
+ $span.toggleClass('hidden');
- $.ajax({
- type: 'POST',
- url: url
- }).done(() => {
- let newStatus, newAction;
+ $.ajax({
+ type: 'POST',
+ url,
+ }).done(() => {
+ let newStatus;
+ let newAction;
- if (oldStatus === 'unsubscribed') {
- [newStatus, newAction] = ['subscribed', 'Unsubscribe'];
- } else {
- [newStatus, newAction] = ['unsubscribed', 'Subscribe'];
- }
+ if (oldStatus === 'unsubscribed') {
+ [newStatus, newAction] = ['subscribed', 'Unsubscribe'];
+ } else {
+ [newStatus, newAction] = ['unsubscribed', 'Subscribe'];
+ }
- $span.toggleClass('hidden');
- $btn.removeClass('disabled');
+ $span.toggleClass('hidden');
+ $btn.removeClass('disabled');
- this.$buttons.attr('data-status', newStatus);
- this.$buttons.find('> span').text(newAction);
+ this.$buttons.attr('data-status', newStatus);
+ this.$buttons.find('> span').text(newAction);
- this.$buttons.map((button) => {
- const $button = $(button);
+ this.$buttons.map((button) => {
+ const $button = $(button);
- if ($button.attr('data-original-title')) {
- $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle');
- }
+ if ($button.attr('data-original-title')) {
+ $button.tooltip('hide').attr('data-original-title', newAction).tooltip('fixTitle');
+ }
- return button;
- });
+ return button;
});
- }
+ });
}
-
- global.ProjectLabelSubscription = ProjectLabelSubscription;
-})(window.gl || (window.gl = {}));
+}
diff --git a/app/assets/javascripts/project_new.js b/app/assets/javascripts/project_new.js
index fd89a1a85c3..ca548d011b6 100644
--- a/app/assets/javascripts/project_new.js
+++ b/app/assets/javascripts/project_new.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, one-var, no-underscore-dangle, prefer-template, no-else-return, prefer-arrow-callback, max-len */
+/* eslint-disable func-names, no-var, no-underscore-dangle, prefer-template, prefer-arrow-callback*/
import VisibilitySelect from './visibility_select';
@@ -7,153 +7,145 @@ function highlightChanges($elm) {
setTimeout(() => $elm.removeClass('highlight-changes'), 10);
}
-(function() {
- this.ProjectNew = (function() {
- function ProjectNew() {
- this.toggleSettings = this.toggleSettings.bind(this);
- this.$selects = $('.features select');
- this.$repoSelects = this.$selects.filter('.js-repo-select');
- this.$projectSelects = this.$selects.not('.js-repo-select');
-
- $('.project-edit-container').on('ajax:before', (function(_this) {
- return function() {
- $('.project-edit-container').hide();
- return $('.save-project-loader').show();
- };
- })(this));
-
- this.initVisibilitySelect();
-
- this.toggleSettings();
- this.toggleSettingsOnclick();
- this.toggleRepoVisibility();
- }
-
- ProjectNew.prototype.initVisibilitySelect = function() {
- const visibilityContainer = document.querySelector('.js-visibility-select');
- if (!visibilityContainer) return;
- const visibilitySelect = new VisibilitySelect(visibilityContainer);
- visibilitySelect.init();
-
- const $visibilitySelect = $(visibilityContainer).find('select');
- let projectVisibility = $visibilitySelect.val();
- const PROJECT_VISIBILITY_PRIVATE = '0';
-
- $visibilitySelect.on('change', () => {
- const newProjectVisibility = $visibilitySelect.val();
-
- if (projectVisibility !== newProjectVisibility) {
- this.$projectSelects.each((idx, select) => {
- const $select = $(select);
- const $options = $select.find('option');
- const values = $.map($options, e => e.value);
-
- // if switched to "private", limit visibility options
- if (newProjectVisibility === PROJECT_VISIBILITY_PRIVATE) {
- if ($select.val() !== values[0] && $select.val() !== values[1]) {
- $select.val(values[1]).trigger('change');
- highlightChanges($select);
- }
- $options.slice(2).disable();
+export default class ProjectNew {
+ constructor() {
+ this.toggleSettings = this.toggleSettings.bind(this);
+ this.$selects = $('.features select');
+ this.$repoSelects = this.$selects.filter('.js-repo-select');
+ this.$projectSelects = this.$selects.not('.js-repo-select');
+
+ $('.project-edit-container').on('ajax:before', () => {
+ $('.project-edit-container').hide();
+ return $('.save-project-loader').show();
+ });
+
+ this.initVisibilitySelect();
+
+ this.toggleSettings();
+ this.toggleSettingsOnclick();
+ this.toggleRepoVisibility();
+ }
+
+ initVisibilitySelect() {
+ const visibilityContainer = document.querySelector('.js-visibility-select');
+ if (!visibilityContainer) return;
+ const visibilitySelect = new VisibilitySelect(visibilityContainer);
+ visibilitySelect.init();
+
+ const $visibilitySelect = $(visibilityContainer).find('select');
+ let projectVisibility = $visibilitySelect.val();
+ const PROJECT_VISIBILITY_PRIVATE = '0';
+
+ $visibilitySelect.on('change', () => {
+ const newProjectVisibility = $visibilitySelect.val();
+
+ if (projectVisibility !== newProjectVisibility) {
+ this.$projectSelects.each((idx, select) => {
+ const $select = $(select);
+ const $options = $select.find('option');
+ const values = $.map($options, e => e.value);
+
+ // if switched to "private", limit visibility options
+ if (newProjectVisibility === PROJECT_VISIBILITY_PRIVATE) {
+ if ($select.val() !== values[0] && $select.val() !== values[1]) {
+ $select.val(values[1]).trigger('change');
+ highlightChanges($select);
}
+ $options.slice(2).disable();
+ }
- // if switched from "private", increase visibility for non-disabled options
- if (projectVisibility === PROJECT_VISIBILITY_PRIVATE) {
- $options.enable();
- if ($select.val() !== values[0] && $select.val() !== values[values.length - 1]) {
- $select.val(values[values.length - 1]).trigger('change');
- highlightChanges($select);
- }
+ // if switched from "private", increase visibility for non-disabled options
+ if (projectVisibility === PROJECT_VISIBILITY_PRIVATE) {
+ $options.enable();
+ if ($select.val() !== values[0] && $select.val() !== values[values.length - 1]) {
+ $select.val(values[values.length - 1]).trigger('change');
+ highlightChanges($select);
}
- });
+ }
+ });
- projectVisibility = newProjectVisibility;
- }
- });
- };
-
- ProjectNew.prototype.toggleSettings = function() {
- var self = this;
-
- this.$selects.each(function () {
- var $select = $(this);
- var className = $select.data('field')
- .replace(/_/g, '-')
- .replace('access-level', 'feature');
- self._showOrHide($select, '.' + className);
- });
- };
-
- ProjectNew.prototype.toggleSettingsOnclick = function() {
- this.$selects.on('change', this.toggleSettings);
- };
-
- ProjectNew.prototype._showOrHide = function(checkElement, container) {
- var $container = $(container);
-
- if ($(checkElement).val() !== '0') {
- return $container.show();
- } else {
- return $container.hide();
+ projectVisibility = newProjectVisibility;
}
- };
-
- ProjectNew.prototype.toggleRepoVisibility = function () {
- var $repoAccessLevel = $('.js-repo-access-level select');
- var $lfsEnabledOption = $('.js-lfs-enabled select');
- var containerRegistry = document.querySelectorAll('.js-container-registry')[0];
- var containerRegistryCheckbox = document.getElementById('project_container_registry_enabled');
- var prevSelectedVal = parseInt($repoAccessLevel.val(), 10);
-
- this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
- .nextAll()
- .hide();
-
- $repoAccessLevel.off('change')
- .on('change', function () {
- var selectedVal = parseInt($repoAccessLevel.val(), 10);
-
- this.$repoSelects.each(function () {
- var $this = $(this);
- var repoSelectVal = parseInt($this.val(), 10);
-
- $this.find('option').enable();
-
- if (selectedVal < repoSelectVal || repoSelectVal === prevSelectedVal) {
- $this.val(selectedVal).trigger('change');
- highlightChanges($this);
- }
-
- $this.find("option[value='" + selectedVal + "']").nextAll().disable();
- });
+ });
+ }
+
+ toggleSettings() {
+ this.$selects.each(function () {
+ var $select = $(this);
+ var className = $select.data('field')
+ .replace(/_/g, '-')
+ .replace('access-level', 'feature');
+ ProjectNew._showOrHide($select, '.' + className);
+ });
+ }
+
+ toggleSettingsOnclick() {
+ this.$selects.on('change', this.toggleSettings);
+ }
+
+ static _showOrHide(checkElement, container) {
+ const $container = $(container);
+
+ if ($(checkElement).val() !== '0') {
+ return $container.show();
+ }
+ return $container.hide();
+ }
+
+ toggleRepoVisibility() {
+ var $repoAccessLevel = $('.js-repo-access-level select');
+ var $lfsEnabledOption = $('.js-lfs-enabled select');
+ var containerRegistry = document.querySelectorAll('.js-container-registry')[0];
+ var containerRegistryCheckbox = document.getElementById('project_container_registry_enabled');
+ var prevSelectedVal = parseInt($repoAccessLevel.val(), 10);
+
+ this.$repoSelects.find("option[value='" + $repoAccessLevel.val() + "']")
+ .nextAll()
+ .hide();
+
+ $repoAccessLevel
+ .off('change')
+ .on('change', function () {
+ var selectedVal = parseInt($repoAccessLevel.val(), 10);
+
+ this.$repoSelects.each(function () {
+ var $this = $(this);
+ var repoSelectVal = parseInt($this.val(), 10);
+
+ $this.find('option').enable();
+
+ if (selectedVal < repoSelectVal || repoSelectVal === prevSelectedVal) {
+ $this.val(selectedVal).trigger('change');
+ highlightChanges($this);
+ }
- if (selectedVal) {
- this.$repoSelects.removeClass('disabled');
+ $this.find("option[value='" + selectedVal + "']").nextAll().disable();
+ });
- if ($lfsEnabledOption.length) {
- $lfsEnabledOption.removeClass('disabled');
- highlightChanges($lfsEnabledOption);
- }
- if (containerRegistry) {
- containerRegistry.style.display = '';
- }
- } else {
- this.$repoSelects.addClass('disabled');
+ if (selectedVal) {
+ this.$repoSelects.removeClass('disabled');
- if ($lfsEnabledOption.length) {
- $lfsEnabledOption.val('false').addClass('disabled');
- highlightChanges($lfsEnabledOption);
- }
- if (containerRegistry) {
- containerRegistry.style.display = 'none';
- containerRegistryCheckbox.checked = false;
- }
+ if ($lfsEnabledOption.length) {
+ $lfsEnabledOption.removeClass('disabled');
+ highlightChanges($lfsEnabledOption);
+ }
+ if (containerRegistry) {
+ containerRegistry.style.display = '';
}
+ } else {
+ this.$repoSelects.addClass('disabled');
- prevSelectedVal = selectedVal;
- }.bind(this));
- };
+ if ($lfsEnabledOption.length) {
+ $lfsEnabledOption.val('false').addClass('disabled');
+ highlightChanges($lfsEnabledOption);
+ }
+ if (containerRegistry) {
+ containerRegistry.style.display = 'none';
+ containerRegistryCheckbox.checked = false;
+ }
+ }
- return ProjectNew;
- })();
-}).call(window);
+ prevSelectedVal = selectedVal;
+ }.bind(this));
+ }
+}
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index bffc85e6315..07a49d1506c 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -2,79 +2,73 @@
import Api from './api';
import ProjectSelectComboButton from './project_select_combo_button';
-(function () {
- this.ProjectSelect = (function () {
- 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');
+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');
- placeholder = "Search for project";
- if (this.includeGroups) {
- placeholder += " or group";
- }
+ placeholder = "Search for project";
+ if (this.includeGroups) {
+ placeholder += " or group";
+ }
- $(select).select2({
- placeholder: placeholder,
- minimumInputLength: 0,
- query: (function (_this) {
- return function (query) {
- var finalCallback, projectsCallback;
- finalCallback = function (projects) {
+ $(select).select2({
+ placeholder: placeholder,
+ minimumInputLength: 0,
+ query: (function (_this) {
+ return function (query) {
+ var finalCallback, projectsCallback;
+ finalCallback = function (projects) {
+ var data;
+ data = {
+ results: projects
+ };
+ return query.callback(data);
+ };
+ if (_this.includeGroups) {
+ projectsCallback = function (projects) {
+ var groupsCallback;
+ groupsCallback = function (groups) {
var data;
- data = {
- results: projects
- };
- return query.callback(data);
+ data = groups.concat(projects);
+ return finalCallback(data);
};
- if (_this.includeGroups) {
- projectsCallback = function (projects) {
- var groupsCallback;
- groupsCallback = function (groups) {
- var data;
- data = groups.concat(projects);
- return finalCallback(data);
- };
- return Api.groups(query.term, {}, groupsCallback);
- };
- } else {
- projectsCallback = finalCallback;
- }
- if (_this.groupId) {
- return Api.groupProjects(_this.groupId, query.term, projectsCallback);
- } else {
- return Api.projects(query.term, {
- order_by: _this.orderBy,
- with_issues_enabled: _this.withIssuesEnabled,
- with_merge_requests_enabled: _this.withMergeRequestsEnabled,
- membership: !_this.allProjects,
- }, projectsCallback);
- }
+ return Api.groups(query.term, {}, groupsCallback);
};
- })(this),
- id: function(project) {
- if (simpleFilter) return project.id;
- return JSON.stringify({
- name: project.name,
- url: project.web_url,
- });
- },
- text: function (project) {
- return project.name_with_namespace || project.name;
- },
- dropdownCssClass: "ajax-project-dropdown"
+ } else {
+ projectsCallback = finalCallback;
+ }
+ if (_this.groupId) {
+ return Api.groupProjects(_this.groupId, query.term, projectsCallback);
+ } else {
+ return Api.projects(query.term, {
+ order_by: _this.orderBy,
+ with_issues_enabled: _this.withIssuesEnabled,
+ with_merge_requests_enabled: _this.withMergeRequestsEnabled,
+ membership: !_this.allProjects,
+ }, projectsCallback);
+ }
+ };
+ })(this),
+ id: function(project) {
+ if (simpleFilter) return project.id;
+ return JSON.stringify({
+ name: project.name,
+ url: project.web_url,
});
- if (simpleFilter) return select;
- return new ProjectSelectComboButton(select);
- });
- }
-
- return ProjectSelect;
- })();
-}).call(window);
+ },
+ text: function (project) {
+ return project.name_with_namespace || project.name;
+ },
+ dropdownCssClass: "ajax-project-dropdown"
+ });
+ if (simpleFilter) return select;
+ return new ProjectSelectComboButton(select);
+ });
+}
diff --git a/app/assets/javascripts/project_show.js b/app/assets/javascripts/project_show.js
deleted file mode 100644
index 3a51c1f26ac..00000000000
--- a/app/assets/javascripts/project_show.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife */
-
-(function() {
- this.ProjectShow = (function() {
- function ProjectShow() {}
-
- return ProjectShow;
- })();
-}).call(window);
-
-// I kept class for future
diff --git a/app/assets/javascripts/project_variables.js b/app/assets/javascripts/project_variables.js
index 4ee2e49306d..567c311f119 100644
--- a/app/assets/javascripts/project_variables.js
+++ b/app/assets/javascripts/project_variables.js
@@ -1,43 +1,39 @@
-(() => {
- const HIDDEN_VALUE_TEXT = '******';
- class ProjectVariables {
- constructor() {
- this.$revealBtn = $('.js-btn-toggle-reveal-values');
- this.$revealBtn.on('click', this.toggleRevealState.bind(this));
- }
+const HIDDEN_VALUE_TEXT = '******';
+
+export default class ProjectVariables {
+ constructor() {
+ this.$revealBtn = $('.js-btn-toggle-reveal-values');
+ this.$revealBtn.on('click', this.toggleRevealState.bind(this));
+ }
- toggleRevealState(e) {
- e.preventDefault();
+ toggleRevealState(e) {
+ e.preventDefault();
- const oldStatus = this.$revealBtn.attr('data-status');
- let newStatus = 'hidden';
- let newAction = 'Reveal Values';
+ const oldStatus = this.$revealBtn.attr('data-status');
+ let newStatus = 'hidden';
+ let newAction = 'Reveal Values';
- if (oldStatus === 'hidden') {
- newStatus = 'revealed';
- newAction = 'Hide Values';
- }
+ if (oldStatus === 'hidden') {
+ newStatus = 'revealed';
+ newAction = 'Hide Values';
+ }
- this.$revealBtn.attr('data-status', newStatus);
+ this.$revealBtn.attr('data-status', newStatus);
- const $variables = $('.variable-value');
+ const $variables = $('.variable-value');
- $variables.each((_, variable) => {
- const $variable = $(variable);
- let newText = HIDDEN_VALUE_TEXT;
+ $variables.each((_, variable) => {
+ const $variable = $(variable);
+ let newText = HIDDEN_VALUE_TEXT;
- if (newStatus === 'revealed') {
- newText = $variable.attr('data-value');
- }
+ if (newStatus === 'revealed') {
+ newText = $variable.attr('data-value');
+ }
- $variable.text(newText);
- });
+ $variable.text(newText);
+ });
- this.$revealBtn.text(newAction);
- }
+ this.$revealBtn.text(newAction);
}
-
- window.gl = window.gl || {};
- window.gl.ProjectVariables = ProjectVariables;
-})();
+}
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 9612b8d8514..56baa19f864 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -54,7 +54,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
if current_user
log_audit_event(current_user, with: :saml)
# Update SAML identity if data has changed.
- identity = current_user.identities.find_by(extern_uid: oauth['uid'], provider: :saml)
+ identity = current_user.identities.with_extern_uid(:saml, oauth['uid']).take
if identity.nil?
current_user.identities.create(extern_uid: oauth['uid'], provider: :saml)
redirect_to profile_account_path, notice: 'Authentication method updated'
@@ -98,7 +98,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def handle_omniauth
if current_user
# Add new authentication method
- current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider'])
+ current_user.identities
+ .with_extern_uid(oauth['provider'], oauth['uid'])
+ .first_or_create(extern_uid: oauth['uid'])
log_audit_event(current_user, with: oauth['provider'])
redirect_to profile_account_path, notice: 'Authentication method updated'
else
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 28920877635..5f4afd2cdee 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -57,6 +57,7 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end
+ @commits = @commits.with_pipeline_status
@commits = prepare_commits_for_rendering(@commits)
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 22de6680511..abe4e5245b1 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -80,7 +80,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def commits
# Get commits from repository
# or from cache if already merged
- @commits = prepare_commits_for_rendering(@merge_request.commits)
+ @commits =
+ prepare_commits_for_rendering(@merge_request.commits.with_pipeline_status)
render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index f7a9c98629d..b51261a49e0 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -74,7 +74,11 @@ class Projects::WikisController < Projects::ApplicationController
def history
@page = @project_wiki.find_page(params[:id])
- unless @page
+ if @page
+ @page_versions = Kaminari.paginate_array(@page.versions(page: params[:page]),
+ total_count: @page.count_versions)
+ .page(params[:page])
+ else
redirect_to(
project_wiki_path(@project, :home),
notice: "Page not found"
@@ -101,7 +105,7 @@ class Projects::WikisController < Projects::ApplicationController
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
- @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages.first(15))
+ @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages(limit: 15))
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = "Could not create Wiki Repository at this time. Please try again later."
redirect_to project_path(@project)
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 4dd573c61f1..636316da80a 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -6,11 +6,6 @@
# See 'detailed_status?` method and `Gitlab::Ci::Status` module.
#
module CiStatusHelper
- def ci_status_path(pipeline)
- project = pipeline.project
- project_pipeline_path(project, pipeline)
- end
-
def ci_label_for_status(status)
if detailed_status?(status)
return status.label
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 19814864e50..c77c837ff3f 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -149,34 +149,70 @@ module Ci
end
end
- # ref can't be HEAD or SHA, can only be branch/tag name
- scope :latest, ->(ref = nil) do
- max_id = unscope(:select)
- .select("max(#{quoted_table_name}.id)")
- .group(:ref, :sha)
-
- if ref
- where(ref: ref, id: max_id.where(ref: ref))
- else
- where(id: max_id)
- end
- end
scope :internal, -> { where(source: internal_sources) }
+ # Returns the pipelines in descending order (= newest first), optionally
+ # limited to a number of references.
+ #
+ # ref - The name (or names) of the branch(es)/tag(s) to limit the list of
+ # pipelines to.
+ def self.newest_first(ref = nil)
+ relation = order(id: :desc)
+
+ ref ? relation.where(ref: ref) : relation
+ end
+
def self.latest_status(ref = nil)
- latest(ref).status
+ newest_first(ref).pluck(:status).first
end
def self.latest_successful_for(ref)
- success.latest(ref).order(id: :desc).first
+ newest_first(ref).success.take
end
def self.latest_successful_for_refs(refs)
- success.latest(refs).order(id: :desc).each_with_object({}) do |pipeline, hash|
+ relation = newest_first(refs).success
+
+ relation.each_with_object({}) do |pipeline, hash|
hash[pipeline.ref] ||= pipeline
end
end
+ # Returns a Hash containing the latest pipeline status for every given
+ # commit.
+ #
+ # The keys of this Hash are the commit SHAs, the values the statuses.
+ #
+ # commits - The list of commit SHAs to get the status for.
+ # ref - The ref to scope the data to (e.g. "master"). If the ref is not
+ # given we simply get the latest status for the commits, regardless
+ # of what refs their pipelines belong to.
+ def self.latest_status_per_commit(commits, ref = nil)
+ p1 = arel_table
+ p2 = arel_table.alias
+
+ # This LEFT JOIN will filter out all but the newest row for every
+ # combination of (project_id, sha) or (project_id, sha, ref) if a ref is
+ # given.
+ cond = p1[:sha].eq(p2[:sha])
+ .and(p1[:project_id].eq(p2[:project_id]))
+ .and(p1[:id].lt(p2[:id]))
+
+ cond = cond.and(p1[:ref].eq(p2[:ref])) if ref
+ join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond)
+
+ relation = select(:sha, :status)
+ .where(sha: commits)
+ .where(p2[:id].eq(nil))
+ .joins(join.join_sources)
+
+ relation = relation.where(ref: ref) if ref
+
+ relation.each_with_object({}) do |row, hash|
+ hash[row[:sha]] = row[:status]
+ end
+ end
+
def self.truncate_sha(sha)
sha[0...8]
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 6dba154a6ea..a31ebe9cc87 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -80,6 +80,7 @@ class Commit
@raw = raw_commit
@project = project
+ @statuses = {}
end
def id
@@ -236,11 +237,13 @@ class Commit
end
def status(ref = nil)
- @statuses ||= {}
-
return @statuses[ref] if @statuses.key?(ref)
- @statuses[ref] = pipelines.latest_status(ref)
+ @statuses[ref] = project.pipelines.latest_status_per_commit(id, ref)[id]
+ end
+
+ def set_status_for_ref(ref, status)
+ @statuses[ref] = status
end
def signature
diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb
new file mode 100644
index 00000000000..dd93af9df64
--- /dev/null
+++ b/app/models/commit_collection.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# A collection of Commit instances for a specific project and Git reference.
+class CommitCollection
+ include Enumerable
+
+ attr_reader :project, :ref, :commits
+
+ # project - The project the commits belong to.
+ # commits - The Commit instances to store.
+ # ref - The name of the ref (e.g. "master").
+ def initialize(project, commits, ref = nil)
+ @project = project
+ @commits = commits
+ @ref = ref
+ end
+
+ def each(&block)
+ commits.each(&block)
+ end
+
+ # Sets the pipeline status for every commit.
+ #
+ # Setting this status ahead of time removes the need for running a query for
+ # every commit we're displaying.
+ def with_pipeline_status
+ statuses = project.pipelines.latest_status_per_commit(map(&:id), ref)
+
+ each do |commit|
+ commit.set_status_for_ref(ref, statuses[commit.id])
+ end
+
+ self
+ end
+
+ def respond_to_missing?(message, inc_private = false)
+ commits.respond_to?(message, inc_private)
+ end
+
+ # rubocop:disable GitlabSecurity/PublicSend
+ def method_missing(message, *args, &block)
+ commits.public_send(message, *args, &block)
+ end
+end
diff --git a/app/models/fork_network_member.rb b/app/models/fork_network_member.rb
index 6a9b52a1ef8..eb9417dc34f 100644
--- a/app/models/fork_network_member.rb
+++ b/app/models/fork_network_member.rb
@@ -4,4 +4,14 @@ class ForkNetworkMember < ActiveRecord::Base
belongs_to :forked_from_project, class_name: 'Project'
validates :fork_network, :project, presence: true
+
+ after_destroy :cleanup_fork_network
+
+ private
+
+ def cleanup_fork_network
+ # Explicitly using `#count` makes sure we have the correct number if the
+ # relation was loaded in the fork_network.
+ fork_network.destroy if fork_network.fork_network_members.count == 0
+ end
end
diff --git a/app/models/identity.rb b/app/models/identity.rb
index ac8094b610e..ff811e19f8a 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -1,18 +1,27 @@
class Identity < ActiveRecord::Base
include Sortable
include CaseSensitivity
+
belongs_to :user
validates :provider, presence: true
- validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
+ validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider, case_sensitive: false }
validates :user_id, uniqueness: { scope: :provider }
+ scope :with_provider, ->(provider) { where(provider: provider) }
scope :with_extern_uid, ->(provider, extern_uid) do
- extern_uid = Gitlab::LDAP::Person.normalize_dn(extern_uid) if provider.starts_with?('ldap')
- where(extern_uid: extern_uid, provider: provider)
+ iwhere(extern_uid: normalize_uid(provider, extern_uid)).with_provider(provider)
end
def ldap?
provider.starts_with?('ldap')
end
+
+ def self.normalize_uid(provider, uid)
+ if provider.to_s.starts_with?('ldap')
+ Gitlab::LDAP::Person.normalize_dn(uid)
+ else
+ uid.to_s
+ end
+ end
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 1eda0f9cbbd..5382f5cc627 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -284,8 +284,10 @@ class MergeRequestDiff < ActiveRecord::Base
def load_commits
commits = st_commits.presence || merge_request_diff_commits
+ commits = commits.map { |commit| Commit.from_hash(commit.to_hash, project) }
- commits.map { |commit| Commit.from_hash(commit.to_hash, project) }
+ CommitCollection
+ .new(merge_request.source_project, commits, merge_request.source_branch)
end
def save_diffs
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 3eecbea8cbf..a0af749a93f 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -76,8 +76,8 @@ class ProjectWiki
# Returns an Array of Gitlab WikiPage instances or an
# empty Array if this Wiki has no pages.
- def pages
- wiki.pages.map { |page| WikiPage.new(self, page, true) }
+ def pages(limit: nil)
+ wiki.pages(limit: limit).map { |page| WikiPage.new(self, page, true) }
end
# Finds a page within the repository based on a tile
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 3a89fa9264b..35ee12bdfa1 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -132,7 +132,8 @@ class Repository
commits = Gitlab::Git::Commit.where(options)
commits = Commit.decorate(commits, @project) if commits.present?
- commits
+
+ CommitCollection.new(project, commits, ref)
end
def commits_between(from, to)
@@ -148,11 +149,14 @@ class Repository
end
raw_repository.gitaly_migrate(:commits_by_message) do |is_enabled|
- if is_enabled
- find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
- else
- find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
- end
+ commits =
+ if is_enabled
+ find_commits_by_message_by_gitaly(query, ref, path, limit, offset)
+ else
+ find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
+ end
+
+ CommitCollection.new(project, commits, ref)
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index be8112749bf..71c34766451 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -269,8 +269,7 @@ class User < ActiveRecord::Base
end
def for_github_id(id)
- joins(:identities)
- .where(identities: { provider: :github, extern_uid: id.to_s })
+ joins(:identities).merge(Identity.with_extern_uid(:github, id))
end
# Find a User by their primary email or any associated secondary email
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 5f710961f95..bdfef677ef3 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -127,19 +127,24 @@ class WikiPage
@version ||= @page.version
end
- # Returns an array of Gitlab Commit instances.
- def versions
+ def versions(options = {})
return [] unless persisted?
- wiki.wiki.page_versions(@page.path)
+ wiki.wiki.page_versions(@page.path, options)
end
- def commit
- versions.first
+ def count_versions
+ return [] unless persisted?
+
+ wiki.wiki.count_page_versions(@page.path)
+ end
+
+ def last_version
+ @last_version ||= versions(limit: 1).first
end
def last_commit_sha
- commit&.sha
+ last_version&.sha
end
# Returns the Date that this latest version was
@@ -151,7 +156,7 @@ class WikiPage
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
- @page.historical? && versions.first.sha != version.sha
+ @page.historical? && last_version.sha != version.sha
end
# Returns boolean True or False if this instance
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 5957f612e84..d3a8ae8d7c6 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -60,15 +60,8 @@ module Projects
# Notifications
project.send_move_instructions(@old_path)
- # Move main repository
- # TODO: check storage type and NOOP when not using Legacy
- unless move_repo_folder(@old_path, @new_path)
- raise TransferError.new('Cannot move project')
- end
-
- # Move wiki repo also if present
- # TODO: check storage type and NOOP when not using Legacy
- move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
+ # Directories on disk
+ move_project_folders(project)
# Move missing group labels to project
Labels::TransferService.new(current_user, @old_group, project).execute
@@ -131,5 +124,20 @@ module Projects
def execute_system_hooks
SystemHooksService.new.execute_hooks_for(project, :transfer)
end
+
+ def move_project_folders(project)
+ return if project.hashed_storage?(:repository)
+
+ # Move main repository
+ unless move_repo_folder(@old_path, @new_path)
+ raise TransferError.new("Cannot move project")
+ end
+
+ # Disk path is changed; we need to ensure we reload it
+ project.reload_repository!
+
+ # Move wiki repo also if present
+ move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
+ end
end
end
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index df2bf27be9d..6d8fad0eb8d 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -99,7 +99,7 @@
%td.build-link
- if project
- = link_to ci_status_path(build.pipeline) do
+ = link_to pipeline_path(build.pipeline) do
%strong= build.pipeline.short_sha
%td.timestamp
diff --git a/app/views/projects/wikis/_pages_wiki_page.html.haml b/app/views/projects/wikis/_pages_wiki_page.html.haml
index 0a1ccbc5f1c..efa16d38f84 100644
--- a/app/views/projects/wikis/_pages_wiki_page.html.haml
+++ b/app/views/projects/wikis/_pages_wiki_page.html.haml
@@ -2,4 +2,4 @@
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.pull-right
- %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.commit.authored_date) }).html_safe
+ %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 9ee09262324..969a1677d9a 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -21,7 +21,7 @@
%th= _("Last updated")
%th= _("Format")
%tbody
- - @page.versions.each_with_index do |version, index|
+ - @page_versions.each_with_index do |version, index|
- commit = version
%tr
%td
@@ -37,5 +37,6 @@
%td
%strong
= version.format
+= paginate @page_versions, theme: 'gitlab'
= render 'sidebar'
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index de15fc99eda..b3b83cee81a 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -11,8 +11,8 @@
.nav-text
%h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by
- = (_("Last edited by %{name}") % { name: "<strong>#{@page.commit.author_name}</strong>" }).html_safe
- #{time_ago_with_tooltip(@page.commit.authored_date)}
+ = (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
+ #{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls
= render 'main_links'