Merge branch 'master' into ci-permissions
# Conflicts: # db/schema.rb
This commit is contained in:
commit
170ca8435a
|
@ -13,9 +13,12 @@ v 8.5.0 (unreleased)
|
|||
set it up
|
||||
- Fix diff comments loaded by AJAX to load comment with diff in discussion tab
|
||||
- Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
|
||||
- Fix label links for a merge request pointing to issues list
|
||||
- Don't vendor minified JS
|
||||
- Display 404 error on group not found
|
||||
- Track project import failure
|
||||
- Support Two-factor Authentication for LDAP users
|
||||
- Display database type and version in Administration dashboard
|
||||
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
|
||||
- Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
|
||||
- Update the ExternalIssue regex pattern (Blake Hitchcock)
|
||||
|
@ -24,6 +27,7 @@ v 8.5.0 (unreleased)
|
|||
- Fix API to keep request parameters in Link header (Michael Potthoff)
|
||||
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
|
||||
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
|
||||
- Prevent parse error when name of project ends with .atom and prevent path issues
|
||||
- Mark inline difference between old and new paths when a file is renamed
|
||||
- Support Akismet spam checking for creation of issues via API (Stan Hu)
|
||||
- Improve UI consistency between projects and groups lists
|
||||
|
|
|
@ -211,8 +211,89 @@ $ ->
|
|||
$this.attr 'value', $this.val()
|
||||
return
|
||||
|
||||
$(document).on 'keyup', 'input[type="search"]' , (e) ->
|
||||
$this = $(this)
|
||||
$this.attr 'value', $this.val()
|
||||
$(document)
|
||||
.off 'keyup', 'input[type="search"]'
|
||||
.on 'keyup', 'input[type="search"]' , (e) ->
|
||||
$this = $(this)
|
||||
$this.attr 'value', $this.val()
|
||||
|
||||
$(document)
|
||||
.off 'breakpoint:change'
|
||||
.on 'breakpoint:change', (e, breakpoint) ->
|
||||
if breakpoint is 'sm' or breakpoint is 'xs'
|
||||
$gutterIcon = $('.gutter-toggle').find('i')
|
||||
if $gutterIcon.hasClass('fa-angle-double-right')
|
||||
$gutterIcon.closest('a').trigger('click')
|
||||
|
||||
$(document)
|
||||
.off 'click', 'aside .gutter-toggle'
|
||||
.on 'click', 'aside .gutter-toggle', (e) ->
|
||||
e.preventDefault()
|
||||
$this = $(this)
|
||||
$thisIcon = $this.find 'i'
|
||||
if $thisIcon.hasClass('fa-angle-double-right')
|
||||
$thisIcon
|
||||
.removeClass('fa-angle-double-right')
|
||||
.addClass('fa-angle-double-left')
|
||||
$this
|
||||
.closest('aside')
|
||||
.removeClass('right-sidebar-expanded')
|
||||
.addClass('right-sidebar-collapsed')
|
||||
$('.page-with-sidebar')
|
||||
.removeClass('right-sidebar-expanded')
|
||||
.addClass('right-sidebar-collapsed')
|
||||
else
|
||||
$thisIcon
|
||||
.removeClass('fa-angle-double-left')
|
||||
.addClass('fa-angle-double-right')
|
||||
$this
|
||||
.closest('aside')
|
||||
.removeClass('right-sidebar-collapsed')
|
||||
.addClass('right-sidebar-expanded')
|
||||
$('.page-with-sidebar')
|
||||
.removeClass('right-sidebar-collapsed')
|
||||
.addClass('right-sidebar-expanded')
|
||||
$.cookie("collapsed_gutter",
|
||||
$('.right-sidebar')
|
||||
.hasClass('right-sidebar-collapsed'), { path: '/' })
|
||||
|
||||
bootstrapBreakpoint = undefined;
|
||||
checkBootstrapBreakpoints = ->
|
||||
if $('.device-xs').is(':visible')
|
||||
bootstrapBreakpoint = "xs"
|
||||
else if $('.device-sm').is(':visible')
|
||||
bootstrapBreakpoint = "sm"
|
||||
else if $('.device-md').is(':visible')
|
||||
bootstrapBreakpoint = "md"
|
||||
else if $('.device-lg').is(':visible')
|
||||
bootstrapBreakpoint = "lg"
|
||||
|
||||
setBootstrapBreakpoints = ->
|
||||
if $('.device-xs').length
|
||||
return
|
||||
|
||||
$("body")
|
||||
.append('<div class="device-xs visible-xs"></div>'+
|
||||
'<div class="device-sm visible-sm"></div>'+
|
||||
'<div class="device-md visible-md"></div>'+
|
||||
'<div class="device-lg visible-lg"></div>')
|
||||
checkBootstrapBreakpoints()
|
||||
|
||||
fitSidebarForSize = ->
|
||||
oldBootstrapBreakpoint = bootstrapBreakpoint
|
||||
checkBootstrapBreakpoints()
|
||||
if bootstrapBreakpoint != oldBootstrapBreakpoint
|
||||
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
|
||||
checkInitialSidebarSize = ->
|
||||
if bootstrapBreakpoint is "xs" or "sm"
|
||||
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
|
||||
|
||||
$(window)
|
||||
.off "resize"
|
||||
.on "resize", (e) ->
|
||||
fitSidebarForSize()
|
||||
|
||||
setBootstrapBreakpoints()
|
||||
checkInitialSidebarSize()
|
||||
new Aside()
|
||||
|
|
|
@ -1,3 +1,30 @@
|
|||
class @Dashboard
|
||||
constructor: ->
|
||||
new ProjectsList()
|
||||
@Dashboard =
|
||||
init: ->
|
||||
this.initSearch()
|
||||
|
||||
initSearch: ->
|
||||
@timer = null
|
||||
$("#project-filter-form-field").on('keyup', ->
|
||||
clearTimeout(@timer)
|
||||
@timer = setTimeout(Dashboard.filterResults, 500)
|
||||
)
|
||||
|
||||
filterResults: =>
|
||||
$('.projects-list-holder').fadeTo(250, 0.5)
|
||||
|
||||
form = null
|
||||
form = $("#project-filter-form")
|
||||
search = $("#project-filter-form-field").val()
|
||||
project_filter_url = form.attr('action') + '?' + form.serialize()
|
||||
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: form.attr('action')
|
||||
data: form.serialize()
|
||||
complete: ->
|
||||
$('.projects-list-holder').fadeTo(250, 1)
|
||||
success: (data) ->
|
||||
$('div.projects-list-holder').replaceWith(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
history.replaceState {page: project_filter_url}, document.title, project_filter_url
|
||||
dataType: "json"
|
||||
|
|
|
@ -58,7 +58,7 @@ class Dispatcher
|
|||
shortcut_handler = new ShortcutsNavigation()
|
||||
MergeRequests.init()
|
||||
when 'dashboard:show', 'root:show'
|
||||
new Dashboard()
|
||||
Dashboard.init()
|
||||
when 'dashboard:activity'
|
||||
new Activities()
|
||||
when 'dashboard:projects:starred'
|
||||
|
|
|
@ -10,19 +10,7 @@ class @IssuableContext
|
|||
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
|
||||
$(this).submit()
|
||||
|
||||
$('.issuable-details').waitForImages ->
|
||||
$('.issuable-affix').on 'affix.bs.affix', ->
|
||||
$(@).width($(@).outerWidth())
|
||||
.on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
|
||||
$(@).width('')
|
||||
|
||||
$('.issuable-affix').affix offset:
|
||||
top: ->
|
||||
@top = ($('.issuable-affix').offset().top - 70)
|
||||
bottom: ->
|
||||
@bottom = $('.footer').outerHeight(true)
|
||||
|
||||
$(".edit-link").click (e) ->
|
||||
$(document).on "click",".edit-link", (e) ->
|
||||
block = $(@).parents('.block')
|
||||
block.find('.selectbox').show()
|
||||
block.find('.value').hide()
|
||||
|
|
|
@ -22,5 +22,3 @@ class @ProjectsList
|
|||
else
|
||||
$(this).show()
|
||||
uiBox.find("ul.projects-list li.bottom").hide()
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
|
||||
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
|
||||
&.s36 { width: 36px; height: 36px; margin-right: 10px; }
|
||||
&.s40 { width: 40px; height: 40px; margin-right: 10px; }
|
||||
&.s46 { width: 46px; height: 46px; margin-right: 15px; }
|
||||
&.s48 { width: 48px; height: 48px; margin-right: 10px; }
|
||||
&.s60 { width: 60px; height: 60px; margin-right: 12px; }
|
||||
|
@ -40,7 +41,8 @@
|
|||
&.s16 { font-size: 12px; line-height: 1.33; }
|
||||
&.s24 { font-size: 14px; line-height: 1.8; }
|
||||
&.s26 { font-size: 20px; line-height: 1.33; }
|
||||
&.s32 { font-size: 22px; line-height: 32px; }
|
||||
&.s32 { font-size: 20px; line-height: 32px; }
|
||||
&.s40 { font-size: 16px; line-height: 40px; }
|
||||
&.s60 { font-size: 32px; line-height: 60px; }
|
||||
&.s90 { font-size: 36px; line-height: 90px; }
|
||||
&.s110 { font-size: 40px; line-height: 112px; font-weight: 300; }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@include border-radius(3px);
|
||||
font-size: $gl-font-size;
|
||||
font-weight: 500;
|
||||
padding: $gl-vert-padding $gl-padding;
|
||||
padding: $gl-vert-padding $gl-btn-padding;
|
||||
|
||||
&:focus,
|
||||
&:active {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 0 $gl-padding;
|
||||
padding: 0 $gl-btn-padding;
|
||||
font-weight: normal;
|
||||
margin-right: 10px;
|
||||
font-size: $gl-font-size;
|
||||
|
|
|
@ -109,7 +109,6 @@ ul.content-list {
|
|||
padding: 0;
|
||||
|
||||
> li {
|
||||
padding: $gl-padding 0;
|
||||
border-color: $table-border-color;
|
||||
color: $gl-gray;
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
aside {
|
||||
aside:not(.right-sidebar){
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,10 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
> form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 34px;
|
||||
display: inline-block;
|
||||
|
|
|
@ -200,6 +200,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
@mixin expanded-gutter {
|
||||
padding-right: $gutter_width;
|
||||
}
|
||||
|
||||
@mixin collapsed-gutter {
|
||||
padding-right: $sidebar_collapsed_width;
|
||||
}
|
||||
|
||||
@mixin collapsed-sidebar {
|
||||
padding-left: $sidebar_collapsed_width;
|
||||
|
||||
|
@ -266,6 +274,7 @@
|
|||
background: #f2f6f7;
|
||||
}
|
||||
|
||||
// page is small enough
|
||||
@media (max-width: $screen-md-max) {
|
||||
.page-sidebar-collapsed {
|
||||
@include collapsed-sidebar;
|
||||
|
@ -275,12 +284,32 @@
|
|||
@include collapsed-sidebar;
|
||||
}
|
||||
|
||||
.page-gutter {
|
||||
&.right-sidebar-collapsed {
|
||||
@include collapsed-gutter;
|
||||
}
|
||||
&.right-sidebar-expanded {
|
||||
@include expanded-gutter;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// page is large enough
|
||||
@media(min-width: $screen-md-max) {
|
||||
|
||||
.page-gutter {
|
||||
&.right-sidebar-collapsed {
|
||||
@include collapsed-gutter;
|
||||
}
|
||||
&.right-sidebar-expanded {
|
||||
@include expanded-gutter;
|
||||
}
|
||||
}
|
||||
|
||||
.page-sidebar-collapsed {
|
||||
@include collapsed-sidebar;
|
||||
}
|
||||
|
@ -288,4 +317,4 @@
|
|||
.page-sidebar-expanded {
|
||||
@include expanded-sidebar;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@ $gl-font-size: 15px;
|
|||
$list-font-size: 15px;
|
||||
$sidebar_collapsed_width: 62px;
|
||||
$sidebar_width: 230px;
|
||||
$gutter_collapsed_width: 62px;
|
||||
$gutter_width: 312px;
|
||||
$gutter_inner_width: 280px;
|
||||
$avatar_radius: 50%;
|
||||
$code_font_size: 13px;
|
||||
$code_line_height: 1.5;
|
||||
|
@ -22,9 +25,10 @@ $header-height: 58px;
|
|||
$fixed-layout-width: 1280px;
|
||||
$gl-gray: #5a5a5a;
|
||||
$gl-padding: 16px;
|
||||
$gl-btn-padding: 10px;
|
||||
$gl-vert-padding: 6px;
|
||||
$gl-padding-top:10px;
|
||||
$gl-avatar-size: 46px;
|
||||
$gl-avatar-size: 40px;
|
||||
$secondary-text: #7f8fa4;
|
||||
$error-exclamation-point: #E62958;
|
||||
|
||||
|
@ -36,11 +40,12 @@ $white-light: #FFFFFF;
|
|||
$white-normal: #ededed;
|
||||
$white-dark: #ededed;
|
||||
|
||||
$gray-light: #f7f7f7;
|
||||
$gray-normal: #ededed;
|
||||
$gray-light: #faf9f9;
|
||||
$gray-normal: #f5f5f5;
|
||||
$gray-dark: #ededed;
|
||||
$gray-darkest: #c9c9c9;
|
||||
|
||||
$green-light: #31AF64;
|
||||
$green-light: #38ae67;
|
||||
$green-normal: #2FAA60;
|
||||
$green-dark: #2CA05B;
|
||||
|
||||
|
@ -52,7 +57,7 @@ $blue-medium-light: #3498CB;
|
|||
$blue-medium: #2F8EBF;
|
||||
$blue-medium-dark: #2D86B4;
|
||||
|
||||
$orange-light: #FC6443;
|
||||
$orange-light: rgba(252, 109, 38, 0.80);
|
||||
$orange-normal: #E75E40;
|
||||
$orange-dark: #CE5237;
|
||||
|
||||
|
@ -64,8 +69,8 @@ $border-white-light: #F1F2F4;
|
|||
$border-white-normal: #D6DAE2;
|
||||
$border-white-dark: #C6CACF;
|
||||
|
||||
$border-gray-light: #d1d1d1;
|
||||
$border-gray-normal: #D6DAE2;
|
||||
$border-gray-light: rgba(0, 0, 0, 0.06);
|
||||
$border-gray-normal: rgba(0, 0, 0, 0.10);;
|
||||
$border-gray-dark: #C6CACF;
|
||||
|
||||
$border-green-light: #2FAA60;
|
||||
|
@ -76,7 +81,7 @@ $border-blue-light: #2D9FD8;
|
|||
$border-blue-normal: #2897CE;
|
||||
$border-blue-dark: #258DC1;
|
||||
|
||||
$border-orange-light: #ED5C3D;
|
||||
$border-orange-light: #fc6d26;
|
||||
$border-orange-normal: #CE5237;
|
||||
$border-orange-dark: #C14E35;
|
||||
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
.avatar {
|
||||
@include border-radius(50%);
|
||||
}
|
||||
|
||||
.identicon {
|
||||
line-height: 46px;
|
||||
}
|
||||
}
|
||||
|
||||
.dash-project-access-icon {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*/
|
||||
.event-item {
|
||||
font-size: $gl-font-size;
|
||||
padding: $gl-padding 0 $gl-padding ($gl-avatar-size + 15px);
|
||||
padding: $gl-padding-top 0 $gl-padding-top ($gl-avatar-size + $gl-padding-top);
|
||||
border-bottom: 1px solid $table-border-color;
|
||||
color: #7f8fa4;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
|
||||
.event-title,
|
||||
.event-item-timestamp {
|
||||
line-height: 44px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
|||
}
|
||||
|
||||
.avatar {
|
||||
margin-left: -($gl-avatar-size + 15px);
|
||||
margin-left: -($gl-avatar-size + $gl-padding-top);
|
||||
}
|
||||
|
||||
.event-title {
|
||||
|
@ -41,7 +41,6 @@
|
|||
margin-right: 174px;
|
||||
|
||||
.event-note {
|
||||
margin-top: 5px;
|
||||
word-wrap: break-word;
|
||||
|
||||
.md {
|
||||
|
@ -98,8 +97,6 @@
|
|||
&:last-child { border:none }
|
||||
|
||||
.event_commits {
|
||||
margin-top: 9px;
|
||||
|
||||
li {
|
||||
&.commit {
|
||||
background: transparent;
|
||||
|
|
|
@ -29,21 +29,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.project-issuable-filter {
|
||||
.controls {
|
||||
float: right;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-details {
|
||||
section {
|
||||
border-right: 1px solid $border-white-light;
|
||||
|
||||
.issuable-discussion {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
@ -73,11 +60,35 @@
|
|||
.block {
|
||||
@include clearfix;
|
||||
padding: $gl-padding 0;
|
||||
border-bottom: 1px solid #F0F0F0;
|
||||
border-bottom: 1px solid $border-gray-light;
|
||||
// This prevents the mess when resizing the sidebar
|
||||
// of elements repositioning themselves..
|
||||
width: $gutter_inner_width;
|
||||
overflow-x: hidden;
|
||||
// --
|
||||
|
||||
&:first-child {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 7px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.issuable-count {
|
||||
|
||||
}
|
||||
|
||||
.gutter-toggle {
|
||||
margin-left: 20px;
|
||||
border-left: 1px solid $border-gray-light;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -133,3 +144,98 @@
|
|||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.right-sidebar {
|
||||
position: fixed;
|
||||
top: 58px;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
transition-duration: .3s;
|
||||
background: $gray-light;
|
||||
overflow: scroll;
|
||||
padding: 10px 20px;
|
||||
|
||||
&.right-sidebar-expanded {
|
||||
width: $gutter_width;
|
||||
|
||||
hr {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.subscribe-button {
|
||||
span {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.right-sidebar-collapsed {
|
||||
width: $sidebar_collapsed_width;
|
||||
padding-top: 0;
|
||||
overflow-x: hidden;
|
||||
|
||||
hr {
|
||||
margin: 0;
|
||||
color: $gray-normal;
|
||||
border-color: $gray-normal;
|
||||
width: 62px;
|
||||
margin-left: -20px
|
||||
}
|
||||
|
||||
.block {
|
||||
border-bottom: none;
|
||||
padding: 15px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: $gray-normal;
|
||||
border: 1px solid $border-gray-normal;
|
||||
}
|
||||
|
||||
&.right-sidebar-collapsed {
|
||||
.issuable-count,
|
||||
.issuable-nav,
|
||||
.assignee > *,
|
||||
.milestone > *,
|
||||
.labels > *,
|
||||
.participants > *,
|
||||
.light > *,
|
||||
.project-reference > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.gutter-toggle {
|
||||
margin-left: -$gutter_inner_width + 4;
|
||||
}
|
||||
|
||||
.sidebar-collapsed-icon {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 62px;
|
||||
text-align: center;
|
||||
margin-left: -19px;
|
||||
padding-bottom: 10px;
|
||||
color: #999999;
|
||||
|
||||
span {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.right-sidebar-expanded {
|
||||
.sidebar-collapsed-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.detail-page-description {
|
||||
small {
|
||||
color: $gray-darkest;
|
||||
}
|
||||
}
|
|
@ -65,10 +65,6 @@ form.edit-issue {
|
|||
width: 3em;
|
||||
}
|
||||
|
||||
.merge-request-info {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.merge-request-status {
|
||||
color: $gl-gray;
|
||||
font-size: 15px;
|
||||
|
@ -143,4 +139,4 @@ form.edit-issue {
|
|||
.issue-closed-by-widget {
|
||||
color: $secondary-text;
|
||||
margin-left: 52px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -391,12 +391,11 @@ pre.light-well {
|
|||
@include basic-list;
|
||||
|
||||
.project-row {
|
||||
padding: $gl-padding 0;
|
||||
border-color: $table-border-color;
|
||||
|
||||
&.no-description {
|
||||
.project {
|
||||
line-height: 44px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,7 +408,7 @@ pre.light-well {
|
|||
.project-controls {
|
||||
float: right;
|
||||
color: $gl-gray;
|
||||
line-height: 45px;
|
||||
line-height: 40px;
|
||||
color: #7f8fa4;
|
||||
|
||||
a:hover {
|
||||
|
|
|
@ -2,7 +2,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
|
|||
before_action :finder, only: [:edit, :update, :destroy]
|
||||
|
||||
def index
|
||||
@broadcast_messages = BroadcastMessage.reorder("starts_at ASC").page(params[:page])
|
||||
@broadcast_messages = BroadcastMessage.reorder("ends_at DESC").page(params[:page])
|
||||
@broadcast_message = BroadcastMessage.new
|
||||
end
|
||||
|
||||
|
|
|
@ -277,9 +277,10 @@ class ApplicationController < ActionController::Base
|
|||
}
|
||||
end
|
||||
|
||||
def view_to_html_string(partial)
|
||||
def view_to_html_string(partial, locals = {})
|
||||
render_to_string(
|
||||
partial,
|
||||
locals: locals,
|
||||
layout: false,
|
||||
formats: [:html]
|
||||
)
|
||||
|
|
|
@ -5,6 +5,14 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
@projects = @projects.includes(:namespace)
|
||||
|
||||
terms = params['filter_projects']
|
||||
|
||||
if terms.present?
|
||||
@projects = @projects.search(terms)
|
||||
end
|
||||
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
@last_push = current_user.recent_push
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -14,6 +22,11 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
load_events
|
||||
render layout: false
|
||||
end
|
||||
format.json do
|
||||
render json: {
|
||||
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -21,6 +34,14 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
@projects = current_user.starred_projects
|
||||
@projects = @projects.includes(:namespace, :forked_from_project, :tags)
|
||||
@projects = @projects.sort(@sort = params[:sort])
|
||||
|
||||
terms = params['filter_projects']
|
||||
|
||||
if terms.present?
|
||||
@projects = @projects.search(terms)
|
||||
end
|
||||
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
@last_push = current_user.recent_push
|
||||
@groups = []
|
||||
|
||||
|
@ -28,8 +49,9 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
|||
format.html
|
||||
|
||||
format.json do
|
||||
load_events
|
||||
pager_json("events/_events", @events.count)
|
||||
render json: {
|
||||
html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,14 +11,14 @@ class Explore::ProjectsController < Explore::ApplicationController
|
|||
end
|
||||
|
||||
def trending
|
||||
@trending_projects = TrendingProjectsFinder.new.execute(current_user)
|
||||
@trending_projects = @trending_projects.non_archived
|
||||
@trending_projects = @trending_projects.page(params[:page]).per(PER_PAGE)
|
||||
@projects = TrendingProjectsFinder.new.execute(current_user)
|
||||
@projects = @projects.non_archived
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def starred
|
||||
@starred_projects = ProjectsFinder.new.execute(current_user)
|
||||
@starred_projects = @starred_projects.reorder('star_count DESC')
|
||||
@starred_projects = @starred_projects.page(params[:page]).per(PER_PAGE)
|
||||
@projects = ProjectsFinder.new.execute(current_user)
|
||||
@projects = @projects.reorder('star_count DESC')
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,6 +41,7 @@ class GroupsController < Groups::ApplicationController
|
|||
def show
|
||||
@last_push = current_user.recent_push if current_user
|
||||
@projects = @projects.includes(:namespace)
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
include AuthenticatesWithTwoFactor
|
||||
|
||||
protect_from_forgery except: [:kerberos, :saml, :cas3]
|
||||
|
||||
|
@ -29,8 +30,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
|
||||
# Do additional LDAP checks for the user filter and EE features
|
||||
if ldap_user.allowed?
|
||||
log_audit_event(@user, with: :ldap)
|
||||
sign_in_and_redirect(@user)
|
||||
if @user.two_factor_enabled?
|
||||
prompt_for_two_factor(@user)
|
||||
else
|
||||
log_audit_event(@user, with: :ldap)
|
||||
sign_in_and_redirect(@user)
|
||||
end
|
||||
else
|
||||
flash[:alert] = "Access denied for your LDAP account."
|
||||
redirect_to new_user_session_path
|
||||
|
|
|
@ -4,8 +4,9 @@ class UsersController < ApplicationController
|
|||
|
||||
def show
|
||||
@contributed_projects = contributed_projects.joined(@user).reject(&:forked?)
|
||||
|
||||
|
||||
@projects = PersonalProjectsFinder.new(@user).execute(current_user)
|
||||
@projects = @projects.page(params[:page]).per(PER_PAGE)
|
||||
|
||||
@groups = @user.groups.order_id_desc
|
||||
|
||||
|
|
|
@ -169,18 +169,6 @@ module ApplicationHelper
|
|||
Gitlab.config.extra
|
||||
end
|
||||
|
||||
def search_placeholder
|
||||
if @project && @project.persisted?
|
||||
'Search'
|
||||
elsif @snippet || @snippets || @show_snippets
|
||||
'Search snippets'
|
||||
elsif @group && @group.persisted?
|
||||
'Search in this group'
|
||||
else
|
||||
'Search'
|
||||
end
|
||||
end
|
||||
|
||||
# Render a `time` element with Javascript-based relative date and tooltip
|
||||
#
|
||||
# time - Time object
|
||||
|
@ -293,6 +281,76 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
def issuable_link_next(project,issuable)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
namespace_project_issue_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
|
||||
elsif current_controller?(:merge_requests)
|
||||
namespace_project_merge_request_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
|
||||
end
|
||||
end
|
||||
|
||||
def issuable_link_prev(project,issuable)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
namespace_project_issue_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
|
||||
elsif current_controller?(:merge_requests)
|
||||
namespace_project_merge_request_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
|
||||
end
|
||||
end
|
||||
|
||||
def issuable_count(entity, project)
|
||||
if project.nil?
|
||||
0
|
||||
elsif current_controller?(:issues)
|
||||
project.issues.send(entity).count
|
||||
elsif current_controller?(:merge_requests)
|
||||
project.merge_requests.send(entity).count
|
||||
end
|
||||
end
|
||||
|
||||
def next_issuable_for(project, id)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
project.issues.where("id > ?", id).last
|
||||
elsif current_controller?(:merge_requests)
|
||||
project.merge_requests.where("id > ?", id).last
|
||||
end
|
||||
end
|
||||
|
||||
def has_next_issuable?(project, id)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
project.issues.where("id > ?", id).last
|
||||
elsif current_controller?(:merge_requests)
|
||||
project.merge_requests.where("id > ?", id).last
|
||||
end
|
||||
end
|
||||
|
||||
def prev_issuable_for(project, id)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
project.issues.where("id < ?", id).first
|
||||
elsif current_controller?(:merge_requests)
|
||||
project.merge_requests.where("id < ?", id).first
|
||||
end
|
||||
end
|
||||
|
||||
def has_prev_issuable?(project, id)
|
||||
if project.nil?
|
||||
nil
|
||||
elsif current_controller?(:issues)
|
||||
project.issues.where("id < ?", id).first
|
||||
elsif current_controller?(:merge_requests)
|
||||
project.merge_requests.where("id < ?", id).first
|
||||
end
|
||||
end
|
||||
|
||||
def state_filters_text_for(entity, project)
|
||||
titles = {
|
||||
opened: "Open"
|
||||
|
|
|
@ -7,6 +7,8 @@ module LabelsHelper
|
|||
# project - Project object which will be used as the context for the label's
|
||||
# link. If omitted, defaults to `@project`, or the label's own
|
||||
# project.
|
||||
# type - The type of item the link will point to (:issue or
|
||||
# :merge_request). If omitted, defaults to :issue.
|
||||
# block - An optional block that will be passed to `link_to`, forming the
|
||||
# body of the link element. If omitted, defaults to
|
||||
# `render_colored_label`.
|
||||
|
@ -23,14 +25,19 @@ module LabelsHelper
|
|||
# # Force the generated link to use a provided project
|
||||
# link_to_label(label, project: Project.last)
|
||||
#
|
||||
# # Force the generated link to point to merge requests instead of issues
|
||||
# link_to_label(label, type: :merge_request)
|
||||
#
|
||||
# # Customize link body with a block
|
||||
# link_to_label(label) { "My Custom Label Text" }
|
||||
#
|
||||
# Returns a String
|
||||
def link_to_label(label, project: nil, &block)
|
||||
def link_to_label(label, project: nil, type: :issue, &block)
|
||||
project ||= @project || label.project
|
||||
link = namespace_project_issues_path(project.namespace, project,
|
||||
label_name: label.name)
|
||||
link = send("namespace_project_#{type.to_s.pluralize}_path",
|
||||
project.namespace,
|
||||
project,
|
||||
label_name: label.name)
|
||||
|
||||
if block_given?
|
||||
link_to link, &block
|
||||
|
|
|
@ -3,6 +3,18 @@ module NavHelper
|
|||
cookies[:collapsed_nav] == 'true'
|
||||
end
|
||||
|
||||
def sidebar_gutter_collapsed_class
|
||||
if cookies[:collapsed_gutter] == 'true'
|
||||
"right-sidebar-collapsed"
|
||||
else
|
||||
"right-sidebar-expanded"
|
||||
end
|
||||
end
|
||||
|
||||
def sidebar_gutter_collapsed?
|
||||
cookies[:collapsed_gutter] == 'true'
|
||||
end
|
||||
|
||||
def nav_sidebar_class
|
||||
if nav_menu_collapsed?
|
||||
"sidebar-collapsed"
|
||||
|
@ -19,6 +31,17 @@ module NavHelper
|
|||
end
|
||||
end
|
||||
|
||||
def page_gutter_class
|
||||
|
||||
if current_path?('merge_requests#show') || current_path?('issues#show')
|
||||
if cookies[:collapsed_gutter] == 'true'
|
||||
"page-gutter right-sidebar-collapsed"
|
||||
else
|
||||
"page-gutter right-sidebar-expanded"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nav_header_class
|
||||
if nav_menu_collapsed?
|
||||
"header-collapsed"
|
||||
|
|
|
@ -20,6 +20,12 @@ module ProjectsHelper
|
|||
end
|
||||
end
|
||||
|
||||
def link_to_member_avatar(author, opts = {})
|
||||
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
|
||||
opts = default_opts.merge(opts)
|
||||
image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
|
||||
end
|
||||
|
||||
def link_to_member(project, author, opts = {})
|
||||
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
|
||||
opts = default_opts.merge(opts)
|
||||
|
|
|
@ -49,7 +49,7 @@ class Event < ActiveRecord::Base
|
|||
scope :code_push, -> { where(action: PUSHED) }
|
||||
|
||||
scope :in_projects, ->(projects) do
|
||||
where(project_id: projects.select(:id).reorder(nil)).recent
|
||||
where(project_id: projects.map(&:id)).recent
|
||||
end
|
||||
|
||||
scope :with_associations, -> { includes(project: :namespace) }
|
||||
|
|
|
@ -34,4 +34,4 @@
|
|||
= 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, theme: 'gitlab'
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
.project-issuable-filter
|
||||
.controls
|
||||
.pull-left.hidden-xs
|
||||
- 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
|
||||
|
||||
.top-area
|
||||
%ul.nav-links
|
||||
%li{class: ('active' if @scope.nil?)}
|
||||
= link_to admin_builds_path do
|
||||
|
@ -20,7 +15,11 @@
|
|||
Finished
|
||||
%span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id))
|
||||
|
||||
.gray-content-block
|
||||
.nav-controls
|
||||
- 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
|
||||
|
||||
.gray-content-block.second-block
|
||||
#{(@scope || 'running').capitalize} builds
|
||||
|
||||
%ul.content-list
|
||||
|
|
|
@ -92,6 +92,11 @@
|
|||
Rails
|
||||
%span.pull-right
|
||||
#{Rails::VERSION::STRING}
|
||||
|
||||
%p
|
||||
= Gitlab::Database.adapter_name
|
||||
%span.pull-right
|
||||
= Gitlab::Database.version
|
||||
%hr
|
||||
.row
|
||||
.col-sm-4
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
Explore Projects
|
||||
|
||||
.nav-controls
|
||||
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs input-short', spellcheck: false
|
||||
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
|
||||
= search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short', spellcheck: false, id: 'project-filter-form-field'
|
||||
= render 'explore/projects/dropdown'
|
||||
- if current_user.can_create_project?
|
||||
= link_to new_project_path, class: 'btn btn-new' do
|
||||
|
|
|
@ -4,17 +4,15 @@
|
|||
- if current_user
|
||||
= auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues")
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
.pull-left
|
||||
- if current_user
|
||||
.hidden-xs.pull-left
|
||||
= link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
|
||||
%i.fa.fa-rss
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues
|
||||
.nav-controls
|
||||
- if current_user
|
||||
= link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
|
||||
.prepend-top-default
|
||||
= render 'shared/issues'
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
- page_title "Merge Requests"
|
||||
- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id)
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
.nav-controls
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
|
||||
.prepend-top-default
|
||||
= render 'shared/merge_requests'
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
.projects-list-holder
|
||||
|
||||
= render 'shared/projects/list', projects: @projects, ci: true
|
||||
|
||||
:javascript
|
||||
Dashboard.init()
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
.event-item-timestamp
|
||||
#{time_ago_with_tooltip(event.created_at)}
|
||||
|
||||
= cache [event, current_application_settings, "v2.1"] do
|
||||
= image_tag avatar_icon(event.author_email, 46), class: "avatar s46", alt:''
|
||||
= cache [event, current_application_settings, "v2.2"] do
|
||||
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
|
||||
- if event.created_project?
|
||||
= render "events/event/created_project", event: event
|
||||
- elsif event.push?
|
||||
|
|
|
@ -13,4 +13,3 @@
|
|||
= render 'filter'
|
||||
|
||||
= render 'projects', projects: @projects
|
||||
= paginate @projects, theme: "gitlab"
|
||||
|
|
|
@ -7,5 +7,4 @@
|
|||
= render 'explore/head'
|
||||
|
||||
= render 'explore/projects/nav'
|
||||
= render 'projects', projects: @starred_projects
|
||||
= paginate @starred_projects, theme: 'gitlab'
|
||||
= render 'projects', projects: @projects
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
= render 'explore/head'
|
||||
|
||||
= render 'explore/projects/nav'
|
||||
= render 'projects', projects: @trending_projects
|
||||
= render 'projects', projects: @projects
|
||||
|
|
|
@ -4,17 +4,15 @@
|
|||
- if current_user
|
||||
= auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues")
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
.pull-left
|
||||
- if current_user
|
||||
.hidden-xs.pull-left
|
||||
= link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
|
||||
%i.fa.fa-rss
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues
|
||||
.nav-controls
|
||||
- if current_user
|
||||
= link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do
|
||||
= icon('rss')
|
||||
= render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue"
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
|
||||
.gray-content-block.second-block
|
||||
Only issues from
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
- page_title "Merge Requests"
|
||||
- header_title group_title(@group, "Merge Requests", merge_requests_group_path(@group))
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
.nav-controls
|
||||
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request"
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
|
||||
.gray-content-block.second-block
|
||||
Only merge requests from
|
||||
|
|
|
@ -40,10 +40,6 @@
|
|||
%td.shortcut
|
||||
.key enter
|
||||
%td Open Selection
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key t
|
||||
%td Go to finding file
|
||||
%tbody
|
||||
%tr
|
||||
%th
|
||||
|
@ -161,6 +157,10 @@
|
|||
.key s
|
||||
%td
|
||||
Go to snippets
|
||||
%tr
|
||||
%td.shortcut
|
||||
.key t
|
||||
%td Go to finding file
|
||||
.col-lg-4
|
||||
%table.shortcut-mappings
|
||||
%tbody{ class: 'hidden-shortcut network', style: 'display:none' }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.page-with-sidebar{ class: page_sidebar_class }
|
||||
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
|
||||
= render "layouts/broadcast"
|
||||
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
|
||||
.header-logo
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.search
|
||||
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
|
||||
= search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control", spellcheck: false
|
||||
= search_field_tag "search", nil, placeholder: 'Search', class: "search-input form-control", spellcheck: false
|
||||
= hidden_field_tag :group_id, @group.try(:id)
|
||||
- if @project && @project.persisted?
|
||||
= hidden_field_tag :project_id, @project.id
|
||||
|
|
|
@ -31,34 +31,33 @@
|
|||
- else
|
||||
= f.submit 'Generate', class: "btn btn-default"
|
||||
|
||||
- unless current_user.ldap_user?
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
Two-factor Authentication
|
||||
.panel-body
|
||||
- if current_user.two_factor_enabled?
|
||||
.pull-right
|
||||
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
|
||||
data: { confirm: 'Are you sure?' }
|
||||
%p.text-success
|
||||
%strong
|
||||
Two-factor Authentication is enabled
|
||||
%p
|
||||
If you lose your recovery codes you can
|
||||
%strong
|
||||
= succeed ',' do
|
||||
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
|
||||
invalidating all previous codes.
|
||||
.panel.panel-default
|
||||
.panel-heading
|
||||
Two-factor Authentication
|
||||
.panel-body
|
||||
- if current_user.two_factor_enabled?
|
||||
.pull-right
|
||||
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
|
||||
data: { confirm: 'Are you sure?' }
|
||||
%p.text-success
|
||||
%strong
|
||||
Two-factor Authentication is enabled
|
||||
%p
|
||||
If you lose your recovery codes you can
|
||||
%strong
|
||||
= succeed ',' do
|
||||
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
|
||||
invalidating all previous codes.
|
||||
|
||||
- else
|
||||
%p
|
||||
Increase your account's security by enabling two-factor authentication (2FA).
|
||||
%p
|
||||
Each time you log in you’ll be required to provide your username and
|
||||
password as usual, plus a randomly-generated code from your phone.
|
||||
- else
|
||||
%p
|
||||
Increase your account's security by enabling two-factor authentication (2FA).
|
||||
%p
|
||||
Each time you log in you’ll be required to provide your username and
|
||||
password as usual, plus a randomly-generated code from your phone.
|
||||
|
||||
.form-actions
|
||||
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
|
||||
.form-actions
|
||||
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
|
||||
|
||||
- if button_based_providers.any?
|
||||
.panel.panel-default
|
||||
|
|
|
@ -5,22 +5,19 @@
|
|||
- if current_user
|
||||
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
|
||||
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
.pull-left
|
||||
- if current_user
|
||||
.hidden-xs.pull-left
|
||||
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
|
||||
%i.fa.fa-rss
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :issues
|
||||
.nav-controls
|
||||
- if current_user
|
||||
= link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
|
||||
= icon('rss')
|
||||
= render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
|
||||
|
||||
- if can? current_user, :create_issue, @project
|
||||
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
|
||||
%i.fa.fa-plus
|
||||
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
|
||||
= icon('plus')
|
||||
New Issue
|
||||
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
= render 'shared/issuable/filter', type: :issues
|
||||
|
||||
.issues-holder
|
||||
= render "issues"
|
||||
|
|
|
@ -15,11 +15,6 @@
|
|||
opened by #{link_to_member(@project, @issue.author, size: 24)}
|
||||
·
|
||||
= time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago')
|
||||
- if @issue.updated_at != @issue.created_at
|
||||
%span
|
||||
·
|
||||
= icon('edit', title: 'edited')
|
||||
= time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
|
||||
|
||||
.pull-right
|
||||
- if can?(current_user, :create_issue, @project)
|
||||
|
@ -46,6 +41,10 @@
|
|||
= markdown(@issue.description, cache_key: [@issue, "description"])
|
||||
%textarea.hidden.js-task-list-field
|
||||
= @issue.description
|
||||
- if @issue.updated_at != @issue.created_at
|
||||
%small
|
||||
Edited
|
||||
= time_ago_with_tooltip(@issue.updated_at, placement: 'bottom', html_class: 'issue_edited_ago')
|
||||
|
||||
.merge-requests
|
||||
= render 'merge_requests'
|
||||
|
@ -54,11 +53,8 @@
|
|||
= render 'votes/votes_block', votable: @issue
|
||||
|
||||
.row
|
||||
%section.col-md-9
|
||||
%section.col-md-12
|
||||
.issuable-discussion
|
||||
= render 'projects/issues/discussion'
|
||||
|
||||
%aside.col-md-3
|
||||
= render 'shared/issuable/sidebar', issuable: @issue
|
||||
|
||||
= render 'shared/show_aside'
|
||||
= render 'shared/issuable/sidebar', issuable: @issue
|
|
@ -1,3 +1,3 @@
|
|||
$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}");
|
||||
$('.issuable-sidebar').parent().effect('highlight')
|
||||
new Issue();
|
||||
$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}";
|
||||
$('aside.right-sidebar').effect('highlight');
|
||||
new Issue();
|
|
@ -53,7 +53,7 @@
|
|||
- if merge_request.labels.any?
|
||||
|
||||
- merge_request.labels.each do |label|
|
||||
= link_to_label(label, project: merge_request.project)
|
||||
= link_to_label(label, project: merge_request.project, type: 'merge_request')
|
||||
- if merge_request.tasks?
|
||||
|
||||
%span.task-status
|
||||
|
|
|
@ -70,12 +70,9 @@
|
|||
= render 'votes/votes_block', votable: @merge_request
|
||||
|
||||
.row
|
||||
%section.col-md-9
|
||||
%section.col-md-12
|
||||
.issuable-discussion
|
||||
= render "projects/merge_requests/discussion"
|
||||
%aside.col-md-3
|
||||
= render 'shared/issuable/sidebar', issuable: @merge_request
|
||||
= render 'shared/show_aside'
|
||||
|
||||
#commits.commits.tab-pane
|
||||
- # This tab is always loaded via AJAX
|
||||
|
@ -87,6 +84,8 @@
|
|||
.mr-loading-status
|
||||
= spinner
|
||||
|
||||
= render 'shared/issuable/sidebar', issuable: @merge_request
|
||||
|
||||
:javascript
|
||||
var merge_request;
|
||||
|
||||
|
|
|
@ -2,16 +2,19 @@
|
|||
= render "header_title"
|
||||
|
||||
= render 'projects/last_push'
|
||||
.project-issuable-filter
|
||||
.controls
|
||||
|
||||
.top-area
|
||||
= render 'shared/issuable/nav', type: :merge_requests
|
||||
.nav-controls
|
||||
= render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
|
||||
|
||||
- merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project))
|
||||
- if merge_project
|
||||
.pull-left.hidden-xs
|
||||
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
|
||||
%i.fa.fa-plus
|
||||
New Merge Request
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
|
||||
= icon('plus')
|
||||
New Merge Request
|
||||
|
||||
= render 'shared/issuable/filter', type: :merge_requests
|
||||
|
||||
.merge-requests-holder
|
||||
= render 'merge_requests'
|
||||
|
|
|
@ -10,3 +10,8 @@
|
|||
= markdown(@merge_request.description, cache_key: [@merge_request, "description"])
|
||||
%textarea.hidden.js-task-list-field
|
||||
= @merge_request.description
|
||||
|
||||
- if @merge_request.updated_at != @merge_request.created_at
|
||||
%small
|
||||
Edited
|
||||
= time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom')
|
||||
|
|
|
@ -8,11 +8,6 @@
|
|||
opened by #{link_to_member(@project, @merge_request.author, size: 24)}
|
||||
·
|
||||
= time_ago_with_tooltip(@merge_request.created_at)
|
||||
- if @merge_request.updated_at != @merge_request.created_at
|
||||
%span
|
||||
·
|
||||
= icon('edit', title: 'edited')
|
||||
= time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom')
|
||||
|
||||
.issue-btn-group.pull-right
|
||||
- if can?(current_user, :update_merge_request, @merge_request)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}");
|
||||
$('.issuable-sidebar').parent().effect('highlight')
|
||||
$('aside.right-sidebar')[0].outerHTML= "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
|
||||
$('aside.right-sidebar').effect('highlight')
|
||||
merge_request = new MergeRequest();
|
||||
|
|
|
@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
|
|||
xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
|
||||
xml.id namespace_project_url(@project.namespace, @project)
|
||||
xml.updated @events[0].updated_at.xmlschema if @events[0?
|
||||
xml.updated @events[0].updated_at.xmlschema if @events[0]
|
||||
|
||||
@events.each do |event|
|
||||
event_to_atom(xml, event)
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
= icon('users')
|
||||
= number_with_delimiter(group.users.count)
|
||||
|
||||
= image_tag group_icon(group), class: "avatar s46 hidden-xs"
|
||||
= image_tag group_icon(group), class: "avatar s40 hidden-xs"
|
||||
= link_to group, class: 'group-name' do
|
||||
%span.item-title= group.name
|
||||
|
||||
|
|
|
@ -1,32 +1,5 @@
|
|||
.issues-filters
|
||||
.issues-state-filters
|
||||
%ul.nav-links
|
||||
- if defined?(type) && type == :merge_requests
|
||||
- page_context_word = 'merge requests'
|
||||
- else
|
||||
- page_context_word = 'issues'
|
||||
%li{class: ("active" if params[:state] == 'opened')}
|
||||
= link_to page_filter_path(state: 'opened'), title: "Filter by #{page_context_word} that are currently opened." do
|
||||
#{state_filters_text_for(:opened, @project)}
|
||||
|
||||
- if defined?(type) && type == :merge_requests
|
||||
%li{class: ("active" if params[:state] == 'merged')}
|
||||
= link_to page_filter_path(state: 'merged'), title: 'Filter by merge requests that are currently merged.' do
|
||||
#{state_filters_text_for(:merged, @project)}
|
||||
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to page_filter_path(state: 'closed'), title: 'Filter by merge requests that are currently closed and unmerged.' do
|
||||
#{state_filters_text_for(:closed, @project)}
|
||||
- else
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to page_filter_path(state: 'closed'), title: 'Filter by issues that are currently closed.' do
|
||||
#{state_filters_text_for(:closed, @project)}
|
||||
|
||||
%li{class: ("active" if params[:state] == 'all')}
|
||||
= link_to page_filter_path(state: 'all'), title: "Show all #{page_context_word}." do
|
||||
#{state_filters_text_for(:all, @project)}
|
||||
|
||||
.issues-details-filters.gray-content-block
|
||||
.issues-details-filters.gray-content-block.second-block
|
||||
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do
|
||||
- if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
|
||||
.check-all-holder
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
%ul.nav-links.issues-state-filters
|
||||
- if defined?(type) && type == :merge_requests
|
||||
- page_context_word = 'merge requests'
|
||||
- else
|
||||
- page_context_word = 'issues'
|
||||
%li{class: ("active" if params[:state] == 'opened')}
|
||||
= link_to page_filter_path(state: 'opened'), title: "Filter by #{page_context_word} that are currently opened." do
|
||||
#{state_filters_text_for(:opened, @project)}
|
||||
|
||||
- if defined?(type) && type == :merge_requests
|
||||
%li{class: ("active" if params[:state] == 'merged')}
|
||||
= link_to page_filter_path(state: 'merged'), title: 'Filter by merge requests that are currently merged.' do
|
||||
#{state_filters_text_for(:merged, @project)}
|
||||
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to page_filter_path(state: 'closed'), title: 'Filter by merge requests that are currently closed and unmerged.' do
|
||||
#{state_filters_text_for(:closed, @project)}
|
||||
- else
|
||||
%li{class: ("active" if params[:state] == 'closed')}
|
||||
= link_to page_filter_path(state: 'closed'), title: 'Filter by issues that are currently closed.' do
|
||||
#{state_filters_text_for(:closed, @project)}
|
||||
|
||||
%li{class: ("active" if params[:state] == 'all')}
|
||||
= link_to page_filter_path(state: 'all'), title: "Show all #{page_context_word}." do
|
||||
#{state_filters_text_for(:all, @project)}
|
|
@ -1,4 +1,8 @@
|
|||
.block.participants
|
||||
.sidebar-collapsed-icon
|
||||
= icon('users')
|
||||
%span
|
||||
= participants.count
|
||||
.title
|
||||
= pluralize participants.count, "participant"
|
||||
- participants.each do |participant|
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
|
||||
.append-right-10.hidden-xs.hidden-sm
|
||||
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input', spellcheck: false }
|
||||
= hidden_field_tag :state, params['state']
|
||||
= hidden_field_tag :scope, params['scope']
|
||||
= hidden_field_tag :assignee_id, params['assignee_id']
|
||||
= hidden_field_tag :author_id, params['author_id']
|
||||
= hidden_field_tag :milestone_id, params['milestone_id']
|
||||
= hidden_field_tag :label_id, params['label_id']
|
||||
= form_tag(path, method: :get, id: "issue_search_form", class: 'issue-search-form') do
|
||||
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input input-short', spellcheck: false }
|
||||
= hidden_field_tag :state, params['state']
|
||||
= hidden_field_tag :scope, params['scope']
|
||||
= hidden_field_tag :assignee_id, params['assignee_id']
|
||||
= hidden_field_tag :author_id, params['author_id']
|
||||
= hidden_field_tag :milestone_id, params['milestone_id']
|
||||
= hidden_field_tag :label_id, params['label_id']
|
||||
|
|
|
@ -1,88 +1,132 @@
|
|||
.issuable-sidebar.issuable-affix
|
||||
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
|
||||
.block.assignee
|
||||
.title
|
||||
%label
|
||||
Assignee
|
||||
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
|
||||
.pull-right
|
||||
= link_to 'Edit', '#', class: 'edit-link'
|
||||
.value
|
||||
- if issuable.assignee
|
||||
%strong= link_to_member(@project, issuable.assignee, size: 24)
|
||||
- if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
|
||||
%a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
|
||||
= icon('exclamation-triangle')
|
||||
%aside.right-sidebar{ class: sidebar_gutter_collapsed_class }
|
||||
.issuable-sidebar
|
||||
.block
|
||||
%span.issuable-count.pull-left
|
||||
= issuable.iid
|
||||
of
|
||||
= issuable_count(:all, @project)
|
||||
%span.pull-right
|
||||
%a.gutter-toggle{href: '#'}
|
||||
- if sidebar_gutter_collapsed?
|
||||
= icon('angle-double-left')
|
||||
- else
|
||||
= icon('angle-double-right')
|
||||
.issuable-nav.pull-right.btn-group{role: 'group', "aria-label" => '...'}
|
||||
- if has_prev_issuable?(@project, issuable.id)
|
||||
= link_to 'Prev', issuable_link_prev(@project, issuable), class: 'btn btn-default'
|
||||
- else
|
||||
.light None
|
||||
|
||||
.selectbox
|
||||
= users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
|
||||
|
||||
.block.milestone
|
||||
.title
|
||||
%label
|
||||
Milestone
|
||||
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
|
||||
.pull-right
|
||||
= link_to 'Edit', '#', class: 'edit-link'
|
||||
.value
|
||||
- if issuable.milestone
|
||||
%span.back-to-milestone
|
||||
= link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
|
||||
%strong
|
||||
= icon('clock-o')
|
||||
= issuable.milestone.title
|
||||
%a.btn.btn-default.disabled{href: '#'}
|
||||
Prev
|
||||
- if has_next_issuable?(@project, issuable.id)
|
||||
= link_to 'Next', issuable_link_next(@project, issuable), class: 'btn btn-default'
|
||||
- else
|
||||
.light None
|
||||
.selectbox
|
||||
= f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
|
||||
= hidden_field_tag :issuable_context
|
||||
= f.submit class: 'btn hide'
|
||||
%a.btn.btn-default.disabled{href: '#'}
|
||||
Next
|
||||
|
||||
- if issuable.project.labels.any?
|
||||
.block
|
||||
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
|
||||
.block.assignee
|
||||
.sidebar-collapsed-icon
|
||||
- if issuable.assignee
|
||||
= link_to_member_avatar(issuable.assignee, size: 24)
|
||||
- else
|
||||
= icon('user')
|
||||
.title
|
||||
%label Labels
|
||||
%label
|
||||
Assignee
|
||||
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
|
||||
.pull-right
|
||||
= link_to 'Edit', '#', class: 'edit-link'
|
||||
.value.issuable-show-labels
|
||||
- if issuable.labels.any?
|
||||
- issuable.labels.each do |label|
|
||||
= link_to_label(label)
|
||||
.value
|
||||
- if issuable.assignee
|
||||
%strong= link_to_member(@project, issuable.assignee, size: 24)
|
||||
- if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
|
||||
%a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
|
||||
= icon('exclamation-triangle')
|
||||
- else
|
||||
.light None
|
||||
|
||||
.selectbox
|
||||
= users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
|
||||
|
||||
.block.milestone
|
||||
.sidebar-collapsed-icon
|
||||
= icon('balance-scale')
|
||||
%span
|
||||
- if issuable.milestone
|
||||
= issuable.milestone.title
|
||||
- else
|
||||
No
|
||||
.title
|
||||
%label
|
||||
Milestone
|
||||
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
|
||||
.pull-right
|
||||
= link_to 'Edit', '#', class: 'edit-link'
|
||||
.value
|
||||
- if issuable.milestone
|
||||
%span.back-to-milestone
|
||||
= link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
|
||||
%strong
|
||||
= icon('clock-o')
|
||||
= issuable.milestone.title
|
||||
- else
|
||||
.light None
|
||||
.selectbox
|
||||
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
|
||||
{ selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
|
||||
= f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
|
||||
= hidden_field_tag :issuable_context
|
||||
= f.submit class: 'btn hide'
|
||||
|
||||
= render "shared/issuable/participants", participants: issuable.participants(current_user)
|
||||
- if issuable.project.labels.any?
|
||||
.block.labels
|
||||
.sidebar-collapsed-icon
|
||||
= icon('tags')
|
||||
%span
|
||||
= issuable.labels.count
|
||||
.title
|
||||
%label Labels
|
||||
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
|
||||
.pull-right
|
||||
= link_to 'Edit', '#', class: 'edit-link'
|
||||
.value.issuable-show-labels
|
||||
- if issuable.labels.any?
|
||||
- issuable.labels.each do |label|
|
||||
= link_to_label(label, type: issuable.to_ability_name)
|
||||
- else
|
||||
.light None
|
||||
.selectbox
|
||||
= f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
|
||||
{ selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
|
||||
|
||||
- if current_user
|
||||
- subscribed = issuable.subscribed?(current_user)
|
||||
.block.light
|
||||
= render "shared/issuable/participants", participants: issuable.participants(current_user)
|
||||
%hr
|
||||
- if current_user
|
||||
- subscribed = issuable.subscribed?(current_user)
|
||||
.block.light
|
||||
.sidebar-collapsed-icon
|
||||
= icon('rss')
|
||||
.title
|
||||
%label.light Notifications
|
||||
- subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
|
||||
%button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'}
|
||||
%span= subscribed ? 'Unsubscribe' : 'Subscribe'
|
||||
.subscription-status{data: {status: subscribtion_status}}
|
||||
.unsubscribed{class: ( 'hidden' if subscribed )}
|
||||
You're not receiving notifications from this thread.
|
||||
.subscribed{class: ( 'hidden' unless subscribed )}
|
||||
You're receiving notifications because you're subscribed to this thread.
|
||||
|
||||
- project_ref = cross_project_reference(@project, issuable)
|
||||
.block.project-reference
|
||||
.sidebar-collapsed-icon
|
||||
= icon('clipboard')
|
||||
.title
|
||||
%label.light Notifications
|
||||
- subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
|
||||
%button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'}
|
||||
%span= subscribed ? 'Unsubscribe' : 'Subscribe'
|
||||
.subscription-status{data: {status: subscribtion_status}}
|
||||
.unsubscribed{class: ( 'hidden' if subscribed )}
|
||||
You're not receiving notifications from this thread.
|
||||
.subscribed{class: ( 'hidden' unless subscribed )}
|
||||
You're receiving notifications because you're subscribed to this thread.
|
||||
.cross-project-reference
|
||||
%span
|
||||
Reference:
|
||||
%cite{title: project_ref}
|
||||
= project_ref
|
||||
= clipboard_button(clipboard_text: project_ref)
|
||||
|
||||
- project_ref = cross_project_reference(@project, issuable)
|
||||
.block
|
||||
.title
|
||||
.cross-project-reference
|
||||
%span
|
||||
Reference:
|
||||
%cite{title: project_ref}
|
||||
= project_ref
|
||||
= clipboard_button(clipboard_text: project_ref)
|
||||
|
||||
:javascript
|
||||
new Subscription("#{toggle_subscription_path(issuable)}");
|
||||
new IssuableContext();
|
||||
:javascript
|
||||
new Subscription("#{toggle_subscription_path(issuable)}");
|
||||
new IssuableContext();
|
||||
|
|
|
@ -8,18 +8,22 @@
|
|||
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true
|
||||
|
||||
%ul.projects-list
|
||||
- projects.each_with_index do |project, i|
|
||||
- css_class = (i >= projects_limit) ? 'hide' : nil
|
||||
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
|
||||
avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
|
||||
forks: forks, show_last_commit_as_description: show_last_commit_as_description
|
||||
- if projects.any?
|
||||
- projects.each_with_index do |project, i|
|
||||
- css_class = (i >= projects_limit) ? 'hide' : nil
|
||||
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
|
||||
avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
|
||||
forks: forks, show_last_commit_as_description: show_last_commit_as_description
|
||||
|
||||
- if projects.size > projects_limit
|
||||
%li.bottom.center
|
||||
.light
|
||||
#{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
|
||||
= link_to '#', class: 'js-expand' do
|
||||
Show all
|
||||
- if projects.size > projects_limit && projects.kind_of?(Array)
|
||||
%li.bottom.center
|
||||
.light
|
||||
#{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
|
||||
= link_to '#', class: 'js-expand' do
|
||||
Show all
|
||||
= paginate projects, theme: "gitlab" if !projects.kind_of?(Array)
|
||||
- else
|
||||
%h3 No projects found
|
||||
|
||||
:javascript
|
||||
new ProjectsList();
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
- if avatar
|
||||
.dash-project-avatar
|
||||
- if use_creator_avatar
|
||||
= image_tag avatar_icon(project.creator.email, 46), class: "avatar s46", alt:''
|
||||
= image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
|
||||
- else
|
||||
= project_icon(project, alt: '', class: 'avatar project-avatar s46')
|
||||
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
|
||||
%span.project-full-name
|
||||
%span.namespace-name
|
||||
- if project.namespace && !skip_namespace
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
class ProjectPath
|
||||
attr_reader :old_path, :id, :namespace_path
|
||||
|
||||
def initialize(old_path, id, namespace_path, namespace_id)
|
||||
@old_path = old_path
|
||||
@id = id
|
||||
@namespace_path = namespace_path
|
||||
@namespace_id = namespace_id
|
||||
end
|
||||
|
||||
def clean_path
|
||||
@_clean_path ||= PathCleaner.clean(@old_path, @namespace_id)
|
||||
end
|
||||
end
|
||||
|
||||
class PathCleaner
|
||||
def initialize(path, namespace_id)
|
||||
@namespace_id = namespace_id
|
||||
@path = path
|
||||
end
|
||||
|
||||
def self.clean(*args)
|
||||
new(*args).clean
|
||||
end
|
||||
|
||||
def clean
|
||||
path = cleaned_path
|
||||
count = 0
|
||||
while path_exists?(path)
|
||||
path = "#{cleaned_path}#{count}"
|
||||
count += 1
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cleaned_path
|
||||
@_cleaned_path ||= @path.gsub(/\.atom\z/, '-atom')
|
||||
end
|
||||
|
||||
def path_exists?(path)
|
||||
Project.find_by_path_and_namespace_id(path, @namespace_id)
|
||||
end
|
||||
end
|
||||
|
||||
def projects_with_dot_atom
|
||||
select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE lower(p.path) LIKE '%.atom'")
|
||||
end
|
||||
|
||||
def up
|
||||
projects_with_dot_atom.each do |project|
|
||||
project_path = ProjectPath.new(project['path'], project['id'], project['namespace_path'], project['namespace_id'])
|
||||
clean_path(project_path) if rename_project_repo(project_path)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def clean_path(project_path)
|
||||
execute "UPDATE projects SET path = #{sanitize(project_path.clean_path)} WHERE id = #{project_path.id}"
|
||||
end
|
||||
|
||||
def rename_project_repo(project_path)
|
||||
old_path_with_namespace = File.join(project_path.namespace_path, project_path.old_path)
|
||||
new_path_with_namespace = File.join(project_path.namespace_path, project_path.clean_path)
|
||||
|
||||
gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
|
||||
gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def sanitize(value)
|
||||
ActiveRecord::Base.connection.quote(value)
|
||||
end
|
||||
end
|
|
@ -1,25 +1,29 @@
|
|||
# Adding deploy keys to multiple projects
|
||||
|
||||
If you want to easily add the same deploy key to multiple projects in the same group, this can be achieved quite easily with the API.
|
||||
If you want to easily add the same deploy key to multiple projects in the same
|
||||
group, this can be achieved quite easily with the API.
|
||||
|
||||
First, find the ID of the projects you're interested in, by either listing all projects:
|
||||
First, find the ID of the projects you're interested in, by either listing all
|
||||
projects:
|
||||
|
||||
```
|
||||
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/projects
|
||||
curl -H 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/projects
|
||||
```
|
||||
|
||||
Or finding the id of a group and then listing all projects in that group:
|
||||
Or finding the ID of a group and then listing all projects in that group:
|
||||
|
||||
```
|
||||
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/groups
|
||||
curl -H 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/groups
|
||||
|
||||
# For group 1234:
|
||||
curl --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/groups/1234
|
||||
curl -H 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' https://gitlab.example.com/api/v3/groups/1234
|
||||
```
|
||||
|
||||
With those IDs, add the same deploy key to all:
|
||||
|
||||
```
|
||||
for project_id in 321 456 987; do
|
||||
curl -X POST --data '{"title": "my key", "key": "ssh-rsa AAAA..."}' --header 'PRIVATE-TOKEN: abcdef' https://gitlab.com/api/v3/projects/${project_id}/keys
|
||||
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" \
|
||||
--data '{"title": "my key", "key": "ssh-rsa AAAA..."}' https://gitlab.example.com/api/v3/projects/${project_id}/keys
|
||||
done
|
||||
```
|
||||
|
|
|
@ -1,39 +1,47 @@
|
|||
# Session
|
||||
|
||||
Login to get private token
|
||||
You can login with both GitLab and LDAP credentials in order to obtain the
|
||||
private token.
|
||||
|
||||
```
|
||||
POST /session
|
||||
```
|
||||
|
||||
Parameters:
|
||||
| Attribute | Type | Required | Description |
|
||||
| ---------- | ------- | -------- | -------- |
|
||||
| `login` | string | yes | The username of the user|
|
||||
| `email` | string | yes if login is not provided | The email of the user |
|
||||
| `password` | string | yes | The password of the user |
|
||||
|
||||
- `login` (required) - The login of user
|
||||
- `email` (required if login missing) - The email of user
|
||||
- `password` (required) - Valid password
|
||||
|
||||
**You can login with both GitLab and LDAP credentials now**
|
||||
```bash
|
||||
curl -X POST "https://gitlab.example.com/api/v3/session?login=john_smith&password=strongpassw0rd"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"username": "john_smith",
|
||||
"email": "john@example.com",
|
||||
"name": "John Smith",
|
||||
"private_token": "dd34asd13as",
|
||||
"blocked": false,
|
||||
"created_at": "2012-05-23T08:00:58Z",
|
||||
"username": "john_smith",
|
||||
"id": 32,
|
||||
"state": "active",
|
||||
"avatar_url": null,
|
||||
"created_at": "2015-01-29T21:07:19.440Z",
|
||||
"is_admin": true,
|
||||
"bio": null,
|
||||
"skype": "",
|
||||
"linkedin": "",
|
||||
"twitter": "",
|
||||
"website_url": "",
|
||||
"dark_scheme": false,
|
||||
"email": "john@example.com",
|
||||
"theme_id": 1,
|
||||
"is_admin": false,
|
||||
"color_scheme_id": 1,
|
||||
"projects_limit": 10,
|
||||
"current_sign_in_at": "2015-07-07T07:10:58.392Z",
|
||||
"identities": [],
|
||||
"can_create_group": true,
|
||||
"can_create_team": true,
|
||||
"can_create_project": true
|
||||
"can_create_project": true,
|
||||
"two_factor_enabled": false,
|
||||
"private_token": "9koXpg98eAheJpvBs5tK"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@ Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that
|
|||
For example:
|
||||
|
||||
```
|
||||
git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#2). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
|
||||
git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#22). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
|
||||
```
|
||||
|
||||
will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does not match the pattern. It also works with multiline commit messages.
|
||||
|
|
|
@ -120,6 +120,17 @@ Inside the document:
|
|||
`http://doc.gitlab.com/ce/administration/restart_gitlab.html`.
|
||||
Replace `reconfigure` with `restart` where appropriate.
|
||||
|
||||
## Installation guide
|
||||
|
||||
- **Ruby:**
|
||||
In [step 2 of the installation guide](../install/installation.md#2-ruby),
|
||||
we install Ruby from source. Whenever there is a new version that needs to
|
||||
be updated, remember to change it throughout the codeblock and also replace
|
||||
the sha256sum (it can be found in the [downloads page][ruby-dl] of the Ruby
|
||||
website).
|
||||
|
||||
[ruby-dl]: https://www.ruby-lang.org/en/downloads/ "Ruby download website"
|
||||
|
||||
## API
|
||||
|
||||
Here is a list of must-have items. Use them in the exact order that appears
|
||||
|
|
|
@ -124,7 +124,7 @@ Download Ruby and compile it:
|
|||
|
||||
mkdir /tmp/ruby && cd /tmp/ruby
|
||||
curl -O --progress https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.4.tar.gz
|
||||
echo 'e2e195a4a58133e3ad33b955c829bb536fa3c075 ruby-2.2.4.tar.gz' | shasum -c - && tar xzf ruby-2.2.4.tar.gz
|
||||
echo 'b6eff568b48e0fda76e5a36333175df049b204e91217aa32a65153cc0cdcb761 ruby-2.2.4.tar.gz' | sha256sum -c - && tar xzf ruby-2.2.4.tar.gz
|
||||
cd ruby-2.2.4
|
||||
./configure --disable-install-rdoc
|
||||
make
|
||||
|
@ -267,6 +267,9 @@ sudo usermod -aG redis git
|
|||
sudo chmod -R u+rwX tmp/pids/
|
||||
sudo chmod -R u+rwX tmp/sockets/
|
||||
|
||||
# Create the public/uploads/ directory
|
||||
sudo -u git -H mkdir public/uploads/
|
||||
|
||||
# Make sure GitLab can write to the public/uploads/ directory
|
||||
sudo chmod -R u+rwX public/uploads
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ module Gitlab
|
|||
# new_path - new project path with namespace
|
||||
#
|
||||
# Ex.
|
||||
# mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git")
|
||||
# mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new")
|
||||
#
|
||||
def mv_repository(path, new_path)
|
||||
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'mv-project',
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
module Gitlab
|
||||
module Database
|
||||
def self.adapter_name
|
||||
connection.adapter_name
|
||||
end
|
||||
|
||||
def self.mysql?
|
||||
ActiveRecord::Base.connection.adapter_name.downcase == 'mysql2'
|
||||
adapter_name.downcase == 'mysql2'
|
||||
end
|
||||
|
||||
def self.postgresql?
|
||||
ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
|
||||
adapter_name.downcase == 'postgresql'
|
||||
end
|
||||
|
||||
def self.version
|
||||
database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
|
||||
end
|
||||
|
||||
def true_value
|
||||
case ActiveRecord::Base.connection.adapter_name.downcase
|
||||
when 'postgresql'
|
||||
if self.class.postgresql?
|
||||
"'t'"
|
||||
else
|
||||
1
|
||||
|
@ -18,12 +25,31 @@ module Gitlab
|
|||
end
|
||||
|
||||
def false_value
|
||||
case ActiveRecord::Base.connection.adapter_name.downcase
|
||||
when 'postgresql'
|
||||
if self.class.postgresql?
|
||||
"'f'"
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.connection
|
||||
ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def self.database_version
|
||||
row = connection.execute("SELECT VERSION()").first
|
||||
|
||||
if postgresql?
|
||||
row['version']
|
||||
else
|
||||
row.first
|
||||
end
|
||||
end
|
||||
|
||||
def connection
|
||||
self.class.connection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,12 +34,12 @@ module Gitlab
|
|||
|
||||
|
||||
def project_path_regex
|
||||
@project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/.freeze
|
||||
@project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git|\.atom)\z/.freeze
|
||||
end
|
||||
|
||||
def project_path_regex_message
|
||||
"can contain only letters, digits, '_', '-' and '.'. " \
|
||||
"Cannot start with '-' or end in '.git'" \
|
||||
"Cannot start with '-', end in '.git' or end in '.atom'" \
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -86,6 +86,14 @@ describe ProjectsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the url contains .atom" do
|
||||
let(:public_project_with_dot_atom) { build(:project, :public, name: 'my.atom', path: 'my.atom') }
|
||||
|
||||
it 'expect an error creating the project' do
|
||||
expect(public_project_with_dot_atom).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#destroy" do
|
||||
|
|
|
@ -18,7 +18,7 @@ describe 'Admin Builds' do
|
|||
|
||||
visit admin_builds_path
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'All')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'All')
|
||||
expect(page.all('.build-link').size).to eq(4)
|
||||
expect(page).to have_link 'Cancel all'
|
||||
end
|
||||
|
@ -28,7 +28,7 @@ describe 'Admin Builds' do
|
|||
it 'shows a message' do
|
||||
visit admin_builds_path
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'All')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'All')
|
||||
expect(page).to have_content 'No builds to show'
|
||||
expect(page).not_to have_link 'Cancel all'
|
||||
end
|
||||
|
@ -44,7 +44,7 @@ describe 'Admin Builds' do
|
|||
|
||||
visit admin_builds_path(scope: :running)
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'Running')
|
||||
expect(page.find('.build-link')).to have_content(build1.id)
|
||||
expect(page.find('.build-link')).not_to have_content(build2.id)
|
||||
expect(page.find('.build-link')).not_to have_content(build3.id)
|
||||
|
@ -58,7 +58,7 @@ describe 'Admin Builds' do
|
|||
|
||||
visit admin_builds_path(scope: :running)
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'Running')
|
||||
expect(page).to have_content 'No builds to show'
|
||||
expect(page).not_to have_link 'Cancel all'
|
||||
end
|
||||
|
@ -74,7 +74,7 @@ describe 'Admin Builds' do
|
|||
|
||||
visit admin_builds_path(scope: :finished)
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'Finished')
|
||||
expect(page.find('.build-link')).not_to have_content(build1.id)
|
||||
expect(page.find('.build-link')).not_to have_content(build2.id)
|
||||
expect(page.find('.build-link')).to have_content(build3.id)
|
||||
|
@ -88,7 +88,7 @@ describe 'Admin Builds' do
|
|||
|
||||
visit admin_builds_path(scope: :finished)
|
||||
|
||||
expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished')
|
||||
expect(page).to have_selector('.nav-links li.active', text: 'Finished')
|
||||
expect(page).to have_content 'No builds to show'
|
||||
expect(page).to have_link 'Cancel all'
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'spec_helper'
|
|||
describe LabelsHelper do
|
||||
describe 'link_to_label' do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:label) { create(:label, project: project) }
|
||||
|
||||
context 'with @project set' do
|
||||
before do
|
||||
|
@ -11,34 +11,31 @@ describe LabelsHelper do
|
|||
end
|
||||
|
||||
it 'uses the instance variable' do
|
||||
expect(label).not_to receive(:project)
|
||||
link_to_label(label)
|
||||
expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}">.*</a>}
|
||||
end
|
||||
end
|
||||
|
||||
context 'without @project set' do
|
||||
it "uses the label's project" do
|
||||
expect(label).to receive(:project).and_return(project)
|
||||
link_to_label(label)
|
||||
expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name=#{label.name}">.*</a>}
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a named project argument' do
|
||||
it 'uses the provided project' do
|
||||
arg = double('project')
|
||||
expect(arg).to receive(:namespace).and_return('foo')
|
||||
expect(arg).to receive(:to_param).and_return('foo')
|
||||
context 'with a project argument' do
|
||||
let(:another_project) { double('project', namespace: 'foo3', to_param: 'bar3') }
|
||||
|
||||
link_to_label(label, project: arg)
|
||||
it 'links to merge requests page' do
|
||||
expect(link_to_label(label, project: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name=#{label.name}">.*</a>}
|
||||
end
|
||||
end
|
||||
|
||||
it 'takes precedence over other types' do
|
||||
@project = project
|
||||
expect(@project).not_to receive(:namespace)
|
||||
expect(label).not_to receive(:project)
|
||||
|
||||
arg = double('project', namespace: 'foo', to_param: 'foo')
|
||||
link_to_label(label, project: arg)
|
||||
context 'with a type argument' do
|
||||
['issue', :issue, 'merge_request', :merge_request].each do |type|
|
||||
context "set to #{type}" do
|
||||
it 'links to correct page' do
|
||||
expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.to_reference}/#{type.to_s.pluralize}\?label_name=#{label.name}">.*</a>}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -42,9 +42,9 @@ describe SearchHelper do
|
|||
expect(search_autocomplete_opts(project.name).size).to eq(1)
|
||||
end
|
||||
|
||||
it "includes the public group" do
|
||||
it "should not include the public group" do
|
||||
group = create(:group)
|
||||
expect(search_autocomplete_opts(group.name).size).to eq(1)
|
||||
expect(search_autocomplete_opts(group.name).size).to eq(0)
|
||||
end
|
||||
|
||||
context "with a current project" do
|
||||
|
|
|
@ -14,4 +14,24 @@ describe Gitlab::Database, lib: true do
|
|||
|
||||
it { is_expected.to satisfy { |val| val == true || val == false } }
|
||||
end
|
||||
|
||||
describe '.version' do
|
||||
context "on mysql" do
|
||||
it "extracts the version number" do
|
||||
allow(described_class).to receive(:database_version).
|
||||
and_return("5.7.12-standard")
|
||||
|
||||
expect(described_class.version).to eq '5.7.12-standard'
|
||||
end
|
||||
end
|
||||
|
||||
context "on postgresql" do
|
||||
it "extracts the version number" do
|
||||
allow(described_class).to receive(:database_version).
|
||||
and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
|
||||
|
||||
expect(described_class.version).to eq '9.4.4'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue