Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 2518-saved-configuration-for-issue-board
This commit is contained in:
commit
d5baa33f7a
485 changed files with 4373 additions and 3151 deletions
|
@ -1,4 +1,4 @@
|
|||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-phantomjs-2.1-node-8.x-yarn-1.0-postgresql-9.6"
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-62.0-node-8.x-yarn-1.2-postgresql-9.6"
|
||||
|
||||
.default-cache: &default-cache
|
||||
key: "ruby-235-with-yarn"
|
||||
|
@ -23,7 +23,6 @@ variables:
|
|||
SIMPLECOV: "true"
|
||||
GIT_DEPTH: "20"
|
||||
GIT_SUBMODULE_STRATEGY: "none"
|
||||
PHANTOMJS_VERSION: "2.1.1"
|
||||
GET_SOURCES_ATTEMPTS: "3"
|
||||
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
|
||||
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-master.json
|
||||
|
@ -455,7 +454,7 @@ db:migrate:reset-mysql:
|
|||
variables:
|
||||
SETUP_DB: "false"
|
||||
script:
|
||||
- git fetch origin v9.3.0
|
||||
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0
|
||||
- git checkout -f FETCH_HEAD
|
||||
- bundle install $BUNDLE_INSTALL_FLAGS
|
||||
- cp config/gitlab.yml.example config/gitlab.yml
|
||||
|
@ -551,7 +550,6 @@ karma:
|
|||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.5-golang-1.8-git-2.13-chrome-61.0-node-8.x-yarn-1.0-postgresql-9.6"
|
||||
stage: test
|
||||
variables:
|
||||
BABEL_ENV: "coverage"
|
||||
|
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
|||
7.5
|
||||
9.0.0
|
||||
|
|
|
@ -112,7 +112,7 @@ linters:
|
|||
|
||||
# Reports when you define the same selector twice in a single sheet.
|
||||
MergeableSelector:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
# Functions, mixins, variables, and placeholders should be declared
|
||||
# with all lowercase letters and hyphens instead of underscores.
|
||||
|
|
42
CHANGELOG.md
42
CHANGELOG.md
|
@ -2,6 +2,30 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 10.1.1 (2017-10-31)
|
||||
|
||||
- [FIXED] Auto Devops kubernetes default namespace is now correctly built out of gitlab project group-name. !14642 (Mircea Danila Dumitrescu)
|
||||
- [FIXED] Forbid the usage of `Redis#keys`. !14889
|
||||
- [FIXED] Make the circuitbreaker more robust by adding higher thresholds, and multiple access attempts. !14933
|
||||
- [FIXED] Only cache last push event for existing projects when pushing to a fork. !14989
|
||||
- [FIXED] Fix bug preventing secondary emails from being confirmed. !15010
|
||||
- [FIXED] Fix broken wiki pages that link to a wiki file. !15019
|
||||
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
|
||||
- [FIXED] Fix bitbucket login. !15051
|
||||
- [FIXED] Update gitaly in Gitlab 10.1 to 0.43.1 for temp file cleanup. !15055
|
||||
- [FIXED] Use the correct visibility attribute for projects in system hooks. !15065
|
||||
- [FIXED] Normalize LDAP DN when looking up identity.
|
||||
- [FIXED] Adds callback functions for initial request in clusters page.
|
||||
- [FIXED] Fix missing Import/Export issue assignees.
|
||||
- [FIXED] Allow boards as top level route.
|
||||
- [FIXED] Fix widget of locked merge requests not being presented.
|
||||
- [FIXED] Fix editing issue description in mobile view.
|
||||
- [FIXED] Fix deletion of container registry or images returning an error.
|
||||
- [FIXED] Fix the writing of invalid environment refs.
|
||||
- [CHANGED] Store circuitbreaker settings in the database instead of config. !14842
|
||||
- [CHANGED] Update default disabled merge request widget message to reflect a general failure. !14960
|
||||
- [PERFORMANCE] Stop merge requests with thousands of commits from timing out. !15063
|
||||
|
||||
## 10.1.0 (2017-10-22)
|
||||
|
||||
- [SECURITY] Use a timeout on certain git operations. !14872
|
||||
|
@ -194,6 +218,24 @@ entry.
|
|||
- creation of keys moved to services. !13331 (haseebeqx)
|
||||
- Add username as GL_USERNAME in hooks.
|
||||
|
||||
## 10.0.5 (2017-11-03)
|
||||
|
||||
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
|
||||
- [FIXED] Fix `rake gitlab:incoming_email:check` and make it report the actual error. !14423
|
||||
- [FIXED] Does not check if an invariant hashed storage path exists on disk when renaming projects. !14428
|
||||
- [FIXED] Fix bottom spacing for dropdowns that open upwards. !14535
|
||||
- [FIXED] Fix the project import with issues and milestones. !14657
|
||||
- [FIXED] Fix broken Y-axis scaling in some Prometheus graphs. !14693
|
||||
- [FIXED] Fixed duplicate notifications when added multiple labels on an issue. !14798
|
||||
- [FIXED] Don't rename paths that were freed up when upgrading. !15029
|
||||
- [FIXED] Fixed issue/merge request breadcrumb titles not having links.
|
||||
- [FIXED] Fix application setting to cache nil object.
|
||||
- [FIXED] Fix missing Import/Export issue assignees.
|
||||
- [FIXED] Allow boards as top level route.
|
||||
- [FIXED] Fixed milestone breadcrumb links.
|
||||
- [FIXED] Fixed merge request widget merged & closed date tooltip text.
|
||||
- [FIXED] fix merge request widget status icon for failed CI.
|
||||
|
||||
## 10.0.4 (2017-10-16)
|
||||
|
||||
- [SECURITY] Move project repositories between namespaces when renaming users.
|
||||
|
|
|
@ -104,8 +104,7 @@ the remaining issues on the GitHub issue tracker.
|
|||
|
||||
## I want to contribute!
|
||||
|
||||
If you want to contribute to GitLab, but are not sure where to start,
|
||||
look for [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight].
|
||||
If you want to contribute to GitLab, [issues with the label `Accepting Merge Requests` and small weight][accepting-mrs-weight] is a great place to start. Issues with a lower weight (1 or 2) are deemed suitable for beginners.
|
||||
These issues will be of reasonable size and challenge, for anyone to start
|
||||
contributing to GitLab.
|
||||
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -324,9 +324,9 @@ group :development, :test do
|
|||
# Generate Fake data
|
||||
gem 'ffaker', '~> 2.4'
|
||||
|
||||
gem 'capybara', '~> 2.15.0'
|
||||
gem 'capybara', '~> 2.15'
|
||||
gem 'capybara-screenshot', '~> 1.0.0'
|
||||
gem 'poltergeist', '~> 1.9.0'
|
||||
gem 'selenium-webdriver', '~> 3.5'
|
||||
|
||||
gem 'spring', '~> 2.0.0'
|
||||
gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -113,12 +113,13 @@ GEM
|
|||
mime-types (>= 1.16)
|
||||
cause (0.1)
|
||||
charlock_holmes (0.7.5)
|
||||
childprocess (0.7.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chronic (0.10.2)
|
||||
chronic_duration (0.10.6)
|
||||
numerizer (~> 0.1.1)
|
||||
chunky_png (1.3.5)
|
||||
citrus (3.0.2)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.1)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
|
@ -604,11 +605,6 @@ GEM
|
|||
pg (0.18.4)
|
||||
po_to_json (1.0.1)
|
||||
json (>= 1.6.0)
|
||||
poltergeist (1.9.0)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
posix-spawn (0.3.13)
|
||||
powerpack (0.1.1)
|
||||
premailer (1.10.4)
|
||||
|
@ -818,6 +814,9 @@ GEM
|
|||
activesupport (>= 3.1)
|
||||
select2-rails (3.5.9.3)
|
||||
thor (~> 0.14)
|
||||
selenium-webdriver (3.5.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.0)
|
||||
sentry-raven (2.5.3)
|
||||
faraday (>= 0.7.6, < 1.0)
|
||||
settingslogic (2.0.9)
|
||||
|
@ -949,9 +948,6 @@ GEM
|
|||
hashdiff
|
||||
webpack-rails (0.9.10)
|
||||
railties (>= 3.2.0)
|
||||
websocket-driver (0.6.3)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
wikicloth (0.8.1)
|
||||
builder
|
||||
expression_parser
|
||||
|
@ -988,7 +984,7 @@ DEPENDENCIES
|
|||
browser (~> 2.2)
|
||||
bullet (~> 5.5.0)
|
||||
bundler-audit (~> 0.5.0)
|
||||
capybara (~> 2.15.0)
|
||||
capybara (~> 2.15)
|
||||
capybara-screenshot (~> 1.0.0)
|
||||
carrierwave (~> 1.2)
|
||||
charlock_holmes (~> 0.7.5)
|
||||
|
@ -1104,7 +1100,6 @@ DEPENDENCIES
|
|||
peek-redis (~> 1.2.0)
|
||||
peek-sidekiq (~> 1.0.3)
|
||||
pg (~> 0.18.2)
|
||||
poltergeist (~> 1.9.0)
|
||||
premailer-rails (~> 1.9.7)
|
||||
prometheus-client-mmap (~> 0.7.0.beta18)
|
||||
pry-byebug (~> 3.4.1)
|
||||
|
@ -1150,6 +1145,7 @@ DEPENDENCIES
|
|||
scss_lint (~> 0.54.0)
|
||||
seed-fu (~> 2.3.5)
|
||||
select2-rails (~> 3.5.9)
|
||||
selenium-webdriver (~> 3.5)
|
||||
sentry-raven (~> 2.5.3)
|
||||
settingslogic (~> 2.0.9)
|
||||
sham_rack (~> 1.3.6)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* globals Flash */
|
||||
import Visibility from 'visibilityjs';
|
||||
import axios from 'axios';
|
||||
import setAxiosCsrfToken from './lib/utils/axios_utils';
|
||||
import Poll from './lib/utils/poll';
|
||||
import { s__ } from './locale';
|
||||
import initSettingsPanels from './settings_panels';
|
||||
|
@ -17,6 +18,7 @@ import Flash from './flash';
|
|||
class ClusterService {
|
||||
constructor(options = {}) {
|
||||
this.options = options;
|
||||
setAxiosCsrfToken();
|
||||
}
|
||||
fetchData() {
|
||||
return axios.get(this.options.endpoint);
|
||||
|
|
|
@ -16,7 +16,7 @@ import CILintEditor from './ci_lint_editor';
|
|||
import groupsSelect from './groups_select';
|
||||
/* global Search */
|
||||
/* global Admin */
|
||||
/* global NamespaceSelects */
|
||||
import NamespaceSelect from './namespace_select';
|
||||
/* global NewCommitForm */
|
||||
/* global NewBranchForm */
|
||||
/* global Project */
|
||||
|
@ -575,7 +575,8 @@ import Diff from './diff';
|
|||
new UsersSelect();
|
||||
break;
|
||||
case 'projects':
|
||||
new NamespaceSelects();
|
||||
document.querySelectorAll('.js-namespace-select')
|
||||
.forEach(dropdown => new NamespaceSelect({ dropdown }));
|
||||
break;
|
||||
case 'labels':
|
||||
switch (path[2]) {
|
||||
|
|
|
@ -79,8 +79,6 @@ const Filter = {
|
|||
|
||||
this.hook.trigger.addEventListener('keydown.dl', this.eventWrapper.debounceKeydown);
|
||||
this.hook.trigger.addEventListener('mousedown.dl', this.eventWrapper.debounceKeydown);
|
||||
|
||||
this.debounceKeydown({ detail: { hook: this.hook } });
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
|
|
|
@ -30,7 +30,7 @@ const utils = {
|
|||
},
|
||||
|
||||
isDropDownParts(target) {
|
||||
if (!target || target.tagName === 'HTML') return false;
|
||||
if (!target || !target.hasAttribute || target.tagName === 'HTML') return false;
|
||||
return target.hasAttribute(DATA_TRIGGER) || target.hasAttribute(DATA_DROPDOWN);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -119,11 +119,9 @@ export default function dropzoneInput(form) {
|
|||
// removeAllFiles(true) stops uploading files (if any)
|
||||
// and remove them from dropzone files queue.
|
||||
$cancelButton.on('click', (e) => {
|
||||
const target = e.target.closest('.js-main-target-form').querySelector('.div-dropzone');
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
Dropzone.forElement(target).removeAllFiles(true);
|
||||
Dropzone.forElement($formDropzone.get(0)).removeAllFiles(true);
|
||||
});
|
||||
|
||||
// If 'error' event is fired, we store a failed files,
|
||||
|
|
|
@ -421,7 +421,11 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div
|
||||
:class="{ 'js-child-row environment-child-row': model.isChildren, 'folder-row': model.isFolder, 'gl-responsive-table-row': !model.isFolder }"
|
||||
class="gl-responsive-table-row"
|
||||
:class="{
|
||||
'js-child-row environment-child-row': model.isChildren,
|
||||
'folder-row': model.isFolder,
|
||||
}"
|
||||
role="row">
|
||||
<div class="table-section section-10" role="gridcell">
|
||||
<div
|
||||
|
@ -495,15 +499,16 @@ export default {
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-25" role="gridcell">
|
||||
<div
|
||||
v-if="!model.isFolder"
|
||||
class="table-section section-25" role="gridcell">
|
||||
<div
|
||||
v-if="!model.isFolder"
|
||||
role="rowheader"
|
||||
class="table-mobile-header">
|
||||
Commit
|
||||
</div>
|
||||
<div
|
||||
v-if="!model.isFolder && hasLastDeploymentKey"
|
||||
v-if="hasLastDeploymentKey"
|
||||
class="js-commit-component table-mobile-content">
|
||||
<commit-component
|
||||
:tag="commitTag"
|
||||
|
@ -514,21 +519,22 @@ export default {
|
|||
:author="commitAuthor"/>
|
||||
</div>
|
||||
<div
|
||||
v-if="!model.isFolder && !hasLastDeploymentKey"
|
||||
v-if="!hasLastDeploymentKey"
|
||||
class="commit-title table-mobile-content">
|
||||
No deployments yet
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-section section-10" role="gridcell">
|
||||
<div
|
||||
v-if="!model.isFolder"
|
||||
class="table-section section-10" role="gridcell">
|
||||
<div
|
||||
v-if="!model.isFolder"
|
||||
role="rowheader"
|
||||
class="table-mobile-header">
|
||||
Updated
|
||||
</div>
|
||||
<span
|
||||
v-if="!model.isFolder && canShowDate"
|
||||
v-if="canShowDate"
|
||||
class="environment-created-date-timeago table-mobile-content">
|
||||
{{createdDate}}
|
||||
</span>
|
||||
|
|
|
@ -4,6 +4,7 @@ import _ from 'underscore';
|
|||
import d3 from 'd3';
|
||||
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
|
||||
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
|
||||
import { n__ } from '../locale';
|
||||
|
||||
export default (function() {
|
||||
function ContributorsStatGraph() {}
|
||||
|
@ -44,7 +45,7 @@ export default (function() {
|
|||
commits = $('<span/>', {
|
||||
"class": 'graph-author-commits-count'
|
||||
});
|
||||
commits.text(author.commits + " commits");
|
||||
commits.text(n__('%d commit', '%d commits', author.commits));
|
||||
return $('<span/>').append(commits);
|
||||
};
|
||||
|
||||
|
|
6
app/assets/javascripts/lib/utils/axios_utils.js
Normal file
6
app/assets/javascripts/lib/utils/axios_utils.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import axios from 'axios';
|
||||
import csrf from './csrf';
|
||||
|
||||
export default function setAxiosCsrfToken() {
|
||||
axios.defaults.headers.common[csrf.headerKey] = csrf.token;
|
||||
}
|
|
@ -1,85 +1,57 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, vars-on-top, one-var-declaration-per-line, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, no-param-reassign, no-cond-assign, max-len */
|
||||
/* eslint-disable func-names, space-before-function-paren, no-var, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */
|
||||
import Api from './api';
|
||||
import './lib/utils/url_utility';
|
||||
|
||||
(function() {
|
||||
window.NamespaceSelect = (function() {
|
||||
function NamespaceSelect(opts) {
|
||||
this.onSelectItem = this.onSelectItem.bind(this);
|
||||
var fieldName, showAny;
|
||||
this.dropdown = opts.dropdown;
|
||||
showAny = true;
|
||||
fieldName = 'namespace_id';
|
||||
if (this.dropdown.attr('data-field-name')) {
|
||||
fieldName = this.dropdown.data('fieldName');
|
||||
}
|
||||
if (this.dropdown.attr('data-show-any')) {
|
||||
showAny = this.dropdown.data('showAny');
|
||||
}
|
||||
this.dropdown.glDropdown({
|
||||
filterable: true,
|
||||
selectable: true,
|
||||
filterRemote: true,
|
||||
search: {
|
||||
fields: ['path']
|
||||
},
|
||||
fieldName: fieldName,
|
||||
toggleLabel: function(selected) {
|
||||
if (selected.id == null) {
|
||||
return selected.text;
|
||||
} else {
|
||||
return selected.kind + ": " + selected.full_path;
|
||||
export default class NamespaceSelect {
|
||||
constructor(opts) {
|
||||
const isFilter = opts.dropdown.dataset.isFilter === 'true';
|
||||
const fieldName = opts.dropdown.dataset.fieldName || 'namespace_id';
|
||||
|
||||
$(opts.dropdown).glDropdown({
|
||||
filterable: true,
|
||||
selectable: true,
|
||||
filterRemote: true,
|
||||
search: {
|
||||
fields: ['path']
|
||||
},
|
||||
fieldName: fieldName,
|
||||
toggleLabel: function(selected) {
|
||||
if (selected.id == null) {
|
||||
return selected.text;
|
||||
} else {
|
||||
return selected.kind + ": " + selected.full_path;
|
||||
}
|
||||
},
|
||||
data: function(term, dataCallback) {
|
||||
return Api.namespaces(term, function(namespaces) {
|
||||
if (isFilter) {
|
||||
const anyNamespace = {
|
||||
text: 'Any namespace',
|
||||
id: null
|
||||
};
|
||||
namespaces.unshift(anyNamespace);
|
||||
namespaces.splice(1, 0, 'divider');
|
||||
}
|
||||
},
|
||||
data: function(term, dataCallback) {
|
||||
return Api.namespaces(term, function(namespaces) {
|
||||
var anyNamespace;
|
||||
if (showAny) {
|
||||
anyNamespace = {
|
||||
text: 'Any namespace',
|
||||
id: null
|
||||
};
|
||||
namespaces.unshift(anyNamespace);
|
||||
namespaces.splice(1, 0, 'divider');
|
||||
}
|
||||
return dataCallback(namespaces);
|
||||
});
|
||||
},
|
||||
text: function(namespace) {
|
||||
if (namespace.id == null) {
|
||||
return namespace.text;
|
||||
} else {
|
||||
return namespace.kind + ": " + namespace.full_path;
|
||||
}
|
||||
},
|
||||
renderRow: this.renderRow,
|
||||
clicked: this.onSelectItem
|
||||
});
|
||||
}
|
||||
|
||||
NamespaceSelect.prototype.onSelectItem = function(options) {
|
||||
const { e } = options;
|
||||
return e.preventDefault();
|
||||
};
|
||||
|
||||
return NamespaceSelect;
|
||||
})();
|
||||
|
||||
window.NamespaceSelects = (function() {
|
||||
function NamespaceSelects(opts) {
|
||||
var ref;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-namespace-select');
|
||||
this.$dropdowns.each(function(i, dropdown) {
|
||||
var $dropdown;
|
||||
$dropdown = $(dropdown);
|
||||
return new window.NamespaceSelect({
|
||||
dropdown: $dropdown
|
||||
return dataCallback(namespaces);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return NamespaceSelects;
|
||||
})();
|
||||
}).call(window);
|
||||
},
|
||||
text: function(namespace) {
|
||||
if (namespace.id == null) {
|
||||
return namespace.text;
|
||||
} else {
|
||||
return namespace.kind + ": " + namespace.full_path;
|
||||
}
|
||||
},
|
||||
renderRow: this.renderRow,
|
||||
clicked(options) {
|
||||
if (!isFilter) {
|
||||
const { e } = options;
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
url(namespace) {
|
||||
return gl.utils.mergeUrlParams({ [fieldName]: namespace.id }, window.location.href);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,9 @@
|
|||
// we need to do this to prevent noteForm inconsistent content warning
|
||||
// this is something we intentionally do so we need to recover the content
|
||||
this.note.note = noteText;
|
||||
this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better
|
||||
if (this.$refs.noteBody) {
|
||||
this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import getActionIcon from '../../../vue_shared/ci_action_icons';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
import icon from '../../../vue_shared/components/icon.vue';
|
||||
|
||||
/**
|
||||
* Renders either a cancel, retry or play icon pointing to the given path.
|
||||
|
@ -29,17 +29,18 @@
|
|||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
|
||||
computed: {
|
||||
actionIconSvg() {
|
||||
return getActionIcon(this.actionIcon);
|
||||
},
|
||||
|
||||
cssClass() {
|
||||
return `js-${gl.text.dasherize(this.actionIcon)}`;
|
||||
const actionIconDash = gl.text.dasherize(this.actionIcon);
|
||||
return `${actionIconDash} js-icon-${actionIconDash}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -50,14 +51,9 @@
|
|||
:data-method="actionMethod"
|
||||
:title="tooltipText"
|
||||
:href="link"
|
||||
class="ci-action-icon-container"
|
||||
class="ci-action-icon-container ci-action-icon-wrapper"
|
||||
:class="cssClass"
|
||||
data-container="body">
|
||||
|
||||
<i
|
||||
class="ci-action-icon-wrapper"
|
||||
:class="cssClass"
|
||||
v-html="actionIconSvg"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<icon :name="actionIcon"/>
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import getActionIcon from '../../../vue_shared/ci_action_icons';
|
||||
import icon from '../../../vue_shared/components/icon.vue';
|
||||
import tooltip from '../../../vue_shared/directives/tooltip';
|
||||
|
||||
/**
|
||||
|
@ -29,14 +29,12 @@
|
|||
},
|
||||
},
|
||||
|
||||
directives: {
|
||||
tooltip,
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
|
||||
computed: {
|
||||
actionIconSvg() {
|
||||
return getActionIcon(this.actionIcon);
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -49,7 +47,7 @@
|
|||
rel="nofollow"
|
||||
class="ci-action-icon-wrapper js-ci-status-icon"
|
||||
data-container="body"
|
||||
v-html="actionIconSvg"
|
||||
aria-label="Job's action">
|
||||
<icon :name="actionIcon"/>
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
* "group": "success",
|
||||
* "details_path": "/root/ci-mock/builds/4256",
|
||||
* "action": {
|
||||
* "icon": "icon_action_retry",
|
||||
* "icon": "retry",
|
||||
* "title": "Retry",
|
||||
* "path": "/root/ci-mock/builds/4256/retry",
|
||||
* "method": "post"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
* "group": "success",
|
||||
* "details_path": "/root/ci-mock/builds/4256",
|
||||
* "action": {
|
||||
* "icon": "icon_action_retry",
|
||||
* "icon": "retry",
|
||||
* "title": "Retry",
|
||||
* "path": "/root/ci-mock/builds/4256/retry",
|
||||
* "method": "post"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
import Flash from '../../flash';
|
||||
import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
|
||||
import icon from '../../vue_shared/components/icon.vue';
|
||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||
import tooltip from '../../vue_shared/directives/tooltip';
|
||||
|
||||
|
@ -45,6 +45,7 @@ export default {
|
|||
|
||||
components: {
|
||||
loadingIcon,
|
||||
icon,
|
||||
},
|
||||
|
||||
updated() {
|
||||
|
@ -122,8 +123,8 @@ export default {
|
|||
return `ci-status-icon-${this.stage.status.group}`;
|
||||
},
|
||||
|
||||
svgIcon() {
|
||||
return borderlessStatusIconEntityMap[this.stage.status.icon];
|
||||
borderlessIcon() {
|
||||
return `${this.stage.status.icon}_borderless`;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -145,9 +146,10 @@ export default {
|
|||
aria-expanded="false">
|
||||
|
||||
<span
|
||||
v-html="svgIcon"
|
||||
aria-hidden="true"
|
||||
:aria-label="stage.title">
|
||||
<icon
|
||||
:name="borderlessIcon"/>
|
||||
</span>
|
||||
|
||||
<i
|
||||
|
|
|
@ -98,7 +98,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
|
|||
@toggle="toggleOpen"
|
||||
@submit="onSubmit">
|
||||
|
||||
<template slot="body" scope="props">
|
||||
<template slot="body" slot-scope="props">
|
||||
<p v-html="props.text"></p>
|
||||
|
||||
<form
|
||||
|
|
|
@ -27,6 +27,8 @@ export default {
|
|||
'changeFileContent',
|
||||
]),
|
||||
initMonaco() {
|
||||
if (this.shouldHideEditor) return;
|
||||
|
||||
if (this.monacoInstance) {
|
||||
this.monacoInstance.setModel(null);
|
||||
}
|
||||
|
@ -94,8 +96,12 @@ export default {
|
|||
<template>
|
||||
<div
|
||||
id="ide"
|
||||
v-if='!shouldHideEditor'
|
||||
class="blob-viewer-container blob-editor-container"
|
||||
>
|
||||
<div
|
||||
v-if="shouldHideEditor"
|
||||
v-html="activeFile.html"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,34 +1,26 @@
|
|||
function expandSectionParent($section, $content) {
|
||||
$section.addClass('expanded');
|
||||
$content.off('animationend.expandSectionParent');
|
||||
}
|
||||
|
||||
function expandSection($section) {
|
||||
$section.find('.js-settings-toggle').text('Collapse');
|
||||
|
||||
const $content = $section.find('.settings-content');
|
||||
$content.addClass('expanded').off('scroll.expandSection').scrollTop(0);
|
||||
|
||||
if ($content.hasClass('no-animate')) {
|
||||
expandSectionParent($section, $content);
|
||||
} else {
|
||||
$content.on('animationend.expandSectionParent', () => expandSectionParent($section, $content));
|
||||
$section.find('.settings-content').off('scroll.expandSection').scrollTop(0);
|
||||
$section.addClass('expanded');
|
||||
if (!$section.hasClass('no-animate')) {
|
||||
$section.addClass('animating')
|
||||
.one('animationend.animateSection', () => $section.removeClass('animating'));
|
||||
}
|
||||
}
|
||||
|
||||
function closeSection($section) {
|
||||
$section.find('.js-settings-toggle').text('Expand');
|
||||
|
||||
const $content = $section.find('.settings-content');
|
||||
$content.removeClass('expanded').on('scroll.expandSection', () => expandSection($section));
|
||||
|
||||
$section.find('.settings-content').on('scroll.expandSection', () => expandSection($section));
|
||||
$section.removeClass('expanded');
|
||||
if (!$section.hasClass('no-animate')) {
|
||||
$section.addClass('animating')
|
||||
.one('animationend.animateSection', () => $section.removeClass('animating'));
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSection($section) {
|
||||
const $content = $section.find('.settings-content');
|
||||
$content.removeClass('no-animate');
|
||||
if ($content.hasClass('expanded')) {
|
||||
$section.removeClass('no-animate');
|
||||
if ($section.hasClass('expanded')) {
|
||||
closeSection($section);
|
||||
} else {
|
||||
expandSection($section);
|
||||
|
@ -39,10 +31,19 @@ export default function initSettingsPanels() {
|
|||
$('.settings').each((i, elm) => {
|
||||
const $section = $(elm);
|
||||
$section.on('click.toggleSection', '.js-settings-toggle', () => toggleSection($section));
|
||||
$section.find('.settings-content:not(.expanded)').on('scroll.expandSection', () => expandSection($section));
|
||||
|
||||
if (!$section.hasClass('expanded')) {
|
||||
$section.find('.settings-content').on('scroll.expandSection', () => {
|
||||
$section.removeClass('no-animate');
|
||||
expandSection($section);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (location.hash) {
|
||||
expandSection($(location.hash));
|
||||
const $target = $(location.hash);
|
||||
if ($target.length && $target.hasClass('.settings')) {
|
||||
expandSection($target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'core-js/es6/map';
|
||||
import 'core-js/es6/set';
|
||||
import simulateDrag from './simulate_drag';
|
||||
import simulateInput from './simulate_input';
|
||||
|
||||
// Export to global space for rspec to use
|
||||
window.simulateDrag = simulateDrag;
|
||||
window.simulateInput = simulateInput;
|
||||
|
|
23
app/assets/javascripts/test_utils/simulate_input.js
Normal file
23
app/assets/javascripts/test_utils/simulate_input.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
function triggerEvents(input) {
|
||||
input.dispatchEvent(new Event('keydown'));
|
||||
input.dispatchEvent(new Event('keypress'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
}
|
||||
|
||||
export default function simulateInput(target, text) {
|
||||
const input = document.querySelector(target);
|
||||
if (!input || !input.matches('textarea, input')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (text.length > 0) {
|
||||
Array.prototype.forEach.call(text, (char) => {
|
||||
input.value += char;
|
||||
triggerEvents(input);
|
||||
});
|
||||
} else {
|
||||
triggerEvents(input);
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import PipelineStage from '../../pipelines/components/stage.vue';
|
||||
import ciIcon from '../../vue_shared/components/ci_icon.vue';
|
||||
import { statusIconEntityMap } from '../../vue_shared/ci_status_icons';
|
||||
import icon from '../../vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetPipeline',
|
||||
|
@ -10,6 +10,7 @@ export default {
|
|||
components: {
|
||||
'pipeline-stage': PipelineStage,
|
||||
ciIcon,
|
||||
icon,
|
||||
},
|
||||
computed: {
|
||||
hasPipeline() {
|
||||
|
@ -20,9 +21,6 @@ export default {
|
|||
|
||||
return hasCI && !ciStatus;
|
||||
},
|
||||
svg() {
|
||||
return statusIconEntityMap.icon_status_failed;
|
||||
},
|
||||
stageText() {
|
||||
return this.mr.pipeline.details.stages.length > 1 ? 'stages' : 'stage';
|
||||
},
|
||||
|
@ -38,8 +36,10 @@ export default {
|
|||
<template v-if="hasCIError">
|
||||
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
|
||||
<span
|
||||
v-html="svg"
|
||||
aria-hidden="true"></span>
|
||||
aria-hidden="true">
|
||||
<icon
|
||||
name="status_failed"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="media-body">
|
||||
Could not connect to the CI server. Please check your settings and try again
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import cancelSVG from 'icons/_icon_action_cancel.svg';
|
||||
import retrySVG from 'icons/_icon_action_retry.svg';
|
||||
import playSVG from 'icons/_icon_action_play.svg';
|
||||
import stopSVG from 'icons/_icon_action_stop.svg';
|
||||
|
||||
/**
|
||||
* For the provided action returns the respective SVG
|
||||
*
|
||||
* @param {String} action
|
||||
* @return {SVG|String}
|
||||
*/
|
||||
export default function getActionIcon(action) {
|
||||
const icons = {
|
||||
icon_action_cancel: cancelSVG,
|
||||
icon_action_play: playSVG,
|
||||
icon_action_retry: retrySVG,
|
||||
icon_action_stop: stopSVG,
|
||||
};
|
||||
|
||||
return icons[action] || '';
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import BORDERLESS_CANCELED_SVG from 'icons/_icon_status_canceled_borderless.svg';
|
||||
import BORDERLESS_CREATED_SVG from 'icons/_icon_status_created_borderless.svg';
|
||||
import BORDERLESS_FAILED_SVG from 'icons/_icon_status_failed_borderless.svg';
|
||||
import BORDERLESS_MANUAL_SVG from 'icons/_icon_status_manual_borderless.svg';
|
||||
import BORDERLESS_PENDING_SVG from 'icons/_icon_status_pending_borderless.svg';
|
||||
import BORDERLESS_RUNNING_SVG from 'icons/_icon_status_running_borderless.svg';
|
||||
import BORDERLESS_SKIPPED_SVG from 'icons/_icon_status_skipped_borderless.svg';
|
||||
import BORDERLESS_SUCCESS_SVG from 'icons/_icon_status_success_borderless.svg';
|
||||
import BORDERLESS_WARNING_SVG from 'icons/_icon_status_warning_borderless.svg';
|
||||
|
||||
import CANCELED_SVG from 'icons/_icon_status_canceled.svg';
|
||||
import CREATED_SVG from 'icons/_icon_status_created.svg';
|
||||
import FAILED_SVG from 'icons/_icon_status_failed.svg';
|
||||
import MANUAL_SVG from 'icons/_icon_status_manual.svg';
|
||||
import PENDING_SVG from 'icons/_icon_status_pending.svg';
|
||||
import RUNNING_SVG from 'icons/_icon_status_running.svg';
|
||||
import SKIPPED_SVG from 'icons/_icon_status_skipped.svg';
|
||||
import SUCCESS_SVG from 'icons/_icon_status_success.svg';
|
||||
import WARNING_SVG from 'icons/_icon_status_warning.svg';
|
||||
|
||||
export const borderlessStatusIconEntityMap = {
|
||||
icon_status_canceled: BORDERLESS_CANCELED_SVG,
|
||||
icon_status_created: BORDERLESS_CREATED_SVG,
|
||||
icon_status_failed: BORDERLESS_FAILED_SVG,
|
||||
icon_status_manual: BORDERLESS_MANUAL_SVG,
|
||||
icon_status_pending: BORDERLESS_PENDING_SVG,
|
||||
icon_status_running: BORDERLESS_RUNNING_SVG,
|
||||
icon_status_skipped: BORDERLESS_SKIPPED_SVG,
|
||||
icon_status_success: BORDERLESS_SUCCESS_SVG,
|
||||
icon_status_warning: BORDERLESS_WARNING_SVG,
|
||||
};
|
||||
|
||||
export const statusIconEntityMap = {
|
||||
icon_status_canceled: CANCELED_SVG,
|
||||
icon_status_created: CREATED_SVG,
|
||||
icon_status_failed: FAILED_SVG,
|
||||
icon_status_manual: MANUAL_SVG,
|
||||
icon_status_pending: PENDING_SVG,
|
||||
icon_status_running: RUNNING_SVG,
|
||||
icon_status_skipped: SKIPPED_SVG,
|
||||
icon_status_success: SUCCESS_SVG,
|
||||
icon_status_warning: WARNING_SVG,
|
||||
};
|
|
@ -43,7 +43,6 @@
|
|||
computed: {
|
||||
cssClass() {
|
||||
const className = this.status.group;
|
||||
|
||||
return className ? `ci-status ci-${className}` : 'ci-status';
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { statusIconEntityMap } from '../ci_status_icons';
|
||||
import icon from '../../vue_shared/components/icon.vue';
|
||||
|
||||
/**
|
||||
* Renders CI icon based on API response shared between all places where it is used.
|
||||
|
@ -30,11 +30,11 @@
|
|||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
statusIconSvg() {
|
||||
return statusIconEntityMap[this.status.icon];
|
||||
},
|
||||
components: {
|
||||
icon,
|
||||
},
|
||||
|
||||
computed: {
|
||||
cssClass() {
|
||||
const status = this.status.group;
|
||||
return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
|
||||
|
@ -44,7 +44,8 @@
|
|||
</script>
|
||||
<template>
|
||||
<span
|
||||
:class="cssClass"
|
||||
v-html="statusIconSvg">
|
||||
:class="cssClass">
|
||||
<icon
|
||||
:name="status.icon"/>
|
||||
</span>
|
||||
</template>
|
||||
|
|
52
app/assets/javascripts/vue_shared/components/icon.vue
Normal file
52
app/assets/javascripts/vue_shared/components/icon.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
|
||||
/* This is a re-usable vue component for rendering a svg sprite
|
||||
icon
|
||||
|
||||
Sample configuration:
|
||||
|
||||
<icon
|
||||
:img-src="userAvatarSrc"
|
||||
:img-alt="tooltipText"
|
||||
:tooltip-text="tooltipText"
|
||||
tooltip-placement="top"
|
||||
/>
|
||||
|
||||
*/
|
||||
export default {
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
size: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
|
||||
cssClasses: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
spriteHref() {
|
||||
return `${gon.sprite_icons}#${this.name}`;
|
||||
},
|
||||
iconSizeClass() {
|
||||
return this.size ? `s${this.size}` : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<svg
|
||||
:class="[iconSizeClass, cssClasses]">
|
||||
<use
|
||||
v-bind="{'xlink:href':spriteHref}"/>
|
||||
</svg>
|
||||
</template>
|
|
@ -56,4 +56,4 @@
|
|||
@import "framework/icons";
|
||||
@import "framework/snippets";
|
||||
@import "framework/memory_graph";
|
||||
@import "framework/responsive-tables";
|
||||
@import "framework/responsive_tables";
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
|
||||
&.top-block {
|
||||
border-top: none;
|
||||
|
||||
.container-fluid {
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&.middle-block {
|
||||
|
@ -98,10 +102,6 @@
|
|||
background-color: $white-light;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
&.top-block .container-fluid {
|
||||
background-color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.sub-header-block {
|
||||
|
|
|
@ -12,15 +12,15 @@
|
|||
border-left: 3px solid $border-color;
|
||||
color: $text-color;
|
||||
background: $gray-light;
|
||||
}
|
||||
|
||||
.bs-callout h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.bs-callout p:last-child {
|
||||
margin-bottom: 0;
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Variations */
|
||||
|
|
|
@ -8,32 +8,6 @@
|
|||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
/** COMMON CLASSES **/
|
||||
.prepend-top-0 { margin-top: 0; }
|
||||
.prepend-top-5 { margin-top: 5px; }
|
||||
.prepend-top-10 { margin-top: 10px; }
|
||||
.prepend-top-default { margin-top: $gl-padding !important; }
|
||||
.prepend-top-20 { margin-top: 20px; }
|
||||
.prepend-left-4 { margin-left: 4px; }
|
||||
.prepend-left-5 { margin-left: 5px; }
|
||||
.prepend-left-10 { margin-left: 10px; }
|
||||
.prepend-left-default { margin-left: $gl-padding; }
|
||||
.prepend-left-20 { margin-left: 20px; }
|
||||
.append-right-5 { margin-right: 5px; }
|
||||
.append-right-8 { margin-right: 8px; }
|
||||
.append-right-10 { margin-right: 10px; }
|
||||
.append-right-default { margin-right: $gl-padding; }
|
||||
.append-right-20 { margin-right: 20px; }
|
||||
.append-bottom-0 { margin-bottom: 0; }
|
||||
.append-bottom-5 { margin-bottom: 5px; }
|
||||
.append-bottom-10 { margin-bottom: 10px; }
|
||||
.append-bottom-15 { margin-bottom: 15px; }
|
||||
.append-bottom-20 { margin-bottom: 20px; }
|
||||
.append-bottom-default { margin-bottom: $gl-padding; }
|
||||
.inline { display: inline-block; }
|
||||
.center { text-align: center; }
|
||||
.vertical-align-middle { vertical-align: middle; }
|
||||
|
||||
.underlined-link { text-decoration: underline; }
|
||||
.hint { font-style: italic; color: $hint-color; }
|
||||
.light { color: $common-gray; }
|
||||
|
@ -82,6 +56,14 @@ hr {
|
|||
|
||||
.str-truncated {
|
||||
@include str-truncated;
|
||||
|
||||
&-60 {
|
||||
@include str-truncated(60%);
|
||||
}
|
||||
|
||||
&-100 {
|
||||
@include str-truncated(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.block-truncated {
|
||||
|
@ -107,10 +89,17 @@ hr {
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
table a code {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-right: 3px;
|
||||
table {
|
||||
a code {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
td.permission-x {
|
||||
background: $table-permission-x-bg !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
|
@ -295,13 +284,6 @@ img.emoji {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
table {
|
||||
td.permission-x {
|
||||
background: $table-permission-x-bg !important;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-sign-in {
|
||||
text-shadow: none;
|
||||
|
||||
|
@ -367,10 +349,11 @@ table {
|
|||
|
||||
.dropzone .dz-preview .dz-progress {
|
||||
border-color: $border-color !important;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||
background: $gl-success !important;
|
||||
.dz-upload {
|
||||
background: $gl-success !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.dz-message {
|
||||
|
@ -431,16 +414,6 @@ table {
|
|||
border-radius: $border-radius-default;
|
||||
}
|
||||
|
||||
.str-truncated {
|
||||
&-60 {
|
||||
@include str-truncated(60%);
|
||||
}
|
||||
|
||||
&-100 {
|
||||
@include str-truncated(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
.tooltip-inner {
|
||||
word-wrap: break-word;
|
||||
|
@ -451,3 +424,30 @@ table {
|
|||
pointer-events: none;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/** COMMON CLASSES **/
|
||||
.prepend-top-0 { margin-top: 0; }
|
||||
.prepend-top-5 { margin-top: 5px; }
|
||||
.prepend-top-10 { margin-top: 10px; }
|
||||
.prepend-top-15 { margin-top: 15px; }
|
||||
.prepend-top-default { margin-top: $gl-padding !important; }
|
||||
.prepend-top-20 { margin-top: 20px; }
|
||||
.prepend-left-4 { margin-left: 4px; }
|
||||
.prepend-left-5 { margin-left: 5px; }
|
||||
.prepend-left-10 { margin-left: 10px; }
|
||||
.prepend-left-default { margin-left: $gl-padding; }
|
||||
.prepend-left-20 { margin-left: 20px; }
|
||||
.append-right-5 { margin-right: 5px; }
|
||||
.append-right-8 { margin-right: 8px; }
|
||||
.append-right-10 { margin-right: 10px; }
|
||||
.append-right-default { margin-right: $gl-padding; }
|
||||
.append-right-20 { margin-right: 20px; }
|
||||
.append-bottom-0 { margin-bottom: 0; }
|
||||
.append-bottom-5 { margin-bottom: 5px; }
|
||||
.append-bottom-10 { margin-bottom: 10px; }
|
||||
.append-bottom-15 { margin-bottom: 15px; }
|
||||
.append-bottom-20 { margin-bottom: 20px; }
|
||||
.append-bottom-default { margin-bottom: $gl-padding; }
|
||||
.inline { display: inline-block; }
|
||||
.center { text-align: center; }
|
||||
.vertical-align-middle { vertical-align: middle; }
|
||||
|
|
|
@ -141,15 +141,15 @@
|
|||
svg {
|
||||
fill: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item-name {
|
||||
flex: 1;
|
||||
}
|
||||
.nav-item-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
li.active {
|
||||
> a {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
&.active {
|
||||
> a {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -484,10 +484,7 @@
|
|||
height: calc(100vh - #{$header-height});
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
height: 475px; // Needed for PhantomJS
|
||||
// scss-lint:disable DuplicateProperty
|
||||
height: calc(100vh - 180px);
|
||||
// scss-lint:enable DuplicateProperty
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -728,11 +728,11 @@
|
|||
|
||||
.pika-single.animate-picker.is-bound {
|
||||
@include set-visible;
|
||||
}
|
||||
|
||||
.pika-single.animate-picker.is-bound.is-hidden {
|
||||
@include set-invisible;
|
||||
overflow: hidden;
|
||||
&.is-hidden {
|
||||
@include set-invisible;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dropdown-item-hover {
|
||||
|
@ -939,9 +939,7 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
|
|||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.projects-dropdown-container {
|
||||
.projects-list-frequent-container,
|
||||
.projects-list-search-container, {
|
||||
padding: 8px 0;
|
||||
|
@ -952,11 +950,6 @@ header.header-content .dropdown-menu.projects-dropdown-menu {
|
|||
.projects-list-frequent-container li.section-empty,
|
||||
.projects-list-search-container li.section-empty {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.section-header,
|
||||
.projects-list-frequent-container li.section-empty,
|
||||
.projects-list-search-container li.section-empty {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: $gl-font-size;
|
||||
}
|
||||
|
|
|
@ -165,22 +165,36 @@
|
|||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
td.blame-commit {
|
||||
padding: 5px 10px;
|
||||
min-width: 400px;
|
||||
max-width: 400px;
|
||||
background: $gray-light;
|
||||
border-left: 3px solid;
|
||||
&.blame-commit {
|
||||
padding: 5px 10px;
|
||||
min-width: 400px;
|
||||
max-width: 400px;
|
||||
background: $gray-light;
|
||||
border-left: 3px solid;
|
||||
|
||||
.commit-row-title {
|
||||
display: flex;
|
||||
.commit-row-title {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
flex: 1;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.item-title {
|
||||
flex: 1;
|
||||
margin-right: 0.5em;
|
||||
&.line-numbers {
|
||||
float: none;
|
||||
border-left: 1px solid $blame-line-numbers-border;
|
||||
|
||||
i {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.lines {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,20 +209,6 @@
|
|||
border-left-color: mix($blame-gray, $blame-cyan, $i / 4.0 * 100%);
|
||||
}
|
||||
}
|
||||
|
||||
td.line-numbers {
|
||||
float: none;
|
||||
border-left: 1px solid $blame-line-numbers-border;
|
||||
|
||||
i {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
td.lines {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.logs {
|
||||
|
|
|
@ -268,12 +268,6 @@
|
|||
.filtered-search-box-input-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
// Fix PhantomJS not supporting `flex: 1;` properly.
|
||||
// This is important because it can change the expected `e.target` when clicking things in tests.
|
||||
// See https://gitlab.com/gitlab-org/gitlab-ce/blob/b54acba8b732688c59fe2f38510c469dc86ee499/spec/features/issues/filtered_search/visual_tokens_spec.rb#L61
|
||||
// - With `width: 100%`: `e.target` = `.tokens-container`, https://i.imgur.com/jGq7wbx.png
|
||||
// - Without `width: 100%`: `e.target` = `.filtered-search`, https://i.imgur.com/cNI2CyT.png
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
|
@ -469,10 +463,10 @@
|
|||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-dropdown-item.droplab-item-active .btn {
|
||||
@extend %filter-dropdown-item-btn-hover;
|
||||
&.droplab-item-active .btn {
|
||||
@extend %filter-dropdown-item-btn-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-dropdown-loading {
|
||||
|
|
|
@ -352,7 +352,77 @@
|
|||
|
||||
.header-user .dropdown-menu-nav,
|
||||
.header-new .dropdown-menu-nav {
|
||||
margin-top: $dropdown-vertical-offset;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 4px 8px 0;
|
||||
|
||||
form {
|
||||
height: 32px;
|
||||
border: 0;
|
||||
border-radius: $border-radius-default;
|
||||
transition: border-color ease-in-out 0.15s, background-color ease-in-out 0.15s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.search-input {
|
||||
color: $white-light;
|
||||
background: none;
|
||||
transition: color ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
transition: color ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
.location-badge {
|
||||
font-size: 12px;
|
||||
margin: -4px 4px -4px -4px;
|
||||
line-height: 25px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 2px 0 0 2px;
|
||||
height: 32px;
|
||||
transition: border-color ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
&.search-active {
|
||||
form {
|
||||
background-color: rgba($indigo-200, .3);
|
||||
box-shadow: none;
|
||||
|
||||
.search-input {
|
||||
color: $gl-text-color;
|
||||
transition: color ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: $gl-text-color-tertiary;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
.search-icon,
|
||||
.clear-icon {
|
||||
color: $gl-text-color-tertiary;
|
||||
transition: color ease-in-out 0.15s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.location-badge {
|
||||
background-color: $nav-badge-bg;
|
||||
border-color: $border-color;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
.clear-icon {
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
|
|
|
@ -30,10 +30,10 @@ body {
|
|||
.container {
|
||||
padding-top: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.container .content {
|
||||
margin: 0;
|
||||
.content {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navless-container {
|
||||
|
@ -82,26 +82,26 @@ body {
|
|||
transition: background-color 0.15s, border-color 0.15s;
|
||||
background-color: $orange-500;
|
||||
border-color: $orange-500;
|
||||
}
|
||||
|
||||
.alert-warning + .alert-warning {
|
||||
background-color: $orange-600;
|
||||
border-color: $orange-600;
|
||||
}
|
||||
&:only-of-type {
|
||||
background-color: $orange-500;
|
||||
border-color: $orange-500;
|
||||
}
|
||||
|
||||
.alert-warning + .alert-warning + .alert-warning {
|
||||
background-color: $orange-700;
|
||||
border-color: $orange-700;
|
||||
}
|
||||
+ .alert-warning {
|
||||
background-color: $orange-600;
|
||||
border-color: $orange-600;
|
||||
|
||||
.alert-warning + .alert-warning + .alert-warning + .alert-warning {
|
||||
background-color: $orange-800;
|
||||
border-color: $orange-800;
|
||||
}
|
||||
+ .alert-warning {
|
||||
background-color: $orange-700;
|
||||
border-color: $orange-700;
|
||||
|
||||
.alert-warning:only-of-type {
|
||||
background-color: $orange-500;
|
||||
border-color: $orange-500;
|
||||
+ .alert-warning {
|
||||
background-color: $orange-800;
|
||||
border-color: $orange-800;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -299,40 +299,40 @@ ul.indent-list {
|
|||
}
|
||||
}
|
||||
|
||||
.group-list-tree .avatar-container.content-loading {
|
||||
position: relative;
|
||||
.group-list-tree {
|
||||
.avatar-container.content-loading {
|
||||
position: relative;
|
||||
|
||||
> a,
|
||||
> a .avatar {
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
> a,
|
||||
> a .avatar {
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
> a {
|
||||
padding: 2px;
|
||||
}
|
||||
> a {
|
||||
padding: 2px;
|
||||
|
||||
> a .avatar {
|
||||
border: 2px solid $white-normal;
|
||||
.avatar {
|
||||
border: 2px solid $white-normal;
|
||||
|
||||
&.identicon {
|
||||
line-height: 30px;
|
||||
&.identicon {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border: 2px outset $kdb-border;
|
||||
border-radius: 50%;
|
||||
animation: spin-avatar 3s infinite linear;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
border: 2px outset $kdb-border;
|
||||
border-radius: 50%;
|
||||
animation: spin-avatar 3s infinite linear;
|
||||
}
|
||||
}
|
||||
|
||||
.group-list-tree {
|
||||
.folder-toggle-wrap {
|
||||
float: left;
|
||||
line-height: $list-text-height;
|
||||
|
|
|
@ -173,21 +173,8 @@
|
|||
ul > li {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $screen-xs-max) {
|
||||
.atwho-view-ul {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.atwho-view ul li {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fallback to global style
|
||||
.atwho-view {
|
||||
// TODO: fallback to global style
|
||||
.atwho-view-ul {
|
||||
padding: 8px 1px;
|
||||
|
||||
|
@ -220,3 +207,14 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-width: $screen-xs-max) {
|
||||
.atwho-view-ul {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.atwho-view ul li {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,57 +3,77 @@
|
|||
max-width: #{$max + '%'};
|
||||
}
|
||||
|
||||
.gl-responsive-table-row {
|
||||
margin-top: 10px;
|
||||
border: 1px solid $border-color;
|
||||
.gl-responsive-table-row-layout {
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
padding: 15px 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-bottom: 1px solid $white-normal;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
white-space: nowrap;
|
||||
|
||||
$section-widths: 10 15 20 25 30 40;
|
||||
@each $width in $section-widths {
|
||||
&.section-#{$width} {
|
||||
flex: 0 0 #{$width + '%'};
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
max-width: #{$width + '%'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.table-button-footer) {
|
||||
@media (max-width: $screen-sm-max) {
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
min-height: 62px;
|
||||
|
||||
&:not(:first-of-type) {
|
||||
border-top: 1px solid $white-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.section-wrap {
|
||||
white-space: normal;
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
& > &:not(:first-child) {
|
||||
margin-top: $gl-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-responsive-table-row {
|
||||
@extend .gl-responsive-table-row-layout;
|
||||
margin-top: 10px;
|
||||
border: 1px solid $border-color;
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
margin: 0;
|
||||
padding: $gl-padding 0;
|
||||
border: none;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid $white-normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-responsive-table-row-col-span {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
white-space: nowrap;
|
||||
|
||||
$section-widths: 10 15 20 25 30 40 100;
|
||||
@each $width in $section-widths {
|
||||
&.section-#{$width} {
|
||||
flex: 0 0 #{$width + '%'};
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
max-width: #{$width + '%'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
min-height: 62px;
|
||||
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid $white-normal;
|
||||
}
|
||||
}
|
||||
|
||||
&.section-wrap {
|
||||
white-space: normal;
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
&.section-align-top {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.table-button-footer {
|
||||
@media (min-width: $screen-md-min) {
|
||||
|
@ -61,12 +81,13 @@
|
|||
}
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
background-color: $gray-normal;
|
||||
display: block;
|
||||
align-self: stretch;
|
||||
min-height: 0;
|
||||
background-color: $gray-normal;
|
||||
border-top: 1px solid $border-color;
|
||||
|
||||
.table-action-buttons {
|
||||
padding: 10px 5px;
|
||||
display: flex;
|
||||
|
||||
.btn {
|
||||
|
@ -77,7 +98,14 @@
|
|||
> .external-url,
|
||||
> .btn {
|
||||
flex: 1 1 28px;
|
||||
margin: 0 5px;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-new {
|
|
@ -340,11 +340,64 @@
|
|||
}
|
||||
}
|
||||
|
||||
.project-item-select-holder.btn-group {
|
||||
display: flex;
|
||||
max-width: 350px;
|
||||
overflow: hidden;
|
||||
float: right;
|
||||
.page-with-layout-nav {
|
||||
.right-sidebar {
|
||||
top: ($header-height + 1) * 2;
|
||||
}
|
||||
|
||||
&.page-with-sub-nav {
|
||||
.right-sidebar {
|
||||
top: ($header-height + 1) * 3;
|
||||
|
||||
&.affix {
|
||||
top: $header-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.with-performance-bar .page-with-layout-nav {
|
||||
.right-sidebar {
|
||||
top: ($header-height + 1) * 2 + $performance-bar-height;
|
||||
}
|
||||
|
||||
&.page-with-sub-nav {
|
||||
.right-sidebar {
|
||||
top: ($header-height + 1) * 3 + $performance-bar-height;
|
||||
|
||||
&.affix {
|
||||
top: $header-height + $performance-bar-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.top-area {
|
||||
flex-flow: row wrap;
|
||||
|
||||
.nav-controls {
|
||||
$controls-margin: $btn-xs-side-margin - 2px;
|
||||
flex: 0 0 100%;
|
||||
|
||||
&.controls-flex {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 0 $gl-padding-top;
|
||||
}
|
||||
|
||||
.controls-item,
|
||||
.controls-item-full,
|
||||
.controls-item:last-child {
|
||||
flex: 1 1 35%;
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: $controls-margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-project-item-link {
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -60,22 +60,12 @@
|
|||
border-radius: $border-radius-base;
|
||||
border: 1px solid $dropdown-border-color;
|
||||
min-width: 175px;
|
||||
color: $gl-text-color;
|
||||
z-index: 999;
|
||||
color: $gl-grayish-blue;
|
||||
}
|
||||
|
||||
.select2-drop-mask {
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
.select2-drop.select2-drop-above.select2-drop-active {
|
||||
border-top: 1px solid $dropdown-border-color;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.select2-results li.select2-result-with-children > .select2-result-label {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
color: $gl-text-color;
|
||||
.select2-results .select2-result-label,
|
||||
.select2-more-results {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.select2-container-active {
|
||||
|
@ -144,58 +134,46 @@
|
|||
.select2-drop-auto-width & {
|
||||
padding: 15px 15px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-search input {
|
||||
padding: 2px 25px 2px 5px;
|
||||
background: $white-light image-url('select2.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0 bottom 6px;
|
||||
border: 1px solid $input-border;
|
||||
border-radius: $border-radius-default;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
input {
|
||||
padding: 2px 25px 2px 5px;
|
||||
background: $white-light image-url('select2.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0 bottom 6px;
|
||||
border: 1px solid $input-border;
|
||||
border-radius: $border-radius-default;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
|
||||
&:focus {
|
||||
border-color: $input-border-focus;
|
||||
&:focus {
|
||||
border-color: $input-border-focus;
|
||||
}
|
||||
|
||||
&.select2-active {
|
||||
background-color: $white-light;
|
||||
background-image: image-url('select2-spinner.gif') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 5px center !important;
|
||||
background-size: 16px 16px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-search input.select2-active {
|
||||
background-color: $white-light;
|
||||
background-image: image-url('select2-spinner.gif') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 5px center !important;
|
||||
background-size: 16px 16px !important;
|
||||
.select2-results .select2-no-results,
|
||||
.select2-results .select2-searching,
|
||||
.select2-results .select2-ajax-error,
|
||||
.select2-results .select2-selection-limit {
|
||||
background: $gray-light;
|
||||
display: list-item;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.select2-results {
|
||||
margin: 0;
|
||||
padding: #{$gl-padding / 2} 0;
|
||||
padding: 10px 0;
|
||||
|
||||
.select2-no-results,
|
||||
.select2-searching,
|
||||
.select2-ajax-error,
|
||||
.select2-selection-limit {
|
||||
background: transparent;
|
||||
padding: #{$gl-padding / 2} $gl-padding;
|
||||
}
|
||||
|
||||
.select2-result-label,
|
||||
.select2-more-results {
|
||||
padding: #{$gl-padding / 2} $gl-padding;
|
||||
}
|
||||
|
||||
.select2-highlighted {
|
||||
background: transparent;
|
||||
li.select2-result-with-children > .select2-result-label {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
color: $gl-text-color;
|
||||
|
||||
.select2-result-label {
|
||||
background: $dropdown-item-hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-result {
|
||||
padding: 0 1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,6 +190,8 @@
|
|||
}
|
||||
|
||||
.select2-highlighted {
|
||||
background: $gl-link-color !important;
|
||||
|
||||
.group-result {
|
||||
.group-path {
|
||||
color: $white-light;
|
||||
|
|
|
@ -217,13 +217,31 @@ $white-gc-bg: #eaf2f5;
|
|||
.cp { color: $white-cp; font-weight: $gl-font-weight-bold; }
|
||||
.c1 { color: $white-c1; font-style: italic; }
|
||||
.cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
|
||||
.gd { color: $white-gd; background-color: $white-gd-bg; }
|
||||
.gd .x { color: $white-gd-x; background-color: $white-gd-x-bg; }
|
||||
|
||||
.gd {
|
||||
color: $white-gd;
|
||||
background-color: $white-gd-bg;
|
||||
|
||||
.x {
|
||||
color: $white-gd-x;
|
||||
background-color: $white-gd-x-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.ge { font-style: italic; }
|
||||
.gr { color: $white-gr; }
|
||||
.gh { color: $white-gh; }
|
||||
.gi { color: $white-gi; background-color: $white-gi-bg; }
|
||||
.gi .x { color: $white-gi-x; background-color: $white-gi-x-bg; }
|
||||
|
||||
.gi {
|
||||
color: $white-gi;
|
||||
background-color: $white-gi-bg;
|
||||
|
||||
.x {
|
||||
color: $white-gi-x;
|
||||
background-color: $white-gi-x-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.go { color: $white-go; }
|
||||
.gp { color: $white-gp; }
|
||||
.gs { font-weight: $gl-font-weight-bold; }
|
||||
|
|
|
@ -158,13 +158,31 @@ span.highlight_word {
|
|||
.cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; }
|
||||
.c1 { color: $highlighted-c1; font-style: italic; }
|
||||
.cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
|
||||
.gd { color: $highlighted-gd; background-color: $highlighted-gd-bg; }
|
||||
.gd .x { color: $highlighted-gd; background-color: $highlighted-gd-x-bg; }
|
||||
|
||||
.gd {
|
||||
color: $highlighted-gd;
|
||||
background-color: $highlighted-gd-bg;
|
||||
|
||||
.x {
|
||||
color: $highlighted-gd;
|
||||
background-color: $highlighted-gd-x-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.ge { font-style: italic; }
|
||||
.gr { color: $highlighted-gr; }
|
||||
.gh { color: $highlighted-gh; }
|
||||
.gi { color: $highlighted-gi; background-color: $highlighted-gi-bg; }
|
||||
.gi .x { color: $highlighted-gi; background-color: $highlighted-gi-x-bg; }
|
||||
|
||||
.gi {
|
||||
color: $highlighted-gi;
|
||||
background-color: $highlighted-gi-bg;
|
||||
|
||||
.x {
|
||||
color: $highlighted-gi;
|
||||
background-color: $highlighted-gi-x-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.go { color: $highlighted-go; }
|
||||
.gp { color: $highlighted-gp; }
|
||||
.gs { font-weight: $gl-font-weight-bold; }
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
}
|
||||
|
||||
.boards-list {
|
||||
height: calc(100vh - 152px);
|
||||
height: calc(100vh - 105px);
|
||||
width: 100%;
|
||||
padding-top: 25px;
|
||||
padding-bottom: 25px;
|
||||
|
@ -81,11 +81,12 @@
|
|||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
height: 475px; // Needed for PhantomJS
|
||||
// scss-lint:disable DuplicateProperty
|
||||
height: calc(100vh - 222px);
|
||||
// scss-lint:enable DuplicateProperty
|
||||
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
|
||||
height: calc(100vh - 90px);
|
||||
}
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
height: calc(100vh - 160px);
|
||||
min-height: 475px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,18 +68,18 @@
|
|||
|
||||
&.affix {
|
||||
top: $header-height;
|
||||
}
|
||||
|
||||
// with sidebar
|
||||
&.affix.sidebar-expanded {
|
||||
right: 306px;
|
||||
left: 16px;
|
||||
}
|
||||
// with sidebar
|
||||
&.sidebar-expanded {
|
||||
right: 306px;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
// without sidebar
|
||||
&.affix.sidebar-collapsed {
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
// without sidebar
|
||||
&.sidebar-collapsed {
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
&.affix-top {
|
||||
|
@ -333,8 +333,10 @@
|
|||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
top: 3px;
|
||||
margin-right: 3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,9 +350,10 @@
|
|||
|
||||
svg {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
top: 3px;
|
||||
margin-right: 3px;
|
||||
height: 13px;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -369,7 +372,7 @@
|
|||
.build-job {
|
||||
position: relative;
|
||||
|
||||
.fa-arrow-right {
|
||||
.icon-arrow-right {
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 20px;
|
||||
|
@ -379,7 +382,7 @@
|
|||
&.active {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
|
||||
.fa-arrow-right {
|
||||
.icon-arrow-right {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
@ -392,8 +395,7 @@
|
|||
background-color: $row-hover;
|
||||
}
|
||||
|
||||
.fa-refresh {
|
||||
font-size: 13px;
|
||||
.icon-retry {
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,4 @@
|
|||
.clipboard-addon {
|
||||
background-color: $white-light;
|
||||
}
|
||||
|
||||
.alert-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 136px;
|
||||
height: 136px;
|
||||
}
|
||||
}
|
||||
|
||||
.col-headers {
|
||||
|
@ -155,11 +160,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.landing svg {
|
||||
width: 136px;
|
||||
height: 136px;
|
||||
}
|
||||
|
||||
.fa-spinner {
|
||||
font-size: 28px;
|
||||
position: relative;
|
||||
|
|
|
@ -380,6 +380,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.file-content .diff-file {
|
||||
|
@ -387,10 +391,6 @@
|
|||
border: none;
|
||||
}
|
||||
|
||||
.diff-file .line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.diff-wrap-lines .line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
|
@ -133,12 +133,11 @@
|
|||
}
|
||||
|
||||
.folder-row {
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid $white-normal;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
border-top: 1px solid $white-normal;
|
||||
margin-top: 10px;
|
||||
@media (min-width: $screen-sm-max) {
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,23 +255,6 @@
|
|||
width: 100%;
|
||||
padding: 0;
|
||||
padding-bottom: 100%;
|
||||
}
|
||||
|
||||
.prometheus-svg-container > svg {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
text {
|
||||
fill: $gl-text-color;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.text-metric-bold {
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
|
||||
.label-axis-text {
|
||||
fill: $black;
|
||||
|
@ -287,42 +269,51 @@
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.legend-axis-text {
|
||||
fill: $black;
|
||||
}
|
||||
> svg {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
.tick {
|
||||
> line {
|
||||
stroke: $gray-darker;
|
||||
}
|
||||
|
||||
> text {
|
||||
.label-axis-text,
|
||||
.text-metric-usage {
|
||||
fill: $black;
|
||||
font-weight: $gl-font-weight-normal;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-metric-title {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.y-label-text,
|
||||
.x-label-text {
|
||||
fill: $gray-darkest;
|
||||
}
|
||||
|
||||
.axis-tick {
|
||||
stroke: $gray-darker;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.label-axis-text,
|
||||
.text-metric-usage,
|
||||
.legend-axis-text {
|
||||
font-size: 8px;
|
||||
fill: $black;
|
||||
}
|
||||
|
||||
.tick > text {
|
||||
font-size: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.text-metric-title {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.y-label-text,
|
||||
.x-label-text {
|
||||
fill: $gray-darkest;
|
||||
}
|
||||
|
||||
.axis-tick {
|
||||
stroke: $gray-darker;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-max) {
|
||||
.label-axis-text,
|
||||
.text-metric-usage,
|
||||
.legend-axis-text {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.tick > text {
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,16 @@
|
|||
}
|
||||
|
||||
.right-sidebar {
|
||||
a:not(.btn-retry),
|
||||
position: absolute;
|
||||
top: $header-height;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transition: width .3s;
|
||||
background: $gray-light;
|
||||
z-index: 200;
|
||||
overflow: hidden;
|
||||
|
||||
a,
|
||||
.btn-link {
|
||||
color: inherit;
|
||||
}
|
||||
|
@ -228,17 +237,6 @@
|
|||
.btn-clipboard:hover {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.right-sidebar {
|
||||
position: absolute;
|
||||
top: $header-height;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
transition: width $right-sidebar-transition-duration;
|
||||
background: $gray-light;
|
||||
z-index: 200;
|
||||
overflow: hidden;
|
||||
|
||||
.issuable-sidebar {
|
||||
width: calc(100% + 100px);
|
||||
|
|
|
@ -109,6 +109,30 @@
|
|||
border-top-right-radius: $border-radius-default;
|
||||
border-top-left-radius: $border-radius-default;
|
||||
|
||||
// Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
|
||||
// These styles prevent this from breaking the layout, and only applied when providers are configured.
|
||||
&.custom-provider-tabs {
|
||||
flex-wrap: wrap;
|
||||
|
||||
li {
|
||||
min-width: 85px;
|
||||
flex-basis: auto;
|
||||
|
||||
// This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
|
||||
// We are making somewhat of an assumption about the configuration here: that users do not have more than
|
||||
// 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
|
||||
// of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
|
||||
// above one of the bottom row elements. If you know a better way, please implement it!
|
||||
&:nth-child(n+5) {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
@ -154,32 +178,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long).
|
||||
// These styles prevent this from breaking the layout, and only applied when providers are configured.
|
||||
|
||||
.new-session-tabs.custom-provider-tabs {
|
||||
flex-wrap: wrap;
|
||||
|
||||
li {
|
||||
min-width: 85px;
|
||||
flex-basis: auto;
|
||||
|
||||
// This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen.
|
||||
// We are making somewhat of an assumption about the configuration here: that users do not have more than
|
||||
// 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any
|
||||
// of them. If either condition is false, this will work as expected. If both are true, there may be a missing border
|
||||
// above one of the bottom row elements. If you know a better way, please implement it!
|
||||
&:nth-child(n+5) {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form-control {
|
||||
&:active,
|
||||
&:focus {
|
||||
|
@ -231,35 +229,35 @@
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// Fixes footer container to bottom of viewport
|
||||
.devise-layout-html body {
|
||||
// offset height of fixed header + 1 to avoid scroll
|
||||
height: calc(100% - 51px);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
// Fixes footer container to bottom of viewport
|
||||
body {
|
||||
// offset height of fixed header + 1 to avoid scroll
|
||||
height: calc(100% - 51px);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.page-wrap {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.page-wrap {
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer-container,
|
||||
hr.footer-fixed {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
background: $white-light;
|
||||
}
|
||||
.footer-container,
|
||||
hr.footer-fixed {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
background: $white-light;
|
||||
}
|
||||
|
||||
.navless-container {
|
||||
padding: 65px 15px; // height of footer + bottom padding of email confirmation link
|
||||
.navless-container {
|
||||
padding: 65px 15px; // height of footer + bottom padding of email confirmation link
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
padding: 0 15px 65px;
|
||||
@media (max-width: $screen-xs-max) {
|
||||
padding: 0 15px 65px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,17 @@
|
|||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.existing-title {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-form-control {
|
||||
@include new-style-dropdown;
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
padding-bottom: 5px;
|
||||
margin-left: 0;
|
||||
|
@ -64,12 +72,6 @@
|
|||
line-height: 43px;
|
||||
}
|
||||
|
||||
.member.existing-title {
|
||||
@media (min-width: $screen-sm-min) {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.member-search-form {
|
||||
@include new-style-dropdown;
|
||||
|
||||
|
@ -281,7 +283,3 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-form-control {
|
||||
@include new-style-dropdown;
|
||||
}
|
||||
|
|
|
@ -156,6 +156,10 @@
|
|||
&.media > *:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.approve-btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-pipeline-graph {
|
||||
|
@ -165,8 +169,9 @@
|
|||
z-index: 300;
|
||||
}
|
||||
|
||||
.ci-action-icon-wrapper {
|
||||
line-height: 16px;
|
||||
.ci-action-icon-wrapper svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +195,10 @@
|
|||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
|
||||
&.media > *:first-child {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
&.label-truncated {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -207,14 +216,7 @@
|
|||
background-color: $gray-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-help {
|
||||
padding: 10px 16px 10px 48px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.mr-widget-body {
|
||||
h4 {
|
||||
float: left;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
|
@ -237,6 +239,10 @@
|
|||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.approve-btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: $gl-font-weight-normal;
|
||||
}
|
||||
|
@ -336,6 +342,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
.mini-pipeline-graph-dropdown-menu .mini-pipeline-graph-dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.ci-status-text,
|
||||
.ci-status-icon {
|
||||
top: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-help {
|
||||
padding: 10px 16px 10px 48px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.ci-coverage {
|
||||
float: right;
|
||||
}
|
||||
|
@ -350,12 +372,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.mr-state-widget .mr-widget-body {
|
||||
.approve-btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.mr-widget-body-controls {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
@ -469,16 +485,16 @@
|
|||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mr-info-list.mr-memory-usage {
|
||||
p {
|
||||
float: left;
|
||||
}
|
||||
&.mr-memory-usage {
|
||||
p {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.memory-graph-container {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
.memory-graph-container {
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,15 @@
|
|||
height: 6px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar-collapsed-icon {
|
||||
clear: both;
|
||||
padding: 15px 5px 5px;
|
||||
|
||||
.progress {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.collapsed-milestone-date {
|
||||
|
@ -93,17 +102,6 @@
|
|||
margin-right: 0;
|
||||
}
|
||||
|
||||
.milestone-progress {
|
||||
.sidebar-collapsed-icon {
|
||||
clear: both;
|
||||
padding: 15px 5px 5px;
|
||||
|
||||
.progress {
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-sidebar-collapsed & {
|
||||
.reference {
|
||||
border-top: 1px solid $border-gray-normal;
|
||||
|
@ -156,18 +154,16 @@
|
|||
|
||||
.status-box {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.milestone-buttons {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.status-box {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.milestone-buttons {
|
||||
margin-left: auto;
|
||||
order: 2;
|
||||
|
||||
.verbose {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header-text-content {
|
||||
|
@ -175,10 +171,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.milestone-buttons .verbose {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-xs-min) {
|
||||
.milestone-buttons .verbose {
|
||||
display: inline;
|
||||
|
|
|
@ -111,24 +111,9 @@
|
|||
margin: auto;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
margin-right: $issuable-warning-icon-margin;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled-comment .issuable-note-warning {
|
||||
border: none;
|
||||
border-radius: $label-border-radius;
|
||||
padding-top: $gl-vert-padding;
|
||||
padding-bottom: $gl-vert-padding;
|
||||
|
||||
.icon svg {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
margin-right: $btn-xs-side-margin;
|
||||
width: $gl-font-size;
|
||||
height: $gl-font-size;
|
||||
fill: $orange-600;
|
||||
+ .md-area {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,11 +140,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.issuable-note-warning + .md-area {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.discussion-form {
|
||||
background-color: $white-light;
|
||||
}
|
||||
|
|
|
@ -312,57 +312,72 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
.diff-file .notes_holder {
|
||||
font-family: $regular_font;
|
||||
|
||||
td {
|
||||
border: 1px solid $white-normal;
|
||||
border-left: none;
|
||||
|
||||
&.notes_line {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
background: $gray-light;
|
||||
color: $text-color;
|
||||
.diff-file {
|
||||
.is-over {
|
||||
.add-diff-note {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
&.notes_line2 {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
border-left: 1px solid $note-line2-border !important;
|
||||
}
|
||||
// Merge request notes in diffs
|
||||
// Diff is inline
|
||||
.notes_content .note-header .note-headline-light {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.notes_content {
|
||||
background-color: $gray-light;
|
||||
border-width: 1px 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
.notes_holder {
|
||||
font-family: $regular_font;
|
||||
|
||||
&.parallel {
|
||||
border-width: 1px;
|
||||
td {
|
||||
border: 1px solid $white-normal;
|
||||
border-left: none;
|
||||
|
||||
&.notes_line {
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
background: $gray-light;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.discussion-notes {
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid $white-normal;
|
||||
margin-top: 20px;
|
||||
&.notes_line2 {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
border-left: 1px solid $note-line2-border !important;
|
||||
}
|
||||
|
||||
&.notes_content {
|
||||
background-color: $gray-light;
|
||||
border-width: 1px 0;
|
||||
padding: 0;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
|
||||
&.parallel {
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid $white-normal;
|
||||
margin-bottom: 20px;
|
||||
.discussion-notes {
|
||||
&:not(:first-child) {
|
||||
border-top: 1px solid $white-normal;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid $white-normal;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notes {
|
||||
background-color: $white-light;
|
||||
}
|
||||
.notes {
|
||||
background-color: $white-light;
|
||||
}
|
||||
|
||||
a code {
|
||||
top: 0;
|
||||
margin-right: 0;
|
||||
a code {
|
||||
top: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -457,8 +472,9 @@ ul.notes {
|
|||
margin-left: 10px;
|
||||
color: $gray-darkest;
|
||||
|
||||
.btn-group > .discussion-next-btn {
|
||||
margin-left: -1px;
|
||||
@include notes-media('max', $screen-md-max) {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,8 +485,6 @@ ul.notes {
|
|||
flex-shrink: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
// For PhantomJS that does not support flex
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
color: $gray-darkest;
|
||||
|
||||
|
@ -481,7 +495,6 @@ ul.notes {
|
|||
}
|
||||
|
||||
.more-actions {
|
||||
float: right; // phantomjs fallback
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
|
@ -502,13 +515,6 @@ ul.notes {
|
|||
min-width: 180px;
|
||||
}
|
||||
|
||||
.discussion-actions {
|
||||
@include notes-media('max', $screen-md-max) {
|
||||
float: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.note-actions-item {
|
||||
margin-left: 12px;
|
||||
display: flex;
|
||||
|
@ -665,14 +671,6 @@ ul.notes {
|
|||
}
|
||||
}
|
||||
|
||||
.diff-file {
|
||||
.is-over {
|
||||
.add-diff-note {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disabled-comment {
|
||||
background-color: $gray-light;
|
||||
border-radius: $border-radius-base;
|
||||
|
@ -714,20 +712,20 @@ ul.notes {
|
|||
svg path {
|
||||
fill: $gray-darkest;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.discussion-create-issue-btn {
|
||||
margin-left: -4px;
|
||||
border-radius: 0;
|
||||
border-right: 0;
|
||||
&.discussion-create-issue-btn {
|
||||
margin-left: -4px;
|
||||
border-radius: 0;
|
||||
border-right: 0;
|
||||
|
||||
a {
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
a {
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
border: 0;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -801,12 +799,3 @@ ul.notes {
|
|||
.line-resolve-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// Merge request notes in diffs
|
||||
.diff-file {
|
||||
// Diff is inline
|
||||
.notes_content .note-header .note-headline-light {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
}
|
||||
|
||||
.pipeline-actions {
|
||||
padding-right: 0;
|
||||
min-width: 170px; //Guarantees buttons don't break in several lines.
|
||||
|
||||
.btn-default {
|
||||
|
@ -176,6 +175,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play button with icon in dropdowns
|
||||
*/
|
||||
.no-btn {
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
.icon-play {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
margin-right: 5px;
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.duration,
|
||||
.finished-at {
|
||||
color: $gl-text-color-secondary;
|
||||
|
@ -451,36 +469,46 @@
|
|||
@extend .build-content:hover;
|
||||
}
|
||||
|
||||
// Action Icons in big pipeline-graph nodes
|
||||
.ci-action-icon-container .ci-action-icon-wrapper {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background: $white-light;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 100%;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
background-color: $stage-hover-bg;
|
||||
border: 1px solid $dropdown-toggle-active-border-color;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $gl-text-color-secondary;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
fill: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ci-action-icon-container {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
|
||||
// Action Icons in big pipeline-graph nodes
|
||||
&.ci-action-icon-wrapper {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
background: $white-light;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 100%;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
background-color: $stage-hover-bg;
|
||||
border: 1px solid $dropdown-toggle-active-border-color;
|
||||
|
||||
svg {
|
||||
fill: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: $gl-text-color-secondary;
|
||||
position: relative;
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
&.play {
|
||||
svg {
|
||||
width: #{$ci-action-icon-size - 8};
|
||||
height: #{$ci-action-icon-size - 8};
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ci-status-icon svg {
|
||||
|
@ -721,17 +749,50 @@ button.mini-pipeline-graph-dropdown-toggle {
|
|||
|
||||
svg {
|
||||
fill: $gl-text-color-secondary;
|
||||
width: $ci-action-icon-size;
|
||||
height: $ci-action-icon-size;
|
||||
left: -6px;
|
||||
width: #{$ci-action-icon-size - 6};
|
||||
height: #{$ci-action-icon-size - 6};
|
||||
left: -3px;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
top: -2px;
|
||||
|
||||
&.icon-action-stop,
|
||||
&.icon-action-cancel {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 1px;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
&.icon-action-play {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
&.icon-action-retry {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
top: 0;
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover svg,
|
||||
&:focus svg {
|
||||
fill: $gl-text-color;
|
||||
}
|
||||
|
||||
&.icon-action-retry,
|
||||
&.icon-action-play {
|
||||
svg {
|
||||
width: #{$ci-action-icon-size - 6};
|
||||
height: #{$ci-action-icon-size - 6};
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// link to the build
|
||||
|
@ -799,13 +860,10 @@ button.mini-pipeline-graph-dropdown-toggle {
|
|||
left: 100%;
|
||||
top: -10px;
|
||||
box-shadow: 0 1px 5px $black-transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top arrow in the dropdown in the big pipeline graph
|
||||
*/
|
||||
.big-pipeline-graph-dropdown-menu {
|
||||
|
||||
/**
|
||||
* Top arrow in the dropdown in the big pipeline graph
|
||||
*/
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
|
@ -867,22 +925,23 @@ button.mini-pipeline-graph-dropdown-toggle {
|
|||
margin-top: 1px;
|
||||
border-bottom-color: $white-light;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Center dropdown menu in mini graph
|
||||
*/
|
||||
.mini-pipeline-graph-dropdown-menu.dropdown-menu {
|
||||
transform: translate(-80%, 0);
|
||||
min-width: 150px;
|
||||
/**
|
||||
* Center dropdown menu in mini graph
|
||||
*/
|
||||
&.dropdown-menu {
|
||||
transform: translate(-80%, 0);
|
||||
min-width: 150px;
|
||||
|
||||
@media(min-width: $screen-md-min) {
|
||||
transform: translate(-50%, 0);
|
||||
right: auto;
|
||||
left: 50%;
|
||||
min-width: 240px;
|
||||
@media(min-width: $screen-md-min) {
|
||||
transform: translate(-50%, 0);
|
||||
right: auto;
|
||||
left: 50%;
|
||||
min-width: 240px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminal
|
||||
*/
|
||||
|
@ -906,25 +965,6 @@ button.mini-pipeline-graph-dropdown-toggle {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play button with icon in dropdowns
|
||||
*/
|
||||
.ci-table .no-btn {
|
||||
border: none;
|
||||
background: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
.icon-play {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
margin-right: 5px;
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.ci-header-container {
|
||||
min-height: 55px;
|
||||
|
||||
|
|
|
@ -88,7 +88,8 @@
|
|||
transition: background 2s ease-out;
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.75;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.highlight-changes & {
|
||||
|
@ -778,35 +779,35 @@ a.deploy-project-label {
|
|||
.nav {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.nav > li {
|
||||
display: inline-block;
|
||||
> li {
|
||||
display: inline-block;
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: $gl-padding;
|
||||
}
|
||||
|
||||
&.right {
|
||||
vertical-align: top;
|
||||
margin-top: 0;
|
||||
|
||||
@media (min-width: $screen-lg-min) {
|
||||
float: right;
|
||||
&:not(:last-child) {
|
||||
margin-right: $gl-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav > li > a {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
line-height: 29px;
|
||||
color: $notes-light-color;
|
||||
&.right {
|
||||
vertical-align: top;
|
||||
margin-top: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $gl-text-color;
|
||||
@media (min-width: $screen-lg-min) {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
> a {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
line-height: 29px;
|
||||
color: $notes-light-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1160,13 +1161,6 @@ pre.light-well {
|
|||
}
|
||||
}
|
||||
|
||||
.project-repo-select {
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.variables-table {
|
||||
table-layout: fixed;
|
||||
|
||||
|
|
|
@ -78,6 +78,10 @@ input[type="checkbox"]:hover {
|
|||
}
|
||||
|
||||
.search-input-wrap {
|
||||
// Fallback if flexbox is not supported
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
.search-icon,
|
||||
.clear-icon {
|
||||
position: absolute;
|
||||
|
|
|
@ -23,15 +23,14 @@
|
|||
}
|
||||
|
||||
.settings {
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid $gray-darker;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
overflow: visible;
|
||||
&.animating {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,14 +55,18 @@
|
|||
overflow-y: scroll;
|
||||
padding-right: 110px;
|
||||
animation: collapseMaxHeight 300ms ease-out;
|
||||
// Keep the section from expanding when we scroll over it
|
||||
pointer-events: none;
|
||||
|
||||
&.expanded {
|
||||
.settings.expanded & {
|
||||
max-height: none;
|
||||
overflow-y: visible;
|
||||
animation: expandMaxHeight 300ms ease-in;
|
||||
// Reset and allow clicks again when expanded
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&.no-animate {
|
||||
.settings.no-animate & {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
|
@ -238,11 +241,11 @@
|
|||
margin-left: 5px;
|
||||
background: $badge-bg;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure we don't add border if there's only single li */
|
||||
li + li {
|
||||
border-top: 1px solid $border-color;
|
||||
/* Ensure we don't add border if there's only single li */
|
||||
+ li {
|
||||
border-top: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ table .sherlock-code {
|
|||
.sherlock-code {
|
||||
pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
pre code {
|
||||
white-space: pre;
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,13 +21,13 @@ table .sherlock-code {
|
|||
text-align: right;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
.slow {
|
||||
color: $red-500;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.sherlock-file-sample pre {
|
||||
padding-top: 28px !important;
|
||||
}
|
||||
|
||||
.sherlock-line-samples-table .slow {
|
||||
color: $red-500;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
|
|
|
@ -40,16 +40,16 @@
|
|||
@media (max-width: $screen-xs-max) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.person .spark {
|
||||
display: block;
|
||||
background: $stat-graph-common-bg;
|
||||
width: 100%;
|
||||
}
|
||||
.spark {
|
||||
display: block;
|
||||
background: $stat-graph-common-bg;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.person .area-contributor {
|
||||
fill: $stat-graph-orange-fill;
|
||||
.area-contributor {
|
||||
fill: $stat-graph-orange-fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,10 +161,10 @@ ul.wiki-pages-list.content-list {
|
|||
list-style: none;
|
||||
margin-left: 0;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
ul li {
|
||||
padding: 5px 0;
|
||||
li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
-ms-transition: none !important;
|
||||
-webkit-transition: none !important;
|
||||
transition: none !important;
|
||||
-o-transform: none !important;
|
||||
-moz-transform: none !important;
|
||||
-ms-transform: none !important;
|
||||
-webkit-transform: none !important;
|
||||
transform: none !important;
|
||||
-webkit-animation: none !important;
|
||||
-moz-animation: none !important;
|
||||
-o-animation: none !important;
|
||||
|
|
|
@ -19,10 +19,12 @@ class Admin::ApplicationsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@application = Doorkeeper::Application.new(application_params)
|
||||
@application = Applications::CreateService.new(current_user, application_params).execute(request)
|
||||
|
||||
if @application.save
|
||||
redirect_to_admin_page
|
||||
if @application.persisted?
|
||||
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
|
||||
|
||||
redirect_to admin_application_url(@application)
|
||||
else
|
||||
render :new
|
||||
end
|
||||
|
@ -41,13 +43,6 @@ class Admin::ApplicationsController < Admin::ApplicationController
|
|||
redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def redirect_to_admin_page
|
||||
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
|
||||
redirect_to admin_application_url(@application)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_application
|
||||
|
|
|
@ -44,7 +44,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def set_index_vars
|
||||
@scopes = Gitlab::Auth::API_SCOPES
|
||||
@scopes = Gitlab::Auth.available_scopes(current_user)
|
||||
|
||||
@impersonation_token ||= finder.build
|
||||
@inactive_impersonation_tokens = finder(state: 'inactive').execute
|
||||
|
|
|
@ -11,7 +11,7 @@ class ApplicationController < ActionController::Base
|
|||
include EnforcesTwoFactorAuthentication
|
||||
include WithPerformanceBar
|
||||
|
||||
before_action :authenticate_user_from_private_token!
|
||||
before_action :authenticate_user_from_personal_access_token!
|
||||
before_action :authenticate_user_from_rss_token!
|
||||
before_action :authenticate_user!
|
||||
before_action :validate_user_service_ticket!
|
||||
|
@ -100,13 +100,12 @@ class ApplicationController < ActionController::Base
|
|||
return try(:authenticated_user)
|
||||
end
|
||||
|
||||
# This filter handles both private tokens and personal access tokens
|
||||
def authenticate_user_from_private_token!
|
||||
def authenticate_user_from_personal_access_token!
|
||||
token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
|
||||
|
||||
return unless token.present?
|
||||
|
||||
user = User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
|
||||
user = User.find_by_personal_access_token(token)
|
||||
|
||||
sessionless_sign_in(user)
|
||||
end
|
||||
|
|
|
@ -7,6 +7,54 @@ module IssuableActions
|
|||
before_action :authorize_admin_issuable!, only: :bulk_update
|
||||
end
|
||||
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
render show_view
|
||||
end
|
||||
format.json do
|
||||
render json: serializer.represent(issuable, serializer: params[:serializer])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@issuable = update_service.execute(issuable)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
recaptcha_check_with_fallback { render :edit }
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_entity_json
|
||||
end
|
||||
end
|
||||
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
render_conflict_response
|
||||
end
|
||||
|
||||
def realtime_changes
|
||||
Gitlab::PollingInterval.set_header(response, interval: 3_000)
|
||||
|
||||
response = {
|
||||
title: view_context.markdown_field(issuable, :title),
|
||||
title_text: issuable.title,
|
||||
description: view_context.markdown_field(issuable, :description),
|
||||
description_text: issuable.description,
|
||||
task_status: issuable.task_status
|
||||
}
|
||||
|
||||
if issuable.edited?
|
||||
response[:updated_at] = issuable.updated_at
|
||||
response[:updated_by_name] = issuable.last_edited_by.name
|
||||
response[:updated_by_path] = user_path(issuable.last_edited_by)
|
||||
end
|
||||
|
||||
render json: response
|
||||
end
|
||||
|
||||
def destroy
|
||||
issuable.destroy
|
||||
destroy_method = "destroy_#{issuable.class.name.underscore}".to_sym
|
||||
|
@ -68,6 +116,10 @@ module IssuableActions
|
|||
end
|
||||
end
|
||||
|
||||
def authorize_update_issuable!
|
||||
render_404 unless can?(current_user, :"update_#{resource_name}", issuable)
|
||||
end
|
||||
|
||||
def bulk_update_params
|
||||
permitted_keys = [
|
||||
:issuable_ids,
|
||||
|
@ -92,4 +144,24 @@ module IssuableActions
|
|||
def resource_name
|
||||
@resource_name ||= controller_name.singularize
|
||||
end
|
||||
|
||||
def render_entity_json
|
||||
if @issuable.valid?
|
||||
render json: serializer.represent(@issuable)
|
||||
else
|
||||
render json: { errors: @issuable.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def show_view
|
||||
'show'
|
||||
end
|
||||
|
||||
def serializer
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def update_service
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
|
|
@ -94,10 +94,9 @@ module LfsRequest
|
|||
@storage_project ||= begin
|
||||
result = project
|
||||
|
||||
loop do
|
||||
break unless result.forked?
|
||||
result = result.forked_from_project
|
||||
end
|
||||
# TODO: Make this go to the fork_network root immeadiatly
|
||||
# dependant on the discussion in: https://gitlab.com/gitlab-org/gitlab-ce/issues/39769
|
||||
result = result.fork_source while result.forked?
|
||||
|
||||
result
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ module NotesActions
|
|||
|
||||
included do
|
||||
before_action :set_polling_interval_header, only: [:index]
|
||||
before_action :noteable, only: :index
|
||||
before_action :authorize_admin_note!, only: [:update, :destroy]
|
||||
before_action :note_project, only: [:create]
|
||||
end
|
||||
|
@ -188,7 +189,7 @@ module NotesActions
|
|||
end
|
||||
|
||||
def noteable
|
||||
@noteable ||= notes_finder.target
|
||||
@noteable ||= notes_finder.target || render_404
|
||||
end
|
||||
|
||||
def last_fetched_at
|
||||
|
|
|
@ -30,11 +30,11 @@ class JwtController < ApplicationController
|
|||
render_unauthorized
|
||||
end
|
||||
end
|
||||
rescue Gitlab::Auth::MissingPersonalTokenError
|
||||
render_missing_personal_token
|
||||
rescue Gitlab::Auth::MissingPersonalAccessTokenError
|
||||
render_missing_personal_access_token
|
||||
end
|
||||
|
||||
def render_missing_personal_token
|
||||
def render_missing_personal_access_token
|
||||
render json: {
|
||||
errors: [
|
||||
{ code: 'UNAUTHORIZED',
|
||||
|
|
|
@ -16,25 +16,18 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
|||
end
|
||||
|
||||
def create
|
||||
@application = Doorkeeper::Application.new(application_params)
|
||||
@application = Applications::CreateService.new(current_user, create_application_params).execute(request)
|
||||
|
||||
@application.owner = current_user
|
||||
if @application.persisted?
|
||||
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
|
||||
|
||||
if @application.save
|
||||
redirect_to_oauth_application_page
|
||||
redirect_to oauth_application_url(@application)
|
||||
else
|
||||
set_index_vars
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def redirect_to_oauth_application_page
|
||||
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
|
||||
redirect_to oauth_application_url(@application)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def verify_user_oauth_applications_enabled
|
||||
|
@ -61,4 +54,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
|||
rescue_from ActiveRecord::RecordNotFound do |exception|
|
||||
render "errors/not_found", layout: "errors", status: 404
|
||||
end
|
||||
|
||||
def create_application_params
|
||||
application_params.tap do |params|
|
||||
params[:owner] = current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,10 +11,10 @@ class Profiles::KeysController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@key = Keys::CreateService.new(current_user, key_params).execute
|
||||
@key = Keys::CreateService.new(current_user, key_params.merge(ip_address: request.remote_ip)).execute
|
||||
|
||||
if @key.persisted?
|
||||
redirect_to_profile_key_path
|
||||
redirect_to profile_key_path(@key)
|
||||
else
|
||||
@keys = current_user.keys.select(&:persisted?)
|
||||
render :index
|
||||
|
@ -50,12 +50,6 @@ class Profiles::KeysController < Profiles::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def redirect_to_profile_key_path
|
||||
redirect_to profile_key_path(@key)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def key_params
|
||||
|
|
|
@ -39,7 +39,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
|
|||
end
|
||||
|
||||
def set_index_vars
|
||||
@scopes = Gitlab::Auth.available_scopes
|
||||
@scopes = Gitlab::Auth.available_scopes(current_user)
|
||||
|
||||
@inactive_personal_access_tokens = finder(state: 'inactive').execute
|
||||
@active_personal_access_tokens = finder(state: 'active').execute.order(:expires_at)
|
||||
|
|
|
@ -24,16 +24,6 @@ class ProfilesController < Profiles::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def reset_private_token
|
||||
Users::UpdateService.new(current_user, user: @user).execute! do |user|
|
||||
user.reset_authentication_token!
|
||||
end
|
||||
|
||||
flash[:notice] = "Private token was successfully reset"
|
||||
|
||||
redirect_to profile_account_path
|
||||
end
|
||||
|
||||
def reset_incoming_email_token
|
||||
Users::UpdateService.new(current_user, user: @user).execute! do |user|
|
||||
user.reset_incoming_email_token!
|
||||
|
@ -41,7 +31,7 @@ class ProfilesController < Profiles::ApplicationController
|
|||
|
||||
flash[:notice] = "Incoming email token was successfully reset"
|
||||
|
||||
redirect_to profile_account_path
|
||||
redirect_to profile_personal_access_tokens_path
|
||||
end
|
||||
|
||||
def reset_rss_token
|
||||
|
@ -51,7 +41,7 @@ class ProfilesController < Profiles::ApplicationController
|
|||
|
||||
flash[:notice] = "RSS token was successfully reset"
|
||||
|
||||
redirect_to profile_account_path
|
||||
redirect_to profile_personal_access_tokens_path
|
||||
end
|
||||
|
||||
def audit_log
|
||||
|
|
|
@ -53,8 +53,8 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
|||
|
||||
send_challenges
|
||||
render plain: "HTTP Basic: Access denied\n", status: 401
|
||||
rescue Gitlab::Auth::MissingPersonalTokenError
|
||||
render_missing_personal_token
|
||||
rescue Gitlab::Auth::MissingPersonalAccessTokenError
|
||||
render_missing_personal_access_token
|
||||
end
|
||||
|
||||
def basic_auth_provided?
|
||||
|
@ -78,7 +78,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
|||
@project, @wiki, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
|
||||
end
|
||||
|
||||
def render_missing_personal_token
|
||||
def render_missing_personal_access_token
|
||||
render plain: "HTTP Basic: Access denied\n" \
|
||||
"You must use a personal access token with 'api' scope for Git over HTTP.\n" \
|
||||
"You can generate one at #{profile_personal_access_tokens_url}",
|
||||
|
|
|
@ -16,7 +16,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
before_action :authorize_create_issue!, only: [:new, :create]
|
||||
|
||||
# Allow modify issue
|
||||
before_action :authorize_update_issue!, only: [:edit, :update, :move]
|
||||
before_action :authorize_update_issuable!, only: [:edit, :update, :move]
|
||||
|
||||
# Allow create a new branch and empty WIP merge request from current issue
|
||||
before_action :authorize_create_merge_request!, only: [:create_merge_request]
|
||||
|
@ -67,18 +67,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
respond_with(@issue)
|
||||
end
|
||||
|
||||
def show
|
||||
@noteable = @issue
|
||||
@note = @project.notes.new(noteable: @issue)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json do
|
||||
render json: serializer.represent(@issue, serializer: params[:serializer])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def discussions
|
||||
notes = @issue.notes
|
||||
.inc_relations_for_view
|
||||
|
@ -120,25 +108,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def update
|
||||
update_params = issue_params.merge(spammable_params)
|
||||
|
||||
@issue = Issues::UpdateService.new(project, current_user, update_params).execute(issue)
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
recaptcha_check_with_fallback { render :edit }
|
||||
end
|
||||
|
||||
format.json do
|
||||
render_issue_json
|
||||
end
|
||||
end
|
||||
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
render_conflict_response
|
||||
end
|
||||
|
||||
def move
|
||||
params.require(:move_to_project_id)
|
||||
|
||||
|
@ -196,26 +165,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def realtime_changes
|
||||
Gitlab::PollingInterval.set_header(response, interval: 3_000)
|
||||
|
||||
response = {
|
||||
title: view_context.markdown_field(@issue, :title),
|
||||
title_text: @issue.title,
|
||||
description: view_context.markdown_field(@issue, :description),
|
||||
description_text: @issue.description,
|
||||
task_status: @issue.task_status
|
||||
}
|
||||
|
||||
if @issue.edited?
|
||||
response[:updated_at] = @issue.updated_at
|
||||
response[:updated_by_name] = @issue.last_edited_by.name
|
||||
response[:updated_by_path] = user_path(@issue.last_edited_by)
|
||||
end
|
||||
|
||||
render json: response
|
||||
end
|
||||
|
||||
def create_merge_request
|
||||
result = ::MergeRequests::CreateFromIssueService.new(project, current_user, issue_iid: issue.iid).execute
|
||||
|
||||
|
@ -231,7 +180,8 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def issue
|
||||
return @issue if defined?(@issue)
|
||||
# The Sortable default scope causes performance issues when used with find_by
|
||||
@noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
|
||||
@issuable = @noteable = @issue ||= @project.issues.where(iid: params[:id]).reorder(nil).take!
|
||||
@note = @project.notes.new(noteable: @issuable)
|
||||
|
||||
return render_404 unless can?(current_user, :read_issue, @issue)
|
||||
|
||||
|
@ -246,14 +196,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
project_issue_path(@project, @issue)
|
||||
end
|
||||
|
||||
def authorize_update_issue!
|
||||
render_404 unless can?(current_user, :update_issue, @issue)
|
||||
end
|
||||
|
||||
def authorize_admin_issues!
|
||||
render_404 unless can?(current_user, :admin_issue, @project)
|
||||
end
|
||||
|
||||
def authorize_create_merge_request!
|
||||
render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user)
|
||||
end
|
||||
|
@ -305,4 +247,9 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def serializer
|
||||
IssueSerializer.new(current_user: current_user, project: issue.project)
|
||||
end
|
||||
|
||||
def update_service
|
||||
update_params = issue_params.merge(spammable_params)
|
||||
Issues::UpdateService.new(project, current_user, update_params)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
skip_before_action :merge_request, only: [:index, :bulk_update]
|
||||
skip_before_action :ensure_ref_fetched, only: [:index, :bulk_update]
|
||||
|
||||
before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort]
|
||||
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
|
||||
|
||||
before_action :authenticate_user!, only: [:assign_related_issues]
|
||||
|
||||
|
@ -256,14 +256,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
alias_method :issuable, :merge_request
|
||||
alias_method :awardable, :merge_request
|
||||
|
||||
def authorize_update_merge_request!
|
||||
return render_404 unless can?(current_user, :update_merge_request, @merge_request)
|
||||
end
|
||||
|
||||
def authorize_admin_merge_request!
|
||||
return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
|
||||
end
|
||||
|
||||
def validates_merge_request
|
||||
# Show git not found page
|
||||
# if there is no saved commits between source & target branch
|
||||
|
|
|
@ -63,34 +63,34 @@ module CiStatusHelper
|
|||
|
||||
def ci_icon_for_status(status)
|
||||
if detailed_status?(status)
|
||||
return custom_icon(status.icon)
|
||||
return sprite_icon(status.icon)
|
||||
end
|
||||
|
||||
icon_name =
|
||||
case status
|
||||
when 'success'
|
||||
'icon_status_success'
|
||||
'status_success'
|
||||
when 'success_with_warnings'
|
||||
'icon_status_warning'
|
||||
'status_warning'
|
||||
when 'failed'
|
||||
'icon_status_failed'
|
||||
'status_failed'
|
||||
when 'pending'
|
||||
'icon_status_pending'
|
||||
'status_pending'
|
||||
when 'running'
|
||||
'icon_status_running'
|
||||
'status_running'
|
||||
when 'play'
|
||||
'icon_play'
|
||||
'play'
|
||||
when 'created'
|
||||
'icon_status_created'
|
||||
'status_created'
|
||||
when 'skipped'
|
||||
'icon_status_skipped'
|
||||
'status_skipped'
|
||||
when 'manual'
|
||||
'icon_status_manual'
|
||||
'status_manual'
|
||||
else
|
||||
'icon_status_canceled'
|
||||
'status_canceled'
|
||||
end
|
||||
|
||||
custom_icon(icon_name)
|
||||
sprite_icon(icon_name, size: 16)
|
||||
end
|
||||
|
||||
def pipeline_status_cache_key(pipeline_status)
|
||||
|
|
|
@ -71,11 +71,13 @@ module GitlabRoutingHelper
|
|||
project_commit_url(entity.project, entity.sha, *args)
|
||||
end
|
||||
|
||||
def preview_markdown_path(project, *args)
|
||||
def preview_markdown_path(parent, *args)
|
||||
return group_preview_markdown_path(parent) if parent.is_a?(Group)
|
||||
|
||||
if @snippet.is_a?(PersonalSnippet)
|
||||
preview_markdown_snippets_path
|
||||
else
|
||||
preview_markdown_project_path(project, *args)
|
||||
preview_markdown_project_path(parent, *args)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -211,15 +211,13 @@ module IssuablesHelper
|
|||
|
||||
def issuable_initial_data(issuable)
|
||||
data = {
|
||||
endpoint: project_issue_path(@project, issuable),
|
||||
canUpdate: can?(current_user, :update_issue, issuable),
|
||||
canDestroy: can?(current_user, :destroy_issue, issuable),
|
||||
endpoint: issuable_path(issuable),
|
||||
canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable),
|
||||
canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable),
|
||||
issuableRef: issuable.to_reference,
|
||||
markdownPreviewPath: preview_markdown_path(@project),
|
||||
markdownPreviewPath: preview_markdown_path(parent),
|
||||
markdownDocsPath: help_page_path('user/markdown'),
|
||||
issuableTemplates: issuable_templates(issuable),
|
||||
projectPath: ref_project.path,
|
||||
projectNamespace: ref_project.namespace.full_path,
|
||||
initialTitleHtml: markdown_field(issuable, :title),
|
||||
initialTitleText: issuable.title,
|
||||
initialDescriptionHtml: markdown_field(issuable, :description),
|
||||
|
@ -227,6 +225,12 @@ module IssuablesHelper
|
|||
initialTaskStatus: issuable.task_status
|
||||
}
|
||||
|
||||
if parent.is_a?(Group)
|
||||
data[:groupPath] = parent.path
|
||||
else
|
||||
data.merge!(projectPath: ref_project.path, projectNamespace: ref_project.namespace.full_path)
|
||||
end
|
||||
|
||||
data.merge!(updated_at_by(issuable))
|
||||
|
||||
data.to_json
|
||||
|
@ -263,12 +267,7 @@ module IssuablesHelper
|
|||
end
|
||||
|
||||
def issuable_path(issuable, *options)
|
||||
case issuable
|
||||
when Issue
|
||||
issue_path(issuable, *options)
|
||||
when MergeRequest
|
||||
merge_request_path(issuable, *options)
|
||||
end
|
||||
polymorphic_path(issuable, *options)
|
||||
end
|
||||
|
||||
def issuable_url(issuable, *options)
|
||||
|
@ -369,4 +368,8 @@ module IssuablesHelper
|
|||
fullPath: @project.full_path
|
||||
}
|
||||
end
|
||||
|
||||
def parent
|
||||
@project || @group
|
||||
end
|
||||
end
|
||||
|
|
|
@ -110,7 +110,15 @@ module ProjectsHelper
|
|||
|
||||
def remove_fork_project_message(project)
|
||||
_("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
|
||||
{ forked_from_project: @project.forked_from_project.name_with_namespace }
|
||||
{ forked_from_project: fork_source_name(project) }
|
||||
end
|
||||
|
||||
def fork_source_name(project)
|
||||
if @project.fork_source
|
||||
@project.fork_source.full_name
|
||||
else
|
||||
@project.fork_network&.deleted_root_project_name
|
||||
end
|
||||
end
|
||||
|
||||
def project_nav_tabs
|
||||
|
@ -140,8 +148,8 @@ module ProjectsHelper
|
|||
def can_change_visibility_level?(project, current_user)
|
||||
return false unless can?(current_user, :change_visibility_level, project)
|
||||
|
||||
if project.forked?
|
||||
project.forked_from_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE
|
||||
if project.fork_source
|
||||
project.fork_source.visibility_level > Gitlab::VisibilityLevel::PRIVATE
|
||||
else
|
||||
true
|
||||
end
|
||||
|
|
|
@ -49,7 +49,8 @@ module CacheMarkdownField
|
|||
|
||||
# Always include a project key, or Banzai complains
|
||||
project = self.project if self.respond_to?(:project)
|
||||
context = cached_markdown_fields[field].merge(project: project)
|
||||
group = self.group if self.respond_to?(:group)
|
||||
context = cached_markdown_fields[field].merge(project: project, group: group)
|
||||
|
||||
# Banzai is less strict about authors, so don't always have an author key
|
||||
context[:author] = self.author if self.respond_to?(:author)
|
||||
|
|
|
@ -14,7 +14,6 @@ module Issuable
|
|||
include StripAttribute
|
||||
include Awardable
|
||||
include Taskable
|
||||
include TimeTrackable
|
||||
include Importable
|
||||
include Editable
|
||||
include AfterCommitQueue
|
||||
|
@ -95,8 +94,6 @@ module Issuable
|
|||
|
||||
strip_attributes :title
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
after_save :record_metrics, unless: :imported?
|
||||
|
||||
# We want to use optimistic lock for cases when only title or description are involved
|
||||
|
|
|
@ -30,7 +30,6 @@ class Environment < ActiveRecord::Base
|
|||
message: Gitlab::Regex.environment_slug_regex_message }
|
||||
|
||||
validates :external_url,
|
||||
uniqueness: { scope: :project_id },
|
||||
length: { maximum: 255 },
|
||||
allow_nil: true,
|
||||
addressable_url: true
|
||||
|
@ -110,7 +109,7 @@ class Environment < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def ref_path
|
||||
"refs/#{Repository::REF_ENVIRONMENTS}/#{generate_slug}"
|
||||
"refs/#{Repository::REF_ENVIRONMENTS}/#{slug}"
|
||||
end
|
||||
|
||||
def formatted_external_url
|
||||
|
@ -164,6 +163,10 @@ class Environment < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def slug
|
||||
super.presence || generate_slug
|
||||
end
|
||||
|
||||
# An environment name is not necessarily suitable for use in URLs, DNS
|
||||
# or other third-party contexts, so provide a slugified version. A slug has
|
||||
# the following properties:
|
||||
|
|
7
app/models/epic.rb
Normal file
7
app/models/epic.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Placeholder class for model that is implemented in EE
|
||||
# It will reserve (ee#3853) '&' as a reference prefix, but the table does not exists in CE
|
||||
class Epic < ActiveRecord::Base
|
||||
# TODO: this will be implemented as part of #3853
|
||||
def to_reference
|
||||
end
|
||||
end
|
|
@ -12,4 +12,8 @@ class ForkNetwork < ActiveRecord::Base
|
|||
def find_forks_in(other_projects)
|
||||
projects.where(id: other_projects)
|
||||
end
|
||||
|
||||
def merge_requests
|
||||
MergeRequest.where(target_project: projects)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,6 +42,7 @@ class Group < Namespace
|
|||
after_create :post_create_hook
|
||||
after_destroy :post_destroy_hook
|
||||
after_save :update_two_factor_requirement
|
||||
after_update :path_changed_hook, if: :path_changed?
|
||||
|
||||
class << self
|
||||
def supports_nested_groups?
|
||||
|
@ -180,6 +181,12 @@ class Group < Namespace
|
|||
add_user(user, :owner, current_user: current_user)
|
||||
end
|
||||
|
||||
def member?(user, min_access_level = Gitlab::Access::GUEST)
|
||||
return false unless user
|
||||
|
||||
max_member_access_for_user(user) >= min_access_level
|
||||
end
|
||||
|
||||
def has_owner?(user)
|
||||
return false unless user
|
||||
|
||||
|
@ -289,6 +296,12 @@ class Group < Namespace
|
|||
list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten
|
||||
end
|
||||
|
||||
def full_path_was
|
||||
return path_was unless has_parent?
|
||||
|
||||
"#{parent.full_path}/#{path_was}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_two_factor_requirement
|
||||
|
@ -297,6 +310,10 @@ class Group < Namespace
|
|||
users.find_each(&:update_two_factor_requirement)
|
||||
end
|
||||
|
||||
def path_changed_hook
|
||||
system_hook_service.execute_hooks_for(self, :rename)
|
||||
end
|
||||
|
||||
def visibility_level_allowed_by_parent
|
||||
return if visibility_level_allowed_by_parent?
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ class Issue < ActiveRecord::Base
|
|||
include FasterCacheKeys
|
||||
include RelativePositioning
|
||||
include CreatedAtFilterable
|
||||
include TimeTrackable
|
||||
|
||||
DueDateStruct = Struct.new(:title, :name).freeze
|
||||
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
|
||||
|
@ -74,6 +75,8 @@ class Issue < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
def self.reference_prefix
|
||||
'#'
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ class MergeRequest < ActiveRecord::Base
|
|||
include Sortable
|
||||
include IgnorableColumn
|
||||
include CreatedAtFilterable
|
||||
include TimeTrackable
|
||||
|
||||
ignore_column :locked_at
|
||||
|
||||
|
@ -119,6 +120,8 @@ class MergeRequest < ActiveRecord::Base
|
|||
|
||||
after_save :keep_around_commit
|
||||
|
||||
acts_as_paranoid
|
||||
|
||||
def self.reference_prefix
|
||||
'!'
|
||||
end
|
||||
|
|
|
@ -48,6 +48,10 @@ class MergeRequestDiff < ActiveRecord::Base
|
|||
# Collect information about commits and diff from repository
|
||||
# and save it to the database as serialized data
|
||||
def save_git_content
|
||||
MergeRequest
|
||||
.where('id = ? AND COALESCE(latest_merge_request_diff_id, 0) < ?', self.merge_request_id, self.id)
|
||||
.update_all(latest_merge_request_diff_id: self.id)
|
||||
|
||||
ensure_commit_shas
|
||||
save_commits
|
||||
save_diffs
|
||||
|
|
|
@ -69,7 +69,7 @@ class Note < ActiveRecord::Base
|
|||
delegate :title, to: :noteable, allow_nil: true
|
||||
|
||||
validates :note, presence: true
|
||||
validates :project, presence: true, unless: :for_personal_snippet?
|
||||
validates :project, presence: true, if: :for_project_noteable?
|
||||
|
||||
# Attachments are deprecated and are handled by Markdown uploader
|
||||
validates :attachment, file_size: { maximum: :max_attachment_size }
|
||||
|
@ -114,7 +114,7 @@ class Note < ActiveRecord::Base
|
|||
after_initialize :ensure_discussion_id
|
||||
before_validation :nullify_blank_type, :nullify_blank_line_code
|
||||
before_validation :set_discussion_id, on: :create
|
||||
after_save :keep_around_commit, unless: :for_personal_snippet?
|
||||
after_save :keep_around_commit, if: :for_project_noteable?
|
||||
after_save :expire_etag_cache
|
||||
after_destroy :expire_etag_cache
|
||||
|
||||
|
@ -208,6 +208,10 @@ class Note < ActiveRecord::Base
|
|||
noteable.is_a?(PersonalSnippet)
|
||||
end
|
||||
|
||||
def for_project_noteable?
|
||||
!for_personal_snippet?
|
||||
end
|
||||
|
||||
def skip_project_check?
|
||||
for_personal_snippet?
|
||||
end
|
||||
|
|
|
@ -2,5 +2,13 @@ class OauthAccessToken < Doorkeeper::AccessToken
|
|||
belongs_to :resource_owner, class_name: 'User'
|
||||
belongs_to :application, class_name: 'Doorkeeper::Application'
|
||||
|
||||
alias_method :user, :resource_owner
|
||||
alias_attribute :user, :resource_owner
|
||||
|
||||
def scopes=(value)
|
||||
if value.is_a?(Array)
|
||||
super(Doorkeeper::OAuth::Scopes.from_array(value).to_s)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue