Commit b85d69d1 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into new-nav-fix-contextual-breadcrumbs

parents 7b1affd4 0c4f512b
......@@ -10,7 +10,7 @@ linters:
# Reports when you use improper spacing around ! (the "bang") in !default,
# !global, !important, and !optional flags.
BangFormat:
enabled: false
enabled: true
# Whether or not to prefer `border: 0` over `border: none`.
BorderZero:
......@@ -43,10 +43,11 @@ linters:
# Rule sets should be ordered as follows:
# - @extend declarations
# - @include declarations without inner @content
# - properties, @include declarations with inner @content
# - properties
# - @include declarations with inner @content
# - nested rule sets.
DeclarationOrder:
enabled: false
enabled: true
# `scss-lint:disable` control comments should be preceded by a comment
# explaining why these linters are being disabled for this file.
......@@ -178,6 +179,10 @@ linters:
SpaceAfterComma:
enabled: true
# Comment literals should be followed by a space.
SpaceAfterComment:
enabled: false
# Properties should be formatted with a single space separating the colon
# from the property's value.
SpaceAfterPropertyColon:
......@@ -240,7 +245,7 @@ linters:
# Do not use parent selector references (&) when they would otherwise
# be unnecessary.
UnnecessaryParentReference:
enabled: false
enabled: true
# URLs should be valid and not contain protocols or domain names.
UrlFormat:
......
5.3.0
\ No newline at end of file
5.3.1
......@@ -334,7 +334,7 @@ group :development, :test do
gem 'rubocop', '~> 0.47.1', require: false
gem 'rubocop-rspec', '~> 1.15.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false
gem 'simplecov', '~> 0.14.0', require: false
gem 'flay', '~> 2.8.0', require: false
......
......@@ -762,9 +762,9 @@ GEM
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
scss_lint (0.47.1)
rake (>= 0.9, < 11)
sass (~> 3.4.15)
scss_lint (0.54.0)
rake (>= 0.9, < 13)
sass (~> 3.4.20)
securecompare (1.0.0)
seed-fu (2.3.6)
activerecord (>= 3.1)
......@@ -1081,7 +1081,7 @@ DEPENDENCIES
rugged (~> 0.25.1.1)
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
scss_lint (~> 0.47.0)
scss_lint (~> 0.54.0)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
sentry-raven (~> 2.5.3)
......
app/assets/images/new_nav.png

23.2 KB | W: | H:

app/assets/images/new_nav.png

14 KB | W: | H:

