Commit 9821bb86 authored by Robert Speicher's avatar Robert Speicher

Merge remote tracking branch 'ce/master' into 'master'

parent 7d415465
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.4.0 (unreleased) v 8.4.0 (unreleased)
- Add pagination headers to already paginated API resources
- Properly generate diff of orphan commits, like the first commit in a repository
- Improve the consistency of commit titles, branch names, tag names, issue/MR titles, on their respective project pages
- Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse) - Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse)
- Improved performance of finding issues for an entire group (Yorick Peterse) - Improved performance of finding issues for an entire group (Yorick Peterse)
- Added custom application performance measuring system powered by InfluxDB (Yorick Peterse) - Added custom application performance measuring system powered by InfluxDB (Yorick Peterse)
...@@ -14,6 +17,7 @@ v 8.4.0 (unreleased) ...@@ -14,6 +17,7 @@ v 8.4.0 (unreleased)
- Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
- Don't notify users twice if they are both project watchers and subscribers (Stan Hu) - Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
- Fix error with file size check with submodules (Stan Hu) - Fix error with file size check with submodules (Stan Hu)
- Remove gray background from layout in UI
- Implement new UI for group page - Implement new UI for group page
- Implement search inside emoji picker - Implement search inside emoji picker
- Add API support for looking up a user by username (Stan Hu) - Add API support for looking up a user by username (Stan Hu)
...@@ -43,9 +47,18 @@ v 8.4.0 (unreleased) ...@@ -43,9 +47,18 @@ v 8.4.0 (unreleased)
- Ajax filter by message for commits page - Ajax filter by message for commits page
- API: Add support for deleting a tag via the API (Robert Schilling) - API: Add support for deleting a tag via the API (Robert Schilling)
- Allow subsequent validations in CI Linter - Allow subsequent validations in CI Linter
- Show referenced MRs & Issues only when the current viewer can access them
- Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee)
- Add API support for managing project's builds
- Add API support for managing project's build triggers
- Add API support for managing project's build variables
- Allow broadcast messages to be edited
- Autosize Markdown textareas
- Import GitHub wiki into GitLab
v 8.3.4 v 8.3.4
- Use gitlab-workhorse 0.5.4 (fixes API routing bug) - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
- Add build artifacts browser
v 8.3.3 v 8.3.3
- Preserve CE behavior with JIRA integration by only calling API if URL is set - Preserve CE behavior with JIRA integration by only calling API if URL is set
...@@ -60,6 +73,8 @@ v 8.3.3 ...@@ -60,6 +73,8 @@ v 8.3.3
- Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu) - Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
- Use WOFF versions of SourceSansPro fonts - Use WOFF versions of SourceSansPro fonts
- Fix regression when builds were not generated for tags created through web/api interface - Fix regression when builds were not generated for tags created through web/api interface
- Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells)
- Fix missing artifacts and build traces for build created before 8.3
v 8.3.2 v 8.3.2
- Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
...@@ -77,6 +92,7 @@ v 8.3.0 ...@@ -77,6 +92,7 @@ v 8.3.0
- Add open_issues_count to project API (Stan Hu) - Add open_issues_count to project API (Stan Hu)
- Expand character set of usernames created by Omniauth (Corey Hinshaw) - Expand character set of usernames created by Omniauth (Corey Hinshaw)
- Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
- Add unsubscribe link in the email footer (Zeger-Jan van de Weg)
- Provide better diagnostic message upon project creation errors (Stan Hu) - Provide better diagnostic message upon project creation errors (Stan Hu)
- Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu) - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
- Remove api credentials from link to build_page - Remove api credentials from link to build_page
......
...@@ -334,9 +334,9 @@ merge request: ...@@ -334,9 +334,9 @@ merge request:
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript) 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
1. [Shell commands](doc/development/shell_commands.md) created by GitLab 1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. [Database Migrations](doc/development/migration_style_guide.md) 1. [Database Migrations](doc/development/migration_style_guide.md)
1. [Documentation styleguide](doc_styleguide.md) 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. [Documentation styleguide](doc/development/doc_styleguide.md)
1. Interface text should be written subjectively instead of objectively. It 1. Interface text should be written subjectively instead of objectively. It
should be the GitLab core team addressing a person. It should be written in should be the GitLab core team addressing a person. It should be written in
present time and never use past tense (has been/was). For example instead present time and never use past tense (has been/was). For example instead
......
class @Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true
$(".event-filter .btn").bind "click", (event) => $(".event-filter a").bind "click", (event) =>
event.preventDefault() event.preventDefault()
@toggleFilter($(event.currentTarget)) @toggleFilter($(event.currentTarget))
@reloadActivities() @reloadActivities()
...@@ -12,7 +12,7 @@ class @Activities ...@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) -> toggleFilter: (sender) ->
sender.toggleClass "active" sender.closest('li').toggleClass "active"
event_filters = $.cookie("event_filter") event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0] filter = sender.attr("id").split("_")[0]
if event_filters if event_filters
......
...@@ -10,19 +10,19 @@ class @Admin ...@@ -10,19 +10,19 @@ class @Admin
$('body').on 'click', '.js-toggle-colors-link', (e) -> $('body').on 'click', '.js-toggle-colors-link', (e) ->
e.preventDefault() e.preventDefault()
$('.js-toggle-colors-link').hide() $('.js-toggle-colors-container').toggle()
$('.js-toggle-colors-container').show()
$('input#broadcast_message_color').on 'input', -> $('input#broadcast_message_color').on 'input', ->
previewColor = $('input#broadcast_message_color').val() previewColor = $(@).val()
$('div.broadcast-message-preview').css('background-color', previewColor) $('div.broadcast-message-preview').css('background-color', previewColor)
$('input#broadcast_message_font').on 'input', -> $('input#broadcast_message_font').on 'input', ->
previewColor = $('input#broadcast_message_font').val() previewColor = $(@).val()
$('div.broadcast-message-preview').css('color', previewColor) $('div.broadcast-message-preview').css('color', previewColor)
$('textarea#broadcast_message_message').on 'input', -> $('textarea#broadcast_message_message').on 'input', ->
previewMessage = $('textarea#broadcast_message_message').val() previewMessage = $(@).val()
previewMessage = "Your message here" if previewMessage.trim() == ''
$('div.broadcast-message-preview span').text(previewMessage) $('div.broadcast-message-preview span').text(previewMessage)
$('.log-tabs a').click (e) -> $('.log-tabs a').click (e) ->
......
#= require autosize
$ ->
autosize($('.js-autosize'))
...@@ -48,15 +48,16 @@ class @MergeRequest ...@@ -48,15 +48,16 @@ class @MergeRequest
_this = @ _this = @
$('a.btn-close, a.btn-reopen').on 'click', (e) -> $('a.btn-close, a.btn-reopen').on 'click', (e) ->
$this = $(this) $this = $(this)
if $this.data('submitted') shouldSubmit = $this.hasClass('btn-comment')
if shouldSubmit && $this.data('submitted')
return return
if shouldSubmit
if $this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')
e.preventDefault() e.preventDefault()
e.stopImmediatePropagation() e.stopImmediatePropagation()
shouldSubmit = $this.hasClass('btn-comment')
console.log("shouldSubmit")
if shouldSubmit
_this.submitNoteForm($this.closest('form'),$this) _this.submitNoteForm($this.closest('form'),$this)
submitNoteForm: (form, $button) => submitNoteForm: (form, $button) =>
noteText = form.find("textarea.js-note-text").val() noteText = form.find("textarea.js-note-text").val()
if noteText.trim().length > 0 if noteText.trim().length > 0
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
# #
# ### Example Markup # ### Example Markup
# #
# <ul class="nav nav-tabs merge-request-tabs"> # <ul class="nav-links merge-request-tabs">
# <li class="notes-tab active"> # <li class="notes-tab active">
# <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1"> # <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
# Discussion # Discussion
......
#= require autosave #= require autosave
#= require autosize
#= require dropzone #= require dropzone
#= require dropzone_input #= require dropzone_input
#= require gfm_auto_complete #= require gfm_auto_complete
...@@ -246,6 +247,7 @@ class @Notes ...@@ -246,6 +247,7 @@ class @Notes
else else
previewButton.removeClass("turn-on").addClass "turn-off" previewButton.removeClass("turn-on").addClass "turn-off"
autosize(textarea)
new Autosave textarea, [ new Autosave textarea, [
"Note" "Note"
form.find("#note_commit_id").val() form.find("#note_commit_id").val()
...@@ -521,9 +523,13 @@ class @Notes ...@@ -521,9 +523,13 @@ class @Notes
if textarea.val().trim().length > 0 if textarea.val().trim().length > 0
form.find('.js-note-target-reopen').text('Comment & reopen') form.find('.js-note-target-reopen').text('Comment & reopen')
form.find('.js-note-target-close').text('Comment & close') form.find('.js-note-target-close').text('Comment & close')
form.find('.js-note-target-reopen').addClass('btn-comment-and-reopen')
form.find('.js-note-target-close').addClass('btn-comment-and-close')
else else
form.find('.js-note-target-reopen').text('Reopen') form.find('.js-note-target-reopen').text('Reopen')
form.find('.js-note-target-close').text('Close') form.find('.js-note-target-close').text('Close')
form.find('.js-note-target-reopen').removeClass('btn-comment-and-reopen')
form.find('.js-note-target-close').removeClass('btn-comment-and-close')
initTaskList: -> initTaskList: ->
@enableTaskList() @enableTaskList()
......
#= require latinise
class @Wikis class @Wikis
constructor: -> constructor: ->
$('.build-new-wiki').bind "click", (e) -> $('.build-new-wiki').bind 'click', (e) =>
$('[data-error~=slug]').addClass("hidden") $('[data-error~=slug]').addClass('hidden')
$('p.hint').show()
field = $('#new_wiki_path') field = $('#new_wiki_path')
valid_slug_pattern = /^[\w\/-]+$/ slug = @slugify(field.val())
slug = field.val() if (slug.length > 0)
if slug.match valid_slug_pattern
path = field.attr('data-wikis-path') path = field.attr('data-wikis-path')
if(slug.length > 0) location.href = path + '/' + slug
location.href = path + "/" + slug
else dasherize: (value) ->
e.preventDefault() value.replace(/[_\s]+/g, '-')
$('p.hint').hide()
$('[data-error~=slug]').removeClass("hidden") slugify: (value) =>
@dasherize(value.trim().toLowerCase().latinise())
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
@import "framework/lists.scss"; @import "framework/lists.scss";
@import "framework/markdown_area.scss"; @import "framework/markdown_area.scss";
@import "framework/mobile.scss"; @import "framework/mobile.scss";
@import "framework/nav.scss";
@import "framework/pagination.scss"; @import "framework/pagination.scss";
@import "framework/panels.scss"; @import "framework/panels.scss";
@import "framework/selects.scss"; @import "framework/selects.scss";
......
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
line-height: 36px; line-height: 36px;
} }
.content-block,
.gray-content-block { .gray-content-block {
margin: -$gl-padding; margin-top: 0;
margin-bottom: -$gl-padding;
background-color: $background-color; background-color: $background-color;
padding: $gl-padding; padding: $gl-padding;
margin-bottom: 0px; margin-bottom: 0px;
...@@ -86,10 +86,7 @@ ...@@ -86,10 +86,7 @@
.cover-block { .cover-block {
text-align: center; text-align: center;
background: $background-color; background: $background-color;
margin: -$gl-padding; padding-top: 44px;
margin-bottom: 0;
padding: 44px $gl-padding;
border-bottom: 1px solid $border-color;
position: relative; position: relative;
.avatar-holder { .avatar-holder {
...@@ -136,3 +133,19 @@ ...@@ -136,3 +133,19 @@
.block-connector { .block-connector {
margin-top: -1px; margin-top: -1px;
} }
.nav-block {
.controls {
float: right;
margin-top: 11px;
}
}
.content-block {
padding: $gl-padding 0;
border-bottom: 1px solid $border-color;
&.oneline-block {
line-height: 42px;
}
}
@mixin btn-default { @mixin btn-default {
@include border-radius(3px); @include border-radius(3px);
border-width: 1px; font-size: $gl-font-size;
border-style: solid;
font-size: 15px;
font-weight: 500; font-weight: 500;
line-height: 18px; padding: $gl-vert-padding $gl-padding;
padding: 11px $gl-padding;
letter-spacing: .4px;
&:focus, &:focus,
&:active { &:active {
...@@ -17,8 +13,6 @@ ...@@ -17,8 +13,6 @@
@mixin btn-middle { @mixin btn-middle {
@include btn-default; @include btn-default;
@include border-radius(3px);
padding: 11px 24px;
} }
@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { @mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
...@@ -74,16 +68,15 @@ ...@@ -74,16 +68,15 @@
@include btn-default; @include btn-default;
@include btn-white; @include btn-white;
&.btn-small,
&.btn-sm { &.btn-sm {
padding: 5px 10px; padding: 4px 10px;
} font-size: 13px;
line-height: 18px;
&.btn-nr {
padding: 7px 10px;
} }
&.btn-xs { &.btn-xs {
padding: 1px 5px; padding: 2px 5px;
} }
&.btn-success, &.btn-success,
...@@ -131,6 +124,12 @@ ...@@ -131,6 +124,12 @@
&:last-child { &:last-child {
margin-right: 0px; margin-right: 0px;
} }
&.btn-xs {
margin-right: 3px;
}
}
&.disabled {
pointer-events: auto !important;
} }
} }
...@@ -153,33 +152,42 @@ ...@@ -153,33 +152,42 @@
} }
} }
.btn-group-next { .btn-clipboard {
border: none;
padding: 0 5px;
}
.input-group-btn {
.btn { .btn {
padding: 9px 0px; @include btn-gray;
font-size: 15px; @include btn-middle;
color: #7f8fa4;
border-color: #e7e9ed; &:hover {
width: 140px; outline: none;
}
.badge { &:focus {
font-weight: normal; outline: none;
background-color: #eee;
color: #78a;
} }
&.active { &:active {
border-color: $gl-info; outline: none;
background: $gl-info; }
color: #fff;
.badge { &.btn-clipboard {
color: $gl-info; padding-left: 15px;
background-color: white; padding-right: 15px;
} }
} }
.active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
} }
}
.btn-clipboard { .btn-green {
border: none; @include btn-green
}
} }
...@@ -383,75 +383,6 @@ table { ...@@ -383,75 +383,6 @@ table {
} }
} }
.center-top-menu, .left-top-menu {
@include nav-menu;
text-align: center;
margin-top: 5px;
margin-bottom: $gl-padding;
height: auto;
margin-top: -$gl-padding;
&.no-bottom {
margin-bottom: 0;
}
&.no-top {
margin-top: 0;
}
li a {
display: inline-block;
padding-top: $gl-padding;
padding-bottom: 11px;
margin-bottom: -1px;
}
&.bottom-border {
border-bottom: 1px solid $border-color;
height: 57px;
}
&.wide {
margin-left: -$gl-padding;
margin-right: -$gl-padding;
}
}
.left-top-menu {
text-align: left;
border-bottom: 1px solid #EEE;
}
.center-middle-menu {
@include nav-menu;
padding: 0;
text-align: center;
margin: -$gl-padding;
margin-top: 0;
margin-bottom: 0;
height: 58px;
border-bottom: 1px solid $border-color;
li {
&:after {
content: "|";
color: $border-gray-light;
}
&:last-child {
&:after {
content: none;
}
}
> a {
display: inline-block;
text-transform: uppercase;
font-size: 13px;
}
}
}
.dropzone .dz-preview .dz-progress { .dropzone .dz-preview .dz-progress {
border-color: $border-color !important; border-color: $border-color !important;
} }
......
...@@ -3,11 +3,8 @@ ...@@ -3,11 +3,8 @@
* *
*/ */
.file-holder { .file-holder {
margin-left: -$gl-padding;
margin-right: -$gl-padding;
border: none; border: none;
border-top: 1px solid #E7E9EE; border: 1px solid $border-color;
border-bottom: 1px solid #E7E9EE;
&.readme-holder { &.readme-holder {
border-bottom: 0; border-bottom: 0;
......
...@@ -8,10 +8,12 @@ ...@@ -8,10 +8,12 @@
.flash-notice { .flash-notice {
@extend .alert; @extend .alert;
@extend .alert-info; @extend .alert-info;
margin: 0;
} }
.flash-alert { .flash-alert {
@extend .alert; @extend .alert;
@extend .alert-danger; @extend .alert-danger;
margin: 0;
} }
} }
...@@ -3,23 +3,39 @@ ...@@ -3,23 +3,39 @@
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 300; font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf.woff'); src:
local('Source Sans Pro Light'),
local('SourceSansPro-Light'),
font-url('SourceSansPro-Light.ttf.woff2') format('woff2'),
font-url('SourceSansPro-Light.ttf.woff') format('woff');
} }
@font-face { @font-face {
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf.woff'); src:
local('Source Sans Pro'),
local('SourceSansPro-Regular'),
font-url('SourceSansPro-Regular.ttf.woff2') format('woff2'),
font-url('SourceSansPro-Regular.ttf.woff') format('woff');
} }
@font-face { @font-face {
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 600; font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf.woff'); src:
local('Source Sans Pro Semibold'),
local('SourceSansPro-Semibold'),
font-url('SourceSansPro-Semibold.ttf.woff2') format('woff2'),
font-url('SourceSansPro-Semibold.ttf.woff') format('woff');
} }
@font-face { @font-face {
font-family: 'Source Sans Pro'; font-family: 'Source Sans Pro';
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf.woff'); src:
local('Source Sans Pro Bold'),
local('SourceSansPro-Bold'),
font-url('SourceSansPro-Bold.ttf.woff2') format('woff2'),
font-url('SourceSansPro-Bold.ttf.woff') format('woff');
} }
...@@ -74,8 +74,10 @@ label { ...@@ -74,8 +74,10 @@ label {
.form-control { .form-control {
@include box-shadow(none); @include box-shadow(none);
height: 42px; }
padding: 8px $gl-padding;
.form-control-inline {
display: inline;
} }
.wiki-content { .wiki-content {
......
...@@ -28,6 +28,7 @@ header { ...@@ -28,6 +28,7 @@ header {
min-height: $header-height; min-height: $header-height;
background-color: #fff; background-color: #fff;
border: none; border: none;
border-bottom: 1px solid #EEE;
.container-fluid { .container-fluid {
width: 100% !important; width: 100% !important;
......
...@@ -5,8 +5,6 @@ html { ...@@ -5,8 +5,6 @@ html {
} }
body { body {
background-color: #F3F3F3 !important;
&.navless { &.navless {
background-color: white !important; background-color: white !important;
} }
......
...@@ -109,10 +109,8 @@ ul.content-list { ...@@ -109,10 +109,8 @@ ul.content-list {
padding: 0; padding: 0;
> li { > li {
padding: $gl-padding; padding: $gl-padding 0;
border-color: $table-border-color; border-color: $table-border-color;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
color: $gl-gray; color: $gl-gray;
.avatar { .avatar {
...@@ -133,6 +131,7 @@ ul.content-list { ...@@ -133,6 +131,7 @@ ul.content-list {
.panel > .content-list { .panel > .content-list {
li { li {
margin: 0; margin: 0;
padding: $gl-padding;
} }
} }
......
...@@ -65,13 +65,6 @@ ...@@ -65,13 +65,6 @@
position: relative; position: relative;
} }
.md-header {
ul {
float: left;
margin-bottom: 1px;
}
}
.referenced-users { .referenced-users {
color: #4c4e54; color: #4c4e54;
padding-top: 10px; padding-top: 10px;
...@@ -85,28 +78,12 @@ ...@@ -85,28 +78,12 @@
box-shadow: none; box-shadow: none;
} }
.new_note,
.edit_note,
.detail-page-description,
.milestone-description,
.wiki-content,
.merge-request-form {
.nav-tabs {
margin-bottom: 0;
border: none;
li a,
li.active a {
border: 1px solid #DDD;
}
}
}
.markdown-area { .markdown-area {
@include border-radius(0); @include border-radius(0);
background: #FFF; background: #FFF;
border: 1px solid #ddd; border: 1px solid #ddd;
min-height: 140px; min-height: 140px;
max-height: 430px;
padding: 5px; padding: 5px;
box-shadow: none; box-shadow: none;
width: 100%; width: 100%;
......
...@@ -118,38 +118,3 @@ ...@@ -118,38 +118,3 @@
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
@mixin nav-menu {
padding: 0;
margin: 0;
list-style: none;
height: 56px;
li {
display: inline-block;
a {
padding: 14px;
font-size: 15px;
line-height: 28px;
color: #959494;
border-bottom: 2px solid transparent;
&:hover, &:active, &:focus {
text-decoration: none;
outline: none;
}
}
&.active a {
color: #616060;
border-bottom: 2px solid #4688f1;
}
.badge {
font-weight: normal;
background-color: #eee;
color: #78a;
}
}
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
padding-right: 5px; padding-right: 5px;
} }
.nav.nav-tabs > li > a { .nav-links > li > a {
padding: 10px; padding: 10px;
font-size: 12px; font-size: 12px;
margin-right: 3px; margin-right: 3px;
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
display: none; display: none;
} }
.center-top-menu, .left-top-menu { .nav-links, .nav-links {
li a { li a {
font-size: 14px; font-size: 14px;
padding: 19px 10px; padding: 19px 10px;
...@@ -100,11 +100,6 @@ ...@@ -100,11 +100,6 @@
} }
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
.page-with-sidebar .content-wrapper {
padding: 0;
padding-top: 1px;
}
.issues-filters { .issues-filters {
.milestone-filter, .labels-filter { .milestone-filter, .labels-filter {
display: none; display: none;
......
.nav-links {
padding: 0;
margin: 0;
list-style: none;
height: auto;
border-bottom: 1px solid $border-color;
li {
display: inline-block;
a {
display: inline-block;
padding: 14px;
padding-top: $gl-padding;
padding-bottom: 11px;
margin-bottom: -1px;
font-size: 15px;
line-height: 28px;
color: #959494;
border-bottom: 2px solid transparent;
&:hover, &:active, &:focus {
text-decoration: none;
outline: none;
}
}
&.active a {
color: #000000;
border-bottom: 2px solid #4688f1;
}
.badge {
font-weight: normal;
background-color: #eee;
color: #78a;
}
}
}
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
.select2-choice { .select2-choice {
background: #FFF; background: #FFF;
border-color: #DDD; border-color: #DDD;
height: 42px; height: 36px;
padding: 8px $gl-padding; padding: 6px $gl-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
......
...@@ -21,11 +21,10 @@ ...@@ -21,11 +21,10 @@
.content-wrapper { .content-wrapper {
width: 100%; width: 100%;
padding: 20px;
.container-fluid { .container-fluid {
background: #FFF; background: #FFF;
padding: $gl-padding; padding: 0 $gl-padding;
&.container-blank { &.container-blank {
background: none; background: none;
......
.table-holder { .table-holder {
margin: -$gl-padding; margin: 0;
margin-top: 0;
margin-bottom: 0;
} }
table { table {
...@@ -32,6 +30,7 @@ table { ...@@ -32,6 +30,7 @@ table {
} }
th { th {
background-color: $background-color;
font-weight: normal; font-weight: normal;
font-size: 15px; font-size: 15px;
border-bottom: 1px solid $border-color !important; border-bottom: 1px solid $border-color !important;
......
...@@ -5,10 +5,8 @@ ...@@ -5,10 +5,8 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding; padding: $gl-padding 0;
border-color: $table-border-color; border-color: $table-border-color;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
color: $gl-gray; color: $gl-gray;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;
......
...@@ -99,47 +99,6 @@ ...@@ -99,47 +99,6 @@
} }
} }
// Nav tabs
.nav.nav-tabs {
margin-bottom: 15px;
li {
> a {
margin-right: 5px;
line-height: 20px;
border-color: #EEE;
color: #888;
border-bottom: 1px solid #ddd;
.badge {
background-color: #eee;
color: #888;
text-shadow: 0 1px 1px #fff;
}
i.fa {
line-height: 14px;
}
}
&.active {
> a {
border-color: #CCC;
border-bottom: 1px solid #fff;
color: #333;
font-weight: bold;
}
}
}
}
.nav-tabs > li > a,
.nav-pills > li > a {
color: #666;
}
.nav-pills > .active > a > span > .badge {
background-color: #fff;
color: $gl-primary;
}
/** /**
* fix to keep tooltips position in top navigation bar * fix to keep tooltips position in top navigation bar
......
...@@ -46,7 +46,7 @@ $font-size-base: $gl-font-size; ...@@ -46,7 +46,7 @@ $font-size-base: $gl-font-size;
// //
//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
$padding-base-vertical: 9px; $padding-base-vertical: $gl-vert-padding;
$padding-base-horizontal: $gl-padding; $padding-base-horizontal: $gl-padding;
$component-active-color: #fff; $component-active-color: #fff;
$component-active-bg: $brand-info; $component-active-bg: $brand-info;
......
...@@ -177,7 +177,7 @@ body { ...@@ -177,7 +177,7 @@ body {
} }
.page-title { .page-title {
margin-top: 0px; margin-top: $gl-padding;
line-height: 1.3; line-height: 1.3;
font-size: 1.25em; font-size: 1.25em;
font-weight: 600; font-weight: 600;
......
...@@ -22,6 +22,7 @@ $header-height: 58px; ...@@ -22,6 +22,7 @@ $header-height: 58px;
$fixed-layout-width: 1280px; $fixed-layout-width: 1280px;
$gl-gray: #5a5a5a; $gl-gray: #5a5a5a;
$gl-padding: 16px; $gl-padding: 16px;
$gl-vert-padding: 6px;
$gl-padding-top:10px; $gl-padding-top:10px;
$gl-avatar-size: 46px; $gl-avatar-size: 46px;
$secondary-text: #7f8fa4; $secondary-text: #7f8fa4;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
position: absolute; position: absolute;
top: 0px; top: 0px;
right: 4px; right: 4px;
line-height: 40px; line-height: 56px;
} }
a.js-zen-leave { a.js-zen-leave {
......
.branch-name{
font-weight: 600;
}
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
display: block; display: block;
} }
.commit-row-title .commit-title {
font-weight: 600;
}
.commit-author, .commit-committer{ .commit-author, .commit-committer{
display: block; display: block;
color: #999; color: #999;
...@@ -35,6 +39,8 @@ ...@@ -35,6 +39,8 @@
} }
.commit-box { .commit-box {
border-top: 1px solid $border-color;
.commit-title { .commit-title {
margin: 0; margin: 0;
font-size: 23px; font-size: 23px;
......
.detail-page-header { .detail-page-header {
margin: -$gl-padding; padding: 11px 0;
padding: 7px $gl-padding;
margin-bottom: 0px;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
color: #5c5d5e; color: #5c5d5e;
font-size: 16px; font-size: 16px;
......
// Common // Common
.diff-file { .diff-file {
margin-left: -$gl-padding; border: 1px solid $border-color;
margin-right: -$gl-padding; border-top: none;
border: none;
border-bottom: 1px solid #E7E9EE;
.diff-header { .diff-header {
position: relative; position: relative;
...@@ -23,14 +21,6 @@ ...@@ -23,14 +21,6 @@
} }
} }
.diff-controls {
.btn {
padding: 0px 10px;
font-size: 13px;
line-height: 28px;
}
}
.commit-short-id { .commit-short-id {
font-family: $monospace_font; font-family: $monospace_font;
font-size: smaller; font-size: smaller;
......
...@@ -4,9 +4,7 @@ ...@@ -4,9 +4,7 @@
*/ */
.event-item { .event-item {
font-size: $gl-font-size; font-size: $gl-font-size;
padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size + 15px); padding: $gl-padding 0 $gl-padding ($gl-avatar-size + 15px);
margin-left: -$gl-padding;
margin-right: -$gl-padding;
border-bottom: 1px solid $table-border-color; border-bottom: 1px solid $table-border-color;
color: #7f8fa4; color: #7f8fa4;
......
...@@ -11,3 +11,8 @@ ...@@ -11,3 +11,8 @@
height: 42px; height: 42px;
} }
} }
.content-list .group-name {
font-weight: 600;
color: #4c4e54;
}
...@@ -27,10 +27,10 @@ ...@@ -27,10 +27,10 @@
.project-issuable-filter { .project-issuable-filter {
.controls { .controls {
float: right; float: right;
margin-top: 7px; margin-top: 11px;
} }
.center-top-menu { .nav-links {
text-align: left; text-align: left;
} }
} }
...@@ -105,8 +105,13 @@ ...@@ -105,8 +105,13 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
cite {
font-style: normal;
}
button { button {
float: right; float: right;
padding: 3px 5px;
} }
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.issue-title { .issue-title {
margin-bottom: 5px; margin-bottom: 5px;
font-size: $list-font-size; font-size: $list-font-size;
font-weight: bold; font-weight: 600;
} }
.issue-info { .issue-info {
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
* *
*/ */
.mr-state-widget { .mr-state-widget {
background: #F7F8FA; background: $background-color;
color: $gl-gray; color: $gl-gray;
border: 1px solid #dce0e6; border: 1px solid $border-color;
@include border-radius(2px); @include border-radius(2px);
form { form {
...@@ -150,7 +150,7 @@ ...@@ -150,7 +150,7 @@
.merge-request-title { .merge-request-title {
margin-bottom: 5px; margin-bottom: 5px;
font-size: $list-font-size; font-size: $list-font-size;
font-weight: bold; font-weight: 600;
} }
.merge-request-info { .merge-request-info {
......
...@@ -159,6 +159,7 @@ ...@@ -159,6 +159,7 @@
.edit_note { .edit_note {
.markdown-area { .markdown-area {
min-height: 140px; min-height: 140px;
max-height: 430px;
} }
.note-form-actions { .note-form-actions {
background: transparent; background: transparent;
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
} }
.project-home-panel { .project-home-panel {
padding-bottom: 40px;
border-bottom: 1px solid $border-color;
.cover-controls { .cover-controls {
.project-settings-dropdown { .project-settings-dropdown {
...@@ -51,6 +53,8 @@ ...@@ -51,6 +53,8 @@
} }
.notifications-btn { .notifications-btn {
margin-top: -28px;
.fa-bell { .fa-bell {
margin-right: 6px; margin-right: 6px;
} }
...@@ -75,17 +79,6 @@ ...@@ -75,17 +79,6 @@
} }
} }
.git-clone-holder {
max-width: 498px;
.form-control {
background: #FFF;
font-size: 14px;
height: 42px;
margin-left: -1px;
}
}
.visibility-level-label { .visibility-level-label {
@extend .btn; @extend .btn;
@extend .btn-gray; @extend .btn-gray;
...@@ -98,25 +91,31 @@ ...@@ -98,25 +91,31 @@
} }
} }
.git-clone-holder {
display: inline-table;
position: relative;
}
.project-repo-buttons { .project-repo-buttons {
margin-top: 12px; margin-top: 12px;
margin-bottom: 0px; margin-bottom: 0px;
}
.count-buttons { .count-buttons {
display: block; display: block;
margin-bottom: 12px; margin-bottom: 12px;
} }
.clone-row {
.split-repo-buttons,
.project-clone-holder {
display: inline-block;
}
.split-repo-buttons {
margin: 0 12px;
}
}
.btn { .btn {
@include btn-gray; @include btn-gray;
text-transform: none; text-transform: none;
} }
.count-with-arrow { .count-with-arrow {
display: inline-block; display: inline-block;
position: relative; position: relative;
...@@ -161,8 +160,8 @@ ...@@ -161,8 +160,8 @@
border-style: solid; border-style: solid;
font-size: 13px; font-size: 13px;
font-weight: 600; font-weight: 600;
line-height: 20px; line-height: 13px;
padding: 11px 16px; padding: $gl-vert-padding $gl-padding;
letter-spacing: .4px; letter-spacing: .4px;
padding: 10px; padding: 10px;
text-align: center; text-align: center;
...@@ -178,6 +177,7 @@ ...@@ -178,6 +177,7 @@
} }
} }
} }
}
} }
.split-one { .split-one {
...@@ -189,121 +189,6 @@ ...@@ -189,121 +189,6 @@
} }
} }
.git-clone-holder {
.project-home-dropdown + & {
margin-right: 45px;
}
.clone-options {
display: table-cell;
a.btn {
width: 100%;
}
}
.form-control {
cursor: auto;
@extend .monospace;
background: #FAFAFA;
width: 101%;
}
.input-group-addon {
background: #f7f8fa;
&.git-protocols {
padding: 0;
border: none;
.input-group-btn:last-child > .btn {
@include border-radius-right(0);
border-left: 1px solid #c6cacf;
margin-left: -2px !important;
}
}
}
}
.projects-search-form {
.input-group .form-control {
height: 42px;
}
}
.input-group-btn {
.btn {
@include btn-gray;
@include btn-middle;
&:hover {
outline: none;
}
&:focus {
outline: none;
}
&:active {
outline: none;
}
&.btn-clipboard {
padding-left: 15px;
padding-right: 15px;
}
}
.active {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
}
.btn-green {
@include btn-green
}
}
.split-repo-buttons {
display: inline-table;
margin: 0 12px 0 12px;
.btn{
@include btn-gray;
@include btn-default;
}
.dropdown-toggle {
margin: -5px;
}
.update-mirror-button {
margin-right: -1px;
}
}
#notification-form {
margin-left: 5px;
}
.dropdown-new {
margin-left: -5px;
}
.open > .dropdown-new.btn {
@include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
text-transform: none;
color: #313236 !important;
font-size: 15px;
}
.dropdown-menu { .dropdown-menu {
@include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
@include border-radius (0px); @include border-radius (0px);
...@@ -355,28 +240,6 @@ ...@@ -355,28 +240,6 @@
color: #555; color: #555;
} }
ul.nav.nav-projects-tabs {
@extend .nav-tabs;
padding-left: 8px;
li {
a {
padding: 6px 25px;
margin-top: 2px;
border-color: #DDD;
background-color: #EEE;
text-shadow: 0 1px 1px white;
color: #555;
}
&.active {
a {
font-weight: bold;
}
}
}
}
.project_member_row form { .project_member_row form {
margin: 0px; margin: 0px;
} }
...@@ -403,9 +266,9 @@ ul.nav.nav-projects-tabs { ...@@ -403,9 +266,9 @@ ul.nav.nav-projects-tabs {
.breadcrumb.repo-breadcrumb { .breadcrumb.repo-breadcrumb {
padding: 0; padding: 0;
line-height: 42px;
background: transparent; background: transparent;
border: none; border: none;
line-height: 42px;
margin: 0; margin: 0;
> li + li:before { > li + li:before {
...@@ -420,11 +283,8 @@ ul.nav.nav-projects-tabs { ...@@ -420,11 +283,8 @@ ul.nav.nav-projects-tabs {
.top-area { .top-area {
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
margin: 0 -16px;
padding: 0 $gl-padding;
height: 42px;
ul.left-top-menu { ul.nav-links {
display: inline-block; display: inline-block;
width: 50%; width: 50%;
margin-bottom: 0px; margin-bottom: 0px;
...@@ -435,12 +295,12 @@ ul.nav.nav-projects-tabs { ...@@ -435,12 +295,12 @@ ul.nav.nav-projects-tabs {
width: 50%; width: 50%;
display: inline-block; display: inline-block;
float: right; float: right;
padding-top: 7px; padding-top: 11px;
text-align: right; text-align: right;
.btn-green { .btn-green {
margin-top: -2px;
margin-left: 10px; margin-left: 10px;
float: right;
} }
} }
...@@ -486,11 +346,11 @@ table.table.protected-branches-list tr.no-border { ...@@ -486,11 +346,11 @@ table.table.protected-branches-list tr.no-border {
padding-top: 10px; padding-top: 10px;
padding-bottom: 4px; padding-bottom: 4px;
ul.nav-pills { ul.nav {
display:inline-block; display:inline-block;
} }
.nav-pills li { .nav li {
display:inline; display:inline;
} }
...@@ -527,8 +387,7 @@ pre.light-well { ...@@ -527,8 +387,7 @@ pre.light-well {
} }
.projects-search-form { .projects-search-form {
margin: -$gl-padding; padding: $gl-padding 0;
padding: $gl-padding;
padding-bottom: 0; padding-bottom: 0;
margin-bottom: 0px; margin-bottom: 0px;
...@@ -578,10 +437,8 @@ pre.light-well { ...@@ -578,10 +437,8 @@ pre.light-well {
@include basic-list; @include basic-list;
.project-row { .project-row {
padding: $gl-padding; padding: $gl-padding 0;
border-color: $table-border-color; border-color: $table-border-color;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
&.no-description { &.no-description {
.project { .project {
...@@ -635,8 +492,6 @@ pre.light-well { ...@@ -635,8 +492,6 @@ pre.light-well {
} }
.project-last-commit { .project-last-commit {
margin: 0 7px;
.ci-status { .ci-status {
margin-right: 16px; margin-right: 16px;
} }
...@@ -666,9 +521,7 @@ pre.light-well { ...@@ -666,9 +521,7 @@ pre.light-well {
} }
.project-show-readme .readme-holder { .project-show-readme .readme-holder {
margin-left: -$gl-padding; padding: $gl-padding 0;
margin-right: -$gl-padding;
padding: ($gl-padding + 7px);
border-top: 0; border-top: 0;
.edit-project-readme { .edit-project-readme {
...@@ -676,3 +529,32 @@ pre.light-well { ...@@ -676,3 +529,32 @@ pre.light-well {
position: relative; position: relative;
} }
} }
.git-clone-holder {
width: 498px;
.btn-clipboard {
border: 1px solid $border-color;
padding: 6px $gl-padding;
}
.project-home-dropdown + & {
margin-right: 45px;
}
.clone-options {
display: table-cell;
a.btn {
width: 100%;
}
}
.form-control {
@extend .monospace;
background: #FFF;
font-size: 14px;
margin-left: -1px;
cursor: auto;
width: 101%;
}
}
.tag-name{
font-weight: 600;
}
.tree-holder { .tree-holder {
> .nav-block {
margin: 11px 0;
}
.file-finder { .file-finder {
width: 50%; width: 50%;
...@@ -13,7 +16,7 @@ ...@@ -13,7 +16,7 @@
tr { tr {
> td, > th { > td, > th {
line-height: 28px; line-height: 26px;
} }
&:hover { &:hover {
...@@ -86,12 +89,14 @@ ...@@ -86,12 +89,14 @@
.blob-commit-info { .blob-commit-info {
list-style: none; list-style: none;
padding: $gl-padding;
background: $background-color;
border: 1px solid $border-color;
border-bottom: none;
margin: 0; margin: 0;
padding: 0;
margin-bottom: 5px;
.commit { .commit {
padding: $gl-padding 0; padding: 0;
.commit-row-title { .commit-row-title {
.commit-row-message { .commit-row-message {
...@@ -115,3 +120,8 @@ ...@@ -115,3 +120,8 @@
font-weight: normal; font-weight: normal;
color: $md-link-color; color: $md-link-color;
} }
.tree-controls {
float: right;
margin-top: 11px;
}
...@@ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -75,6 +75,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_pool_size, :metrics_pool_size,
:metrics_timeout, :metrics_timeout,
:metrics_method_call_threshold, :metrics_method_call_threshold,
:metrics_sample_interval,
:recaptcha_enabled, :recaptcha_enabled,
:recaptcha_site_key, :recaptcha_site_key,
:recaptcha_private_key, :recaptcha_private_key,
......
class Admin::BroadcastMessagesController < Admin::ApplicationController class Admin::BroadcastMessagesController < Admin::ApplicationController
before_action :broadcast_messages before_action :finder, only: [:edit, :update, :destroy]
def index def index
@broadcast_messages = BroadcastMessage.reorder("starts_at ASC").page(params[:page])
@broadcast_message = BroadcastMessage.new @broadcast_message = BroadcastMessage.new
end end
def edit
end
def create def create
@broadcast_message = BroadcastMessage.new(broadcast_message_params) @broadcast_message = BroadcastMessage.new(broadcast_message_params)
...@@ -15,8 +19,16 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController ...@@ -15,8 +19,16 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
end end
end end
def update
if @broadcast_message.update(broadcast_message_params)
redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully updated.'
else
render :edit
end
end
def destroy def destroy
BroadcastMessage.find(params[:id]).destroy @broadcast_message.destroy
respond_to do |format| respond_to do |format|
format.html { redirect_back_or_default(default: { action: 'index' }) } format.html { redirect_back_or_default(default: { action: 'index' }) }
...@@ -26,14 +38,17 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController ...@@ -26,14 +38,17 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
protected protected
def broadcast_messages def finder
@broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page]) @broadcast_message = BroadcastMessage.find(params[:id])
end end
def broadcast_message_params def broadcast_message_params
params.require(:broadcast_message).permit( params.require(:broadcast_message).permit(%i(
:alert_type, :color, :ends_at, :font, color
:message, :starts_at ends_at
) font
message
starts_at
))
end end
end end
...@@ -26,6 +26,7 @@ class Admin::IdentitiesController < Admin::ApplicationController ...@@ -26,6 +26,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
def update def update
if @identity.update_attributes(identity_params) if @identity.update_attributes(identity_params)
RepairLdapBlockedUserService.new(@user).execute
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.' redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
else else
render :edit render :edit
...@@ -34,6 +35,7 @@ class Admin::IdentitiesController < Admin::ApplicationController ...@@ -34,6 +35,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
def destroy def destroy
if @identity.destroy if @identity.destroy
RepairLdapBlockedUserService.new(@user).execute
redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully removed.' redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully removed.'
else else
redirect_to admin_user_identities_path(@user), alert: 'Failed to remove user identity.' redirect_to admin_user_identities_path(@user), alert: 'Failed to remove user identity.'
......
...@@ -40,7 +40,9 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -40,7 +40,9 @@ class Admin::UsersController < Admin::ApplicationController
end end
def unblock def unblock
if user.activate if user.ldap_blocked?
redirect_back_or_admin_user(alert: "This user cannot be unlocked manually from GitLab")
elsif user.activate
redirect_back_or_admin_user(notice: "Successfully unblocked") redirect_back_or_admin_user(notice: "Successfully unblocked")
else else
redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked") redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked")
......
class Projects::ArtifactsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_build_artifacts!
def download
unless artifacts_file.file_storage?
return redirect_to artifacts_file.url
end
unless artifacts_file.exists?
return not_found!
end
send_file artifacts_file.path, disposition: 'attachment'
end
def browse
return render_404 unless build.artifacts?
directory = params[:path] ? "#{params[:path]}/" : ''
@entry = build.artifacts_metadata_entry(directory)
return render_404 unless @entry.exists?
end
def file
entry = build.artifacts_metadata_entry(params[:path])
if entry.exists?
render json: { archive: build.artifacts_file.path,
entry: Base64.encode64(entry.path) }
else
render json: {}, status: 404
end
end
private
def build
@build ||= project.builds.unscoped.find_by!(id: params[:build_id])
end
def artifacts_file
@artifacts_file ||= build.artifacts_file
end
def authorize_read_build_artifacts!
unless can?(current_user, :read_build_artifacts, @project)
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
end
...@@ -2,7 +2,6 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -2,7 +2,6 @@ class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all] before_action :build, except: [:index, :cancel_all]
before_action :authorize_manage_builds!, except: [:index, :show, :status] before_action :authorize_manage_builds!, except: [:index, :show, :status]
before_action :authorize_download_build_artifacts!, only: [:download]
layout "project" layout "project"
...@@ -51,18 +50,6 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -51,18 +50,6 @@ class Projects::BuildsController < Projects::ApplicationController
redirect_to build_path(build) redirect_to build_path(build)
end end
def download
unless artifacts_file.file_storage?
return redirect_to artifacts_file.url
end
unless artifacts_file.exists?
return not_found!
end
send_file artifacts_file.path, disposition: 'attachment'
end
def status def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end end
...@@ -79,10 +66,6 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -79,10 +66,6 @@ class Projects::BuildsController < Projects::ApplicationController
@build ||= project.builds.unscoped.find_by!(id: params[:id]) @build ||= project.builds.unscoped.find_by!(id: params[:id])
end end
def artifacts_file
build.artifacts_file
end
def build_path(build) def build_path(build)
namespace_project_build_path(build.project.namespace, build.project, build) namespace_project_build_path(build.project.namespace, build.project, build)
end end
...@@ -92,14 +75,4 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -92,14 +75,4 @@ class Projects::BuildsController < Projects::ApplicationController
return page_404 return page_404
end end
end end
def authorize_download_build_artifacts!
unless can?(current_user, :download_build_artifacts, @project)
if current_user.nil?
return authenticate_user!
else
return render_404
end
end
end
end end
...@@ -67,7 +67,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -67,7 +67,7 @@ class Projects::IssuesController < Projects::ApplicationController
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.nonawards.with_associations.fresh @notes = @issue.notes.nonawards.with_associations.fresh
@noteable = @issue @noteable = @issue
@merge_requests = @issue.referenced_merge_requests @merge_requests = @issue.referenced_merge_requests(current_user)
respond_with(@issue) respond_with(@issue)
end end
......
class SentNotificationsController < ApplicationController
skip_before_action :authenticate_user!
def unsubscribe
@sent_notification = SentNotification.for(params[:id])
return render_404 unless @sent_notification && @sent_notification.unsubscribable?
noteable = @sent_notification.noteable
noteable.unsubscribe(@sent_notification.recipient)
flash[:notice] = "You have been unsubscribed from this thread."
if current_user
case noteable
when Issue
redirect_to issue_path(noteable)
when MergeRequest
redirect_to merge_request_path(noteable)
else
redirect_to root_path
end
else
redirect_to new_user_session_path
end
end
end
...@@ -181,10 +181,6 @@ module ApplicationHelper ...@@ -181,10 +181,6 @@ module ApplicationHelper
end end
end end
def broadcast_message
BroadcastMessage.current
end
# Render a `time` element with Javascript-based relative date and tooltip # Render a `time` element with Javascript-based relative date and tooltip
# #
# time - Time object # time - Time object
...@@ -266,7 +262,7 @@ module ApplicationHelper ...@@ -266,7 +262,7 @@ module ApplicationHelper
state: params[:state], state: params[:state],
scope: params[:scope], scope: params[:scope],
label_name: params[:label_name], label_name: params[:label_name],
milestone_id: params[:milestone_id], milestone_title: params[:milestone_title],
assignee_id: params[:assignee_id], assignee_id: params[:assignee_id],
author_id: params[:author_id], author_id: params[:author_id],
sort: params[:sort], sort: params[:sort],
......
module BroadcastMessagesHelper module BroadcastMessagesHelper
def broadcast_styling(broadcast_message) def broadcast_message(message = BroadcastMessage.current)
styling = '' return unless message.present?
content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
icon('bullhorn') << ' ' << message.message
end
end
def broadcast_message_style(broadcast_message)
style = ''
if broadcast_message.color.present? if broadcast_message.color.present?
styling << "background-color: #{broadcast_message.color}" style << "background-color: #{broadcast_message.color}"
styling << '; ' if broadcast_message.font.present? style << '; ' if broadcast_message.font.present?
end end
if broadcast_message.font.present? if broadcast_message.font.present?
styling << "color: #{broadcast_message.font}" style << "color: #{broadcast_message.font}"
end end
styling style
end
def broadcast_message_status(broadcast_message)
if broadcast_message.active?
'Active'
elsif broadcast_message.ended?
'Expired'
else
'Pending'
end
end end
end end
...@@ -17,7 +17,7 @@ module ButtonHelper ...@@ -17,7 +17,7 @@ module ButtonHelper
def clipboard_button(data = {}) def clipboard_button(data = {})
content_tag :button, content_tag :button,
icon('clipboard'), icon('clipboard'),
class: 'btn btn-xs btn-clipboard', class: 'btn btn-clipboard',
data: data, data: data,
type: :button type: :button
end end
......
...@@ -27,15 +27,17 @@ module EventsHelper ...@@ -27,15 +27,17 @@ module EventsHelper
key = key.to_s key = key.to_s
active = 'active' if @event_filter.active?(key) active = 'active' if @event_filter.active?(key)
link_opts = { link_opts = {
class: "event-filter-link btn btn-default #{active}", class: "event-filter-link",
id: "#{key}_event_filter", id: "#{key}_event_filter",
title: "Filter by #{tooltip.downcase}", title: "Filter by #{tooltip.downcase}",
} }
content_tag :li, class: active do
link_to request.path, link_opts do link_to request.path, link_opts do
content_tag(:span, ' ' + tooltip) content_tag(:span, ' ' + tooltip)
end end
end end
end
def icon_for_event def icon_for_event
{ {
......
...@@ -91,7 +91,7 @@ module GitlabMarkdownHelper ...@@ -91,7 +91,7 @@ module GitlabMarkdownHelper
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
case wiki_page.format case wiki_page.format
when :markdown when :markdown
markdown(wiki_page.content) markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki)
when :asciidoc when :asciidoc
asciidoc(wiki_page.content) asciidoc(wiki_page.content)
else else
......
module Emails module Emails
module Issues module Issues
def new_issue_email(recipient_id, issue_id) def new_issue_email(recipient_id, issue_id)
issue_mail_with_notification(issue_id, recipient_id) do setup_issue_mail(issue_id, recipient_id)
mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id)) mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
end end
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id) def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do setup_issue_mail(issue_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end end
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id) def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do setup_issue_mail(issue_id, recipient_id)
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end end
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
issue_mail_with_notification(issue_id, recipient_id) do setup_issue_mail(issue_id, recipient_id)
@issue_status = status @issue_status = status
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
end end
end
private private
...@@ -38,14 +38,12 @@ module Emails ...@@ -38,14 +38,12 @@ module Emails
} }
end end
def issue_mail_with_notification(issue_id, recipient_id) def setup_issue_mail(issue_id, recipient_id)
@issue = Issue.find(issue_id) @issue = Issue.find(issue_id)
@project = @issue.project @project = @issue.project
@target_url = namespace_project_issue_url(@project.namespace, @project, @issue) @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
yield @sent_notification = SentNotification.record(@issue, recipient_id, reply_key)
SentNotification.record(@issue, recipient_id, reply_key)
end end
end end
end end
module Emails module Emails
module MergeRequests module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id) def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id) setup_merge_request_mail(merge_request_id, recipient_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_new_thread(@merge_request, mail_new_thread(@merge_request,
from: sender(@merge_request.author_id), from: sender(@merge_request.author_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id) def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) setup_merge_request_mail(merge_request_id, recipient_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request, mail_answer_thread(@merge_request,
from: sender(updated_by_user_id), from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request, mail_answer_thread(@merge_request,
from: sender(updated_by_user_id), from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) setup_merge_request_mail(merge_request_id, recipient_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request, mail_answer_thread(@merge_request,
from: sender(updated_by_user_id), from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
SentNotification.record(@merge_request, recipient_id, reply_key)
end end
def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id) def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id) setup_merge_request_mail(merge_request_id, recipient_id)
@mr_status = status @mr_status = status
@project = @merge_request.project
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
mail_answer_thread(@merge_request, mail_answer_thread(@merge_request,
from: sender(updated_by_user_id), from: sender(updated_by_user_id),
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
private
def setup_merge_request_mail(merge_request_id, recipient_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = namespace_project_merge_request_url(@project.namespace,
@project,
@merge_request)
SentNotification.record(@merge_request, recipient_id, reply_key) @sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end end
end end
end end
module Emails module Emails
module Notes module Notes
def note_commit_email(recipient_id, note_id) def note_commit_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do setup_note_mail(note_id, recipient_id)
@commit = @note.noteable @commit = @note.noteable
@target_url = namespace_project_commit_url(*note_target_url_options) @target_url = namespace_project_commit_url(*note_target_url_options)
...@@ -10,23 +11,22 @@ module Emails ...@@ -10,23 +11,22 @@ module Emails
to: recipient(recipient_id), to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})")) subject: subject("#{@commit.title} (#{@commit.short_id})"))
end end
end
def note_issue_email(recipient_id, note_id) def note_issue_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do setup_note_mail(note_id, recipient_id)
@issue = @note.noteable @issue = @note.noteable
@target_url = namespace_project_issue_url(*note_target_url_options) @target_url = namespace_project_issue_url(*note_target_url_options)
mail_answer_thread(@issue, note_thread_options(recipient_id)) mail_answer_thread(@issue, note_thread_options(recipient_id))
end end
end
def note_merge_request_email(recipient_id, note_id) def note_merge_request_email(recipient_id, note_id)
note_mail_with_notification(note_id, recipient_id) do setup_note_mail(note_id, recipient_id)
@merge_request = @note.noteable @merge_request = @note.noteable
@target_url = namespace_project_merge_request_url(*note_target_url_options) @target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id)) mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end end
end
private private
...@@ -42,13 +42,11 @@ module Emails ...@@ -42,13 +42,11 @@ module Emails
} }
end end
def note_mail_with_notification(note_id, recipient_id) def setup_note_mail(note_id, recipient_id)
@note = Note.find(note_id) @note = Note.find(note_id)
@project = @note.project @project = @note.project
yield @sent_notification = SentNotification.record_note(@note, recipient_id, reply_key)
SentNotification.record_note(@note, recipient_id, reply_key)
end end
end end
end end
...@@ -108,10 +108,9 @@ class Notify < BaseMailer ...@@ -108,10 +108,9 @@ class Notify < BaseMailer
end end
headers["X-GitLab-#{model.class.name}-ID"] = model.id headers["X-GitLab-#{model.class.name}-ID"] = model.id
if reply_key
headers['X-GitLab-Reply-Key'] = reply_key headers['X-GitLab-Reply-Key'] = reply_key
if Gitlab::IncomingEmail.enabled?
address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key)) address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
address.display_name = @project.name_with_namespace address.display_name = @project.name_with_namespace
......
...@@ -191,7 +191,7 @@ class Ability ...@@ -191,7 +191,7 @@ class Ability
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:manage_builds, :manage_builds,
:download_build_artifacts, :read_build_artifacts,
:push_code :push_code
] ]
end end
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
# message :text not null # message :text not null
# starts_at :datetime # starts_at :datetime
# ends_at :datetime # ends_at :datetime
# alert_type :integer
# created_at :datetime # created_at :datetime
# updated_at :datetime # updated_at :datetime
# color :string(255) # color :string(255)
...@@ -23,7 +22,22 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -23,7 +22,22 @@ class BroadcastMessage < ActiveRecord::Base
validates :color, allow_blank: true, color: true validates :color, allow_blank: true, color: true
validates :font, allow_blank: true, color: true validates :font, allow_blank: true, color: true
default_value_for :color, '#E75E40'
default_value_for :font, '#FFFFFF'
def self.current def self.current
where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last where("ends_at > :now AND starts_at <= :now", now: Time.zone.now).last
end
def active?
started? && !ended?
end
def started?
Time.zone.now >= starts_at
end
def ended?
ends_at < Time.zone.now
end end
end end
...@@ -30,10 +30,12 @@ ...@@ -30,10 +30,12 @@
# description :string(255) # description :string(255)
# artifacts_file :text # artifacts_file :text
# gl_project_id :integer # gl_project_id :integer
# artifacts_metadata :text
# #
module Ci module Ci
class Build < CommitStatus class Build < CommitStatus
include Gitlab::Application.routes.url_helpers
LAZY_ATTRIBUTES = ['trace'] LAZY_ATTRIBUTES = ['trace']
belongs_to :runner, class_name: 'Ci::Runner' belongs_to :runner, class_name: 'Ci::Runner'
...@@ -49,6 +51,7 @@ module Ci ...@@ -49,6 +51,7 @@ module Ci
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) } scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
acts_as_taggable acts_as_taggable
...@@ -291,20 +294,17 @@ module Ci ...@@ -291,20 +294,17 @@ module Ci
end end
def target_url def target_url
Gitlab::Application.routes.url_helpers.
namespace_project_build_url(project.namespace, project, self) namespace_project_build_url(project.namespace, project, self)
end end
def cancel_url def cancel_url
if active? if active?
Gitlab::Application.routes.url_helpers.
cancel_namespace_project_build_path(project.namespace, project, self) cancel_namespace_project_build_path(project.namespace, project, self)
end end
end end
def retry_url def retry_url
if retryable? if retryable?
Gitlab::Application.routes.url_helpers.
retry_namespace_project_build_path(project.namespace, project, self) retry_namespace_project_build_path(project.namespace, project, self)
end end
end end
...@@ -321,13 +321,6 @@ module Ci ...@@ -321,13 +321,6 @@ module Ci
pending? && !any_runners_online? pending? && !any_runners_online?
end end
def download_url
if artifacts_file.exists?
Gitlab::Application.routes.url_helpers.
download_namespace_project_build_path(project.namespace, project, self)
end
end
def execute_hooks def execute_hooks
build_data = Gitlab::BuildDataBuilder.build(self) build_data = Gitlab::BuildDataBuilder.build(self)
project.execute_hooks(build_data.dup, :build_hooks) project.execute_hooks(build_data.dup, :build_hooks)
...@@ -335,6 +328,30 @@ module Ci ...@@ -335,6 +328,30 @@ module Ci
UpdatePagesService.new(build_data).execute UpdatePagesService.new(build_data).execute
end end
def artifacts?
artifacts_file.exists?
end
def artifacts_download_url
if artifacts?
download_namespace_project_build_artifacts_path(project.namespace, project, self)
end
end
def artifacts_browse_url
if artifacts_browser_supported?
browse_namespace_project_build_artifacts_path(project.namespace, project, self)
end
end
def artifacts_browser_supported?
artifacts? && artifacts_metadata.exists?
end
def artifacts_metadata_entry(path)
Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path).to_entry
end
private private
def yaml_variables def yaml_variables
......
...@@ -33,6 +33,10 @@ module Ci ...@@ -33,6 +33,10 @@ module Ci
trigger_requests.last trigger_requests.last
end end
def last_used
last_trigger_request.try(:created_at)
end
def short_token def short_token
token[0...10] token[0...10]
end end
......
...@@ -18,8 +18,12 @@ module Ci ...@@ -18,8 +18,12 @@ module Ci
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
validates_presence_of :key
validates_uniqueness_of :key, scope: :gl_project_id validates_uniqueness_of :key, scope: :gl_project_id
validates :key,
presence: true,
length: { within: 0..255 },
format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." }
attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
end end
......
...@@ -56,6 +56,8 @@ class CommitStatus < ActiveRecord::Base ...@@ -56,6 +56,8 @@ class CommitStatus < ActiveRecord::Base
scope :ordered, -> { order(:ref, :stage_idx, :name) } scope :ordered, -> { order(:ref, :stage_idx, :name) }
scope :for_ref, ->(ref) { where(ref: ref) } scope :for_ref, ->(ref) { where(ref: ref) }
AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled']
state_machine :status, initial: :pending do state_machine :status, initial: :pending do
event :run do event :run do
transition pending: :running transition pending: :running
...@@ -131,7 +133,11 @@ class CommitStatus < ActiveRecord::Base ...@@ -131,7 +133,11 @@ class CommitStatus < ActiveRecord::Base
false false
end end
def download_url def artifacts_download_url
nil
end
def artifacts_browse_url
nil nil
end end
end end
...@@ -119,6 +119,12 @@ module Issuable ...@@ -119,6 +119,12 @@ module Issuable
update(subscribed: !subscribed?(user)) update(subscribed: !subscribed?(user))
end end
def unsubscribe(user)
subscriptions.
find_or_initialize_by(user_id: user.id).
update(subscribed: false)
end
def to_hook_data(user) def to_hook_data(user)
{ {
object_kind: self.class.name.underscore, object_kind: self.class.name.underscore,
......
...@@ -48,8 +48,8 @@ class WebHook < ActiveRecord::Base ...@@ -48,8 +48,8 @@ class WebHook < ActiveRecord::Base
else else
post_url = url.gsub("#{parsed_url.userinfo}@", "") post_url = url.gsub("#{parsed_url.userinfo}@", "")
auth = { auth = {
username: URI.decode(parsed_url.user), username: CGI.unescape(parsed_url.user),
password: URI.decode(parsed_url.password), password: CGI.unescape(parsed_url.password),
} }
response = WebHook.post(post_url, response = WebHook.post(post_url,
body: data.to_json, body: data.to_json,
......
...@@ -18,4 +18,8 @@ class Identity < ActiveRecord::Base ...@@ -18,4 +18,8 @@ class Identity < ActiveRecord::Base
validates :provider, presence: true validates :provider, presence: true
validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider } validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
validates :user_id, uniqueness: { scope: :provider } validates :user_id, uniqueness: { scope: :provider }
def ldap?
provider.starts_with?('ldap')
end
end end
...@@ -86,10 +86,10 @@ class Issue < ActiveRecord::Base ...@@ -86,10 +86,10 @@ class Issue < ActiveRecord::Base
reference reference
end end
def referenced_merge_requests def referenced_merge_requests(current_user = nil)
Gitlab::ReferenceExtractor.lazily do Gitlab::ReferenceExtractor.lazily do
[self, *notes].flat_map do |note| [self, *notes].flat_map do |note|
note.all_references.merge_requests note.all_references(current_user).merge_requests
end end
end.sort_by(&:iid) end.sort_by(&:iid)
end end
......
...@@ -358,6 +358,10 @@ class Note < ActiveRecord::Base ...@@ -358,6 +358,10 @@ class Note < ActiveRecord::Base
!system? && !is_award !system? && !is_award
end end
def cross_reference_not_visible_for?(user)
cross_reference? && referenced_mentionables(user).empty?
end
# Checks if note is an award added as a comment # Checks if note is an award added as a comment
# #
# If note is an award, this method sets is_award to true # If note is an award, this method sets is_award to true
......
...@@ -433,7 +433,7 @@ class Project < ActiveRecord::Base ...@@ -433,7 +433,7 @@ class Project < ActiveRecord::Base
result.password = '*****' unless result.password.nil? result.password = '*****' unless result.password.nil?
result.to_s result.to_s
rescue rescue
original_url self.import_url
end end
def mirror_updated? def mirror_updated?
......
...@@ -120,13 +120,13 @@ class HipchatService < Service ...@@ -120,13 +120,13 @@ class HipchatService < Service
message << "#{push[:user_name]} " message << "#{push[:user_name]} "
if Gitlab::Git.blank_ref?(before) if Gitlab::Git.blank_ref?(before)
message << "pushed new #{ref_type} <a href=\""\ message << "pushed new #{ref_type} <a href=\""\
"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\ "#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
" to #{project_link}\n" " to #{project_link}\n"
elsif Gitlab::Git.blank_ref?(after) elsif Gitlab::Git.blank_ref?(after)
message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n" message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
else else
message << "pushed to #{ref_type} <a href=\""\ message << "pushed to #{ref_type} <a href=\""\
"#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> " "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> "
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
...@@ -255,8 +255,8 @@ class HipchatService < Service ...@@ -255,8 +255,8 @@ class HipchatService < Service
status = data[:commit][:status] status = data[:commit][:status]
duration = data[:commit][:duration] duration = data[:commit][:duration]
branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>" branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>" commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)" "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
end end
......
...@@ -38,6 +38,10 @@ class ProjectWiki ...@@ -38,6 +38,10 @@ class ProjectWiki
[Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
end end
def wiki_base_path
["/", @project.path_with_namespace, "/wikis"].join('')
end
# Returns the Gollum::Wiki object. # Returns the Gollum::Wiki object.
def wiki def wiki
@wiki ||= begin @wiki ||= begin
......
...@@ -25,8 +25,6 @@ class SentNotification < ActiveRecord::Base ...@@ -25,8 +25,6 @@ class SentNotification < ActiveRecord::Base
class << self class << self
def reply_key def reply_key
return nil unless Gitlab::IncomingEmail.enabled?
SecureRandom.hex(16) SecureRandom.hex(16)
end end
...@@ -64,6 +62,10 @@ class SentNotification < ActiveRecord::Base ...@@ -64,6 +62,10 @@ class SentNotification < ActiveRecord::Base
end end
end end
def unsubscribable?
!for_commit?
end
def for_commit? def for_commit?
noteable_type == "Commit" noteable_type == "Commit"
end end
...@@ -75,4 +77,8 @@ class SentNotification < ActiveRecord::Base ...@@ -75,4 +77,8 @@ class SentNotification < ActiveRecord::Base
super super
end end
end end
def to_param
self.reply_key
end
end end
...@@ -198,10 +198,22 @@ class User < ActiveRecord::Base ...@@ -198,10 +198,22 @@ class User < ActiveRecord::Base
state_machine :state, initial: :active do state_machine :state, initial: :active do
event :block do event :block do
transition active: :blocked transition active: :blocked
transition ldap_blocked: :blocked
end
event :ldap_block do
transition active: :ldap_blocked
end end
event :activate do event :activate do
transition blocked: :active transition blocked: :active
transition ldap_blocked: :active
end
state :blocked, :ldap_blocked do
def blocked?
true
end
end end
end end
...@@ -209,7 +221,7 @@ class User < ActiveRecord::Base ...@@ -209,7 +221,7 @@ class User < ActiveRecord::Base
# Scopes # Scopes
scope :admins, -> { where(admin: true) } scope :admins, -> { where(admin: true) }
scope :blocked, -> { with_state(:blocked) } scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
scope :active, -> { with_state(:active) } scope :active, -> { with_state(:active) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
......
...@@ -169,7 +169,7 @@ class WikiPage ...@@ -169,7 +169,7 @@ class WikiPage
private private
def set_attributes def set_attributes
attributes[:slug] = @page.escaped_url_path attributes[:slug] = @page.url_path
attributes[:title] = @page.title attributes[:title] = @page.title
attributes[:format] = @page.format attributes[:format] = @page.format
end end
......
class RepairLdapBlockedUserService
attr_accessor :user
def initialize(user)
@user = user
end
def execute
user.block if ldap_hard_blocked?
end
private
def ldap_hard_blocked?
user.ldap_blocked? && !user.ldap_user?
end
end
...@@ -215,6 +215,13 @@ ...@@ -215,6 +215,13 @@
.help-block .help-block
A method call is only tracked when it takes longer to complete than A method call is only tracked when it takes longer to complete than
the given amount of milliseconds. the given amount of milliseconds.
.form-group
= f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :metrics_sample_interval, class: 'form-control'
.help-block
The sampling interval in seconds. Sampled data includes memory usage,
retained Ruby objects, file descriptors and so on.
%fieldset %fieldset
%legend Spam and Anti-bot Protection %legend Spam and Anti-bot Protection
......
.broadcast-message-preview{ style: broadcast_message_style(@broadcast_message) }
= icon('bullhorn')
%span= @broadcast_message.message || "Your message here"
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-requires-input'} do |f|
-if @broadcast_message.errors.any?
.alert.alert-danger
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
= f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
.form-group.js-toggle-colors-container
.col-sm-10.col-sm-offset-2
= link_to 'Customize colors', '#', class: 'js-toggle-colors-link'
.form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
= f.color_field :color, class: "form-control"
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
= f.color_field :font, class: "form-control"
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
= f.datetime_select :starts_at, {}, class: 'form-control form-control-inline'
.form-group
= f.label :ends_at, class: 'control-label'
.col-sm-10.datetime-controls
= f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
.form-actions
- if @broadcast_message.persisted?
= f.submit "Update broadcast message", class: "btn btn-create"
- else
= f.submit "Add broadcast message", class: "btn btn-create"
- page_title "Broadcast Messages"
= render 'form'
- page_title "Broadcast Messages" - page_title "Broadcast Messages"
%h3.page-title %h3.page-title
Broadcast Messages Broadcast Messages
%p.light %p.light
Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more. Broadcast messages are displayed for every user and can be used to notify
.broadcast-message-preview users about scheduled maintenance, recent upgrades and more.
%i.fa.fa-bullhorn
%span Your message here
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal'} do |f|
-if @broadcast_message.errors.any?
.alert.alert-danger
- @broadcast_message.errors.full_messages.each do |msg|
%p= msg
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
= f.text_area :message, class: "form-control", rows: 2, required: true
%div
= link_to '#', class: 'js-toggle-colors-link' do
Customize colors
.form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
= f.color_field :color, value: "#eb9532", class: "form-control"
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
= f.color_field :font, value: "#FFFFFF", class: "form-control"
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
= f.datetime_select :starts_at
.form-group
= f.label :ends_at, class: 'control-label'
.col-sm-10.datetime-controls
= f.datetime_select :ends_at
.form-actions
= f.submit "Add broadcast message", class: "btn btn-create"
-if @broadcast_messages.any? = render 'form'
%ul.bordered-list.broadcast-messages
- @broadcast_messages.each do |broadcast_message|
%li
.pull-right
- if broadcast_message.starts_at
%strong
#{broadcast_message.starts_at.to_s(:short)}
\...
- if broadcast_message.ends_at
%strong
#{broadcast_message.ends_at.to_s(:short)}
&nbsp;
= link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-xs' do
%i.fa.fa-times.cred
.message= broadcast_message.message %br.clearfix
-if @broadcast_messages.any?
%table.table
%thead
%tr
%th Status
%th Preview
%th Starts
%th Ends
%th &nbsp;
%tbody
- @broadcast_messages.each do |message|
%tr
%td
= broadcast_message_status(message)
%td
= broadcast_message(message)
%td
= message.starts_at
%td
= message.ends_at
%td
= link_to icon('pencil-square-o'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn btn-xs'
= link_to icon('times'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-xs btn-danger'
= paginate @broadcast_messages = paginate @broadcast_messages
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
%td %td
.pull-right .pull-right
- if current_user && can?(current_user, :download_build_artifacts, project) && build.download_url - if current_user && can?(current_user, :read_build_artifacts, project) && build.artifacts?
= link_to build.download_url, title: 'Download artifacts' do = link_to build.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download %i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, build.project) - if current_user && can?(current_user, :manage_builds, build.project)
- if build.active? - if build.active?
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
%ul.center-top-menu %ul.nav-links
%li{class: ('active' if @scope.nil?)} %li{class: ('active' if @scope.nil?)}
= link_to admin_builds_path do = link_to admin_builds_path do
All All
......
- page_title "Logs" - page_title "Logs"
- loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
Gitlab::ProductionLogger, Gitlab::SidekiqLogger] Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
%ul.nav.nav-tabs.log-tabs %ul.nav-links.log-tabs
- loggers.each do |klass| - loggers.each do |klass|
%li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') }
= link_to klass::file_name, "##{klass::file_name_noext}", = link_to klass::file_name, "##{klass::file_name_noext}",
'data-toggle' => 'tab' 'data-toggle' => 'tab'
%p.light To prevent performance issues admin logs output the last 2000 lines .gray-content-block
To prevent performance issues admin logs output the last 2000 lines
.tab-content .tab-content
- loggers.each do |klass| - loggers.each do |klass|
.tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''), .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''),
......
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.
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