summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/dispatcher.js2
-rw-r--r--app/assets/javascripts/environments/components/environment.vue28
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue8
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js22
-rw-r--r--app/assets/javascripts/oauth_remember_me.js32
-rw-r--r--app/assets/javascripts/signin_tabs_memoizer.js82
-rw-r--r--app/assets/javascripts/single_file_diff.js158
-rw-r--r--app/assets/javascripts/smart_interval.js253
-rw-r--r--app/assets/javascripts/snippets_list.js16
-rw-r--r--app/assets/javascripts/star.js50
-rw-r--r--app/assets/javascripts/subscription.js74
-rw-r--r--app/assets/javascripts/subscription_select.js61
-rw-r--r--app/assets/javascripts/syntax_highlight.js27
-rw-r--r--app/assets/javascripts/tree.js118
-rw-r--r--app/assets/javascripts/user.js57
-rw-r--r--app/assets/javascripts/user_tabs.js187
-rw-r--r--app/assets/javascripts/username_validator.js216
-rw-r--r--app/assets/javascripts/visibility_select.js41
-rw-r--r--app/assets/javascripts/wikis.js95
-rw-r--r--app/assets/javascripts/zen_mode.js113
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/wells.scss14
-rw-r--r--app/assets/stylesheets/pages/issuable.scss5
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb8
-rw-r--r--app/views/admin/dashboard/index.html.haml322
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml5
-rw-r--r--changelogs/unreleased/18000-remember-me-for-oauth-login.yml4
-rw-r--r--changelogs/unreleased/23036-replace-snippets-spinach.yml4
-rw-r--r--changelogs/unreleased/32838-admin-panel-spacing.yml4
-rw-r--r--changelogs/unreleased/34578-sidebar-padding.yml4
-rw-r--r--config/gitlab.yml.example47
-rw-r--r--config/initializers/flipper.rb6
-rw-r--r--features/snippets/snippets.feature40
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--features/steps/shared/snippet.rb63
-rw-r--r--features/steps/snippets/snippets.rb86
-rw-r--r--lib/tasks/gitlab/info.rake3
-rw-r--r--spec/factories/personal_snippets.rb4
-rw-r--r--spec/factories/project_snippets.rb5
-rw-r--r--spec/factories/snippets.rb7
-rw-r--r--spec/features/oauth_login_spec.rb112
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb (renamed from spec/features/snippets/create_snippet_spec.rb)6
-rw-r--r--spec/features/snippets/user_deletes_snippet_spec.rb19
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb (renamed from spec/features/snippets/edit_snippet_spec.rb)26
-rw-r--r--spec/javascripts/environments/environments_store_spec.js21
-rw-r--r--spec/javascripts/fixtures/oauth_remember_me.html.haml5
-rw-r--r--spec/javascripts/oauth_remember_me_spec.js26
-rw-r--r--spec/support/capybara_helpers.rb5
-rw-r--r--spec/support/login_helpers.rb13
49 files changed, 1061 insertions, 1449 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index e924fde60bf..4247540de22 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -56,7 +56,6 @@ import GfmAutoComplete from './gfm_auto_complete';
import ShortcutsBlob from './shortcuts_blob';
import initSettingsPanels from './settings_panels';
import initExperimentalFlags from './experimental_flags';
-import OAuthRememberMe from './oauth_remember_me';
(function() {
var Dispatcher;
@@ -128,7 +127,6 @@ import OAuthRememberMe from './oauth_remember_me';
case 'sessions:new':
new UsernameValidator();
new ActiveTabMemoizer();
- new OAuthRememberMe({ container: $(".omniauth-container") }).bindEvents();
break;
case 'projects:boards:show':
case 'projects:boards:index':
diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue
index 8120ef182d4..91ed8c8467f 100644
--- a/app/assets/javascripts/environments/components/environment.vue
+++ b/app/assets/javascripts/environments/components/environment.vue
@@ -32,7 +32,6 @@ export default {
state: store.state,
visibility: 'available',
isLoading: false,
- isLoadingFolderContent: false,
cssContainerClass: environmentsData.cssClass,
endpoint: environmentsData.environmentsDataEndpoint,
canCreateDeployment: environmentsData.canCreateDeployment,
@@ -86,9 +85,6 @@ export default {
errorCallback: this.errorCallback,
notificationCallback: (isMakingRequest) => {
this.isMakingRequest = isMakingRequest;
-
- // We need to verify if any folder is open to also fecth it
- this.openFolders = this.store.getOpenFolders();
},
});
@@ -119,7 +115,7 @@ export default {
this.store.toggleFolder(folder);
if (!folder.isOpen) {
- this.fetchChildEnvironments(folder, folderUrl);
+ this.fetchChildEnvironments(folder, folderUrl, true);
}
},
@@ -147,19 +143,17 @@ export default {
.catch(this.errorCallback);
},
- fetchChildEnvironments(folder, folderUrl) {
- this.isLoadingFolderContent = true;
+ fetchChildEnvironments(folder, folderUrl, showLoader = false) {
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader);
this.service.getFolderContent(folderUrl)
.then(resp => resp.json())
- .then((response) => {
- this.store.setfolderContent(folder, response.environments);
- this.isLoadingFolderContent = false;
- })
+ .then(response => this.store.setfolderContent(folder, response.environments))
+ .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false))
.catch(() => {
- this.isLoadingFolderContent = false;
// eslint-disable-next-line no-new
new Flash('An error occurred while fetching the environments.');
+ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false);
});
},
@@ -176,13 +170,13 @@ export default {
successCallback(resp) {
this.saveData(resp);
- // If folders are open while polling we need to open them again
- if (this.openFolders.length) {
- this.openFolders.map((folder) => {
+ // We need to verify if any folder is open to also update it
+ const openFolders = this.store.getOpenFolders();
+ if (openFolders.length) {
+ openFolders.forEach((folder) => {
// TODO - Move this to the backend
const folderUrl = `${window.location.pathname}/folders/${folder.folderName}`;
- this.store.updateFolder(folder, 'isOpen', true);
return this.fetchChildEnvironments(folder, folderUrl);
});
}
@@ -267,7 +261,7 @@ export default {
:environments="state.environments"
:can-create-deployment="canCreateDeploymentParsed"
:can-read-environment="canReadEnvironmentParsed"
- :is-loading-folder-content="isLoadingFolderContent" />
+ />
</div>
<table-pagination
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index b1fd9db650b..175cc8f1f72 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -29,12 +29,6 @@ export default {
required: false,
default: false,
},
-
- isLoadingFolderContent: {
- type: Boolean,
- required: false,
- default: false,
- },
},
methods: {
@@ -74,7 +68,7 @@ export default {
/>
<template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
- <div v-if="isLoadingFolderContent">
+ <div v-if="model.isLoadingFolderContent">
<loading-icon size="2" />
</div>
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index a5773dd7e4f..038c149be2d 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -35,14 +35,18 @@ export default class EnvironmentsStore {
*/
storeEnvironments(environments = []) {
const filteredEnvironments = environments.map((env) => {
+ const oldEnvironmentState = this.state.environments
+ .find(element => element.id === env.latest.id) || {};
+
let filtered = {};
if (env.size > 1) {
filtered = Object.assign({}, env, {
isFolder: true,
+ isLoadingFolderContent: oldEnvironmentState.isLoading || false,
folderName: env.name,
- isOpen: false,
- children: [],
+ isOpen: oldEnvironmentState.isOpen || false,
+ children: oldEnvironmentState.children || [],
});
}
@@ -98,7 +102,7 @@ export default class EnvironmentsStore {
* @return {Array}
*/
toggleFolder(folder) {
- return this.updateFolder(folder, 'isOpen', !folder.isOpen);
+ return this.updateEnvironmentProp(folder, 'isOpen', !folder.isOpen);
}
/**
@@ -125,23 +129,23 @@ export default class EnvironmentsStore {
return updated;
});
- return this.updateFolder(folder, 'children', updatedEnvironments);
+ return this.updateEnvironmentProp(folder, 'children', updatedEnvironments);
}
/**
- * Given a folder a prop and a new value updates the correct folder.
+ * Given a environment, a prop and a new value updates the correct environment.
*
- * @param {Object} folder
+ * @param {Object} environment
* @param {String} prop
* @param {String|Boolean|Object|Array} newValue
* @return {Array}
*/
- updateFolder(folder, prop, newValue) {
+ updateEnvironmentProp(environment, prop, newValue) {
const environments = this.state.environments;
const updatedEnvironments = environments.map((env) => {
const updateEnv = Object.assign({}, env);
- if (env.isFolder && env.id === folder.id) {
+ if (env.id === environment.id) {
updateEnv[prop] = newValue;
}
@@ -149,8 +153,6 @@ export default class EnvironmentsStore {
});
this.state.environments = updatedEnvironments;
-
- return updatedEnvironments;
}
getOpenFolders() {
diff --git a/app/assets/javascripts/oauth_remember_me.js b/app/assets/javascripts/oauth_remember_me.js
deleted file mode 100644
index ffc2dd6bbca..00000000000
--- a/app/assets/javascripts/oauth_remember_me.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * OAuth-based login buttons have a separate "remember me" checkbox.
- *
- * Toggling this checkbox adds/removes a `remember_me` parameter to the
- * login buttons' href, which is passed on to the omniauth callback.
- **/
-
-export default class OAuthRememberMe {
- constructor(opts = {}) {
- this.container = opts.container || '';
- this.loginLinkSelector = '.oauth-login';
- }
-
- bindEvents() {
- $('#remember_me', this.container).on('click', this.toggleRememberMe);
- }
-
- // eslint-disable-next-line class-methods-use-this
- toggleRememberMe(event) {
- const rememberMe = $(event.target).is(':checked');
-
- $('.oauth-login', this.container).each((i, element) => {
- const href = $(element).attr('href');
-
- if (rememberMe) {
- $(element).attr('href', `${href}?remember_me=1`);
- } else {
- $(element).attr('href', href.replace('?remember_me=1', ''));
- }
- });
- }
-}
diff --git a/app/assets/javascripts/signin_tabs_memoizer.js b/app/assets/javascripts/signin_tabs_memoizer.js
index 2587facc582..3997a695d15 100644
--- a/app/assets/javascripts/signin_tabs_memoizer.js
+++ b/app/assets/javascripts/signin_tabs_memoizer.js
@@ -2,56 +2,54 @@
/* eslint no-new: "off" */
import AccessorUtilities from './lib/utils/accessor';
-((global) => {
- /**
- * Memorize the last selected tab after reloading a page.
- * Does that setting the current selected tab in the localStorage
- */
- class ActiveTabMemoizer {
- constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) {
- this.currentTabKey = currentTabKey;
- this.tabSelector = tabSelector;
- this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
-
- this.bootstrap();
- }
-
- bootstrap() {
- const tabs = document.querySelectorAll(this.tabSelector);
- if (tabs.length > 0) {
- tabs[0].addEventListener('click', (e) => {
- if (e.target && e.target.nodeName === 'A') {
- const anchorName = e.target.getAttribute('href');
- this.saveData(anchorName);
- }
- });
- }
+/**
+ * Memorize the last selected tab after reloading a page.
+ * Does that setting the current selected tab in the localStorage
+ */
+class ActiveTabMemoizer {
+ constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) {
+ this.currentTabKey = currentTabKey;
+ this.tabSelector = tabSelector;
+ this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
+
+ this.bootstrap();
+ }
- this.showTab();
+ bootstrap() {
+ const tabs = document.querySelectorAll(this.tabSelector);
+ if (tabs.length > 0) {
+ tabs[0].addEventListener('click', (e) => {
+ if (e.target && e.target.nodeName === 'A') {
+ const anchorName = e.target.getAttribute('href');
+ this.saveData(anchorName);
+ }
+ });
}
- showTab() {
- const anchorName = this.readData();
- if (anchorName) {
- const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`);
- if (tab) {
- tab.click();
- }
+ this.showTab();
+ }
+
+ showTab() {
+ const anchorName = this.readData();
+ if (anchorName) {
+ const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`);
+ if (tab) {
+ tab.click();
}
}
+ }
- saveData(val) {
- if (!this.isLocalStorageAvailable) return undefined;
+ saveData(val) {
+ if (!this.isLocalStorageAvailable) return undefined;
- return window.localStorage.setItem(this.currentTabKey, val);
- }
+ return window.localStorage.setItem(this.currentTabKey, val);
+ }
- readData() {
- if (!this.isLocalStorageAvailable) return null;
+ readData() {
+ if (!this.isLocalStorageAvailable) return null;
- return window.localStorage.getItem(this.currentTabKey);
- }
+ return window.localStorage.getItem(this.currentTabKey);
}
+}
- global.ActiveTabMemoizer = ActiveTabMemoizer;
-})(window);
+window.ActiveTabMemoizer = ActiveTabMemoizer;
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 9316a2af0b7..00d04ce0c33 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -2,99 +2,97 @@
import FilesCommentButton from './files_comment_button';
-(function() {
- window.SingleFileDiff = (function() {
- var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER;
+window.SingleFileDiff = (function() {
+ var COLLAPSED_HTML, ERROR_HTML, LOADING_HTML, WRAPPER;
- WRAPPER = '<div class="diff-content"></div>';
+ WRAPPER = '<div class="diff-content"></div>';
- LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
+ LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
- ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
+ ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
- COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
+ COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
- function SingleFileDiff(file) {
- this.file = file;
- this.toggleDiff = this.toggleDiff.bind(this);
- this.content = $('.diff-content', this.file);
- this.$toggleIcon = $('.diff-toggle-caret', this.file);
- this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path');
- this.isOpen = !this.diffForPath;
- if (this.diffForPath) {
- this.collapsedContent = this.content;
- this.loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide();
- this.content = null;
- this.collapsedContent.after(this.loadingContent);
- this.$toggleIcon.addClass('fa-caret-right');
- } else {
- this.collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide();
- this.content.after(this.collapsedContent);
- this.$toggleIcon.addClass('fa-caret-down');
- }
+ function SingleFileDiff(file) {
+ this.file = file;
+ this.toggleDiff = this.toggleDiff.bind(this);
+ this.content = $('.diff-content', this.file);
+ this.$toggleIcon = $('.diff-toggle-caret', this.file);
+ this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path');
+ this.isOpen = !this.diffForPath;
+ if (this.diffForPath) {
+ this.collapsedContent = this.content;
+ this.loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide();
+ this.content = null;
+ this.collapsedContent.after(this.loadingContent);
+ this.$toggleIcon.addClass('fa-caret-right');
+ } else {
+ this.collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide();
+ this.content.after(this.collapsedContent);
+ this.$toggleIcon.addClass('fa-caret-down');
+ }
+
+ $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
+ this.toggleDiff($(e.target));
+ }).bind(this));
+ }
- $('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
- this.toggleDiff($(e.target));
- }).bind(this));
+ SingleFileDiff.prototype.toggleDiff = function($target, cb) {
+ if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
+ this.isOpen = !this.isOpen;
+ if (!this.isOpen && !this.hasError) {
+ this.content.hide();
+ this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down');
+ this.collapsedContent.show();
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
+ }
+ } else if (this.content) {
+ this.collapsedContent.hide();
+ this.content.show();
+ this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ if (typeof gl.diffNotesCompileComponents !== 'undefined') {
+ gl.diffNotesCompileComponents();
+ }
+ } else {
+ this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ return this.getContentHTML(cb);
}
+ };
- SingleFileDiff.prototype.toggleDiff = function($target, cb) {
- if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
- this.isOpen = !this.isOpen;
- if (!this.isOpen && !this.hasError) {
- this.content.hide();
- this.$toggleIcon.addClass('fa-caret-right').removeClass('fa-caret-down');
- this.collapsedContent.show();
- if (typeof gl.diffNotesCompileComponents !== 'undefined') {
- gl.diffNotesCompileComponents();
+ SingleFileDiff.prototype.getContentHTML = function(cb) {
+ this.collapsedContent.hide();
+ this.loadingContent.show();
+ $.get(this.diffForPath, (function(_this) {
+ return function(data) {
+ _this.loadingContent.hide();
+ if (data.html) {
+ _this.content = $(data.html);
+ _this.content.syntaxHighlight();
+ } else {
+ _this.hasError = true;
+ _this.content = $(ERROR_HTML);
}
- } else if (this.content) {
- this.collapsedContent.hide();
- this.content.show();
- this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
+ _this.collapsedContent.after(_this.content);
+
if (typeof gl.diffNotesCompileComponents !== 'undefined') {
gl.diffNotesCompileComponents();
}
- } else {
- this.$toggleIcon.addClass('fa-caret-down').removeClass('fa-caret-right');
- return this.getContentHTML(cb);
- }
- };
-
- SingleFileDiff.prototype.getContentHTML = function(cb) {
- this.collapsedContent.hide();
- this.loadingContent.show();
- $.get(this.diffForPath, (function(_this) {
- return function(data) {
- _this.loadingContent.hide();
- if (data.html) {
- _this.content = $(data.html);
- _this.content.syntaxHighlight();
- } else {
- _this.hasError = true;
- _this.content = $(ERROR_HTML);
- }
- _this.collapsedContent.after(_this.content);
-
- if (typeof gl.diffNotesCompileComponents !== 'undefined') {
- gl.diffNotesCompileComponents();
- }
- FilesCommentButton.init($(_this.file));
+ FilesCommentButton.init($(_this.file));
- if (cb) cb();
- };
- })(this));
- };
+ if (cb) cb();
+ };
+ })(this));
+ };
- return SingleFileDiff;
- })();
+ return SingleFileDiff;
+})();
- $.fn.singleFileDiff = function() {
- return this.each(function() {
- if (!$.data(this, 'singleFileDiff')) {
- return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this));
- }
- });
- };
-}).call(window);
+$.fn.singleFileDiff = function() {
+ return this.each(function() {
+ if (!$.data(this, 'singleFileDiff')) {
+ return $.data(this, 'singleFileDiff', new window.SingleFileDiff(this));
+ }
+ });
+};
diff --git a/app/assets/javascripts/smart_interval.js b/app/assets/javascripts/smart_interval.js
index d1bdc353be2..2bf7a3a5d61 100644
--- a/app/assets/javascripts/smart_interval.js
+++ b/app/assets/javascripts/smart_interval.js
@@ -1,158 +1,157 @@
-/*
-* Instances of SmartInterval extend the functionality of `setInterval`, make it configurable
-* and controllable by a public API.
-*
-* */
-
-(() => {
- class SmartInterval {
- /**
- * @param { function } opts.callback Function to be called on each iteration (required)
- * @param { milliseconds } opts.startingInterval `currentInterval` is set to this initially
- * @param { milliseconds } opts.maxInterval `currentInterval` will be incremented to this
- * @param { milliseconds } opts.hiddenInterval `currentInterval` is set to this
- * when the page is hidden
- * @param { integer } opts.incrementByFactorOf `currentInterval` is incremented by this factor
- * @param { boolean } opts.lazyStart Configure if timer is initialized on
- * instantiation or lazily
- * @param { boolean } opts.immediateExecution Configure if callback should
- * be executed before the first interval.
- */
- constructor(opts = {}) {
- this.cfg = {
- callback: opts.callback,
- startingInterval: opts.startingInterval,
- maxInterval: opts.maxInterval,
- hiddenInterval: opts.hiddenInterval,
- incrementByFactorOf: opts.incrementByFactorOf,
- lazyStart: opts.lazyStart,
- immediateExecution: opts.immediateExecution,
- };
-
- this.state = {
- intervalId: null,
- currentInterval: this.cfg.startingInterval,
- pageVisibility: 'visible',
- };
-
- this.initInterval();
- }
- /* public */
-
- start() {
- const cfg = this.cfg;
- const state = this.state;
-
- if (cfg.immediateExecution) {
- cfg.immediateExecution = false;
- cfg.callback();
- }
+/**
+ * Instances of SmartInterval extend the functionality of `setInterval`, make it configurable
+ * and controllable by a public API.
+ */
+
+class SmartInterval {
+ /**
+ * @param { function } opts.callback Function to be called on each iteration (required)
+ * @param { milliseconds } opts.startingInterval `currentInterval` is set to this initially
+ * @param { milliseconds } opts.maxInterval `currentInterval` will be incremented to this
+ * @param { milliseconds } opts.hiddenInterval `currentInterval` is set to this
+ * when the page is hidden
+ * @param { integer } opts.incrementByFactorOf `currentInterval` is incremented by this factor
+ * @param { boolean } opts.lazyStart Configure if timer is initialized on
+ * instantiation or lazily
+ * @param { boolean } opts.immediateExecution Configure if callback should
+ * be executed before the first interval.
+ */
+ constructor(opts = {}) {
+ this.cfg = {
+ callback: opts.callback,
+ startingInterval: opts.startingInterval,
+ maxInterval: opts.maxInterval,
+ hiddenInterval: opts.hiddenInterval,
+ incrementByFactorOf: opts.incrementByFactorOf,
+ lazyStart: opts.lazyStart,
+ immediateExecution: opts.immediateExecution,
+ };
+
+ this.state = {
+ intervalId: null,
+ currentInterval: this.cfg.startingInterval,
+ pageVisibility: 'visible',
+ };
+
+ this.initInterval();
+ }
- state.intervalId = window.setInterval(() => {
- cfg.callback();
+ /* public */
- if (this.getCurrentInterval() === cfg.maxInterval) {
- return;
- }
+ start() {
+ const cfg = this.cfg;
+ const state = this.state;
- this.incrementInterval();
- this.resume();
- }, this.getCurrentInterval());
+ if (cfg.immediateExecution) {
+ cfg.immediateExecution = false;
+ cfg.callback();
}
- // cancel the existing timer, setting the currentInterval back to startingInterval
- cancel() {
- this.setCurrentInterval(this.cfg.startingInterval);
- this.stopTimer();
- }
+ state.intervalId = window.setInterval(() => {
+ cfg.callback();
- onVisibilityHidden() {
- if (this.cfg.hiddenInterval) {
- this.setCurrentInterval(this.cfg.hiddenInterval);
- this.resume();
- } else {
- this.cancel();
+ if (this.getCurrentInterval() === cfg.maxInterval) {
+ return;
}
- }
- // start a timer, using the existing interval
- resume() {
- this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped
- this.start();
- }
+ this.incrementInterval();
+ this.resume();
+ }, this.getCurrentInterval());
+ }
- onVisibilityVisible() {
- this.cancel();
- this.start();
- }
+ // cancel the existing timer, setting the currentInterval back to startingInterval
+ cancel() {
+ this.setCurrentInterval(this.cfg.startingInterval);
+ this.stopTimer();
+ }
- destroy() {
+ onVisibilityHidden() {
+ if (this.cfg.hiddenInterval) {
+ this.setCurrentInterval(this.cfg.hiddenInterval);
+ this.resume();
+ } else {
this.cancel();
- document.removeEventListener('visibilitychange', this.handleVisibilityChange);
- $(document).off('visibilitychange').off('beforeunload');
}
+ }
- /* private */
+ // start a timer, using the existing interval
+ resume() {
+ this.stopTimer(); // stop exsiting timer, in case timer was not previously stopped
+ this.start();
+ }
- initInterval() {
- const cfg = this.cfg;
+ onVisibilityVisible() {
+ this.cancel();
+ this.start();
+ }
- if (!cfg.lazyStart) {
- this.start();
- }
+ destroy() {
+ this.cancel();
+ document.removeEventListener('visibilitychange', this.handleVisibilityChange);
+ $(document).off('visibilitychange').off('beforeunload');
+ }
- this.initVisibilityChangeHandling();
- this.initPageUnloadHandling();
- }
+ /* private */
- initVisibilityChangeHandling() {
- // cancel interval when tab no longer shown (prevents cached pages from polling)
- document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
- }
+ initInterval() {
+ const cfg = this.cfg;
- initPageUnloadHandling() {
- // TODO: Consider refactoring in light of turbolinks removal.
- // prevent interval continuing after page change, when kept in cache by Turbolinks
- $(document).on('beforeunload', () => this.cancel());
+ if (!cfg.lazyStart) {
+ this.start();
}
- handleVisibilityChange(e) {
- this.state.pageVisibility = e.target.visibilityState;
- const intervalAction = this.isPageVisible() ?
- this.onVisibilityVisible :
- this.onVisibilityHidden;
+ this.initVisibilityChangeHandling();
+ this.initPageUnloadHandling();
+ }
- intervalAction.apply(this);
- }
+ initVisibilityChangeHandling() {
+ // cancel interval when tab no longer shown (prevents cached pages from polling)
+ document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this));
+ }
- getCurrentInterval() {
- return this.state.currentInterval;
- }
+ initPageUnloadHandling() {
+ // TODO: Consider refactoring in light of turbolinks removal.
+ // prevent interval continuing after page change, when kept in cache by Turbolinks
+ $(document).on('beforeunload', () => this.cancel());
+ }
- setCurrentInterval(newInterval) {
- this.state.currentInterval = newInterval;
- }
+ handleVisibilityChange(e) {
+ this.state.pageVisibility = e.target.visibilityState;
+ const intervalAction = this.isPageVisible() ?
+ this.onVisibilityVisible :
+ this.onVisibilityHidden;
- incrementInterval() {
- const cfg = this.cfg;
- const currentInterval = this.getCurrentInterval();
- if (cfg.hiddenInterval && !this.isPageVisible()) return;
- let nextInterval = currentInterval * cfg.incrementByFactorOf;
+ intervalAction.apply(this);
+ }
- if (nextInterval > cfg.maxInterval) {
- nextInterval = cfg.maxInterval;
- }
+ getCurrentInterval() {
+ return this.state.currentInterval;
+ }
+
+ setCurrentInterval(newInterval) {
+ this.state.currentInterval = newInterval;
+ }
+
+ incrementInterval() {
+ const cfg = this.cfg;
+ const currentInterval = this.getCurrentInterval();
+ if (cfg.hiddenInterval && !this.isPageVisible()) return;
+ let nextInterval = currentInterval * cfg.incrementByFactorOf;
- this.setCurrentInterval(nextInterval);
+ if (nextInterval > cfg.maxInterval) {
+ nextInterval = cfg.maxInterval;
}
- isPageVisible() { return this.state.pageVisibility === 'visible'; }
+ this.setCurrentInterval(nextInterval);
+ }
- stopTimer() {
- const state = this.state;
+ isPageVisible() { return this.state.pageVisibility === 'visible'; }
- state.intervalId = window.clearInterval(state.intervalId);
- }
+ stopTimer() {
+ const state = this.state;
+
+ state.intervalId = window.clearInterval(state.intervalId);
}
- gl.SmartInterval = SmartInterval;
-})(window.gl || (window.gl = {}));
+}
+
+window.gl.SmartInterval = SmartInterval;
diff --git a/app/assets/javascripts/snippets_list.js b/app/assets/javascripts/snippets_list.js
index 2128007113f..da7b9e08447 100644
--- a/app/assets/javascripts/snippets_list.js
+++ b/app/assets/javascripts/snippets_list.js
@@ -1,13 +1,9 @@
/* eslint-disable arrow-parens, no-param-reassign, space-before-function-paren, func-names, no-var, max-len */
-(global => {
- global.gl = global.gl || {};
+window.gl.SnippetsList = function() {
+ var $holder = $('.snippets-list-holder');
- gl.SnippetsList = function() {
- var $holder = $('.snippets-list-holder');
-
- $holder.find('.pagination').on('ajax:success', (e, data) => {
- $holder.replaceWith(data.html);
- });
- };
-})(window);
+ $holder.find('.pagination').on('ajax:success', (e, data) => {
+ $holder.replaceWith(data.html);
+ });
+};
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index c75b44cc2fd..840ae1edd9d 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,30 +1,28 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-unused-vars, one-var, no-var, one-var-declaration-per-line, prefer-arrow-callback, no-new, max-len */
/* global Flash */
-(function() {
- this.Star = (function() {
- function Star() {
- $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) {
- var $starIcon, $starSpan, $this, toggleStar;
- $this = $(this);
- $starSpan = $this.find('span');
- $starIcon = $this.find('i');
- toggleStar = function(isStarred) {
- $this.parent().find('.star-count').text(data.star_count);
- if (isStarred) {
- $starSpan.removeClass('starred').text('Star');
- $starIcon.removeClass('fa-star').addClass('fa-star-o');
- } else {
- $starSpan.addClass('starred').text('Unstar');
- $starIcon.removeClass('fa-star-o').addClass('fa-star');
- }
- };
- toggleStar($starSpan.hasClass('starred'));
- }).on('ajax:error', function(e, xhr, status, error) {
- new Flash('Star toggle failed. Try again later.', 'alert');
- });
- }
+window.Star = (function() {
+ function Star() {
+ $('.project-home-panel .toggle-star').on('ajax:success', function(e, data, status, xhr) {
+ var $starIcon, $starSpan, $this, toggleStar;
+ $this = $(this);
+ $starSpan = $this.find('span');
+ $starIcon = $this.find('i');
+ toggleStar = function(isStarred) {
+ $this.parent().find('.star-count').text(data.star_count);
+ if (isStarred) {
+ $starSpan.removeClass('starred').text('Star');
+ $starIcon.removeClass('fa-star').addClass('fa-star-o');
+ } else {
+ $starSpan.addClass('starred').text('Unstar');
+ $starIcon.removeClass('fa-star-o').addClass('fa-star');
+ }
+ };
+ toggleStar($starSpan.hasClass('starred'));
+ }).on('ajax:error', function(e, xhr, status, error) {
+ new Flash('Star toggle failed. Try again later.', 'alert');
+ });
+ }
- return Star;
- })();
-}).call(window);
+ return Star;
+})();
diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js
index 5f9a3e00c22..bb4d68fcd49 100644
--- a/app/assets/javascripts/subscription.js
+++ b/app/assets/javascripts/subscription.js
@@ -1,47 +1,45 @@
-(() => {
- class Subscription {
- constructor(containerElm) {
- this.containerElm = containerElm;
+class Subscription {
+ constructor(containerElm) {
+ this.containerElm = containerElm;
- const subscribeButton = containerElm.querySelector('.js-subscribe-button');
- if (subscribeButton) {
- // remove class so we don't bind twice
- subscribeButton.classList.remove('js-subscribe-button');
- subscribeButton.addEventListener('click', this.toggleSubscription.bind(this));
- }
+ const subscribeButton = containerElm.querySelector('.js-subscribe-button');
+ if (subscribeButton) {
+ // remove class so we don't bind twice
+ subscribeButton.classList.remove('js-subscribe-button');
+ subscribeButton.addEventListener('click', this.toggleSubscription.bind(this));
}
+ }
- toggleSubscription(event) {
- const button = event.currentTarget;
- const buttonSpan = button.querySelector('span');
- if (!buttonSpan || button.classList.contains('disabled')) {
- return;
- }
- button.classList.add('disabled');
+ toggleSubscription(event) {
+ const button = event.currentTarget;
+ const buttonSpan = button.querySelector('span');
+ if (!buttonSpan || button.classList.contains('disabled')) {
+ return;
+ }
+ button.classList.add('disabled');
- const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe';
- const toggleActionUrl = this.containerElm.dataset.url;
+ const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe';
+ const toggleActionUrl = this.containerElm.dataset.url;
- $.post(toggleActionUrl, () => {
- button.classList.remove('disabled');
+ $.post(toggleActionUrl, () => {
+ button.classList.remove('disabled');
- // hack to allow this to work with the issue boards Vue object
- if (document.querySelector('html').classList.contains('issue-boards-page')) {
- gl.issueBoards.boardStoreIssueSet(
- 'subscribed',
- !gl.issueBoards.BoardsStore.detail.issue.subscribed,
- );
- } else {
- buttonSpan.innerHTML = isSubscribed ? 'Subscribe' : 'Unsubscribe';
- }
- });
- }
+ // hack to allow this to work with the issue boards Vue object
+ if (document.querySelector('html').classList.contains('issue-boards-page')) {
+ gl.issueBoards.boardStoreIssueSet(
+ 'subscribed',
+ !gl.issueBoards.BoardsStore.detail.issue.subscribed,
+ );
+ } else {
+ buttonSpan.innerHTML = isSubscribed ? 'Subscribe' : 'Unsubscribe';
+ }
+ });
+ }
- static bindAll(selector) {
- [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm));
- }
+ static bindAll(selector) {
+ [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm));
}
+}
- window.gl = window.gl || {};
- window.gl.Subscription = Subscription;
-})();
+window.gl = window.gl || {};
+window.gl.Subscription = Subscription;
diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js
index 0cd591c7320..a48434181b6 100644
--- a/app/assets/javascripts/subscription_select.js
+++ b/app/assets/javascripts/subscription_select.js
@@ -1,34 +1,33 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, object-shorthand, no-unused-vars, no-shadow, one-var, one-var-declaration-per-line, comma-dangle, max-len */
-(function() {
- this.SubscriptionSelect = (function() {
- function SubscriptionSelect() {
- $('.js-subscription-event').each(function(i, el) {
- var fieldName;
- fieldName = $(el).data("field-name");
- return $(el).glDropdown({
- selectable: true,
- fieldName: fieldName,
- toggleLabel: (function(_this) {
- return function(selected, el, instance) {
- var $item, label;
- label = 'Subscription';
- $item = instance.dropdown.find('.is-active');
- if ($item.length) {
- label = $item.text();
- }
- return label;
- };
- })(this),
- clicked: function(options) {
- return options.e.preventDefault();
- },
- id: function(obj, el) {
- return $(el).data("id");
- }
- });
+
+window.SubscriptionSelect = (function() {
+ function SubscriptionSelect() {
+ $('.js-subscription-event').each(function(i, el) {
+ var fieldName;
+ fieldName = $(el).data("field-name");
+ return $(el).glDropdown({
+ selectable: true,
+ fieldName: fieldName,
+ toggleLabel: (function(_this) {
+ return function(selected, el, instance) {
+ var $item, label;
+ label = 'Subscription';
+ $item = instance.dropdown.find('.is-active');
+ if ($item.length) {
+ label = $item.text();
+ }
+ return label;
+ };
+ })(this),
+ clicked: function(options) {
+ return options.e.preventDefault();
+ },
+ id: function(obj, el) {
+ return $(el).data("id");
+ }
});
- }
+ });
+ }
- return SubscriptionSelect;
- })();
-}).call(window);
+ return SubscriptionSelect;
+})();
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index 7c063fae045..662d6b36c16 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -9,19 +9,18 @@
//
// <div class="js-syntax-highlight"></div>
//
-(function() {
- $.fn.syntaxHighlight = function() {
- var $children;
- if ($(this).hasClass('js-syntax-highlight')) {
- // Given the element itself, apply highlighting
- return $(this).addClass(gon.user_color_scheme);
- } else {
- // Given a parent element, recurse to any of its applicable children
- $children = $(this).find('.js-syntax-highlight');
- if ($children.length) {
- return $children.syntaxHighlight();
- }
+$.fn.syntaxHighlight = function() {
+ var $children;
+
+ if ($(this).hasClass('js-syntax-highlight')) {
+ // Given the element itself, apply highlighting
+ return $(this).addClass(gon.user_color_scheme);
+ } else {
+ // Given a parent element, recurse to any of its applicable children
+ $children = $(this).find('.js-syntax-highlight');
+ if ($children.length) {
+ return $children.syntaxHighlight();
}
- };
-}).call(window);
+ }
+};
diff --git a/app/assets/javascripts/tree.js b/app/assets/javascripts/tree.js
index 76a821c7a17..77ae6109bc6 100644
--- a/app/assets/javascripts/tree.js
+++ b/app/assets/javascripts/tree.js
@@ -1,68 +1,66 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, quotes, consistent-return, no-var, one-var, one-var-declaration-per-line, no-else-return, prefer-arrow-callback, max-len */
-(function() {
- this.TreeView = (function() {
- function TreeView() {
- this.initKeyNav();
- // Code browser tree slider
- // Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
- $(".tree-content-holder .tree-item").on('click', function(e) {
- var $clickedEl, path;
- $clickedEl = $(e.target);
- path = $('.tree-item-file-name a', this).attr('href');
- if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) {
- if (e.metaKey || e.which === 2) {
- e.preventDefault();
- return window.open(path, '_blank');
- } else {
- return gl.utils.visitUrl(path);
- }
+window.TreeView = (function() {
+ function TreeView() {
+ this.initKeyNav();
+ // Code browser tree slider
+ // Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
+ $(".tree-content-holder .tree-item").on('click', function(e) {
+ var $clickedEl, path;
+ $clickedEl = $(e.target);
+ path = $('.tree-item-file-name a', this).attr('href');
+ if (!$clickedEl.is('a') && !$clickedEl.is('.str-truncated')) {
+ if (e.metaKey || e.which === 2) {
+ e.preventDefault();
+ return window.open(path, '_blank');
+ } else {
+ return gl.utils.visitUrl(path);
}
- });
- // Show the "Loading commit data" for only the first element
- $('span.log_loading:first').removeClass('hide');
- }
+ }
+ });
+ // Show the "Loading commit data" for only the first element
+ $('span.log_loading:first').removeClass('hide');
+ }
- TreeView.prototype.initKeyNav = function() {
- var li, liSelected;
- li = $("tr.tree-item");
- liSelected = null;
- return $('body').keydown(function(e) {
- var next, path;
- if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) {
- return false;
- }
- if (e.which === 40) {
- if (liSelected) {
- next = liSelected.next();
- if (next.length > 0) {
- liSelected.removeClass("selected");
- liSelected = next.addClass("selected");
- }
- } else {
- liSelected = li.eq(0).addClass("selected");
- }
- return $(liSelected).focus();
- } else if (e.which === 38) {
- if (liSelected) {
- next = liSelected.prev();
- if (next.length > 0) {
- liSelected.removeClass("selected");
- liSelected = next.addClass("selected");
- }
- } else {
- liSelected = li.last().addClass("selected");
+ TreeView.prototype.initKeyNav = function() {
+ var li, liSelected;
+ li = $("tr.tree-item");
+ liSelected = null;
+ return $('body').keydown(function(e) {
+ var next, path;
+ if ($("input:focus").length > 0 && (e.which === 38 || e.which === 40)) {
+ return false;
+ }
+ if (e.which === 40) {
+ if (liSelected) {
+ next = liSelected.next();
+ if (next.length > 0) {
+ liSelected.removeClass("selected");
+ liSelected = next.addClass("selected");
}
- return $(liSelected).focus();
- } else if (e.which === 13) {
- path = $('.tree-item.selected .tree-item-file-name a').attr('href');
- if (path) {
- return gl.utils.visitUrl(path);
+ } else {
+ liSelected = li.eq(0).addClass("selected");
+ }
+ return $(liSelected).focus();
+ } else if (e.which === 38) {
+ if (liSelected) {
+ next = liSelected.prev();
+ if (next.length > 0) {
+ liSelected.removeClass("selected");
+ liSelected = next.addClass("selected");
}
+ } else {
+ liSelected = li.last().addClass("selected");
+ }
+ return $(liSelected).focus();
+ } else if (e.which === 13) {
+ path = $('.tree-item.selected .tree-item-file-name a').attr('href');
+ if (path) {
+ return gl.utils.visitUrl(path);
}
- });
- };
+ }
+ });
+ };
- return TreeView;
- })();
-}).call(window);
+ return TreeView;
+})();
diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js
index 19c9efe7fbd..3ab9ef5408e 100644
--- a/app/assets/javascripts/user.js
+++ b/app/assets/javascripts/user.js
@@ -2,34 +2,35 @@
import Cookies from 'js-cookie';
-((global) => {
- global.User = class {
- constructor({ action }) {
- this.action = action;
- this.placeProfileAvatarsToTop();
- this.initTabs();
- this.hideProjectLimitMessage();
- }
+class User {
+ constructor({ action }) {
+ this.action = action;
+ this.placeProfileAvatarsToTop();
+ this.initTabs();
+ this.hideProjectLimitMessage();
+ }
- placeProfileAvatarsToTop() {
- $('.profile-groups-avatars').tooltip({
- placement: 'top'
- });
- }
+ placeProfileAvatarsToTop() {
+ $('.profile-groups-avatars').tooltip({
+ placement: 'top'
+ });
+ }
- initTabs() {
- return new global.UserTabs({
- parentEl: '.user-profile',
- action: this.action
- });
- }
+ initTabs() {
+ return new window.gl.UserTabs({
+ parentEl: '.user-profile',
+ action: this.action
+ });
+ }
- hideProjectLimitMessage() {
- $('.hide-project-limit-message').on('click', e => {
- e.preventDefault();
- Cookies.set('hide_project_limit_message', 'false');
- $(this).parents('.project-limit-message').remove();
- });
- }
- };
-})(window.gl || (window.gl = {}));
+ hideProjectLimitMessage() {
+ $('.hide-project-limit-message').on('click', e => {
+ e.preventDefault();
+ Cookies.set('hide_project_limit_message', 'false');
+ $(this).parents('.project-limit-message').remove();
+ });
+ }
+}
+
+window.gl = window.gl || {};
+window.gl.User = User;
diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js
index ce7eb76dc71..be70f4cb4e2 100644
--- a/app/assets/javascripts/user_tabs.js
+++ b/app/assets/javascripts/user_tabs.js
@@ -59,117 +59,118 @@ content on the Users#show page.
</div>
</div>
*/
-((global) => {
- class UserTabs {
- constructor ({ defaultAction, action, parentEl }) {
- this.loaded = {};
- this.defaultAction = defaultAction || 'activity';
- this.action = action || this.defaultAction;
- this.$parentEl = $(parentEl) || $(document);
- this._location = window.location;
- this.$parentEl.find('.nav-links a')
- .each((i, navLink) => {
- this.loaded[$(navLink).attr('data-action')] = false;
- });
- this.actions = Object.keys(this.loaded);
- this.bindEvents();
-
- if (this.action === 'show') {
- this.action = this.defaultAction;
- }
- this.activateTab(this.action);
+class UserTabs {
+ constructor ({ defaultAction, action, parentEl }) {
+ this.loaded = {};
+ this.defaultAction = defaultAction || 'activity';
+ this.action = action || this.defaultAction;
+ this.$parentEl = $(parentEl) || $(document);
+ this._location = window.location;
+ this.$parentEl.find('.nav-links a')
+ .each((i, navLink) => {
+ this.loaded[$(navLink).attr('data-action')] = false;
+ });
+ this.actions = Object.keys(this.loaded);
+ this.bindEvents();
+
+ if (this.action === 'show') {
+ this.action = this.defaultAction;
}
- bindEvents() {
- this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this);
+ this.activateTab(this.action);
+ }
- this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
- .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
+ bindEvents() {
+ this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this);
- this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
- }
+ this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
+ .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
- changeProjectsPage(e) {
- e.preventDefault();
+ this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
+ }
- $('.tab-pane.active').empty();
- const endpoint = $(e.target).attr('href');
- this.loadTab(this.getCurrentAction(), endpoint);
- }
+ changeProjectsPage(e) {
+ e.preventDefault();
- tabShown(event) {
- const $target = $(event.target);
- const action = $target.data('action');
- const source = $target.attr('href');
- const endpoint = $target.data('endpoint');
- this.setTab(action, endpoint);
- return this.setCurrentAction(source);
- }
+ $('.tab-pane.active').empty();
+ const endpoint = $(e.target).attr('href');
+ this.loadTab(this.getCurrentAction(), endpoint);
+ }
- activateTab(action) {
- return this.$parentEl.find(`.nav-links .js-${action}-tab a`)
- .tab('show');
- }
+ tabShown(event) {
+ const $target = $(event.target);
+ const action = $target.data('action');
+ const source = $target.attr('href');
+ const endpoint = $target.data('endpoint');
+ this.setTab(action, endpoint);
+ return this.setCurrentAction(source);
+ }
- setTab(action, endpoint) {
- if (this.loaded[action]) {
- return;
- }
- if (action === 'activity') {
- this.loadActivities();
- }
+ activateTab(action) {
+ return this.$parentEl.find(`.nav-links .js-${action}-tab a`)
+ .tab('show');
+ }
- const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
- if (loadableActions.indexOf(action) > -1) {
- return this.loadTab(action, endpoint);
- }
+ setTab(action, endpoint) {
+ if (this.loaded[action]) {
+ return;
+ }
+ if (action === 'activity') {
+ this.loadActivities();
}
- loadTab(action, endpoint) {
- return $.ajax({
- beforeSend: () => this.toggleLoading(true),
- complete: () => this.toggleLoading(false),
- dataType: 'json',
- type: 'GET',
- url: endpoint,
- success: (data) => {
- const tabSelector = `div#${action}`;
- this.$parentEl.find(tabSelector).html(data.html);
- this.loaded[action] = true;
- return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
- }
- });
+ const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
+ if (loadableActions.indexOf(action) > -1) {
+ return this.loadTab(action, endpoint);
}
+ }
- loadActivities() {
- if (this.loaded['activity']) {
- return;
+ loadTab(action, endpoint) {
+ return $.ajax({
+ beforeSend: () => this.toggleLoading(true),
+ complete: () => this.toggleLoading(false),
+ dataType: 'json',
+ type: 'GET',
+ url: endpoint,
+ success: (data) => {
+ const tabSelector = `div#${action}`;
+ this.$parentEl.find(tabSelector).html(data.html);
+ this.loaded[action] = true;
+ return gl.utils.localTimeAgo($('.js-timeago', tabSelector));
}
- const $calendarWrap = this.$parentEl.find('.user-calendar');
- $calendarWrap.load($calendarWrap.data('href'));
- new gl.Activities();
- return this.loaded['activity'] = true;
- }
+ });
+ }
- toggleLoading(status) {
- return this.$parentEl.find('.loading-status .loading')
- .toggle(status);
+ loadActivities() {
+ if (this.loaded['activity']) {
+ return;
}
+ const $calendarWrap = this.$parentEl.find('.user-calendar');
+ $calendarWrap.load($calendarWrap.data('href'));
+ new gl.Activities();
+ return this.loaded['activity'] = true;
+ }
- setCurrentAction(source) {
- let new_state = source;
- new_state = new_state.replace(/\/+$/, '');
- new_state += this._location.search + this._location.hash;
- history.replaceState({
- url: new_state
- }, document.title, new_state);
- return new_state;
- }
+ toggleLoading(status) {
+ return this.$parentEl.find('.loading-status .loading')
+ .toggle(status);
+ }
- getCurrentAction() {
- return this.$parentEl.find('.nav-links .active a').data('action');
- }
+ setCurrentAction(source) {
+ let new_state = source;
+ new_state = new_state.replace(/\/+$/, '');
+ new_state += this._location.search + this._location.hash;
+ history.replaceState({
+ url: new_state
+ }, document.title, new_state);
+ return new_state;
}
- global.UserTabs = UserTabs;
-})(window.gl || (window.gl = {}));
+
+ getCurrentAction() {
+ return this.$parentEl.find('.nav-links .active a').data('action');
+ }
+}
+
+window.gl = window.gl || {};
+window.gl.UserTabs = UserTabs;
diff --git a/app/assets/javascripts/username_validator.js b/app/assets/javascripts/username_validator.js
index 137cefa3b8e..abe6c30f4f3 100644
--- a/app/assets/javascripts/username_validator.js
+++ b/app/assets/javascripts/username_validator.js
@@ -1,135 +1,133 @@
/* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */
-((global) => {
- const debounceTimeoutDuration = 1000;
- const invalidInputClass = 'gl-field-error-outline';
- const successInputClass = 'gl-field-success-outline';
- const unavailableMessageSelector = '.username .validation-error';
- const successMessageSelector = '.username .validation-success';
- const pendingMessageSelector = '.username .validation-pending';
- const invalidMessageSelector = '.username .gl-field-error';
-
- class UsernameValidator {
- constructor() {
- this.inputElement = $('#new_user_username');
- this.inputDomElement = this.inputElement.get(0);
- this.state = {
- available: false,
- valid: false,
- pending: false,
- empty: true
- };
-
- const debounceTimeout = _.debounce((username) => {
- this.validateUsername(username);
- }, debounceTimeoutDuration);
-
- this.inputElement.on('keyup.username_check', () => {
- const username = this.inputElement.val();
-
- this.state.valid = this.inputDomElement.validity.valid;
- this.state.empty = !username.length;
-
- if (this.state.valid) {
- return debounceTimeout(username);
- }
-
- this.renderState();
- });
-
- // Override generic field validation
- this.inputElement.on('invalid', this.interceptInvalid.bind(this));
- }
+const debounceTimeoutDuration = 1000;
+const invalidInputClass = 'gl-field-error-outline';
+const successInputClass = 'gl-field-success-outline';
+const unavailableMessageSelector = '.username .validation-error';
+const successMessageSelector = '.username .validation-success';
+const pendingMessageSelector = '.username .validation-pending';
+const invalidMessageSelector = '.username .gl-field-error';
+
+class UsernameValidator {
+ constructor() {
+ this.inputElement = $('#new_user_username');
+ this.inputDomElement = this.inputElement.get(0);
+ this.state = {
+ available: false,
+ valid: false,
+ pending: false,
+ empty: true
+ };
+
+ const debounceTimeout = _.debounce((username) => {
+ this.validateUsername(username);
+ }, debounceTimeoutDuration);
+
+ this.inputElement.on('keyup.username_check', () => {
+ const username = this.inputElement.val();
+
+ this.state.valid = this.inputDomElement.validity.valid;
+ this.state.empty = !username.length;
- renderState() {
- // Clear all state
- this.clearFieldValidationState();
-
- if (this.state.valid && this.state.available) {
- return this.setSuccessState();
+ if (this.state.valid) {
+ return debounceTimeout(username);
}
- if (this.state.empty) {
- return this.clearFieldValidationState();
- }
+ this.renderState();
+ });
- if (this.state.pending) {
- return this.setPendingState();
- }
+ // Override generic field validation
+ this.inputElement.on('invalid', this.interceptInvalid.bind(this));
+ }
- if (!this.state.available) {
- return this.setUnavailableState();
- }
+ renderState() {
+ // Clear all state
+ this.clearFieldValidationState();
- if (!this.state.valid) {
- return this.setInvalidState();
- }
+ if (this.state.valid && this.state.available) {
+ return this.setSuccessState();
}
- interceptInvalid(event) {
- event.preventDefault();
- event.stopPropagation();
+ if (this.state.empty) {
+ return this.clearFieldValidationState();
}
- validateUsername(username) {
- if (this.state.valid) {
- this.state.pending = true;
- this.state.available = false;
- this.renderState();
- return $.ajax({
- type: 'GET',
- url: `${gon.relative_url_root}/users/${username}/exists`,
- dataType: 'json',
- success: (res) => this.setAvailabilityState(res.exists)
- });
- }
+ if (this.state.pending) {
+ return this.setPendingState();
}
- setAvailabilityState(usernameTaken) {
- if (usernameTaken) {
- this.state.valid = false;
- this.state.available = false;
- } else {
- this.state.available = true;
- }
- this.state.pending = false;
- this.renderState();
+ if (!this.state.available) {
+ return this.setUnavailableState();
}
- clearFieldValidationState() {
- this.inputElement.siblings('p').hide();
-
- this.inputElement.removeClass(invalidInputClass)
- .removeClass(successInputClass);
+ if (!this.state.valid) {
+ return this.setInvalidState();
}
+ }
- setUnavailableState() {
- const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
- this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
- $usernameUnavailableMessage.show();
- }
+ interceptInvalid(event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
- setSuccessState() {
- const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
- this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
- $usernameSuccessMessage.show();
+ validateUsername(username) {
+ if (this.state.valid) {
+ this.state.pending = true;
+ this.state.available = false;
+ this.renderState();
+ return $.ajax({
+ type: 'GET',
+ url: `${gon.relative_url_root}/users/${username}/exists`,
+ dataType: 'json',
+ success: (res) => this.setAvailabilityState(res.exists)
+ });
}
+ }
- setPendingState() {
- const $usernamePendingMessage = $(pendingMessageSelector);
- if (this.state.pending) {
- $usernamePendingMessage.show();
- } else {
- $usernamePendingMessage.hide();
- }
+ setAvailabilityState(usernameTaken) {
+ if (usernameTaken) {
+ this.state.valid = false;
+ this.state.available = false;
+ } else {
+ this.state.available = true;
}
+ this.state.pending = false;
+ this.renderState();
+ }
+
+ clearFieldValidationState() {
+ this.inputElement.siblings('p').hide();
- setInvalidState() {
- const $inputErrorMessage = $(invalidMessageSelector);
- this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
- $inputErrorMessage.show();
+ this.inputElement.removeClass(invalidInputClass)
+ .removeClass(successInputClass);
+ }
+
+ setUnavailableState() {
+ const $usernameUnavailableMessage = this.inputElement.siblings(unavailableMessageSelector);
+ this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+ $usernameUnavailableMessage.show();
+ }
+
+ setSuccessState() {
+ const $usernameSuccessMessage = this.inputElement.siblings(successMessageSelector);
+ this.inputElement.addClass(successInputClass).removeClass(invalidInputClass);
+ $usernameSuccessMessage.show();
+ }
+
+ setPendingState() {
+ const $usernamePendingMessage = $(pendingMessageSelector);
+ if (this.state.pending) {
+ $usernamePendingMessage.show();
+ } else {
+ $usernamePendingMessage.hide();
}
}
- global.UsernameValidator = UsernameValidator;
-})(window);
+ setInvalidState() {
+ const $inputErrorMessage = $(invalidMessageSelector);
+ this.inputElement.addClass(invalidInputClass).removeClass(successInputClass);
+ $inputErrorMessage.show();
+ }
+}
+
+window.UsernameValidator = UsernameValidator;
diff --git a/app/assets/javascripts/visibility_select.js b/app/assets/javascripts/visibility_select.js
index f712d7ba930..b6bbbaa0936 100644
--- a/app/assets/javascripts/visibility_select.js
+++ b/app/assets/javascripts/visibility_select.js
@@ -1,27 +1,24 @@
-(() => {
- const gl = window.gl || (window.gl = {});
-
- class VisibilitySelect {
- constructor(container) {
- if (!container) throw new Error('VisibilitySelect requires a container element as argument 1');
- this.container = container;
- this.helpBlock = this.container.querySelector('.help-block');
- this.select = this.container.querySelector('select');
- }
+class VisibilitySelect {
+ constructor(container) {
+ if (!container) throw new Error('VisibilitySelect requires a container element as argument 1');
+ this.container = container;
+ this.helpBlock = this.container.querySelector('.help-block');
+ this.select = this.container.querySelector('select');
+ }
- init() {
- if (this.select) {
- this.updateHelpText();
- this.select.addEventListener('change', this.updateHelpText.bind(this));
- } else {
- this.helpBlock.textContent = this.container.querySelector('.js-locked').dataset.helpBlock;
- }
+ init() {
+ if (this.select) {
+ this.updateHelpText();
+ this.select.addEventListener('change', this.updateHelpText.bind(this));
+ } else {
+ this.helpBlock.textContent = this.container.querySelector('.js-locked').dataset.helpBlock;
}
+ }
- updateHelpText() {
- this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description;
- }
+ updateHelpText() {
+ this.helpBlock.textContent = this.select.querySelector('option:checked').dataset.description;
}
+}
- gl.VisibilitySelect = VisibilitySelect;
-})();
+window.gl = window.gl || {};
+window.gl.VisibilitySelect = VisibilitySelect;
diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js
index 4194c1bc08d..03d183ebd84 100644
--- a/app/assets/javascripts/wikis.js
+++ b/app/assets/javascripts/wikis.js
@@ -4,66 +4,65 @@
import 'vendor/jquery.nicescroll';
import './breakpoints';
-((global) => {
- class Wikis {
- constructor() {
- this.bp = Breakpoints.get();
- this.sidebarEl = document.querySelector('.js-wiki-sidebar');
- this.sidebarExpanded = false;
- $(this.sidebarEl).niceScroll();
+class Wikis {
+ constructor() {
+ this.bp = Breakpoints.get();
+ this.sidebarEl = document.querySelector('.js-wiki-sidebar');
+ this.sidebarExpanded = false;
+ $(this.sidebarEl).niceScroll();
- const sidebarToggles = document.querySelectorAll('.js-sidebar-wiki-toggle');
- for (let i = 0; i < sidebarToggles.length; i += 1) {
- sidebarToggles[i].addEventListener('click', e => this.handleToggleSidebar(e));
- }
-
- this.newWikiForm = document.querySelector('form.new-wiki-page');
- if (this.newWikiForm) {
- this.newWikiForm.addEventListener('submit', e => this.handleNewWikiSubmit(e));
- }
+ const sidebarToggles = document.querySelectorAll('.js-sidebar-wiki-toggle');
+ for (let i = 0; i < sidebarToggles.length; i += 1) {
+ sidebarToggles[i].addEventListener('click', e => this.handleToggleSidebar(e));
+ }
- window.addEventListener('resize', () => this.renderSidebar());
- this.renderSidebar();
+ this.newWikiForm = document.querySelector('form.new-wiki-page');
+ if (this.newWikiForm) {
+ this.newWikiForm.addEventListener('submit', e => this.handleNewWikiSubmit(e));
}
- handleNewWikiSubmit(e) {
- if (!this.newWikiForm) return;
+ window.addEventListener('resize', () => this.renderSidebar());
+ this.renderSidebar();
+ }
- const slugInput = this.newWikiForm.querySelector('#new_wiki_path');
- const slug = gl.text.slugify(slugInput.value);
+ handleNewWikiSubmit(e) {
+ if (!this.newWikiForm) return;
- if (slug.length > 0) {
- const wikisPath = slugInput.getAttribute('data-wikis-path');
- window.location.href = `${wikisPath}/${slug}`;
- e.preventDefault();
- }
- }
+ const slugInput = this.newWikiForm.querySelector('#new_wiki_path');
+ const slug = gl.text.slugify(slugInput.value);
- handleToggleSidebar(e) {
+ if (slug.length > 0) {
+ const wikisPath = slugInput.getAttribute('data-wikis-path');
+ window.location.href = `${wikisPath}/${slug}`;
e.preventDefault();
- this.sidebarExpanded = !this.sidebarExpanded;
- this.renderSidebar();
}
+ }
- sidebarCanCollapse() {
- const bootstrapBreakpoint = this.bp.getBreakpointSize();
- return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
- }
+ handleToggleSidebar(e) {
+ e.preventDefault();
+ this.sidebarExpanded = !this.sidebarExpanded;
+ this.renderSidebar();
+ }
+
+ sidebarCanCollapse() {
+ const bootstrapBreakpoint = this.bp.getBreakpointSize();
+ return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
+ }
- renderSidebar() {
- if (!this.sidebarEl) return;
- const { classList } = this.sidebarEl;
- if (this.sidebarExpanded || !this.sidebarCanCollapse()) {
- if (!classList.contains('right-sidebar-expanded')) {
- classList.remove('right-sidebar-collapsed');
- classList.add('right-sidebar-expanded');
- }
- } else if (classList.contains('right-sidebar-expanded')) {
- classList.add('right-sidebar-collapsed');
- classList.remove('right-sidebar-expanded');
+ renderSidebar() {
+ if (!this.sidebarEl) return;
+ const { classList } = this.sidebarEl;
+ if (this.sidebarExpanded || !this.sidebarCanCollapse()) {
+ if (!classList.contains('right-sidebar-expanded')) {
+ classList.remove('right-sidebar-collapsed');
+ classList.add('right-sidebar-expanded');
}
+ } else if (classList.contains('right-sidebar-expanded')) {
+ classList.add('right-sidebar-collapsed');
+ classList.remove('right-sidebar-expanded');
}
}
+}
- global.Wikis = Wikis;
-})(window.gl || (window.gl = {}));
+window.gl = window.gl || {};
+window.gl.Wikis = Wikis;
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index b7fe552dec2..08f80735e93 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -34,65 +34,64 @@ window.Dropzone = Dropzone;
// **Cancelable** No
// **Target** a.js-zen-leave
//
-(function() {
- this.ZenMode = (function() {
- function ZenMode() {
- this.active_backdrop = null;
- this.active_textarea = null;
- $(document).on('click', '.js-zen-enter', function(e) {
- e.preventDefault();
- return $(e.currentTarget).trigger('zen_mode:enter');
- });
- $(document).on('click', '.js-zen-leave', function(e) {
+
+window.ZenMode = (function() {
+ function ZenMode() {
+ this.active_backdrop = null;
+ this.active_textarea = null;
+ $(document).on('click', '.js-zen-enter', function(e) {
+ e.preventDefault();
+ return $(e.currentTarget).trigger('zen_mode:enter');
+ });
+ $(document).on('click', '.js-zen-leave', function(e) {
+ e.preventDefault();
+ return $(e.currentTarget).trigger('zen_mode:leave');
+ });
+ $(document).on('zen_mode:enter', (function(_this) {
+ return function(e) {
+ return _this.enter($(e.target).closest('.md-area').find('.zen-backdrop'));
+ };
+ })(this));
+ $(document).on('zen_mode:leave', (function(_this) {
+ return function(e) {
+ return _this.exit();
+ };
+ })(this));
+ $(document).on('keydown', function(e) {
+ // Esc
+ if (e.keyCode === 27) {
e.preventDefault();
- return $(e.currentTarget).trigger('zen_mode:leave');
- });
- $(document).on('zen_mode:enter', (function(_this) {
- return function(e) {
- return _this.enter($(e.target).closest('.md-area').find('.zen-backdrop'));
- };
- })(this));
- $(document).on('zen_mode:leave', (function(_this) {
- return function(e) {
- return _this.exit();
- };
- })(this));
- $(document).on('keydown', function(e) {
- // Esc
- if (e.keyCode === 27) {
- e.preventDefault();
- return $(document).trigger('zen_mode:leave');
- }
- });
- }
+ return $(document).trigger('zen_mode:leave');
+ }
+ });
+ }
- ZenMode.prototype.enter = function(backdrop) {
- Mousetrap.pause();
- this.active_backdrop = $(backdrop);
- this.active_backdrop.addClass('fullscreen');
- this.active_textarea = this.active_backdrop.find('textarea');
- // Prevent a user-resized textarea from persisting to fullscreen
- this.active_textarea.removeAttr('style');
- return this.active_textarea.focus();
- };
+ ZenMode.prototype.enter = function(backdrop) {
+ Mousetrap.pause();
+ this.active_backdrop = $(backdrop);
+ this.active_backdrop.addClass('fullscreen');
+ this.active_textarea = this.active_backdrop.find('textarea');
+ // Prevent a user-resized textarea from persisting to fullscreen
+ this.active_textarea.removeAttr('style');
+ return this.active_textarea.focus();
+ };
- ZenMode.prototype.exit = function() {
- if (this.active_textarea) {
- Mousetrap.unpause();
- this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen');
- this.scrollTo(this.active_textarea);
- this.active_textarea = null;
- this.active_backdrop = null;
- return Dropzone.forElement('.div-dropzone').enable();
- }
- };
+ ZenMode.prototype.exit = function() {
+ if (this.active_textarea) {
+ Mousetrap.unpause();
+ this.active_textarea.closest('.zen-backdrop').removeClass('fullscreen');
+ this.scrollTo(this.active_textarea);
+ this.active_textarea = null;
+ this.active_backdrop = null;
+ return Dropzone.forElement('.div-dropzone').enable();
+ }
+ };
- ZenMode.prototype.scrollTo = function(zen_area) {
- return $.scrollTo(zen_area, 0, {
- offset: -150
- });
- };
+ ZenMode.prototype.scrollTo = function(zen_area) {
+ return $.scrollTo(zen_area, 0, {
+ offset: -150
+ });
+ };
- return ZenMode;
- })();
-}).call(window);
+ return ZenMode;
+})();
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 5cf9330b8f8..542b641e3dd 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -92,7 +92,7 @@
@mixin maintain-sidebar-dimensions {
display: block;
width: $gutter-width;
- padding: 10px 20px;
+ padding: 10px 0;
}
.issues-bulk-update.right-sidebar {
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index 1c1392f8f67..b1ff2659131 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -3,6 +3,7 @@
color: $gl-text-color;
border: 1px solid $border-color;
border-radius: $border-radius-default;
+ margin-bottom: $gl-padding;
.well-segment {
padding: $gl-padding;
@@ -21,6 +22,11 @@
font-size: 12px;
}
}
+
+ &.admin-well h4 {
+ border-bottom: 1px solid $border-color;
+ padding-bottom: 8px;
+ }
}
.icon-container {
@@ -53,6 +59,14 @@
padding: 15px;
}
+.dark-well {
+ background-color: $gray-normal;
+
+ .btn {
+ width: 100%;
+ }
+}
+
.well-centered {
h1 {
font-weight: normal;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 057d457b3a2..47f50083726 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -200,7 +200,6 @@
right: 0;
transition: width .3s;
background: $gray-light;
- padding: 0 20px;
z-index: 200;
overflow: hidden;
@@ -224,6 +223,10 @@
}
}
+ .issuable-sidebar {
+ padding: 0 20px;
+ }
+
.issuable-sidebar-header {
padding-top: 10px;
}
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 323d5d26eb6..b82681b197e 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,6 +1,5 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
- include Devise::Controllers::Rememberable
protect_from_forgery except: [:kerberos, :saml, :cas3]
@@ -116,10 +115,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider'])
if @user.two_factor_enabled?
- params[:remember_me] = '1' if remember_me?
prompt_for_two_factor(@user)
else
- remember_me(@user) if remember_me?
sign_in_and_redirect(@user)
end
else
@@ -150,9 +147,4 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
AuditEventService.new(user, user, options)
.for_authentication.security_event
end
-
- def remember_me?
- request_params = request.env['omniauth.params']
- (request_params['remember_me'] == '1') if request_params.present?
- end
end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 3c9f932a225..128b5dc01ab 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -5,182 +5,182 @@
.admin-dashboard.prepend-top-default
.row
.col-md-4
- %h4 Statistics
- %hr
- %p
- Forks
- %span.light.pull-right
- = number_with_delimiter(ForkedProjectLink.count)
- %p
- Issues
- %span.light.pull-right
- = number_with_delimiter(Issue.count)
- %p
- Merge Requests
- %span.light.pull-right
- = number_with_delimiter(MergeRequest.count)
- %p
- Notes
- %span.light.pull-right
- = number_with_delimiter(Note.count)
- %p
- Snippets
- %span.light.pull-right
- = number_with_delimiter(Snippet.count)
- %p
- SSH Keys
- %span.light.pull-right
- = number_with_delimiter(Key.count)
- %p
- Milestones
- %span.light.pull-right
- = number_with_delimiter(Milestone.count)
- %p
- Active Users
- %span.light.pull-right
- = number_with_delimiter(User.active.count)
+ .info-well
+ .well-segment.admin-well
+ %h4 Statistics
+ %p
+ Forks
+ %span.light.pull-right
+ = number_with_delimiter(ForkedProjectLink.count)
+ %p
+ Issues
+ %span.light.pull-right
+ = number_with_delimiter(Issue.count)
+ %p
+ Merge Requests
+ %span.light.pull-right
+ = number_with_delimiter(MergeRequest.count)
+ %p
+ Notes
+ %span.light.pull-right
+ = number_with_delimiter(Note.count)
+ %p
+ Snippets
+ %span.light.pull-right
+ = number_with_delimiter(Snippet.count)
+ %p
+ SSH Keys
+ %span.light.pull-right
+ = number_with_delimiter(Key.count)
+ %p
+ Milestones
+ %span.light.pull-right
+ = number_with_delimiter(Milestone.count)
+ %p
+ Active Users
+ %span.light.pull-right
+ = number_with_delimiter(User.active.count)
.col-md-4
- %h4
- Features
- %hr
- - sign_up = "Sign up"
- %p{ "aria-label" => "#{sign_up}: status " + (signup_enabled? ? "on" : "off") }
- = sign_up
- %span.light.pull-right
- = boolean_to_icon signup_enabled?
- - ldap = "LDAP"
- %p{ "aria-label" => "#{ldap}: status " + (Gitlab.config.ldap.enabled ? "on" : "off") }
- = ldap
- %span.light.pull-right
- = boolean_to_icon Gitlab.config.ldap.enabled
- - gravatar = "Gravatar"
- %p{ "aria-label" => "#{gravatar}: status " + (gravatar_enabled? ? "on" : "off") }
- = gravatar
- %span.light.pull-right
- = boolean_to_icon gravatar_enabled?
- - omniauth = "OmniAuth"
- %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") }
- = omniauth
- %span.light.pull-right
- = boolean_to_icon Gitlab.config.omniauth.enabled
- - reply_email = "Reply by email"
- %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") }
- = reply_email
- %span.light.pull-right
- = boolean_to_icon Gitlab::IncomingEmail.enabled?
- - container_reg = "Container Registry"
- %p{ "aria-label" => "#{container_reg}: status " + (Gitlab.config.registry.enabled ? "on" : "off") }
- = container_reg
- %span.light.pull-right
- = boolean_to_icon Gitlab.config.registry.enabled
- - gitlab_pages = 'GitLab Pages'
- - gitlab_pages_enabled = Gitlab.config.pages.enabled
- %p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") }
- = gitlab_pages
- %span.light.pull-right
- = boolean_to_icon gitlab_pages_enabled
- - gitlab_shared_runners = 'Shared Runners'
- - gitlab_shared_runners_enabled = Gitlab.config.gitlab_ci.shared_runners_enabled
- %p{ "aria-label" => "#{gitlab_shared_runners}: status " + (gitlab_shared_runners_enabled ? "on" : "off") }
- = gitlab_shared_runners
- %span.light.pull-right
- = boolean_to_icon gitlab_shared_runners_enabled
-
+ .info-well
+ .well-segment.admin-well
+ %h4 Features
+ - sign_up = "Sign up"
+ %p{ "aria-label" => "#{sign_up}: status " + (signup_enabled? ? "on" : "off") }
+ = sign_up
+ %span.light.pull-right
+ = boolean_to_icon signup_enabled?
+ - ldap = "LDAP"
+ %p{ "aria-label" => "#{ldap}: status " + (Gitlab.config.ldap.enabled ? "on" : "off") }
+ = ldap
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.ldap.enabled
+ - gravatar = "Gravatar"
+ %p{ "aria-label" => "#{gravatar}: status " + (gravatar_enabled? ? "on" : "off") }
+ = gravatar
+ %span.light.pull-right
+ = boolean_to_icon gravatar_enabled?
+ - omniauth = "OmniAuth"
+ %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") }
+ = omniauth
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.omniauth.enabled
+ - reply_email = "Reply by email"
+ %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") }
+ = reply_email
+ %span.light.pull-right
+ = boolean_to_icon Gitlab::IncomingEmail.enabled?
+ - container_reg = "Container Registry"
+ %p{ "aria-label" => "#{container_reg}: status " + (Gitlab.config.registry.enabled ? "on" : "off") }
+ = container_reg
+ %span.light.pull-right
+ = boolean_to_icon Gitlab.config.registry.enabled
+ - gitlab_pages = 'GitLab Pages'
+ - gitlab_pages_enabled = Gitlab.config.pages.enabled
+ %p{ "aria-label" => "#{gitlab_pages}: status " + (gitlab_pages_enabled ? "on" : "off") }
+ = gitlab_pages
+ %span.light.pull-right
+ = boolean_to_icon gitlab_pages_enabled
+ - gitlab_shared_runners = 'Shared Runners'
+ - gitlab_shared_runners_enabled = Gitlab.config.gitlab_ci.shared_runners_enabled
+ %p{ "aria-label" => "#{gitlab_shared_runners}: status " + (gitlab_shared_runners_enabled ? "on" : "off") }
+ = gitlab_shared_runners
+ %span.light.pull-right
+ = boolean_to_icon gitlab_shared_runners_enabled
.col-md-4
- %h4
- Components
- - if current_application_settings.version_check_enabled
- .pull-right
- = version_status_badge
-
- %hr
- %p
- GitLab
- %span.pull-right
- = Gitlab::VERSION
- %p
- GitLab Shell
- %span.pull-right
- = Gitlab::Shell.new.version
- %p
- GitLab Workhorse
- %span.pull-right
- = gitlab_workhorse_version
- %p
- GitLab API
- %span.pull-right
- = API::API::version
- %p
- Git
- %span.pull-right
- = Gitlab::Git.version
- %p
- Ruby
- %span.pull-right
- #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
-
- %p
- Rails
- %span.pull-right
- #{Rails::VERSION::STRING}
-
- %p
- = Gitlab::Database.adapter_name
- %span.pull-right
- = Gitlab::Database.version
- %hr
+ .info-well
+ .well-segment.admin-well
+ %h4
+ Components
+ - if current_application_settings.version_check_enabled
+ .pull-right
+ = version_status_badge
+ %p
+ GitLab
+ %span.pull-right
+ = Gitlab::VERSION
+ %p
+ GitLab Shell
+ %span.pull-right
+ = Gitlab::Shell.new.version
+ %p
+ GitLab Workhorse
+ %span.pull-right
+ = gitlab_workhorse_version
+ %p
+ GitLab API
+ %span.pull-right
+ = API::API::version
+ %p
+ Git
+ %span.pull-right
+ = Gitlab::Git.version
+ %p
+ Ruby
+ %span.pull-right
+ #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}
+ %p
+ Rails
+ %span.pull-right
+ #{Rails::VERSION::STRING}
+ %p
+ = Gitlab::Database.adapter_name
+ %span.pull-right
+ = Gitlab::Database.version
.row
.col-sm-4
- .light-well.well-centered
- %h4 Projects
- .data
+ .info-well.dark-well
+ .well-segment.well-centered
= link_to admin_projects_path do
- %h1= number_with_delimiter(Project.cached_count)
+ %h3.text-center
+ Projects:
+ = number_with_delimiter(Project.cached_count)
%hr
= link_to('New project', new_project_path, class: "btn btn-new")
.col-sm-4
- .light-well.well-centered
- %h4 Users
- .data
+ .info-well.dark-well
+ .well-segment.well-centered
= link_to admin_users_path do
- %h1= number_with_delimiter(User.count)
+ %h3.text-center
+ Users:
+ = number_with_delimiter(User.count)
%hr
= link_to 'New user', new_admin_user_path, class: "btn btn-new"
.col-sm-4
- .light-well.well-centered
- %h4 Groups
- .data
+ .info-well.dark-well
+ .well-segment.well-centered
= link_to admin_groups_path do
- %h1= number_with_delimiter(Group.count)
+ %h3.text-center
+ Groups
+ = number_with_delimiter(Group.count)
%hr
= link_to 'New group', new_admin_group_path, class: "btn btn-new"
-
- .row.prepend-top-10
+ .row
.col-md-4
- %h4 Latest projects
- %hr
- - @projects.each do |project|
- %p
- = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60'
- %span.light.pull-right
- #{time_ago_with_tooltip(project.created_at)}
-
+ .info-well
+ .well-segment.admin-well
+ %h4 Latest projects
+ - @projects.each do |project|
+ %p
+ = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60'
+ %span.light.pull-right
+ #{time_ago_with_tooltip(project.created_at)}
.col-md-4
- %h4 Latest users
- %hr
- - @users.each do |user|
- %p
- = link_to [:admin, user], class: 'str-truncated-60' do
- = user.name
- %span.light.pull-right
- #{time_ago_with_tooltip(user.created_at)}
-
+ .info-well
+ .well-segment.admin-well
+ %h4 Latest users
+ - @users.each do |user|
+ %p
+ = link_to [:admin, user], class: 'str-truncated-60' do
+ = user.name
+ %span.light.pull-right
+ #{time_ago_with_tooltip(user.created_at)}
.col-md-4
- %h4 Latest groups
- %hr
- - @groups.each do |group|
- %p
- = link_to [:admin, group], class: 'str-truncated-60' do
- = group.full_name
- %span.light.pull-right
- #{time_ago_with_tooltip(group.created_at)}
+ .info-well
+ .well-segment.admin-well
+ %h4 Latest groups
+ - @groups.each do |group|
+ %p
+ = link_to [:admin, group], class: 'str-truncated-60' do
+ = group.full_name
+ %span.light.pull-right
+ #{time_ago_with_tooltip(group.created_at)}
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index e80d10dc8f1..f92f89e73ff 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -6,7 +6,4 @@
- providers.each do |provider|
%span.light
- has_icon = provider_has_icon?(provider)
- = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}"
- %fieldset
- = check_box_tag :remember_me
- = label_tag :remember_me, 'Remember Me'
+ = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn')
diff --git a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml b/changelogs/unreleased/18000-remember-me-for-oauth-login.yml
deleted file mode 100644
index 1ef92756a76..00000000000
--- a/changelogs/unreleased/18000-remember-me-for-oauth-login.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Honor the "Remember me" parameter for OAuth-based login
-merge_request: 11963
-author:
diff --git a/changelogs/unreleased/23036-replace-snippets-spinach.yml b/changelogs/unreleased/23036-replace-snippets-spinach.yml
new file mode 100644
index 00000000000..545805b1302
--- /dev/null
+++ b/changelogs/unreleased/23036-replace-snippets-spinach.yml
@@ -0,0 +1,4 @@
+---
+title: Replace 'snippets/snippets.feature' spinach with rspec
+merge_request: 12385
+author: Alexander Randa @randaalex
diff --git a/changelogs/unreleased/32838-admin-panel-spacing.yml b/changelogs/unreleased/32838-admin-panel-spacing.yml
new file mode 100644
index 00000000000..ccd703fa43f
--- /dev/null
+++ b/changelogs/unreleased/32838-admin-panel-spacing.yml
@@ -0,0 +1,4 @@
+---
+title: Add wells to admin dashboard overview to fix spacing problems
+merge_request:
+author:
diff --git a/changelogs/unreleased/34578-sidebar-padding.yml b/changelogs/unreleased/34578-sidebar-padding.yml
new file mode 100644
index 00000000000..dc4647298e6
--- /dev/null
+++ b/changelogs/unreleased/34578-sidebar-padding.yml
@@ -0,0 +1,4 @@
+---
+title: fix left & right padding on sidebar
+merge_request:
+author:
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index fdc2b24e110..4b81fd90f59 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -619,53 +619,6 @@ test:
title: "JIRA"
url: https://sample_company.atlassian.net
project_key: PROJECT
-
- omniauth:
- enabled: true
- allow_single_sign_on: true
- external_providers: []
-
- providers:
- - { name: 'cas3',
- label: 'cas3',
- args: { url: 'https://sso.example.com',
- disable_ssl_verification: false,
- login_url: '/cas/login',
- service_validate_url: '/cas/p3/serviceValidate',
- logout_url: '/cas/logout'} }
- - { name: 'authentiq',
- app_id: 'YOUR_CLIENT_ID',
- app_secret: 'YOUR_CLIENT_SECRET',
- args: { scope: 'aq:name email~rs address aq:push' } }
- - { name: 'github',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET',
- url: "https://github.com/",
- verify_ssl: false,
- args: { scope: 'user:email' } }
- - { name: 'bitbucket',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET' }
- - { name: 'gitlab',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET',
- args: { scope: 'api' } }
- - { name: 'google_oauth2',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET',
- args: { access_type: 'offline', approval_prompt: '' } }
- - { name: 'facebook',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET' }
- - { name: 'twitter',
- app_id: 'YOUR_APP_ID',
- app_secret: 'YOUR_APP_SECRET' }
- - { name: 'auth0',
- args: {
- client_id: 'YOUR_AUTH0_CLIENT_ID',
- client_secret: 'YOUR_AUTH0_CLIENT_SECRET',
- namespace: 'YOUR_AUTH0_DOMAIN' } }
-
ldap:
enabled: false
servers:
diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb
index 0fee832788d..8ec9613a4b7 100644
--- a/config/initializers/flipper.rb
+++ b/config/initializers/flipper.rb
@@ -1,4 +1,6 @@
require 'flipper/middleware/memoizer'
-Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
- lambda { Feature.flipper }
+unless Rails.env.test?
+ Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
+ lambda { Feature.flipper }
+end
diff --git a/features/snippets/snippets.feature b/features/snippets/snippets.feature
deleted file mode 100644
index 1ad02780229..00000000000
--- a/features/snippets/snippets.feature
+++ /dev/null
@@ -1,40 +0,0 @@
-@snippets
-Feature: Snippets
- Background:
- Given I sign in as a user
- And I have public "Personal snippet one" snippet
- And I have private "Personal snippet private" snippet
-
- @javascript
- Scenario: I create new snippet
- Given I visit new snippet page
- And I submit new snippet "Personal snippet three"
- Then I should see snippet "Personal snippet three"
-
- Scenario: I update "Personal snippet one"
- Given I visit snippet page "Personal snippet one"
- And I click link "Edit"
- And I submit new title "Personal snippet new title"
- Then I should see "Personal snippet new title"
-
- Scenario: Set "Personal snippet one" public
- Given I visit snippet page "Personal snippet one"
- And I click link "Edit"
- And I uncheck "Private" checkbox
- Then I should see "Personal snippet one" public
-
- Scenario: I destroy "Personal snippet one"
- Given I visit snippet page "Personal snippet one"
- And I click link "Delete"
- Then I should not see "Personal snippet one" in snippets
-
- Scenario: I create new internal snippet
- Given I logout directly
- And I sign in as an admin
- Then I visit new snippet page
- And I submit new internal snippet
- Then I visit snippet page "Internal personal snippet one"
- And I logout directly
- Then I sign in as a user
- Given I visit new snippet page
- Then I visit snippet page "Internal personal snippet one"
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 8a5b4112ffe..1f062dc4009 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -487,10 +487,6 @@ module SharedPaths
visit explore_snippets_path
end
- step 'I visit new snippet page' do
- visit new_snippet_path
- end
-
def root_ref
@project.repository.root_ref
end
diff --git a/features/steps/shared/snippet.rb b/features/steps/shared/snippet.rb
deleted file mode 100644
index bb596c1620a..00000000000
--- a/features/steps/shared/snippet.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-module SharedSnippet
- include Spinach::DSL
-
- step 'I have public "Personal snippet one" snippet' do
- create(:personal_snippet,
- title: "Personal snippet one",
- content: "Test content",
- file_name: "snippet.rb",
- visibility_level: Snippet::PUBLIC,
- author: current_user)
- end
-
- step 'I have private "Personal snippet private" snippet' do
- create(:personal_snippet,
- title: "Personal snippet private",
- content: "Provate content",
- file_name: "private_snippet.rb",
- visibility_level: Snippet::PRIVATE,
- author: current_user)
- end
-
- step 'I have internal "Personal snippet internal" snippet' do
- create(:personal_snippet,
- title: "Personal snippet internal",
- content: "Provate content",
- file_name: "internal_snippet.rb",
- visibility_level: Snippet::INTERNAL,
- author: current_user)
- end
-
- step 'I have a public many lined snippet' do
- create(:personal_snippet,
- title: 'Many lined snippet',
- content: <<-END.gsub(/^\s+\|/, ''),
- |line one
- |line two
- |line three
- |line four
- |line five
- |line six
- |line seven
- |line eight
- |line nine
- |line ten
- |line eleven
- |line twelve
- |line thirteen
- |line fourteen
- END
- file_name: 'many_lined_snippet.rb',
- visibility_level: Snippet::PUBLIC,
- author: current_user)
- end
-
- step 'There is public "Personal snippet one" snippet' do
- create(:personal_snippet,
- title: "Personal snippet one",
- content: "Test content",
- file_name: "snippet.rb",
- visibility_level: Snippet::PUBLIC,
- author: create(:user))
- end
-end
diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb
deleted file mode 100644
index a4fc77746ee..00000000000
--- a/features/steps/snippets/snippets.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-class Spinach::Features::Snippets < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
- include SharedSnippet
- include WaitForRequests
-
- step 'I click link "Personal snippet one"' do
- click_link "Personal snippet one"
- end
-
- step 'I should not see "Personal snippet one" in snippets' do
- expect(page).not_to have_content "Personal snippet one"
- end
-
- step 'I click link "Edit"' do
- page.within ".detail-page-header" do
- first(:link, "Edit").click
- end
- end
-
- step 'I click link "Delete"' do
- first(:link, "Delete").click
- end
-
- step 'I submit new snippet "Personal snippet three"' do
- fill_in "personal_snippet_title", with: "Personal snippet three"
- fill_in "personal_snippet_file_name", with: "my_snippet.rb"
- page.within('.file-editor') do
- find('.ace_editor').native.send_keys 'Content of snippet three'
- end
- click_button "Create snippet"
- wait_for_requests
- end
-
- step 'I submit new internal snippet' do
- fill_in "personal_snippet_title", with: "Internal personal snippet one"
- fill_in "personal_snippet_file_name", with: "my_snippet.rb"
- choose 'personal_snippet_visibility_level_10'
-
- page.within('.file-editor') do
- find(:xpath, "//input[@id='personal_snippet_content']").set 'Content of internal snippet'
- end
-
- click_button "Create snippet"
- end
-
- step 'I should see snippet "Personal snippet three"' do
- expect(page).to have_content "Personal snippet three"
- expect(page).to have_content "Content of snippet three"
- end
-
- step 'I submit new title "Personal snippet new title"' do
- fill_in "personal_snippet_title", with: "Personal snippet new title"
- click_button "Save"
- end
-
- step 'I should see "Personal snippet new title"' do
- expect(page).to have_content "Personal snippet new title"
- end
-
- step 'I uncheck "Private" checkbox' do
- choose "Internal"
- click_button "Save"
- end
-
- step 'I should see "Personal snippet one" public' do
- expect(page).to have_no_xpath("//i[@class='public-snippet']")
- end
-
- step 'I visit snippet page "Personal snippet one"' do
- visit snippet_path(snippet)
- end
-
- step 'I visit snippet page "Internal personal snippet one"' do
- visit snippet_path(internal_snippet)
- end
-
- def snippet
- @snippet ||= PersonalSnippet.find_by!(title: "Personal snippet one")
- end
-
- def internal_snippet
- @snippet ||= PersonalSnippet.find_by!(title: "Internal personal snippet one")
- end
-end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index e9fb6a008b0..e3883278886 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -42,7 +42,8 @@ namespace :gitlab do
http_clone_url = project.http_url_to_repo
ssh_clone_url = project.ssh_url_to_repo
- omniauth_providers = Gitlab.config.omniauth.providers.map { |provider| provider['name'] }
+ omniauth_providers = Gitlab.config.omniauth.providers
+ omniauth_providers.map! { |provider| provider['name'] }
puts ""
puts "GitLab information".color(:yellow)
diff --git a/spec/factories/personal_snippets.rb b/spec/factories/personal_snippets.rb
deleted file mode 100644
index 0f13b2c1020..00000000000
--- a/spec/factories/personal_snippets.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-FactoryGirl.define do
- factory :personal_snippet, parent: :snippet, class: :PersonalSnippet do
- end
-end
diff --git a/spec/factories/project_snippets.rb b/spec/factories/project_snippets.rb
deleted file mode 100644
index e0fe1b36fd3..00000000000
--- a/spec/factories/project_snippets.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-FactoryGirl.define do
- factory :project_snippet, parent: :snippet, class: :ProjectSnippet do
- project factory: :empty_project
- end
-end
diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb
index 388f662e6e5..f6ce99d50f9 100644
--- a/spec/factories/snippets.rb
+++ b/spec/factories/snippets.rb
@@ -18,4 +18,11 @@ FactoryGirl.define do
visibility_level Snippet::PRIVATE
end
end
+
+ factory :project_snippet, parent: :snippet, class: :ProjectSnippet do
+ project factory: :empty_project
+ end
+
+ factory :personal_snippet, parent: :snippet, class: :PersonalSnippet do
+ end
end
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
deleted file mode 100644
index 452b920307c..00000000000
--- a/spec/features/oauth_login_spec.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-require 'spec_helper'
-
-feature 'OAuth Login', js: true do
- def enter_code(code)
- fill_in 'user_otp_attempt', with: code
- click_button 'Verify code'
- end
-
- def stub_omniauth_config(provider)
- OmniAuth.config.add_mock(provider, OmniAuth::AuthHash.new(provider: provider.to_s, uid: "12345"))
- Rails.application.env_config['devise.mapping'] = Devise.mappings[:user]
- Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
- end
-
- providers = [:github, :twitter, :bitbucket, :gitlab, :google_oauth2,
- :facebook, :authentiq, :cas3, :auth0]
-
- before(:all) do
- # The OmniAuth `full_host` parameter doesn't get set correctly (it gets set to something like `http://localhost`
- # here), and causes integration tests to fail with 404s. We set the `full_host` by removing the request path (and
- # anything after it) from the request URI.
- @omniauth_config_full_host = OmniAuth.config.full_host
- OmniAuth.config.full_host = ->(request) { request['REQUEST_URI'].sub(/#{request['REQUEST_PATH']}.*/, '') }
- end
-
- after(:all) do
- OmniAuth.config.full_host = @omniauth_config_full_host
- end
-
- providers.each do |provider|
- context "when the user logs in using the #{provider} provider" do
- context 'when two-factor authentication is disabled' do
- it 'logs the user in' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid')
-
- expect(current_path).to eq root_path
- end
- end
-
- context 'when two-factor authentication is enabled' do
- it 'logs the user in' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid')
-
- enter_code(user.current_otp)
- expect(current_path).to eq root_path
- end
- end
-
- context 'when "remember me" is checked' do
- context 'when two-factor authentication is disabled' do
- it 'remembers the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: true)
-
- clear_browser_session
-
- visit(root_path)
- expect(current_path).to eq root_path
- end
- end
-
- context 'when two-factor authentication is enabled' do
- it 'remembers the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: true)
- enter_code(user.current_otp)
-
- clear_browser_session
-
- visit(root_path)
- expect(current_path).to eq root_path
- end
- end
- end
-
- context 'when "remember me" is not checked' do
- context 'when two-factor authentication is disabled' do
- it 'does not remember the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: false)
-
- clear_browser_session
-
- visit(root_path)
- expect(current_path).to eq new_user_session_path
- end
- end
-
- context 'when two-factor authentication is enabled' do
- it 'does not remember the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: false)
- enter_code(user.current_otp)
-
- clear_browser_session
-
- visit(root_path)
- expect(current_path).to eq new_user_session_path
- end
- end
- end
- end
- end
-end
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index ac5c14ed427..57dec14b480 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -1,10 +1,12 @@
require 'rails_helper'
-feature 'Create Snippet', :js, feature: true do
+feature 'User creates snippet', :js, feature: true do
include DropzoneHelper
+ let(:user) { create(:user) }
+
before do
- gitlab_sign_in :user
+ sign_in(user)
visit new_snippet_path
end
diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb
new file mode 100644
index 00000000000..162c2c9e730
--- /dev/null
+++ b/spec/features/snippets/user_deletes_snippet_spec.rb
@@ -0,0 +1,19 @@
+require 'rails_helper'
+
+feature 'User deletes snippet', feature: true do
+ let(:user) { create(:user) }
+ let(:content) { 'puts "test"' }
+ let(:snippet) { create(:personal_snippet, :public, content: content, author: user) }
+
+ before do
+ sign_in(user)
+
+ visit snippet_path(snippet)
+ end
+
+ it 'deletes the snippet' do
+ first(:link, 'Delete').click
+
+ expect(page).not_to have_content(snippet.title)
+ end
+end
diff --git a/spec/features/snippets/edit_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index 860e1b156d6..cff64423873 100644
--- a/spec/features/snippets/edit_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Edit Snippet', :js, feature: true do
+feature 'User edits snippet', :js, feature: true do
include DropzoneHelper
let(:file_name) { 'test.rb' }
@@ -10,7 +10,7 @@ feature 'Edit Snippet', :js, feature: true do
let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
before do
- gitlab_sign_in(user)
+ sign_in(user)
visit edit_snippet_path(snippet)
wait_for_requests
@@ -27,7 +27,7 @@ feature 'Edit Snippet', :js, feature: true do
it 'updates the snippet with files attached' do
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
- expect(page.find_field("personal_snippet_description").value).to have_content('banana_sample')
+ expect(page.find_field('personal_snippet_description').value).to have_content('banana_sample')
click_button('Save changes')
wait_for_requests
@@ -35,4 +35,24 @@ feature 'Edit Snippet', :js, feature: true do
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
expect(link).to match(%r{/uploads/personal_snippet/#{snippet.id}/\h{32}/banana_sample\.gif\z})
end
+
+ it 'updates the snippet to make it internal' do
+ choose 'Internal'
+
+ click_button 'Save changes'
+ wait_for_requests
+
+ expect(page).to have_no_xpath("//i[@class='fa fa-lock']")
+ expect(page).to have_xpath("//i[@class='fa fa-shield']")
+ end
+
+ it 'updates the snippet to make it public' do
+ choose 'Public'
+
+ click_button 'Save changes'
+ wait_for_requests
+
+ expect(page).to have_no_xpath("//i[@class='fa fa-lock']")
+ expect(page).to have_xpath("//i[@class='fa fa-globe']")
+ end
end
diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js
index 6e855530b21..f2c6ec24dd7 100644
--- a/spec/javascripts/environments/environments_store_spec.js
+++ b/spec/javascripts/environments/environments_store_spec.js
@@ -86,6 +86,16 @@ describe('Store', () => {
store.toggleFolder(store.state.environments[1]);
expect(store.state.environments[1].isOpen).toEqual(false);
});
+
+ it('should keep folder open when environments are updated', () => {
+ store.storeEnvironments(serverData);
+
+ store.toggleFolder(store.state.environments[1]);
+ expect(store.state.environments[1].isOpen).toEqual(true);
+
+ store.storeEnvironments(serverData);
+ expect(store.state.environments[1].isOpen).toEqual(true);
+ });
});
describe('setfolderContent', () => {
@@ -97,6 +107,17 @@ describe('Store', () => {
expect(store.state.environments[1].children.length).toEqual(serverData.length);
expect(store.state.environments[1].children[0].isChildren).toEqual(true);
});
+
+ it('should keep folder content when environments are updated', () => {
+ store.storeEnvironments(serverData);
+
+ store.setfolderContent(store.state.environments[1], serverData);
+
+ expect(store.state.environments[1].children.length).toEqual(serverData.length);
+ // poll
+ store.storeEnvironments(serverData);
+ expect(store.state.environments[1].children.length).toEqual(serverData.length);
+ });
});
describe('store pagination', () => {
diff --git a/spec/javascripts/fixtures/oauth_remember_me.html.haml b/spec/javascripts/fixtures/oauth_remember_me.html.haml
deleted file mode 100644
index 7886e995e57..00000000000
--- a/spec/javascripts/fixtures/oauth_remember_me.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-#oauth-container
- %input#remember_me{ type: "checkbox" }
-
- %a.oauth-login.twitter{ href: "http://example.com/" }
- %a.oauth-login.github{ href: "http://example.com/" }
diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js
deleted file mode 100644
index f90e0093d25..00000000000
--- a/spec/javascripts/oauth_remember_me_spec.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import OAuthRememberMe from '~/oauth_remember_me';
-
-describe('OAuthRememberMe', () => {
- preloadFixtures('static/oauth_remember_me.html.raw');
-
- beforeEach(() => {
- loadFixtures('static/oauth_remember_me.html.raw');
-
- new OAuthRememberMe({ container: $('#oauth-container') }).bindEvents();
- });
-
- it('adds the "remember_me" query parameter to all OAuth login buttons', () => {
- $('#oauth-container #remember_me').click();
-
- expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/?remember_me=1');
- expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/?remember_me=1');
- });
-
- it('removes the "remember_me" query parameter from all OAuth login buttons', () => {
- $('#oauth-container #remember_me').click();
- $('#oauth-container #remember_me').click();
-
- expect($('#oauth-container .oauth-login.twitter').attr('href')).toBe('http://example.com/');
- expect($('#oauth-container .oauth-login.github').attr('href')).toBe('http://example.com/');
- });
-});
diff --git a/spec/support/capybara_helpers.rb b/spec/support/capybara_helpers.rb
index 3eb7bea3227..b57a3493aff 100644
--- a/spec/support/capybara_helpers.rb
+++ b/spec/support/capybara_helpers.rb
@@ -35,11 +35,6 @@ module CapybaraHelpers
visit 'about:blank'
visit url
end
-
- # Simulate a browser restart by clearing the session cookie.
- def clear_browser_session
- page.driver.remove_cookie('_gitlab_session')
- end
end
RSpec.configure do |config|
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index 99e7806353d..4c88958264b 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -62,16 +62,6 @@ module LoginHelpers
Thread.current[:current_user] = user
end
- def login_via(provider, user, uid, remember_me: false)
- mock_auth_hash(provider, uid, user.email)
- visit new_user_session_path
- expect(page).to have_content('Sign in with')
-
- check 'Remember Me' if remember_me
-
- click_link "oauth-login-#{provider}"
- end
-
def mock_auth_hash(provider, uid, email)
# The mock_auth configuration allows you to set per-provider (or default)
# authentication hashes to return during integration testing.
@@ -118,7 +108,6 @@ module LoginHelpers
end
allow(Gitlab::OAuth::Provider).to receive_messages(providers: [:saml], config_for: mock_saml_config)
stub_omniauth_setting(messages)
- allow_any_instance_of(Object).to receive(:user_saml_omniauth_authorize_path).and_return('/users/auth/saml')
- allow_any_instance_of(Object).to receive(:omniauth_authorize_path).with(:user, "saml").and_return('/users/auth/saml')
+ expect_any_instance_of(Object).to receive(:omniauth_authorize_path).with(:user, "saml").and_return('/users/auth/saml')
end
end