app/assets/images/new_nav.png
app/assets/images/new_nav.png
app/assets/images/new_nav.png
app/assets/images/new_nav.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -51,8 +51,9 @@ export default () => {
methods: {
loadFile() {
this.$http.get(el.dataset.endpoint)
.then(response => response.json())
.then((res) => {
this.json = res.json();
this.json = res;
this.loading = false;
})
.catch((e) => {
......
......@@ -81,8 +81,9 @@ $(() => {
mounted () {
Store.disabled = this.disabled;
gl.boardService.all()
.then(response => response.json())
.then((resp) => {
resp.json().forEach((board) => {
resp.forEach((board) => {
const list = Store.addList(board, this.defaultAvatar);
if (list.type === 'closed') {
......@@ -97,7 +98,8 @@ $(() => {
Store.addBlankState();
this.loading = false;
}).catch(() => new Flash('An error occurred. Please try again.'));
})
.catch(() => new Flash('An error occurred. Please try again.'));
},
methods: {
updateTokens() {
......
......@@ -64,8 +64,9 @@ export default {
// Save the labels
gl.boardService.generateDefaultLists()
.then((resp) => {
resp.json().forEach((listObj) => {
.then(resp => resp.json())
.then((data) => {
data.forEach((listObj) => {
const list = Store.findList('title', listObj.title);
list.id = listObj.id;
......
......@@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
return gl.boardService.getBacklog(queryData(this.filter.path, {
page: this.page,
per: this.perPage,
})).then((res) => {
const data = res.json();
}))
.then(resp => resp.json())
.then((data) => {
if (clearIssues) {
this.issues = [];
}
......
......@@ -40,9 +40,8 @@ class List {
save () {
return gl.boardService.createList(this.label.id)
.then((resp) => {
const data = resp.json();
.then(resp => resp.json())
.then((data) => {
this.id = data.id;
this.type = data.list_type;
this.position = data.position;
......@@ -91,8 +90,8 @@ class List {
}
return gl.boardService.getIssuesForList(this.id, data)
.then((resp) => {
const data = resp.json();
.then(resp => resp.json())
.then((data) => {
this.loading = false;
this.issuesSize = data.size;
......@@ -109,8 +108,8 @@ class List {
this.issuesSize += 1;
return gl.boardService.newIssue(this.id, issue)
.then((resp) => {
const data = resp.json();
.then(resp => resp.json())
.then((data) => {
issue.id = data.iid;
if (this.issuesSize > 1) {
......
......@@ -23,11 +23,6 @@ class BoardService {
url: bulkUpdatePath,
},
});
Vue.http.interceptors.push((request, next) => {
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
next();
});
}
all () {
......
......@@ -51,11 +51,11 @@
},
methods: {
successCallback(resp) {
const response = resp.json();
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response;
this.setCommonData(pipelines);
return resp.json().then((response) => {
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response;
this.setCommonData(pipelines);
});
},
},
};
......
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, max-len */
/* global CommentsStore */
/* global ResolveService */
/* global Flash */
......@@ -64,8 +64,6 @@ const ResolveBtn = Vue.extend({
});
},
resolve: function () {
const errorFlashMsg = 'An error occurred when trying to resolve a comment. Please try again.';
if (!this.canResolve) return;
let promise;
......@@ -79,24 +77,20 @@ const ResolveBtn = Vue.extend({
.resolve(this.noteId);
}
promise.then((response) => {
this.loading = false;
promise
.then(resp => resp.json())
.then((data) => {
this.loading = false;
if (response.status === 200) {
const data = response.json();
const resolved_by = data ? data.resolved_by : null;
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
this.discussion.updateHeadline(data);
gl.mrWidget.checkStatus();
} else {
new Flash(errorFlashMsg);
}
this.updateTooltip();
}).catch(() => {
new Flash(errorFlashMsg);
});
this.updateTooltip();
})
.catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.'));
}
},
mounted: function () {
......
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
/* global Flash */
/* global CommentsStore */
......@@ -32,27 +31,22 @@ class ResolveServiceClass {
promise = this.resolveAll(mergeRequestId, discussionId);
}
promise.then((response) => {
discussion.loading = false;
if (response.status === 200) {
const data = response.json();
const resolved_by = data ? data.resolved_by : null;
promise
.then(resp => resp.json())
.then((data) => {
discussion.loading = false;
const resolvedBy = data ? data.resolved_by : null;
if (isResolved) {
discussion.unResolveAllNotes();
} else {
discussion.resolveAllNotes(resolved_by);
discussion.resolveAllNotes(resolvedBy);
}
gl.mrWidget.checkStatus();
discussion.updateHeadline(data);
} else {
throw new Error('An error occurred when trying to resolve discussion.');
}
}).catch(() => {
new Flash('An error occurred when trying to resolve a discussion. Please try again.');
});
})
.catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.'));
}
resolveAll(mergeRequestId, discussionId) {
......@@ -62,7 +56,7 @@ class ResolveServiceClass {
return this.discussionResource.save({
mergeRequestId,
discussionId
discussionId,
}, {});
}
......@@ -73,7 +67,7 @@ class ResolveServiceClass {
return this.discussionResource.delete({
mergeRequestId,
discussionId
discussionId,
}, {});
}
}
......
......@@ -498,9 +498,9 @@ export default {
<div class="table-section section-15 hidden-xs hidden-sm" role="gridcell">
<a
v-if="shouldRenderBuildName"
class="build-link"
class="build-link flex-truncate-parent"
:href="buildPath">
{{buildName}}
<span class="flex-truncate-child">{{buildName}}</span>
</a>
</div>
......
export default {
methods: {
saveData(resp) {
const response = {
headers: resp.headers,
body: resp.json(),
};
const headers = resp.headers;
return resp.json().then((response) => {
this.isLoading = false;
this.isLoading = false;
this.store.storeAvailableCount(response.body.available_count);
this.store.storeStoppedCount(response.body.stopped_count);
this.store.storeEnvironments(response.body.environments);
this.store.setPagination(response.headers);
this.store.storeAvailableCount(response.available_count);
this.store.storeStoppedCount(response.stopped_count);
this.store.storeEnvironments(response.environments);
this.store.setPagination(headers);
});
},
},
};
......@@ -7,5 +7,8 @@ export default () => {
Cookies.set(el.name, el.value, {
expires: 365 * 10,
});
document.body.scrollTop = 0;
window.location.reload();
});
};
......@@ -99,8 +99,10 @@ document.addEventListener('DOMContentLoaded', () => {
page: currentPath,
}, document.title, currentPath);
this.updateGroups(response.json());
this.updatePagination(response.headers);
return response.json().then((data) => {
this.updateGroups(data);
this.updatePagination(response.headers);
});
})
.catch(this.handleErrorResponse);
},
......@@ -114,18 +116,19 @@ document.addEventListener('DOMContentLoaded', () => {
},
leaveGroup(group, collection) {
this.service.leaveGroup(group.leavePath)
.then(resp => resp.json())
.then((response) => {
$.scrollTo(0);
this.store.removeGroup(group, collection);
// eslint-disable-next-line no-new
new Flash(response.json().notice, 'notice');
new Flash(response.notice, 'notice');
})
.catch((response) => {
.catch((error) => {
let message = 'An error occurred. Please try again.';
if (response.status === 403) {
if (error.status === 403) {
message = 'Failed to leave the group. Please make sure you are not the only owner';
}
......
......@@ -202,10 +202,7 @@ export default {
this.poll = new Poll({
resource: this.service,
method: 'getData',
successCallback: (res) => {
const data = res.json();
this.store.updateState(data);
},
successCallback: res => res.json().then(data => this.store.updateState(data)),
errorCallback(err) {
throw new Error(err);
},
......
......@@ -54,9 +54,8 @@ export default class JobMediator {
}
successCallback(response) {
const data = response.json();
this.state.isLoading = false;
this.store.storeJob(data);
return response.json().then(data => this.store.storeJob(data));
}
errorCallback() {
......
......@@ -1270,7 +1270,7 @@ export default class Notes {
<div class="timeline-entry-inner">
<div class="timeline-icon">
<a href="/${currentUsername}">
<img class="avatar s40" src="${currentUserAvatar}">
<img class="avatar s40" src="${currentUserAvatar}" />
</a>
</div>
<div class="timeline-content ${discussionClass}">
......
......@@ -129,14 +129,11 @@
},
successCallback(resp) {
const response = {
headers: resp.headers,
body: resp.json(),
};
this.store.storeCount(response.body.count);
this.store.storePagination(response.headers);
this.setCommonData(response.body.pipelines);
return resp.json().then((response) => {
this.store.storeCount(response.count);
this.store.storePagination(resp.headers);
this.setCommonData(response.pipelines);
});
},
},
};
......
......@@ -73,8 +73,9 @@ export default {
fetchJobs() {
this.$http.get(this.stage.dropdown_path)
.then((response) => {
this.dropdownContent = response.json().html;
.then(response => response.json())
.then((data) => {
this.dropdownContent = data.html;
this.isLoading = false;
})
.catch(() => {
......
......@@ -40,10 +40,10 @@ export default class pipelinesMediator {
}
successCallback(response) {
const data = response.json();
this.state.isLoading = false;
this.store.storePipeline(data);
return response.json().then((data) => {
this.state.isLoading = false;
this.store.storePipeline(data);
});
}
errorCallback() {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, prefer-arrow-callback, consistent-return, object-shorthand, no-unused-vars, one-var, one-var-declaration-per-line, no-else-return, comma-dangle, max-len */
/* global Mousetrap */
/* global findFileURL */
import Cookies from 'js-cookie';
import findAndFollowLink from './shortcuts_dashboard_navigation';
......@@ -20,6 +19,7 @@ import findAndFollowLink from './shortcuts_dashboard_navigation';
const $globalDropdownMenu = $('.global-dropdown-menu');
const $globalDropdownToggle = $('.global-dropdown-toggle');
const findFileURL = document.body.dataset.findFile;
$('.global-dropdown').on('hide.bs.dropdown', () => {
$globalDropdownMenu.removeClass('shortcuts');
......
......@@ -28,8 +28,8 @@ export default class SidebarMediator {
fetch() {
this.service.get()
.then((response) => {
const data = response.json();
.then(response => response.json())
.then((data) => {
this.store.setAssigneeData(data);
this.store.setTimeTrackingData(data);
})
......
......@@ -92,13 +92,13 @@ export default {
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
({{mr.divergedCommitsCount}} {{commitsText}} behind)
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
</div>
......
......@@ -48,6 +48,7 @@ export default class MergeRequestStore {
this.sourceBranchLink = data.source_branch_with_namespace_link;
this.mergeError = data.merge_error;
this.targetBranchPath = data.target_branch_commits_path;
this.targetBranchTreePath = data.target_branch_tree_path;
this.conflictResolutionPath = data.conflict_resolution_path;
this.cancelAutoMergePath = data.cancel_merge_when_pipeline_succeeds_path;
this.removeWIPPath = data.remove_wip_path;
......
......@@ -44,9 +44,8 @@
text: this.$slots.textarea[0].elm.value,
},
)
.then((res) => {
const data = res.json();
.then(resp => resp.json())
.then((data) => {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body;
......
......@@ -14,11 +14,22 @@ Vue.http.interceptors.push((request, next) => {
});
});
// Inject CSRF token so we don't break any tests.
// Inject CSRF token and parse headers.
// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
// and polling.
Vue.http.interceptors.push((request, next) => {
if ($.rails) {
// eslint-disable-next-line no-param-reassign
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
request.headers.set('X-CSRF-Token', $.rails.csrfToken());
}
next();
next((response) => {
// Headers object has a `forEach` property that iterates through all values.
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
// eslint-disable-next-line no-param-reassign
response.headers = headers;
});
});
......@@ -231,11 +231,11 @@
.award-control-icon-positive,
.award-control-icon-super-positive {
@include transition(opacity, transform);
position: absolute;
left: 10px;
bottom: 6px;
opacity: 0;
@include transition(opacity, transform);
}
.award-control-text {
......
......@@ -35,8 +35,8 @@
.open {
.dropdown-menu,
.dropdown-menu-nav {
display: block;
@include set-visible;
display: block;
@media (max-width: $screen-xs-max) {
width: 100%;
......@@ -184,13 +184,15 @@
.dropdown-menu,
.dropdown-menu-nav {
@include set-invisible;
display: block;
position: absolute;
width: 100%;
width: auto;
top: 100%;
left: 0;
z-index: 9;
min-width: 240px;
max-width: 500px;
margin-top: 2px;
margin-bottom: 0;
font-size: 14px;
......@@ -200,7 +202,6 @@
border: 1px solid $dropdown-border-color;
border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color;
@include set-invisible;
@media (max-width: $screen-sm-min) {
width: 100%;
......@@ -675,8 +676,8 @@
}
.pika-single {
position: relative!important;
top: 0!important;
position: relative !important;
top: 0 !important;
border: 0;
box-shadow: none;
}
......
......@@ -368,7 +368,7 @@
margin-right: 0.3em;
}
& > .value {
> .value {
font-weight: 600;
}
}
......@@ -467,7 +467,7 @@
-webkit-flex-direction: column;
flex-direction: column;
&> span {
> span {
white-space: normal;
word-break: break-all;
}
......
......@@ -330,7 +330,7 @@ header {
padding-left: 5px;
.nav > li:not(.hidden-xs) {
display: table-cell!important;
display: table-cell !important;
width: 25%;
a {
......
......@@ -61,7 +61,7 @@
&:focus {
outline: none;
& i {
i {
visibility: visible;
}
}
......
......@@ -165,8 +165,8 @@
.cur {
.avatar {
border: 1px solid $white-light;
@include disableAllAnimation;
border: 1px solid $white-light;
}
}
......
......@@ -100,9 +100,9 @@
}
.table-mobile-header {
@include flex-max-width(40);
color: $gl-text-color-secondary;
text-align: left;
@include flex-max-width(40);
@media (min-width: $screen-md-min) {
display: none;
......
......@@ -11,7 +11,7 @@
.is-dragging {
// Important because plugin sets inline CSS
opacity: 1!important;
opacity: 1 !important;
* {
-webkit-user-select: none;
......@@ -19,8 +19,8 @@
-ms-user-select: none;
user-select: none;
// !important to make sure no style can override this when dragging
cursor: -webkit-grabbing!important;
cursor: grabbing!important;
cursor: -webkit-grabbing !important;
cursor: grabbing !important;
}
}
......
......@@ -244,6 +244,10 @@
}
}
.block-last {
padding: 16px 0;
}
.trigger-build-variable {
color: $code-color;
}
......
......@@ -250,8 +250,8 @@
}
.committed_ago {
float: right;
@extend .cgray;
float: right;
}
}
}
......
......@@ -24,9 +24,9 @@
.col-headers {
ul {
@include clearfix;
margin: 0;
padding: 0;
@include clearfix;
}
li {
......@@ -189,8 +189,8 @@
}
li {
list-style-type: none;
@include clearfix;
list-style-type: none;
}
.stage-nav-item {
......@@ -281,11 +281,11 @@
}
.stage-event-item {
@include clearfix;
list-style-type: none;
padding: 0 0 $gl-padding;
margin: 0 $gl-padding $gl-padding;
border-bottom: 1px solid $gray-darker;
@include clearfix;
&:last-child {
border-bottom: none;
......@@ -307,9 +307,9 @@
&.issue-title,
&.commit-title,
&.merge-merquest-title {
@include text-overflow();
max-width: 100%;
display: block;
@include text-overflow();
a {
color: $gl-text-color;
......
......@@ -91,6 +91,7 @@
.old_line,
.new_line {
@include user-select(none);
margin: 0;
border: none;
padding: 0 5px;
......@@ -99,7 +100,6 @@
min-width: 35px;
max-width: 50px;
width: 35px;
@include user-select(none);
a {
float: left;
......@@ -354,12 +354,12 @@
}
&.active {
cursor: default;
color: $gl-text-color;
&:hover {
text-decoration: none;
}
cursor: default;
color: $gl-text-color;
}
&.disabled {
......
......@@ -96,7 +96,7 @@
overflow: visible;
}
& > span {
> span {
padding-right: 4px;
}
......
......@@ -121,10 +121,11 @@ ul.notes {
overflow-y: hidden;
.note-text {
word-wrap: break-word;
@include md-typography;
// Reset ul style types since we're nested inside a ul already
@include bulleted-list;
word-wrap: break-word;
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
......@@ -250,7 +251,7 @@ ul.notes {
}
.note-text {
& p:first-child {
p:first-child {
display: none;
}
......
.js-pipeline-schedule-form {
.dropdown-select,
.dropdown-menu-toggle {
width: 100%!important;
width: 100% !important;
}
.gl-field-error {
......@@ -96,12 +96,12 @@
}
&:last-child {
& .pipeline-variable-row-remove-button {
.pipeline-variable-row-remove-button {
display: none;
}
@media (max-width: $screen-sm-max) {
& .pipeline-variable-value-input {
.pipeline-variable-value-input {
margin-right: $pipeline-variable-remove-button-width;
}
}
......@@ -137,6 +137,7 @@
}
.pipeline-variable-row-remove-button {
@include transition(color);
flex-shrink: 0;
display: flex;
justify-content: center;
......@@ -147,7 +148,6 @@
background: transparent;
border: 0;
color: $gl-text-color-secondary;
@include transition(color);
&:hover,
&:focus {
......
......@@ -26,7 +26,7 @@
margin-bottom: 5px;
}
& > .form-group {
> .form-group {
padding-left: 0;
}
......@@ -83,7 +83,7 @@
border: 1px solid $border-color;
}
& + .select2 a {
+ .select2 a {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
......@@ -587,9 +587,9 @@ pre.light-well {
}
.project-row {
@include basic-list-stats;
display: flex;
align-items: center;
@include basic-list-stats;
}
h3 {
......
......@@ -81,7 +81,7 @@
.todo-title {
display: flex;
& > .title-item {
> .title-item {
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
margin: 0 2px;
......
......@@ -5,13 +5,13 @@
}
.example {
padding: 15px;
border: 1px dashed $ui-dev-kit-example-border;
margin-bottom: 15px;
&::before {
content: "Example";
color: $ui-dev-kit-example-color;
}
padding: 15px;
border: 1px dashed $ui-dev-kit-example-border;
margin-bottom: 15px;
}
}
......@@ -147,13 +147,13 @@
}
ul.wiki-pages-list.content-list {
& ul {
ul {
list-style: none;
margin-left: 0;
padding-left: 15px;
}
& ul li {
ul li {
padding: 5px 0;
}
}
......
......@@ -37,7 +37,7 @@ ul.notes-form,
.issuable-details .content-block-small,
.edit-link,
.note-action-button {
display: none!important;
display: none !important;
}
pre {
......
......@@ -113,6 +113,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:html_emails_enabled,
:koding_enabled,
:koding_url,
:password_authentication_enabled,
:plantuml_enabled,
:plantuml_url,
:max_artifacts_size,
......@@ -135,7 +136,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:require_two_factor_authentication,
:session_expire_delay,
:sign_in_text,
:signin_enabled,
:signup_enabled,
:sentry_dsn,
:sentry_enabled,
......
......@@ -170,7 +170,7 @@ class ApplicationController < ActionController::Base
end
def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && current_user.allow_password_authentication?
return redirect_to new_profile_password_path
end
end
......
class PasswordsController < Devise::PasswordsController
include Gitlab::CurrentSettings
before_action :resource_from_email, only: [:create]
before_action :prevent_ldap_reset, only: [:create]
before_action :check_password_authentication_available, only: [:create]
before_action :throttle_reset, only: [:create]
def edit
......@@ -25,7 +27,7 @@ class PasswordsController < Devise::PasswordsController
def update
super do |resource|
if resource.valid? && resource.require_password?
if resource.valid? && resource.require_password_creation?
resource.update_attribute(:password_automatically_set, false)
end
end
......@@ -38,11 +40,11 @@ class PasswordsController < Devise::PasswordsController
self.resource = resource_class.find_by_email(email)
end
def prevent_ldap_reset
return unless resource && resource.ldap_user?
def check_password_authentication_available
return if current_application_settings.password_authentication_enabled? && (resource.nil? || resource.allow_password_authentication?)
redirect_to after_sending_reset_password_instructions_path_for(resource_name),
alert: "Cannot reset password for LDAP user."
alert: "Password authentication is unavailable."
end
def throttle_reset
......
......@@ -77,7 +77,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
end
def authorize_change_password!
return render_404 if @user.ldap_user?
render_404 unless @user.allow_password_authentication?
end
def user_params
......
......@@ -107,7 +107,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@target_project = @merge_request.target_project
@source_project = @merge_request.source_project
@commits = @merge_request.compare_commits.reverse
@commits = @merge_request.commits
@commit = @merge_request.diff_head_commit
@note_counts = Note.where(commit_id: @commits.map(&:id))
......
......@@ -58,7 +58,7 @@ class SessionsController < Devise::SessionsController
user = User.admins.last
return unless user && user.require_password?
return unless user && user.require_password_creation?
Users::UpdateService.new(user).execute do |user|
@token = user.generate_reset_token
......
module ApplicationSettingsHelper
delegate :gravatar_enabled?,
:signup_enabled?,
:signin_enabled?,
:password_authentication_enabled?,
:akismet_enabled?,
:koding_enabled?,
to: :current_application_settings
......@@ -35,7 +35,7 @@ module ApplicationSettingsHelper
# Return a group of checkboxes that use Bootstrap's button plugin for a
# toggle button effect.
def restricted_level_checkboxes(help_block_id, checkbox_name)
Gitlab::VisibilityLevel.options.map do |name, level|
Gitlab::VisibilityLevel.values.map do |level|
checked = restricted_visibility_levels(true).include?(level)
css_class = checked ? 'active' : ''
tag_name = "application_setting_visibility_level_#{level}"
......@@ -44,7 +44,7 @@ module ApplicationSettingsHelper
check_box_tag(checkbox_name, level, checked,
autocomplete: 'off',
'aria-describedby' => help_block_id,
id: tag_name) + visibility_level_icon(level) + name
id: tag_name) + visibility_level_icon(level) + visibility_level_label(level)
end
end
end
......
......@@ -50,12 +50,12 @@ module ButtonHelper
def http_clone_button(project, placement = 'right', append_link: true)
klass = 'http-selector'
klass << ' has-tooltip' if current_user.try(:require_password?) || current_user.try(:require_personal_access_token?)
klass << ' has-tooltip' if current_user.try(:require_password_creation?) || current_user.try(:require_personal_access_token_creation_for_git_auth?)
protocol = gitlab_config.protocol.upcase
tooltip_title =
if current_user.try(:require_password?)
if current_user.try(:require_password_creation?)
_("Set a password on your account to pull or push via %{protocol}.") % { protocol: protocol }
else
_("Create a personal access token on your account to pull or push via %{protocol}.") % { protocol: protocol }
......
......@@ -214,11 +214,11 @@ module ProjectsHelper
def show_no_password_message?
cookies[:hide_no_password_message].blank? && !current_user.hide_no_password &&
( current_user.require_password? || current_user.require_personal_access_token? )
( current_user.require_password_creation? || current_user.require_personal_access_token_creation_for_git_auth? )
end
def link_to_set_password
if current_user.require_password?
if current_user.require_password_creation?
link_to s_('SetPasswordToCloneLink|set a password'), edit_profile_password_path
else
link_to s_('CreateTokenToCloneLink|create a personal access token'), profile_personal_access_tokens_path
......@@ -518,4 +518,12 @@ module ProjectsHelper
current_application_settings.restricted_visibility_levels || []
end
def find_file_path
return unless @project && !@project.empty_repo?
ref = @ref || @project.repository.root_ref
project_find_file_path(@project, ref)
end
end
......@@ -237,6 +237,7 @@ class ApplicationSetting < ActiveRecord::Base
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
password_authentication_enabled: Settings.gitlab['password_authentication_enabled'],
performance_bar_allowed_group_id: nil,
plantuml_enabled: false,
plantuml_url: nil,
......@@ -251,7 +252,6 @@ class ApplicationSetting < ActiveRecord::Base
shared_runners_text: nil,
sidekiq_throttling_enabled: false,
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
two_factor_grace_period: 48,
......
......@@ -32,9 +32,6 @@ class MergeRequest < ActiveRecord::Base
after_create :ensure_merge_request_diff, unless: :importing?
after_update :reload_diff_if_branch_changed
delegate :commits, :real_size, :commit_shas, :commits_count,
to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
attr_accessor :allow_broken
......@@ -224,6 +221,36 @@ class MergeRequest < ActiveRecord::Base
"#{project.to_reference(from, full: full)}#{reference}"
end
def commits
if persisted?
merge_request_diff.commits
elsif compare_commits
compare_commits.reverse
else
[]
end
end
def commits_count
if persisted?
merge_request_diff.commits_count
elsif compare_commits
compare_commits.size
else
0
end
end
def commit_shas
if persisted?
merge_request_diff.commit_shas
elsif compare_commits
compare_commits.reverse.map(&:sha)
else
[]
end
end
def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
......@@ -246,9 +273,7 @@ class MergeRequest < ActiveRecord::Base
def diff_size
# Calling `merge_request_diff.diffs.real_size` will also perform
# highlighting, which we don't need here.
return real_size if merge_request_diff
diffs.real_size
merge_request_diff&.real_size || diffs.real_size
end
def diff_base_commit
......
......@@ -580,16 +580,20 @@ class User < ActiveRecord::Base
keys.count == 0 && Gitlab::ProtocolAccess.allowed?('ssh')
end
def require_password?
password_automatically_set? && !ldap_user? && current_application_settings.signin_enabled?
def require_password_creation?
password_automatically_set? && allow_password_authentication?
end
def require_personal_access_token?
return false if current_application_settings.signin_enabled? || ldap_user?
def require_personal_access_token_creation_for_git_auth?
return false if allow_password_authentication? || ldap_user?
PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none?
end
def allow_password_authentication?
!ldap_user? && current_application_settings.password_authentication_enabled?
end
def can_change_username?
gitlab_config.username_changing_enabled
end
......@@ -699,7 +703,7 @@ class User < ActiveRecord::Base
end
def sanitize_attrs
%w[name username skype linkedin twitter].each do |attr|
%w[username skype linkedin twitter].each do |attr|
value = public_send(attr)
public_send("#{attr}=", Sanitize.clean(value)) if value.present?
end
......
......@@ -76,6 +76,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
end
def target_branch_tree_path
if target_branch_exists?
project_tree_path(project, target_branch)
end
end
def target_branch_commits_path
if target_branch_exists?
project_commits_path(project, target_branch)
......@@ -94,7 +100,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
if source_branch_exists?
namespace = link_to(namespace, project_path(source_project))
branch = link_to(branch, project_commits_path(source_project, source_branch))
branch = link_to(branch, project_tree_path(source_project, source_branch))
end
if for_fork?
......
......@@ -97,6 +97,10 @@ class MergeRequestEntity < IssuableEntity
presenter(merge_request).target_branch_commits_path
end
expose :target_branch_tree_path do |merge_request|
presenter(merge_request).target_branch_tree_path
end
expose :new_blob_path do |merge_request|
if can?(current_user, :push_code, merge_request.project)
project_new_blob_path(merge_request.project, merge_request.source_branch)
......
......@@ -7,9 +7,8 @@ module MergeRequests
source_project = @project
@project = Project.find(params[:target_project_id]) if params[:target_project_id]
params[:target_project_id] ||= source_project.id
merge_request = MergeRequest.new
merge_request.target_project = @project
merge_request.source_project = source_project
merge_request.source_branch = params[:source_branch]
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch)
......
......@@ -145,9 +145,9 @@
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :signin_enabled do
= f.check_box :signin_enabled
Sign-in enabled
= f.label :password_authentication_enabled do
= f.check_box :password_authentication_enabled
Password authentication enabled
- if omniauth_enabled? && button_based_providers.any?
.form-group
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2'
......
......@@ -6,15 +6,15 @@
- else
= render 'devise/shared/tabs_normal'
.tab-content
- if signin_enabled? || ldap_enabled? || crowd_enabled?
- if password_authentication_enabled? || ldap_enabled? || crowd_enabled?
= render 'devise/shared/signin_box'
-# Signup only makes sense if you can also sign-in
- if signin_enabled? && signup_enabled?
- if password_authentication_enabled? && signup_enabled?
= render 'devise/shared/signup_box'
-# Show a message if none of the mechanisms above are enabled
- if !signin_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?)
- if !password_authentication_enabled? && !ldap_enabled? && !(omniauth_enabled? && devise_mapping.omniauthable?)
%div
No authentication methods configured.
......
......@@ -7,12 +7,12 @@
.login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) }
.login-body
= render 'devise/sessions/new_ldap', server: server
- if signin_enabled?
- if password_authentication_enabled?
.login-box.tab-pane{ id: 'ldap-standard', role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_base'
- elsif signin_enabled?
- elsif password_authentication_enabled?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body
= render 'devise/sessions/new_base'
......@@ -5,9 +5,9 @@
- @ldap_servers.each_with_index do |server, i|
%li{ class: active_when(i.zero? && !crowd_enabled?) }
= link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab'
- if signin_enabled?
- if password_authentication_enabled?
%li
= link_to 'Standard', '#ldap-standard', 'data-toggle' => 'tab'
- if signin_enabled? && signup_enabled?
- if password_authentication_enabled? && signup_enabled?
%li
= link_to 'Register', '#register-pane', 'data-toggle' => 'tab'
%ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist' }
%li.active{ role: 'presentation' }
%a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab' } Sign in
- if signin_enabled? && signup_enabled?
- if password_authentication_enabled? && signup_enabled?
%li{ role: 'presentation' }
%a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab' } Register
- BroadcastMessage.current.each do |message|
- BroadcastMessage.current&.each do |message|
= broadcast_message(message)
!!! 5
%html{ lang: I18n.locale, class: "#{page_class}" }
= render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } }
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
- if show_new_nav?
= render "layouts/header/new"
......
......@@ -75,7 +75,7 @@
%li
= link_to "Settings", profile_path
%li
= link_to "Turn on new nav", profile_preferences_path(anchor: "new-navigation")
= link_to "Turn on new navigation", profile_preferences_path(anchor: "new-navigation")
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
......@@ -91,8 +91,3 @@
= yield :header_content
= render 'shared/outdated_browser'
- if @project && !@project.empty_repo?
- if ref = @ref || @project.repository.root_ref
:javascript
var findFileURL = "#{project_find_file_path(@project, ref)}";
......@@ -69,7 +69,7 @@
%li
= link_to "Settings", profile_path
%li
= link_to "Turn off new nav", profile_preferences_path(anchor: "new-navigation")
= link_to "Turn off new navigation", profile_preferences_path(anchor: "new-navigation")
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, method: :delete, class: "sign-out-link"
......@@ -84,8 +84,3 @@
= icon('times', class: 'js-navbar-toggle-left', style: 'display: none;')
= render 'shared/outdated_browser'
- if @project && !@project.empty_repo?
- if ref = @ref || @project.repository.root_ref
:javascript
var findFileURL = "#{project_find_file_path(@project, ref)}";
......@@ -29,7 +29,7 @@
= link_to profile_emails_path, title: 'Emails' do
%span
Emails
- unless current_user.ldap_user?
- if current_user.allow_password_authentication?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do
%span
......
......@@ -30,7 +30,7 @@
%ul.dropdown-menu.dropdown-menu-align-right
%li.visible-xs-block.visible-sm-block
= link_to project_tree_path(@project, @commit) do
_('Browse Files')
#{ _('Browse Files') }
- unless @commit.has_been_reverted?(current_user)
%li.clearfix
= revert_commit_link(@commit, project_commit_path(@project, @commit.id), has_tooltip: false)
......
......@@ -11,7 +11,7 @@
#js-details-block-vue
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
.block{ class: ("block-first" if !@build.coverage) }
.block
.title
Job artifacts
- if @build.artifacts_expired?
......@@ -37,7 +37,7 @@
Browse
- if @build.trigger_request
.build-widget
.build-widget.block
%h4.title
Trigger
......@@ -55,7 +55,7 @@
.js-build-variable.trigger-build-variable= key
.js-build-value.trigger-build-value= value
.block
%div{ class: (@build.pipeline.stages_count > 1 ? "block" : "block-last") }
%p
Commit
= link_to @build.pipeline.short_sha, project_commit_path(@project, @build.pipeline.sha), class: 'commit-sha link-commit'
......@@ -69,7 +69,7 @@
- if @build.pipeline.stages_count > 1
.dropdown.build-dropdown
.title
%div
%span{ class: "ci-status-icon-#{@build.pipeline.status}" }
= ci_icon_for_status(@build.pipeline.status)
Pipeline
......
......@@ -8,7 +8,7 @@
%p
Metrics are automatically configured and monitored
based on a library of metrics from popular exporters.
= link_to 'More information', '#'
= link_to 'More information', help_page_path('user/project/integrations/prometheus')
.col-lg-9
.panel.panel-default.js-panel-monitored-metrics{ data: { "active-metrics" => "#{project_prometheus_active_metrics_path(@project, :json)}" } }
......@@ -41,5 +41,5 @@
%code
$CI_ENVIRONMENT_SLUG
to exporter&rsquo;s queries.
= link_to 'More information', '#'
= link_to 'More information', help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels')
%ul.list-unstyled.metrics-list.js-missing-var-metrics-list
---
title: "Insert user name directly without encoding"
merge_request: 10085
author: Nathan Neulinger <nneul@neulinger.org>
---
title: Supplement Portuguese Brazil translation of Project Page & Repository Page
merge_request: 12156
author: Huang Tao
---
title: Return `is_admin` attribute in the GET /user endpoint for admins
merge_request: 12811
author:
---
title: Updates vue resource and code according to breaking changes
merge_request:
author:
---
title: Bump scss-lint to 0.54.0
merge_request: 12733
author: Takuya Noguchi
---
title: Remove public/ci/favicon.ico
merge_request: 12803
author: Takuya Noguchi
---
title: Fix vertical space in job details sidebar
merge_request:
author:
---
title: Increase width of dropdown menus automatically
merge_request: 12809
author: Thomas Wucher
---
title: Enable BangFormat in scss-lint [ci skip]
merge_request: 12815
author: Takuya Noguchi
---
title: Enable DeclarationOrder in scss-lint
merge_request: 12805
author: Takuya Noguchi
---
title: Enable UnnecessaryParentReference in scss-lint
merge_request: 12738
author: Takuya Noguchi
---
title: Fixes needed when GitLab sign-in is not enabled
merge_request: 12491
author: Robin Bobbitt
---
title: MR branch link now links to tree instead of commits
merge_request:
author:
---
title: Replace 'browse_files.feature' spinach test with an rspec analog
merge_request: 12251
author: @blackst0ne
......@@ -223,7 +223,7 @@ rescue ArgumentError # no user configured
end
Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['password_authentication_enabled'] ||= true if Settings.gitlab['password_authentication_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing))(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
......
class RenameApplicationSettingsSigninEnabledToPasswordAuthenticationEnabled < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
rename_column_concurrently :application_settings, :signin_enabled, :password_authentication_enabled
end
def down
cleanup_concurrent_column_rename :application_settings, :password_authentication_enabled, :signin_enabled
end
end
class CleanupApplicationSettingsSigninEnabledRename < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
cleanup_concurrent_column_rename :application_settings, :signin_enabled, :password_authentication_enabled
end
def down
rename_column_concurrently :application_settings, :password_authentication_enabled, :signin_enabled
end
end
......@@ -41,7 +41,6 @@ ActiveRecord::Schema.define(version: 20170707184244) do
create_table "application_settings", force: :cascade do |t|
t.integer "default_projects_limit"
t.boolean "signup_enabled"
t.boolean "signin_enabled"
t.boolean "gravatar_enabled"
t.text "sign_in_text"
t.datetime "created_at"
......@@ -127,6 +126,7 @@ ActiveRecord::Schema.define(version: 20170707184244) do
t.boolean "help_page_hide_commercial_content", default: false
t.string "help_page_support_url"
t.integer "performance_bar_allowed_group_id"
t.boolean "password_authentication_enabled"
end
create_table "audit_events", force: :cascade do |t|
......
......@@ -25,7 +25,7 @@ Example response:
"id" : 1,
"default_branch_protection" : 2,
"restricted_visibility_levels" : [],
"signin_enabled" : true,
"password_authentication_enabled" : true,
"after_sign_out_path" : null,
"max_attachment_size" : 10,
"user_oauth_applications" : true,
......@@ -63,7 +63,7 @@ PUT /application/settings
| --------- | ---- | :------: | ----------- |
| `default_projects_limit` | integer | no | Project limit per user. Default is `100000` |
| `signup_enabled` | boolean | no | Enable registration. Default is `true`. |
| `signin_enabled` | boolean | no | Enable login via a GitLab account. Default is `true`. |
| `password_authentication_enabled` | boolean | no | Enable authentication via a GitLab account password. Default is `true`. |
| `gravatar_enabled` | boolean | no | Enable Gravatar |
| `sign_in_text` | string | no | Text on login page |
| `home_page_url` | string | no | Redirect to this URL when not logged in |
......@@ -102,7 +102,7 @@ Example response:
"id": 1,
"default_projects_limit": 100000,
"signup_enabled": true,
"signin_enabled": true,
"password_authentication_enabled": true,
"gravatar_enabled": true,
"sign_in_text": "",
"created_at": "2015-06-12T15:51:55.432Z",
......
......@@ -364,7 +364,7 @@ GET /user
Parameters:
- `sudo` (required) - the ID of a user
- `sudo` (optional) - the ID of a user to make the call in their place
```
GET /user
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment