diff options
Diffstat (limited to 'app')
295 files changed, 2868 insertions, 1910 deletions
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf b/app/assets/fonts/SourceSansPro-Black.ttf Binary files differindex cb89a2d171e..9c9b5cb7f03 100755..100644 --- a/app/assets/fonts/SourceSansPro-Black.ttf +++ b/app/assets/fonts/SourceSansPro-Black.ttf diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf b/app/assets/fonts/SourceSansPro-BlackIt.ttf Binary files differnew file mode 100644 index 00000000000..294ce5abe8f --- /dev/null +++ b/app/assets/fonts/SourceSansPro-BlackIt.ttf diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf b/app/assets/fonts/SourceSansPro-Bold.ttf Binary files differindex 5d65c93242f..5d65c93242f 100755..100644 --- a/app/assets/fonts/SourceSansPro-Bold.ttf +++ b/app/assets/fonts/SourceSansPro-Bold.ttf diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf b/app/assets/fonts/SourceSansPro-BoldIt.ttf Binary files differnew file mode 100644 index 00000000000..3decd130070 --- /dev/null +++ b/app/assets/fonts/SourceSansPro-BoldIt.ttf diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf b/app/assets/fonts/SourceSansPro-ExtraLight.ttf Binary files differindex bb4176c6fff..253eafa3783 100755..100644 --- a/app/assets/fonts/SourceSansPro-ExtraLight.ttf +++ b/app/assets/fonts/SourceSansPro-ExtraLight.ttf diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf Binary files differnew file mode 100644 index 00000000000..00d7e9a7aa8 --- /dev/null +++ b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf diff --git a/app/assets/fonts/SourceSansPro-It.ttf b/app/assets/fonts/SourceSansPro-It.ttf Binary files differnew file mode 100644 index 00000000000..f7af5377595 --- /dev/null +++ b/app/assets/fonts/SourceSansPro-It.ttf diff --git a/app/assets/fonts/SourceSansPro-Light.ttf b/app/assets/fonts/SourceSansPro-Light.ttf Binary files differindex 83a0a336661..83a0a336661 100755..100644 --- a/app/assets/fonts/SourceSansPro-Light.ttf +++ b/app/assets/fonts/SourceSansPro-Light.ttf diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf b/app/assets/fonts/SourceSansPro-LightIt.ttf Binary files differnew file mode 100644 index 00000000000..f18827985ef --- /dev/null +++ b/app/assets/fonts/SourceSansPro-LightIt.ttf diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf b/app/assets/fonts/SourceSansPro-Regular.ttf Binary files differindex 44486cdc670..44486cdc670 100755..100644 --- a/app/assets/fonts/SourceSansPro-Regular.ttf +++ b/app/assets/fonts/SourceSansPro-Regular.ttf diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf b/app/assets/fonts/SourceSansPro-Semibold.ttf Binary files differindex 86b00c067e0..86b00c067e0 100755..100644 --- a/app/assets/fonts/SourceSansPro-Semibold.ttf +++ b/app/assets/fonts/SourceSansPro-Semibold.ttf diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf Binary files differnew file mode 100644 index 00000000000..13d66a1fc45 --- /dev/null +++ b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf diff --git a/app/assets/images/logo.svg b/app/assets/images/logo.svg index c09785cb96f..f4e19b67008 100644 --- a/app/assets/images/logo.svg +++ b/app/assets/images/logo.svg @@ -10,17 +10,17 @@ <g id="Fill-1-+-Group-24"> <g id="Group-24"> <g id="Group"> - <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329"></path> - <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26"></path> - <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326"></path> - <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329"></path> - <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26"></path> - <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326"></path> - <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329"></path> + <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path> + <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path> + <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path> </g> </g> </g> </g> </g> </g> -</svg>
\ No newline at end of file +</svg> diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 050888f9c15..f6bf836f19f 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,10 +11,10 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $(".js-commit-button").click -> - $("#file-content").val editor.getValue() - $(".file-editor form").submit() - return false + # Before a form submission, move the content from the Ace editor into the + # submitted textarea + $('form').submit -> + $("#file-content").val(editor.getValue()) editModePanes = $(".js-edit-mode-pane") editModeLinks = $(".js-edit-mode a") diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index 1f36a53f191..68c5e5195e3 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,10 +11,10 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $(".js-commit-button").click -> - $("#file-content").val editor.getValue() - $(".file-editor form").submit() - return false + # Before a form submission, move the content from the Ace editor into the + # submitted textarea + $('form').submit -> + $("#file-content").val(editor.getValue()) editor: -> return @editor diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 4c4bc3d66ed..2b1e20d3225 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -25,7 +25,7 @@ class @Calendar 30 ] legendCellPadding: 3 - cellSize: $('.user-calendar').width() / 80 + cellSize: $('.user-calendar').width() / 76 onClick: (date, count) -> formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() $.ajax diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee index c30859b484b..44d5ddb7d95 100644 --- a/app/assets/javascripts/ci/build.coffee +++ b/app/assets/javascripts/ci/build.coffee @@ -22,7 +22,7 @@ class CiBuild # Only valid for runnig build when output changes during time # CiBuild.interval = setInterval => - if window.location.href is build_url + if window.location.href.split("#").first() is build_url $.ajax url: build_url dataType: "json" @@ -31,7 +31,7 @@ class CiBuild $('#build-trace code').html build.trace_html $('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>' @checkAutoscroll() - else + else if build.status != build_status Turbolinks.visit build_url , 4000 diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee new file mode 100644 index 00000000000..ec4b80cca6f --- /dev/null +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -0,0 +1,21 @@ +#= require clipboard + +$ -> + clipboard = new Clipboard '.js-clipboard-trigger', + text: (trigger) -> + $target = $(trigger.nextElementSibling || trigger.previousElementSibling) + $target.data('clipboard-text') || $target.text().trim() + + clipboard.on 'success', (e) -> + $(e.trigger). + tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). + tooltip('show') + + # Clear the selection and blur the trigger so it loses its border + e.clearSelection() + $(e.trigger).blur() + + # Manually hide the tooltip after 1 second + setTimeout(-> + $(e.trigger).tooltip('hide') + , 1000) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 3e77ea515f8..593a8f42130 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -68,8 +68,8 @@ class @MergeRequestTabs scrollToElement: (container) -> if window.location.hash - top = $(container + " " + window.location.hash).offset().top - $('body').scrollTo(top) + $el = $("#{container} #{window.location.hash}") + $('body').scrollTo($el.offset().top) if $el.length # Activate a tab based on the current action activateTab: (action) -> @@ -127,7 +127,7 @@ class @MergeRequestTabs document.getElementById('commits').innerHTML = data.html $('.js-timeago').timeago() @commitsLoaded = true - @scrollToElement(".commits") + @scrollToElement("#commits") loadDiff: (source) -> return if @diffsLoaded @@ -137,7 +137,7 @@ class @MergeRequestTabs success: (data) => document.getElementById('diffs').innerHTML = data.html @diffsLoaded = true - @scrollToElement(".diffs") + @scrollToElement("#diffs") # Show or hide the loading spinner # diff --git a/app/assets/javascripts/shortcuts_navigation.coffee b/app/assets/javascripts/shortcuts_navigation.coffee index 5b6f9e7e3f2..8decaedd87b 100644 --- a/app/assets/javascripts/shortcuts_navigation.coffee +++ b/app/assets/javascripts/shortcuts_navigation.coffee @@ -7,6 +7,7 @@ class @ShortcutsNavigation extends Shortcuts Mousetrap.bind('g e', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-project-activity')) Mousetrap.bind('g f', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-tree')) Mousetrap.bind('g c', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-commits')) + Mousetrap.bind('g b', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-builds')) Mousetrap.bind('g n', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-network')) Mousetrap.bind('g g', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-graphs')) Mousetrap.bind('g i', -> ShortcutsNavigation.findAndFollowLink('.shortcuts-issues')) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 6ce34b5c3e8..8917c53b1f5 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -18,6 +18,7 @@ line-height: 36px; } +.content-block, .gray-content-block { margin: -$gl-padding; background-color: $background-color; @@ -27,6 +28,10 @@ border-bottom: 1px solid $border-color; color: $gl-gray; + &.white { + background-color: white; + } + &.top-block { border-top: none; } @@ -60,3 +65,48 @@ line-height: 42px; } } + +.cover-block { + text-align: center; + background: #f7f8fa; + margin: -$gl-padding; + margin-bottom: 0; + padding: 44px $gl-padding; + border-bottom: 1px solid $border-color; + position: relative; + + .avatar-holder { + margin-bottom: 16px; + + .avatar, .identicon { + margin: 0 auto; + float: none; + } + + .identicon { + @include border-radius(50%); + } + } + + .cover-title { + color: $gl-header-color; + margin: 0; + font-size: 23px; + font-weight: normal; + margin: 16px 0 5px 0; + color: #4c4e54; + font-size: 23px; + line-height: 1.1; + } + + .cover-desc { + padding: 0 $gl-padding 3px; + color: $gl-text-color; + } + + .cover-controls { + position: absolute; + top: 10px; + right: 10px; + } +} diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index e5f0c0ad9ef..fe56266284b 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -162,10 +162,25 @@ border-color: #e7e9ed; width: 140px; + .badge { + font-weight: normal; + background-color: #eee; + color: #78a; + } + &.active { border-color: $gl-info; background: $gl-info; color: #fff; + + .badge { + color: $gl-info; + background-color: white; + } } } } + +.btn-clipboard { + border: none; +} diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index e1a1793be9c..3d0b71e066e 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -387,6 +387,16 @@ table { } } +.center-middle-menu { + @include nav-menu; + text-align: center; + margin: -$gl-padding; + height: auto; + margin-top: 0; + margin-bottom: 0; + border-bottom: 1px solid $border-color; +} + .dropzone .dz-preview .dz-progress { border-color: $border-color !important; } diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 9dd77747884..35db00281e5 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -10,6 +10,10 @@ border-bottom: 1px solid #E7E9EE; margin-bottom: 1em; + &.readme-holder { + border-bottom: 0; + } + table { @extend .table; } @@ -94,7 +98,6 @@ border-right: none; } background: #fff; - padding: 10px $gl-padding; } .lines { pre { diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index c5764c36597..f6942db5816 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -107,7 +107,7 @@ ul.content-list { > li { padding: $gl-padding; - border-color: #f1f2f4; + border-color: $table-border-color; margin-left: -$gl-padding; margin-right: -$gl-padding; color: $gl-gray; diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 089e6958eeb..fe078d016d7 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -147,14 +147,8 @@ .badge { font-weight: normal; - background-color: #fff; background-color: #eee; color: #78a; } } } - -.fa-align { - top: 20px; - position: relative; -} diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index cba621635b6..78fff58d232 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -32,7 +32,7 @@ } .select2-results .select2-result-label { - padding: 16px; + padding: 9px; } .select2-drop{ @@ -143,4 +143,4 @@ .ajax-users-dropdown { min-width: 250px !important; -} +}
\ No newline at end of file diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c5ea3aca7ca..985ea164576 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -242,6 +242,9 @@ img { width: 36px; height: 36px; + } + + #tanuki-logo, img { float: left; } @@ -265,3 +268,13 @@ } } } + + +.tanuki-shape { + transition: all 0.8s; + + &:hover { + fill: rgb(255, 255, 255); + transition: all 0.1s; + } +} diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 789b34020c1..66e16e8df75 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -1,3 +1,9 @@ +.table-holder { + margin: -$gl-padding; + margin-top: 0; + margin-bottom: 0; +} + table { &.table { .dropdown-menu a { @@ -18,15 +24,17 @@ table { tr { td, th { - padding: 8px 10px; + padding: 10px $gl-padding; line-height: 20px; vertical-align: middle; } + th { font-weight: normal; font-size: 15px; border-bottom: 1px solid $border-color !important; } + td { border-color: $table-border-color !important; border-bottom: 1px solid; diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss index bf21d7fce76..eb53c4153d3 100644 --- a/app/assets/stylesheets/framework/timeline.scss +++ b/app/assets/stylesheets/framework/timeline.scss @@ -6,13 +6,17 @@ .timeline-entry { padding: $gl-padding; - border-color: #f1f2f4; + border-color: $table-border-color; margin-left: -$gl-padding; margin-right: -$gl-padding; color: $gl-gray; border-bottom: 1px solid #ECEEF1; border-right: 1px solid #ECEEF1; + &:target { + background: $hover; + } + &:last-child { border-bottom: none; } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index bf36f96cc97..e6558a23858 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -1,5 +1,6 @@ @mixin md-typography { color: $md-text-color; + word-wrap: break-word; a { color: $md-link-color; @@ -17,7 +18,6 @@ font-family: $monospace_font; white-space: pre; word-wrap: normal; - padding: 1px 2px; } kbd { @@ -73,6 +73,8 @@ } blockquote { + color: #7f8fa4; + font-size: inherit; padding: 8px 21px; margin: 12px 0 12px; border-left: 3px solid #e7e9ed; @@ -80,7 +82,7 @@ blockquote p { color: #7f8fa4 !important; - font-size: 15px; + font-size: inherit; line-height: 1.5; } @@ -101,9 +103,9 @@ pre { margin: 12px 0 12px 0 !important; - background-color: #f8fafc !important; + background-color: #f8fafc; font-size: 13px !important; - color: #5b6169 !important; + color: #5b6169; line-height: 1.6em !important; @include border-radius(2px); } @@ -112,9 +114,9 @@ font-weight: inherit; } - - ul { - color: #5c5d5e; + ul, ol { + padding: 0; + margin: 6px 0 6px 18px !important; } li { @@ -136,6 +138,33 @@ text-decoration: none; } } + + /* Link to current header. */ + h1, h2, h3, h4, h5, h6 { + position: relative; + + a.anchor { + // Setting `display: none` would prevent the anchor being scrolled to, so + // instead we set the height to 0 and it gets updated on hover. + height: 0; + } + + &:hover > a.anchor { + $size: 16px; + position: absolute; + right: 100%; + top: 50%; + margin-top: -$size/2; + margin-right: 0px; + padding-right: 20px; + display: inline-block; + width: $size; + height: $size; + background-image: image-url("icon-link.png"); + background-size: contain; + background-repeat: no-repeat; + } + } } @@ -202,53 +231,11 @@ a > code { } /** - * Wiki typography + * Apply Markdown typography * */ .wiki { @include md-typography; - - word-wrap: break-word; - padding: 7px; - - /* Link to current header. */ - h1, h2, h3, h4, h5, h6 { - position: relative; - - a.anchor { - // Setting `display: none` would prevent the anchor being scrolled to, so - // instead we set the height to 0 and it gets updated on hover. - height: 0; - } - - &:hover > a.anchor { - $size: 16px; - position: absolute; - right: 100%; - top: 50%; - margin-top: -$size/2; - margin-right: 0px; - padding-right: 20px; - display: inline-block; - width: $size; - height: $size; - background-image: image-url("icon-link.png"); - background-size: contain; - background-repeat: no-repeat; - } - } - - ul,ol { - padding: 0; - margin: 6px 0 6px 18px !important; - } - ol { - color: #5c5d5e; - } -} - -.md-area { - @include md-typography; } .md { @@ -261,6 +248,7 @@ a > code { */ textarea.js-gfm-input { font-family: $monospace_font; + color: $gl-text-color; } .md-preview { diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index 8323a8598ec..6a2b25ddc67 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -1,11 +1,10 @@ /* https://github.com/MozMorris/tomorrow-pygments */ -pre.code.highlight.dark, .code.dark { - background-color: #1d1f21; - color: #c5c8c6; + background-color: #1d1f21 !important; + color: #c5c8c6 !important; - pre.code, + pre.highlight, .line-numbers, .line-numbers a { background-color: #1d1f21 !important; @@ -23,8 +22,8 @@ pre.code.highlight.dark, // Search result highlight span.highlight_word { - background: #ffe792; - color: #000000; + background-color: #ffe792 !important; + color: #000000 !important; } .hll { background-color: #373b41 } diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index e8381674336..8560c3c490f 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -1,15 +1,14 @@ /* https://github.com/richleland/pygments-css/blob/master/monokai.css */ -pre.code.monokai, .code.monokai { - background: #272822; - color: #f8f8f2; + background-color: #272822 !important; + color: #f8f8f2 !important; pre.highlight, .line-numbers, .line-numbers a { - background:#272822 !important; - color:#f8f8f2 !important; + background-color :#272822 !important; + color: #f8f8f2 !important; } pre.code { @@ -23,8 +22,8 @@ pre.code.monokai, // Search result highlight span.highlight_word { - background: #ffe792; - color: #000000; + background-color: #ffe792 !important; + color: #000000 !important; } .hll { background-color: #49483e } diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index bd41480aefb..7d489a9666b 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -1,11 +1,10 @@ /* https://gist.github.com/qguv/7936275 */ -pre.code.highlight.solarized-dark, .code.solarized-dark { - background-color: #002b36; - color: #93a1a1; + background-color: #002b36 !important; + color: #93a1a1 !important; - pre.code, + pre.highlight, .line-numbers, .line-numbers a { background-color: #002b36 !important; @@ -23,7 +22,7 @@ pre.code.highlight.solarized-dark, // Search result highlight span.highlight_word { - background: #094554; + background-color: #094554 !important; } /* Solarized Dark diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 4cc62863870..200ed346446 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -1,11 +1,10 @@ /* https://gist.github.com/qguv/7936275 */ -pre.code.highlight.solarized-light, .code.solarized-light { - background-color: #fdf6e3; - color: #586e75; + background-color: #fdf6e3 !important; + color: #586e75 !important; - pre.code, + pre.highlight, .line-numbers, .line-numbers a { background-color: #fdf6e3 !important; @@ -23,7 +22,7 @@ pre.code.highlight.solarized-light, // Search result highlight span.highlight_word { - background: #eee8d5; + background-color: #eee8d5 !important; } /* Solarized Light diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 20a144ef952..e2626da7871 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -1,24 +1,20 @@ /* https://github.com/aahan/pygments-github-style */ -pre.code.highlight.white, .code.white { - background-color: #f8fafc; - font-size: 13px; - color: #5b6169; - line-height: 1.6em; + background-color: #f8fafc !important; + color: #5b6169 !important; + + pre.highlight, .line-numbers, .line-numbers a { background-color: $background-color !important; color: $gl-gray !important; } - pre.highlight { - background-color: #fff !important; - color: #333 !important; - } - pre.code { border-left: 1px solid $border-color; + background-color: #fff !important; + color: #333 !important; } // highlight line via anchor @@ -28,7 +24,7 @@ pre.code.highlight.white, // Search result highlight span.highlight_word { - background: #fafe3d; + background-color: #fafe3d !important; } .hll { background-color: #f8f8f8 } diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss index 8c5273abcda..2a7b5cfc7fd 100644 --- a/app/assets/stylesheets/pages/ci_projects.scss +++ b/app/assets/stylesheets/pages/ci_projects.scss @@ -6,11 +6,6 @@ line-height: 1.5; } - .wide-table-holder { - margin-left: -$gl-padding; - margin-right: -$gl-padding; - } - .builds, .projects-table { .light { diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..e485487bcfd 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,6 +33,8 @@ } li.commit { + list-style: none; + .commit-row-title { font-size: $list-font-size; line-height: 20px; diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 1d565477dd4..e2c521af91e 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -50,7 +50,7 @@ .editor-file-name { .new-file-name { display: inline-block; - width: 200px; + width: 450px; } .form-control { diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index ca2ee455423..dfb901652bf 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -7,7 +7,7 @@ padding: $gl-padding; margin-left: -$gl-padding; margin-right: -$gl-padding; - border-bottom: 1px solid #f1f2f4; + border-bottom: 1px solid $table-border-color; color: #7f8fa4; &.event-inline { diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 6da7a2511a2..bd224705f04 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -68,3 +68,7 @@ body.modal-open { .modal .modal-dialog { width: 860px; } + +.documentation { + padding: 7px; +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9da085a3473..abc27a19e32 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -80,3 +80,24 @@ } } } + +.issuable-filter-count { + span { + display: block; + margin-bottom: -16px; + padding: 13px 0; + } +} + +.cross-project-reference { + text-align: center; + width: 100%; + + .slead { + padding: 5px; + } + + span, button { + background-color: $background-color; + } +} diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 4bf58cb4a59..41c069f0ad3 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -132,6 +132,11 @@ form.edit-issue { } } +.issue-closed-by-widget { + padding: 16px 0; + margin: 0px; +} + .issue-form .select2-container { width: 250px !important; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index a1a5208c59c..f0b3667acca 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -205,6 +205,15 @@ #modal_merge_info .modal-dialog { width: 600px; + + .btn-clipboard { + @extend .pull-right; + + margin-right: 18px; + margin-top: 5px; + position: absolute; + right: 0; + } } .mr-source-target { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index abb03b07f51..1980fe0d458 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -30,7 +30,6 @@ ul.notes { .discussion-header, .note-header { @extend .cgray; - padding-bottom: 15px; a:hover { text-decoration: none; @@ -75,6 +74,10 @@ ul.notes { } } + .discussion-body { + padding-top: 15px; + } + .discussion { overflow: hidden; display: block; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8e4f0eb2b25..bc1ad21305a 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -47,3 +47,35 @@ } } } + +.calendar-hint { + margin-top: -12px; + float: right; + font-size: 12px; +} + +.profile-link-holder { + display: inline; + + &:after { + content: "\00B7"; + padding: 0px 6px; + font-weight: bold; + } + + &:last-child { + &:after { + content: ""; + padding: 0; + } + } + + a { + color: $blue-dark; + text-decoration: none; + } +} + +.cal-heatmap-container { + margin: 0 auto; +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index b384e3fae6c..2e7ad1173a5 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -50,7 +50,17 @@ } .project-home-dropdown { - margin: 11px 3px 0; + margin: 13px 0px 0; + } + + .notifications-btn { + .fa-bell { + margin-right: 6px; + } + + .fa-angle-down { + margin-left: 6px; + } } .project-home-desc { @@ -85,6 +95,7 @@ color: inherit; } } + .input-group { display: inline-table; position: relative; @@ -233,23 +244,11 @@ } } - .fa-fw { + i { margin-right: 8px; } } -.fa-bell { - margin-right: 6px; -} - -.fa-angle-down { - margin-left: 6px; -} - -.project-home-panel .project-home-dropdown { - margin: 13px 0px 0; -} - .project-visibility-level-holder { .radio { margin-bottom: 10px; @@ -457,7 +456,7 @@ pre.light-well { .project-row { padding: $gl-padding; - border-color: #f1f2f4; + border-color: $table-border-color; margin-left: -$gl-padding; margin-right: -$gl-padding; @@ -512,6 +511,49 @@ pre.light-well { } } +.project-last-commit { + margin: 0 7px; + + .ci-status { + margin-right: 16px; + } + + .commit-row-message { + color: $gl-gray; + } + + .commit_short_id { + margin-right: 5px; + color: $gl-link-color; + font-weight: 600; + } + + .commit-author-link { + margin-left: 7px; + text-decoration: none; + .avatar { + float: none; + margin-right: 4px; + } + + .commit-author-name { + font-weight: 600; + } + } +} + +.project-show-readme .readme-holder { + margin-left: -$gl-padding; + margin-right: -$gl-padding; + padding: ($gl-padding + 7px); + border-top: 0; + + .edit-project-readme { + z-index: 100; + position: relative; + } +} + .form-control-padding-top { padding-top: 10px; -} +}
\ No newline at end of file diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss index 2b15ab83129..a9111a7388f 100644 --- a/app/assets/stylesheets/pages/runners.scss +++ b/app/assets/stylesheets/pages/runners.scss @@ -1,36 +1,34 @@ -.ci-body { - .runner-state { - padding: 6px 12px; - margin-right: 10px; - color: #FFF; +.runner-state { + padding: 6px 12px; + margin-right: 10px; + color: #FFF; - &.runner-state-shared { - background: #32b186; - } - &.runner-state-specific { - background: #3498db; - } + &.runner-state-shared { + background: #32b186; } - - .runner-status-online { - color: green; + &.runner-state-specific { + background: #3498db; } +} - .runner-status-offline { - color: gray; - } +.runner-status-online { + color: green; +} - .runner-status-paused { - color: red; - } +.runner-status-offline { + color: gray; +} + +.runner-status-paused { + color: red; +} - .runner { - .btn { - padding: 1px 6px; - } +.runner { + .btn { + padding: 1px 6px; + } - h4 { - font-weight: normal; - } + h4 { + font-weight: normal; } } diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index a3d7aba054d..242783a7b7e 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -1,8 +1,3 @@ -.my-snippets li:first-child { - h4 { margin-top: 0; } - padding-top: 0; -} - .snippet-form-holder .file-holder .file-title { padding: 2px; } @@ -30,3 +25,58 @@ } } } + +.snippet-holder { + .snippet-details { + .page-title { + margin-top: -15px; + padding: 10px 0; + margin-bottom: 0; + color: #5c5d5e; + font-size: 16px; + + .author { + color: #5c5d5e; + } + + .snippet-id { + color: #5c5d5e; + } + } + + .snippet-title { + margin: 0; + font-size: 23px; + color: #313236; + } + + @media (max-width: $screen-md-max) { + .new-snippet-link { + display: none; + } + } + + @media (max-width: $screen-sm-max) { + .creator, + .page-title .btn-close { + display: none; + } + } + } + + .file-holder { + border-top: 0; + } +} + + +.snippet-box { + @include border-radius(2px); + + display: inline-block; + padding: 10px $gl-padding; + font-weight: normal; + margin-right: 10px; + font-size: $gl-font-size; + border: 1px solid; +} diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index dadd86e88cc..d4ab6967ccd 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,25 +1,11 @@ .tree-holder { - .tree-table-holder { - margin-left: -$gl-padding; - margin-right: -$gl-padding; - } - - .tree_progress { - display: none; - margin: 20px; - &.loading { - display: block; - } - } .tree-table { margin-bottom: 0; tr { > td, > th { - padding: 10px $gl-padding; - line-height: 32px; - border-color: $table-border-color !important; + line-height: 28px; } &:hover { diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 65dbd5ef551..2f4054eaa11 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -9,6 +9,10 @@ class AbuseReportsController < ApplicationController @abuse_report.reporter = current_user if @abuse_report.save + if current_application_settings.admin_notification_email.present? + AbuseReportMailer.delay.notify(@abuse_report.id) + end + message = "Thank you for your report. A GitLab administrator will look into it shortly." redirect_to root_path, notice: message else diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 7c134d2ec9b..039f18f23e0 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -55,6 +55,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_snippet_visibility, :restricted_signup_domains_raw, :version_check_enabled, + :admin_notification_email, :user_oauth_applications, restricted_visibility_levels: [], import_sources: [] diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 0808024fc39..497c34f8f49 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -19,7 +19,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController BroadcastMessage.find(params[:id]).destroy respond_to do |format| - format.html { redirect_to :back } + format.html { redirect_back_or_default(default: { action: 'index' }) } format.js { render nothing: true } end end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index d670386f8c6..0bd19c49d8f 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -35,7 +35,7 @@ class Admin::HooksController < Admin::ApplicationController } @hook.execute(data, 'system_hooks') - redirect_to :back + redirect_back_or_default end def hook_params diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index a62170662e1..46133588332 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -39,7 +39,13 @@ class Admin::ServicesController < Admin::ApplicationController end def application_services_params - params.permit(:id, + application_services_params = params.permit(:id, service: Projects::ServicesController::ALLOWED_PARAMS) + if application_services_params[:service].is_a?(Hash) + Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param| + application_services_params[:service].delete(param) if application_services_params[:service][param].blank? + end + end + application_services_params end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 00f41a10dd1..c63d0793e31 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -33,33 +33,33 @@ class Admin::UsersController < Admin::ApplicationController def block if user.block - redirect_to :back, notice: "Successfully blocked" + redirect_back_or_admin_user(notice: "Successfully blocked") else - redirect_to :back, alert: "Error occurred. User was not blocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not blocked") end end def unblock if user.activate - redirect_to :back, notice: "Successfully unblocked" + redirect_back_or_admin_user(notice: "Successfully unblocked") else - redirect_to :back, alert: "Error occurred. User was not unblocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked") end end def unlock if user.unlock_access! - redirect_to :back, alert: "Successfully unlocked" + redirect_back_or_admin_user(alert: "Successfully unlocked") else - redirect_to :back, alert: "Error occurred. User was not unlocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not unlocked") end end def confirm if user.confirm - redirect_to :back, notice: "Successfully confirmed" + redirect_back_or_admin_user(notice: "Successfully confirmed") else - redirect_to :back, alert: "Error occurred. User was not confirmed" + redirect_back_or_admin_user(alert: "Error occurred. User was not confirmed") end end @@ -138,7 +138,7 @@ class Admin::UsersController < Admin::ApplicationController user.update_secondary_emails! respond_to do |format| - format.html { redirect_to :back, notice: "Successfully removed email." } + format.html { redirect_back_or_admin_user(notice: "Successfully removed email.") } format.js { render nothing: true } end end @@ -157,4 +157,12 @@ class Admin::UsersController < Admin::ApplicationController :projects_limit, :can_create_group, :admin, :key_id ) end + + def redirect_back_or_admin_user(options = {}) + redirect_back_or_default(default: default_route, options: options) + end + + def default_route + [:admin, @user] + end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 527c9da0faa..1b0609e279e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -30,7 +30,11 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound do |exception| log_exception(exception) - render "errors/not_found", layout: "errors", status: 404 + render_404 + end + + def redirect_back_or_default(default: root_path, options: {}) + redirect_to request.referer.present? ? :back : default, options end protected @@ -120,7 +124,6 @@ class ApplicationController < ActionController::Base project_path = "#{namespace}/#{id}" @project = Project.find_with_namespace(project_path) - if @project and can?(current_user, :read_project, @project) if @project.path_with_namespace != project_path redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return @@ -149,12 +152,8 @@ class ApplicationController < ActionController::Base render "errors/access_denied", layout: "errors", status: 404 end - def not_found! - render "errors/not_found", layout: "errors", status: 404 - end - def git_not_found! - render "errors/git_not_found", layout: "errors", status: 404 + render html: "errors/git_not_found", layout: "errors", status: 404 end def method_missing(method_sym, *arguments, &block) diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb index 9a68add9083..0cafad27418 100644 --- a/app/controllers/ci/admin/runners_controller.rb +++ b/app/controllers/ci/admin/runners_controller.rb @@ -6,7 +6,7 @@ module Ci @runners = Ci::Runner.order('id DESC') @runners = @runners.search(params[:search]) if params[:search].present? @runners = @runners.page(params[:page]).per(30) - @active_runners_cnt = Ci::Runner.where("contacted_at > ?", 1.minutes.ago).count + @active_runners_cnt = Ci::Runner.online.count end def show @@ -17,6 +17,7 @@ module Ci @projects = @projects.where(gitlab_id: @gl_projects.select(:id)) end @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? + @projects = @projects.joins(:gl_project) @projects = @projects.page(params[:page]).per(30) end @@ -66,7 +67,7 @@ module Ci end def runner_params - params.require(:runner).permit(:token, :description, :tag_list, :contacted_at, :active) + params.require(:runner).permit(:token, :description, :tag_list, :active) end end end diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 9be470660e6..848f2b4e314 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -8,14 +8,6 @@ module Ci private - def authenticate_public_page! - unless project.public - authenticate_user! - - return access_denied! unless can?(current_user, :read_project, gl_project) - end - end - def authenticate_token! unless project.valid_token?(params[:token]) return head(403) diff --git a/app/controllers/ci/events_controller.rb b/app/controllers/ci/events_controller.rb deleted file mode 100644 index 89b784a1e89..00000000000 --- a/app/controllers/ci/events_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Ci - class EventsController < Ci::ApplicationController - EVENTS_PER_PAGE = 50 - - before_action :authenticate_user! - before_action :project - before_action :authorize_manage_project! - - layout 'ci/project' - - def index - @events = project.events.order("created_at DESC").page(params[:page]).per(EVENTS_PER_PAGE) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 7777aa18031..809b44387ba 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,12 +1,17 @@ module Ci class ProjectsController < Ci::ApplicationController - before_action :project - before_action :authenticate_user!, except: [:build, :badge] - before_action :authorize_access_project!, except: [:badge] + before_action :project, except: [:index] + before_action :authenticate_user!, except: [:index, :build, :badge] + before_action :authorize_access_project!, except: [:index, :badge] before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] before_action :no_cache, only: [:badge] protect_from_forgery + def show + # Temporary compatibility with CI badges pointing to CI project page + redirect_to namespace_project_path(project.gl_project.namespace, project.gl_project) + end + # Project status badge # Image with build status for sha or ref def badge diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb index 97f01d40af5..9d555313369 100644 --- a/app/controllers/ci/runner_projects_controller.rb +++ b/app/controllers/ci/runner_projects_controller.rb @@ -4,8 +4,6 @@ module Ci before_action :project before_action :authorize_manage_project! - layout 'ci/project' - def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index f84f85a7df8..25e58724860 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController end def verify_bitbucket_import_enabled - not_found! unless bitbucket_import_enabled? + render_404 unless bitbucket_import_enabled? end def bitbucket_auth diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index 849646cd665..18300390851 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -99,6 +99,6 @@ class Import::FogbugzController < Import::BaseController end def verify_fogbugz_import_enabled - not_found! unless fogbugz_import_enabled? + render_404 unless fogbugz_import_enabled? end end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index f21fbd9ecca..67bf4190e7e 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -11,10 +11,6 @@ class Import::GithubController < Import::BaseController def status @repos = client.repos - client.orgs.each do |org| - @repos += client.org_repos(org.login) - end - @already_added_projects = current_user.created_projects.where(import_type: "github") already_added_projects_names = @already_added_projects.pluck(:import_source) @@ -47,7 +43,7 @@ class Import::GithubController < Import::BaseController end def verify_github_import_enabled - not_found! unless github_import_enabled? + render_404 unless github_import_enabled? end def github_auth diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb index 27af19f5f61..23a396e8084 100644 --- a/app/controllers/import/gitlab_controller.rb +++ b/app/controllers/import/gitlab_controller.rb @@ -44,7 +44,7 @@ class Import::GitlabController < Import::BaseController end def verify_gitlab_import_enabled - not_found! unless gitlab_import_enabled? + render_404 unless gitlab_import_enabled? end def gitlab_auth diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb index f24cdb3709a..eecbe380c9e 100644 --- a/app/controllers/import/gitorious_controller.rb +++ b/app/controllers/import/gitorious_controller.rb @@ -42,7 +42,7 @@ class Import::GitoriousController < Import::BaseController end def verify_gitorious_import_enabled - not_found! unless gitorious_import_enabled? + render_404 unless gitorious_import_enabled? end end diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb index 82fadeb7e83..e0de31f2251 100644 --- a/app/controllers/import/google_code_controller.rb +++ b/app/controllers/import/google_code_controller.rb @@ -10,18 +10,18 @@ class Import::GoogleCodeController < Import::BaseController dump_file = params[:dump_file] unless dump_file.respond_to?(:read) - return redirect_to :back, alert: "You need to upload a Google Takeout archive." + return redirect_back_or_default(options: { alert: "You need to upload a Google Takeout archive." }) end begin dump = JSON.parse(dump_file.read) rescue - return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive." + return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." }) end client = Gitlab::GoogleCodeImport::Client.new(dump) unless client.valid? - return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive." + return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." }) end session[:google_code_dump] = dump @@ -106,7 +106,7 @@ class Import::GoogleCodeController < Import::BaseController end def verify_google_code_import_enabled - not_found! unless google_code_import_enabled? + render_404 unless google_code_import_enabled? end def user_map diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 8ef10a17f55..94bb108c5f5 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -14,7 +14,7 @@ class InvitesController < ApplicationController redirect_to path, notice: "You have been granted #{member.human_access} access to #{label}." else - redirect_to :back, alert: "The invitation could not be accepted." + redirect_back_or_default(options: { alert: "The invitation could not be accepted." }) end end @@ -31,7 +31,7 @@ class InvitesController < ApplicationController redirect_to path, notice: "You have declined the invitation to join #{label}." else - redirect_to :back, alert: "The invitation could not be declined." + redirect_back_or_default(options: { alert: "The invitation could not be declined." }) end end diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 22423651c17..1fd1d6882df 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -29,7 +29,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController flash[:alert] = "Failed to save new settings" end - redirect_to :back + redirect_back_or_default(default: profile_notifications_path) end format.js diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 26a4de15462..8da7b4d50ea 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -26,7 +26,7 @@ class ProfilesController < Profiles::ApplicationController end respond_to do |format| - format.html { redirect_to :back } + format.html { redirect_back_or_default(default: { action: 'show' }) } end end diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 9c3763d5934..548f1b9ebfe 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -12,7 +12,7 @@ class Projects::AvatarsController < Projects::ApplicationController filename: @blob.name ) else - not_found! + render_404 end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index ae9b1384463..93738aa1ee5 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -113,14 +113,14 @@ class Projects::BlobController < Projects::ApplicationController end end - return not_found! + return render_404 end end def commit @commit = @repository.commit(@ref) - return not_found! unless @commit + return render_404 unless @commit end def assign_blob_vars @@ -128,7 +128,7 @@ class Projects::BlobController < Projects::ApplicationController @ref, @path = extract_ref(@id) rescue InvalidPathError - not_found! + render_404 end def after_edit_path @@ -161,7 +161,7 @@ class Projects::BlobController < Projects::ApplicationController if params[:file].present? params[:file_name] = params[:file].original_filename end - File.join(@path, File.basename(params[:file_name])) + File.join(@path, params[:file_name]) else @path end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 4e4ac6689d3..7d72e0b951b 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -1,11 +1,33 @@ class Projects::BuildsController < Projects::ApplicationController before_action :ci_project - before_action :build + before_action :build, except: [:index, :cancel_all] - before_action :authorize_admin_project!, except: [:show, :status] + before_action :authorize_manage_builds!, except: [:index, :show, :status] layout "project" + def index + @scope = params[:scope] + @all_builds = project.ci_builds + @builds = @all_builds.order('created_at DESC') + @builds = + case @scope + when 'all' + @builds + when 'finished' + @builds.finished + else + @builds.running_or_pending.reverse_order + end + @builds = @builds.page(params[:page]).per(30) + end + + def cancel_all + @project.ci_builds.running_or_pending.each(&:cancel) + + redirect_to namespace_project_builds_path(project.namespace, project) + end + def show @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC') @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20) @@ -52,4 +74,10 @@ class Projects::BuildsController < Projects::ApplicationController def build_path(build) namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) end + + def authorize_manage_builds! + unless can?(current_user, :manage_builds, project) + return page_404 + end + end end diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb index 6d2756eba3d..550a019e8e2 100644 --- a/app/controllers/projects/ci_services_controller.rb +++ b/app/controllers/projects/ci_services_controller.rb @@ -14,23 +14,23 @@ class Projects::CiServicesController < Projects::ApplicationController end def update - if @service.update_attributes(service_params) - redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param) + if service.update_attributes(service_params) + redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) else render 'edit' end end def test - last_build = @project.builds.last + last_build = @project.ci_builds.last - if @service.execute(last_build) + if service.execute(last_build) message = { notice: 'We successfully tested the service' } else message = { alert: 'We tried to test the service but error occurred' } end - redirect_to :back, message + redirect_back_or_default(options: message) end private diff --git a/app/controllers/projects/ci_web_hooks_controller.rb b/app/controllers/projects/ci_web_hooks_controller.rb index 7f40ddcb3f3..a2d470d4a69 100644 --- a/app/controllers/projects/ci_web_hooks_controller.rb +++ b/app/controllers/projects/ci_web_hooks_controller.rb @@ -24,7 +24,7 @@ class Projects::CiWebHooksController < Projects::ApplicationController def test Ci::TestHookService.new.execute(hook, current_user) - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end def destroy diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 7886f3c6deb..878c3a66e7d 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -4,7 +4,8 @@ class Projects::CommitController < Projects::ApplicationController # Authorize before_action :require_non_empty_project - before_action :authorize_download_code! + before_action :authorize_download_code!, except: [:cancel_builds] + before_action :authorize_manage_builds!, only: [:cancel_builds] before_action :commit def show @@ -55,4 +56,12 @@ class Projects::CommitController < Projects::ApplicationController def commit @commit ||= @project.commit(params[:id]) end + + private + + def authorize_manage_builds! + unless can?(current_user, :manage_builds, project) + return page_404 + end + end end diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index d1c15174aea..58fb946dbc2 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @commits = @repo.commits(@ref, @path, @limit, @offset) - @note_counts = Note.where(commit_id: @commits.map(&:id)). + @note_counts = project.notes.where(commit_id: @commits.map(&:id)). group(:commit_id).count respond_to do |format| diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index d15004f93a6..71aaad1fad6 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -17,9 +17,10 @@ class Projects::CompareController < Projects::ApplicationController execute(@project, head_ref, @project, base_ref) if compare_result - @commits = compare_result.commits + @commits = Commit.decorate(compare_result.commits, @project) @diffs = compare_result.diffs @commit = @commits.last + @first_commit = @commits.first @line_notes = [] end end diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 40e2b37912b..7d09288bc80 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -46,7 +46,7 @@ class Projects::DeployKeysController < Projects::ApplicationController def disable @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end protected diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 4e5b4125f5a..c7569541899 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -37,7 +37,7 @@ class Projects::HooksController < Projects::ApplicationController flash[:alert] = 'Hook execution failed. Ensure the project has commits.' end - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end def destroy diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 0f89f2e88cc..e767efbdc0c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -14,6 +14,9 @@ class Projects::IssuesController < Projects::ApplicationController # Allow issues bulk update before_action :authorize_admin_issues!, only: [:bulk_update] + # Cross-reference merge requests + before_action :closed_by_merge_requests, only: [:show] + respond_to :html def index @@ -55,9 +58,9 @@ class Projects::IssuesController < Projects::ApplicationController end def show - @participants = @issue.participants(current_user, @project) + @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.inc_author.fresh + @notes = @issue.notes.with_associations.fresh @noteable = @issue respond_with(@issue) @@ -103,7 +106,7 @@ class Projects::IssuesController < Projects::ApplicationController def bulk_update result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute - redirect_to :back, notice: "#{result[:count]} issues updated" + redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" }) end def toggle_subscription @@ -112,6 +115,10 @@ class Projects::IssuesController < Projects::ApplicationController render nothing: true end + def closed_by_merge_requests + @closed_by_merge_requests ||= @issue.closed_by_merge_requests(current_user) + end + protected def issue diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 7570934e727..16c42386623 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -56,6 +56,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController def diffs @commit = @merge_request.last_commit + @first_commit = @merge_request.first_commit + @comments_allowed = @reply_allowed = true @comments_target = { noteable_type: 'MergeRequest', @@ -89,7 +91,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController @target_project = merge_request.target_project @source_project = merge_request.source_project @commits = @merge_request.compare_commits - @commit = @merge_request.compare_commits.last + @commit = @merge_request.last_commit + @first_commit = @merge_request.first_commit @diffs = @merge_request.compare_diffs @note_counts = Note.where(commit_id: @commits.map(&:id)). group(:commit_id).count @@ -246,7 +249,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def define_show_vars - @participants = @merge_request.participants(current_user, @project) + @participants = @merge_request.participants(current_user) # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) @@ -259,7 +262,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @commits = @merge_request.commits @merge_request_diff = @merge_request.merge_request_diff - + if @merge_request.locked_long_ago? @merge_request.unlock_mr @merge_request.close diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 86f4a02a6e9..15506bd677a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -75,11 +75,7 @@ class Projects::MilestonesController < Projects::ApplicationController end def sort_issues - @issues = @milestone.issues.where(id: params['sortable_issue']) - @issues.each do |issue| - issue.position = params['sortable_issue'].index(issue.id.to_s) + 1 - issue.save - end + @milestone.sort_issues(params['sortable_issue'].map(&:to_i)) render json: { saved: true } end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 0f5d82ce133..41cd08c93c6 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -25,7 +25,7 @@ class Projects::NotesController < Projects::ApplicationController respond_to do |format| format.json { render_note_json(@note) } - format.html { redirect_to :back } + format.html { redirect_back_or_default } end end @@ -34,7 +34,7 @@ class Projects::NotesController < Projects::ApplicationController respond_to do |format| format.json { render_note_json(@note) } - format.html { redirect_to :back } + format.html { redirect_back_or_default } end end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index cf73bc01c8f..9de5269cd25 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -72,7 +72,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController def leave if @project.namespace == current_user.namespace - return redirect_to(:back, alert: 'You can not leave your own project. Transfer or delete the project.') + message = 'You can not leave your own project. Transfer or delete the project.' + return redirect_back_or_default(default: { action: 'index' }, options: { alert: message }) end @project.project_members.find_by(user_id: current_user).destroy diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 5f6fbce795e..d5ee6ac8663 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -20,7 +20,7 @@ class Projects::RawController < Projects::ApplicationController disposition: 'inline' ) else - not_found! + render_404 end end diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 6080c849c8d..c4e18c17077 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -3,6 +3,7 @@ class Projects::RefsController < Projects::ApplicationController include TreeHelper before_action :require_non_empty_project + before_action :validate_ref_id before_action :assign_ref_vars before_action :authorize_download_code! @@ -71,4 +72,10 @@ class Projects::RefsController < Projects::ApplicationController format.js end end + + private + + def validate_ref_id + return not_found! if params[:id].present? && params[:id] !~ Gitlab::Regex.git_reference_regex + end end diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index c4a5e2d6359..ba9aea1c165 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -11,18 +11,9 @@ class Projects::RepositoriesController < Projects::ApplicationController end def archive - begin - file_path = ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute - rescue - return head :not_found - end - - if file_path - # Send file to user - response.headers["Content-Length"] = File.open(file_path).size.to_s - send_file file_path - else - redirect_to request.fullpath - end + render json: ArchiveRepositoryService.new(@project, params[:ref], params[:format]).execute + rescue => ex + logger.error("#{self.class.name}: #{ex}") + return git_not_found! end end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 6cb6e3ef6d4..bfbcf2567f3 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -6,11 +6,10 @@ class Projects::RunnersController < Projects::ApplicationController layout 'project_settings' def index - @runners = @ci_project.runners.order('id DESC') - @specific_runners = - Ci::Runner.specific.includes(:runner_projects). - where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ). - where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20) + @runners = @ci_project.runners.ordered + @specific_runners = current_user.ci_authorized_runners. + where.not(id: @ci_project.runners). + ordered.page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) end @@ -60,6 +59,6 @@ class Projects::RunnersController < Projects::ApplicationController end def runner_params - params.require(:runner).permit(:description, :tag_list, :contacted_at, :active) + params.require(:runner).permit(:description, :tag_list, :active) end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 3047ee8a1ff..42dbb497e01 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -9,6 +9,10 @@ class Projects::ServicesController < Projects::ApplicationController :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification] + + # Parameters to ignore if no value is specified + FILTER_BLANK_PARAMS = [:password] + # Authorize before_action :authorize_admin_project! before_action :service, only: [:edit, :update, :test] @@ -48,7 +52,7 @@ class Projects::ServicesController < Projects::ApplicationController message = { alert: error_message } end - redirect_to :back, message + redirect_back_or_default(options: message) end private @@ -59,7 +63,9 @@ class Projects::ServicesController < Projects::ApplicationController def service_params service_params = params.require(:service).permit(ALLOWED_PARAMS) - service_params.delete("password") if service_params["password"].blank? + FILTER_BLANK_PARAMS.each do |param| + service_params.delete(param) if service_params[param].blank? + end service_params end end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index b07a2a8db2f..2104c7a7a71 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -21,6 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController filter: :by_project, project: @project }) + @snippets = @snippets.page(params[:page]).per(PER_PAGE) end def new diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 7eaff1d61ee..bdcb1a3e297 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -10,7 +10,7 @@ class Projects::TreeController < Projects::ApplicationController before_action :authorize_push_code!, only: [:create_dir] def show - return not_found! unless @repository.commit(@ref) + return render_404 unless @repository.commit(@ref) if tree.entries.empty? if @repository.blob_at(@commit.id, @path) @@ -19,7 +19,7 @@ class Projects::TreeController < Projects::ApplicationController File.join(@ref, @path)) ) and return elsif @path.present? - return not_found! + return render_404 end end @@ -31,7 +31,7 @@ class Projects::TreeController < Projects::ApplicationController end def create_dir - return not_found! unless @commit_params.values.all? + return render_404 unless @commit_params.values.all? begin result = Files::CreateDirService.new(@project, current_user, @commit_params).execute diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 71ecc20dd95..e1fe7ea2114 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -20,7 +20,7 @@ class Projects::UploadsController < Projects::ApplicationController end def show - return not_found! if uploader.nil? || !uploader.file.exists? + return render_404 if uploader.nil? || !uploader.file.exists? disposition = uploader.image? ? 'inline' : 'attachment' send_file uploader.file.path, disposition: disposition diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 213c2a7173b..00d13a83ce8 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,11 +1,14 @@ class ProjectsController < ApplicationController - prepend_before_filter :render_go_import, only: [:show] + include ExtractsPath + + prepend_before_action :render_go_import, only: [:show] skip_before_action :authenticate_user!, only: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] + before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists? # Authorize - before_action :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive] + before_action :authorize_admin_project!, only: [:edit, :update] before_action :event_filter, only: [:show, :activity] layout :determine_layout @@ -56,6 +59,8 @@ class ProjectsController < ApplicationController end def transfer + return access_denied! unless can?(current_user, :change_namespace, @project) + namespace = Namespace.find_by(id: params[:new_namespace_id]) ::Projects::TransferService.new(project, current_user).execute(namespace) @@ -64,6 +69,15 @@ class ProjectsController < ApplicationController end end + def remove_fork + return access_denied! unless can?(current_user, :remove_fork_project, @project) + + if @project.forked? + @project.forked_project_link.destroy + flash[:notice] = 'The fork relationship has been removed.' + end + end + def activity respond_to do |format| format.html @@ -87,7 +101,7 @@ class ProjectsController < ApplicationController render 'projects/empty' else if current_user - @membership = @project.project_member_by_id(current_user.id) + @membership = @project.team.find_member(current_user.id) end render :show @@ -110,11 +124,7 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute flash[:alert] = "Project '#{@project.name}' was deleted." - if request.referer.include?('/admin') - redirect_to admin_namespaces_projects_path - else - redirect_to dashboard_projects_path - end + redirect_back_or_default(default: dashboard_projects_path, options: {}) rescue Projects::DestroyService::DestroyError => ex redirect_to edit_project_path(@project), alert: ex.message end @@ -139,6 +149,7 @@ class ProjectsController < ApplicationController def archive return access_denied! unless can?(current_user, :archive_project, @project) + @project.archive! respond_to do |format| @@ -148,6 +159,7 @@ class ProjectsController < ApplicationController def unarchive return access_denied! unless can?(current_user, :archive_project, @project) + @project.unarchive! respond_to do |format| @@ -225,4 +237,14 @@ class ProjectsController < ApplicationController render "go_import", layout: false end + + def repo_exists? + project.repository_exists? && !project.empty_repo? + end + + # Override get_id from ExtractsPath, which returns the branch and file path + # for the blob/tree, which in this case is just the root of the default branch. + def get_id + project.repository.root_ref + end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index eb0408a95e5..9bb42ec86b3 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -23,8 +23,8 @@ class SearchController < ApplicationController @search_results = if @project - unless %w(blobs notes issues merge_requests milestones wiki_blobs). - include?(@scope) + unless %w(blobs notes issues merge_requests milestones wiki_blobs + commits).include?(@scope) @scope = 'blobs' end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 28536e359e5..868b05929d7 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -10,7 +10,7 @@ class UploadsController < ApplicationController end unless uploader.file && uploader.file.exists? - return not_found! + return render_404 end disposition = uploader.image? ? 'inline' : 'attachment' @@ -21,7 +21,7 @@ class UploadsController < ApplicationController def find_model unless upload_model && upload_mount - return not_found! + return render_404 end @model = upload_model.find(params[:id]) @@ -44,7 +44,7 @@ class UploadsController < ApplicationController return if authorized if current_user - not_found! + render_404 else authenticate_user! end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 97c7e74c294..c407dfc163a 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -53,15 +53,36 @@ class IssuableFinder end end + def project? + params[:project_id].present? + end + def project return @project if defined?(@project) - @project = - if params[:project_id].present? - Project.find(params[:project_id]) - else - nil - end + if project? + @project = Project.find(params[:project_id]) + + unless Ability.abilities.allowed?(current_user, :read_project, @project) + @project = nil + end + else + @project = nil + end + + @project + end + + def projects + return @projects if defined?(@projects) + + if project? + project + elsif current_user && params[:authorized_only].presence && !current_user_related? + current_user.authorized_projects + else + ProjectsFinder.new.execute(current_user) + end end def search @@ -72,7 +93,7 @@ class IssuableFinder params[:milestone_title].present? end - def no_milestones? + def filter_by_no_milestone? milestones? && params[:milestone_title] == Milestone::None.title end @@ -81,12 +102,22 @@ class IssuableFinder @milestones = if milestones? - Milestone.where(title: params[:milestone_title]) + scope = Milestone.where(project_id: projects) + + scope.where(title: params[:milestone_title]) else nil end end + def labels? + params[:label_name].present? + end + + def filter_by_no_label? + labels? && params[:label_name] == Label::None.title + end + def assignee? params[:assignee_id].present? end @@ -120,19 +151,7 @@ class IssuableFinder private def init_collection - table_name = klass.table_name - - if project - if Ability.abilities.allowed?(current_user, :read_project, project) - project.send(table_name) - else - [] - end - elsif current_user && params[:authorized_only].presence && !current_user_related? - klass.of_projects(current_user.authorized_projects).references(:project) - else - klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project) - end + klass.all end def by_scope(items) @@ -170,7 +189,12 @@ class IssuableFinder end def by_project(items) - items = items.of_projects(project.id) if project + items = + if projects + items.of_projects(projects).references(:project) + else + items.none + end items end @@ -185,18 +209,6 @@ class IssuableFinder items.sort(params[:sort]) end - def by_milestone(items) - if milestones? - if no_milestones? - items = items.where(milestone_id: [-1, nil]) - else - items = items.where(milestone_id: milestones.try(:pluck, :id)) - end - end - - items - end - def by_assignee(items) if assignee? items = items.where(assignee_id: assignee.try(:id)) @@ -213,20 +225,36 @@ class IssuableFinder items end - def by_label(items) - if params[:label_name].present? - if params[:label_name] == Label::None.title - item_ids = LabelLink.where(target_type: klass.name).pluck(:target_id) + def by_milestone(items) + if milestones? + if filter_by_no_milestone? + items = items.where(milestone_id: [-1, nil]) + else + items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) + + if projects + items = items.where(milestones: { project_id: projects }) + end + end + end + + items + end - items = items.where('id NOT IN (?)', item_ids) + def by_label(items) + if labels? + if filter_by_no_label? + items = items. + joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id"). + where(label_links: { id: nil }) else label_names = params[:label_name].split(",") - item_ids = LabelLink.joins(:label). - where('labels.title in (?)', label_names). - where(target_type: klass.name).pluck(:target_id) + items = items.joins(:labels).where(labels: { title: label_names }) - items = items.where(id: item_ids) + if projects + items = items.where(labels: { project_id: projects }) + end end end diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb index 14df8d4cbd7..c5820bf4c50 100644 --- a/app/helpers/appearances_helper.rb +++ b/app/helpers/appearances_helper.rb @@ -16,6 +16,6 @@ module AppearancesHelper end def brand_header_logo - image_tag 'logo.svg' + render 'shared/logo.svg' end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index cab2278adb7..8ecdeaf8e76 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,13 +68,17 @@ module ApplicationHelper end end - def avatar_icon(user_email = '', size = nil) - user = User.find_by(email: user_email) + def avatar_icon(user_or_email = nil, size = nil) + if user_or_email.is_a?(User) + user = user_or_email + else + user = User.find_by(email: user_or_email) + end if user user.avatar_url(size) || default_avatar else - gravatar_icon(user_email, size) + gravatar_icon(user_or_email, size) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index dbd1e26fa79..ed88df5dd86 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -42,4 +42,13 @@ module CiStatusHelper icon(icon_name) end + + def render_ci_status(ci_commit) + link_to ci_status_path(ci_commit), + class: "c#{ci_status_color(ci_commit)}", + title: "Build status: #{ci_commit.status}", + data: { toggle: 'tooltip', placement: 'left' } do + ci_status_icon(ci_commit) + end + end end diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb new file mode 100644 index 00000000000..3c1d7569fac --- /dev/null +++ b/app/helpers/clipboard_helper.rb @@ -0,0 +1,8 @@ +module ClipboardHelper + def clipboard_button + content_tag :button, + icon('clipboard'), + class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + type: :button + end +end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b896fba3704..e65e37211c4 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -170,7 +170,8 @@ module DiffHelper def commit_for_diff(diff) if diff.deleted_file - @merge_request ? @merge_request.commits.last : @commit.parents.first + first_commit = @first_commit || @commit + first_commit.parent else @commit end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 12b87dca798..65813482120 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -19,7 +19,8 @@ module GitlabMarkdownHelper escape_once(body) end - gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: current_user) + user = current_user if defined?(current_user) + gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: user) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) if fragment.children.size == 1 && fragment.children[0].name == 'a' @@ -45,29 +46,39 @@ module GitlabMarkdownHelper end def markdown(text, context = {}) + return "" unless text.present? + context.reverse_merge!( - current_user: current_user, path: @path, + pipeline: :default, project: @project, project_wiki: @project_wiki, ref: @ref ) - Gitlab::Markdown.render(text, context) + user = current_user if defined?(current_user) + + html = Gitlab::Markdown.render(text, context) + Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user) end # TODO (rspeicher): Remove all usages of this helper and just call `markdown` # with a custom pipeline depending on the content being rendered def gfm(text, options = {}) + return "" unless text.present? + options.reverse_merge!( - current_user: current_user, path: @path, + pipeline: :default, project: @project, project_wiki: @project_wiki, ref: @ref ) - Gitlab::Markdown.gfm(text, options) + user = current_user if defined?(current_user) + + html = Gitlab::Markdown.gfm(text, options) + Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user) end def asciidoc(text) diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 4d9da6ff837..b0b536d4649 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -25,6 +25,10 @@ module GitlabRoutingHelper namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref) end + def project_builds_path(project, *args) + namespace_project_builds_path(project.namespace, project, *args) + end + def activity_project_path(project, *args) activity_namespace_project_path(project.namespace, project, *args) end diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 6ddb37cd0dc..fda18e7b316 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -83,6 +83,10 @@ module IssuesHelper end end + def merge_requests_sentence(merge_requests) + merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 66b18eea699..ee04ace35d0 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -92,11 +92,19 @@ module LabelsHelper end end - def project_labels_options(project) - labels = project.labels.to_a - labels.unshift(Label::None) - labels.unshift(Label::Any) - options_from_collection_for_select(labels, 'name', 'title', params[:label_name]) + def projects_labels_options + labels = + if @project + @project.labels + else + Label.where(project_id: @projects) + end + + grouped_labels = Labels::GroupService.new(labels).execute + grouped_labels.unshift(Label::None) + grouped_labels.unshift(Label::Any) + + options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name]) end # Required for Gitlab::Markdown::LabelReferenceFilter diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 81773e7afcf..728d877ace2 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -47,7 +47,7 @@ module MergeRequestsHelper end def issues_sentence(issues) - issues.map { |i| "##{i.iid}" }.to_sentence + issues.map(&:to_reference).to_sentence end def mr_change_branches_path(merge_request) diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 4710171ebaa..c73cb3028ee 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -34,7 +34,8 @@ module PreferencesHelper def project_view_choices [ ['Readme (default)', :readme], - ['Activity view', :activity] + ['Activity view', :activity], + ['Files view', :files] ] end @@ -46,8 +47,7 @@ module PreferencesHelper Gitlab::ColorSchemes.for_user(current_user).css_class end - def prefer_readme? - !current_user || - current_user.project_view == 'readme' + def default_project_view + current_user ? current_user.project_view : 'readme' end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index a0220af4c30..5301c2ccf76 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -29,7 +29,7 @@ module ProjectsHelper author_html = "" # Build avatar image tag - author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] # Build name span tag author_html << content_tag(:span, sanitize(author.name), class: opts[:author_class]) if opts[:name] @@ -70,6 +70,10 @@ module ProjectsHelper "You are going to transfer #{project.name_with_namespace} to another owner. Are you ABSOLUTELY sure?" end + def remove_fork_project_message(project) + "You are going to remove the fork relationship to source project #{@project.forked_from_project.name_with_namespace}. Are you ABSOLUTELY sure?" + end + def project_nav_tabs @nav_tabs ||= get_project_nav_tabs(@project, current_user) end @@ -113,6 +117,10 @@ module ProjectsHelper nav_tabs << :merge_requests end + if project.gitlab_ci? && can?(current_user, :read_build, project) + nav_tabs << :builds + end + if can?(current_user, :admin_project, project) nav_tabs << :settings end diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb index 5d7d06c8490..46eb82a354f 100644 --- a/app/helpers/runners_helper.rb +++ b/app/helpers/runners_helper.rb @@ -1,20 +1,29 @@ module RunnersHelper def runner_status_icon(runner) - unless runner.contacted_at - return content_tag :i, nil, - class: "fa fa-warning-sign", - title: "New runner. Has not connected yet" + status = runner.status + case status + when :not_connected + content_tag :i, nil, + class: "fa fa-warning", + title: "New runner. Has not connected yet" + + when :online, :offline, :paused + content_tag :i, nil, + class: "fa fa-circle runner-status-#{status}", + title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" end + end - status = - if runner.active? - runner.contacted_at > 3.hour.ago ? :online : :offline - else - :paused - end + def runner_link(runner) + display_name = truncate(runner.display_name, length: 15) + id = "\##{runner.id}" - content_tag :i, nil, - class: "fa fa-circle runner-status-#{status}", - title: "Runner is #{status}, last contact was #{time_ago_in_words(runner.contacted_at)} ago" + if current_user && current_user.admin + link_to ci_admin_runner_path(runner) do + display_name + id + end + else + display_name + id + end end end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 0e7d8065ac7..04e53fe7c61 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -110,22 +110,4 @@ module TabHelper 'active' end end - - # Use nav_tab for save controller/action but different params - def nav_tab(key, value, &block) - o = {} - o[:class] = "" - - if value.nil? - o[:class] << " active" if params[key].blank? - else - o[:class] << " active" if params[key] == value - end - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end end diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb new file mode 100644 index 00000000000..f0c41f69a5c --- /dev/null +++ b/app/mailers/abuse_report_mailer.rb @@ -0,0 +1,12 @@ +class AbuseReportMailer < BaseMailer + include Gitlab::CurrentSettings + + def notify(abuse_report_id) + @abuse_report = AbuseReport.find(abuse_report_id) + + mail( + to: current_application_settings.admin_notification_email, + subject: "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse" + ) + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 77c121ca5e8..b72178fa126 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -41,6 +41,7 @@ class Ability :read_project_member, :read_merge_request, :read_note, + :read_build, :download_code ] @@ -127,6 +128,7 @@ class Ability :read_project_member, :read_merge_request, :read_note, + :read_build, :create_project, :create_issue, :create_note @@ -187,7 +189,8 @@ class Ability :change_visibility_level, :rename_project, :remove_project, - :archive_project + :archive_project, + :remove_fork_project ] end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c8841178e93..05430c2ee18 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -44,6 +44,10 @@ class ApplicationSetting < ActiveRecord::Base allow_blank: true, format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } + validates :admin_notification_email, + allow_blank: true, + email: true + validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? value.each do |level| diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index f8c731a7bf7..b19e2ac1363 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -93,10 +93,7 @@ module Ci Ci::WebHookService.new.build_end(build) end - if build.commit.should_create_next_builds?(build) - build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request) - end - + build.commit.create_next_builds(build) project.execute_services(build) if project.coverage_enabled? @@ -119,7 +116,7 @@ module Ci end def variables - yaml_variables + project_variables + trigger_variables + predefined_variables + yaml_variables + project_variables + trigger_variables end def project @@ -220,17 +217,29 @@ module Ci def cancel_url if active? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self) end end def retry_url if commands.present? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + retry_namespace_project_build_path(gl_project.namespace, gl_project, self) end end + def can_be_served?(runner) + (tag_list - runner.tag_list).empty? + end + + def any_runners_online? + project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) } + end + + def show_warning? + pending? && !any_runners_online? + end + private def yaml_variables @@ -258,5 +267,14 @@ module Ci [] end end + + def predefined_variables + variables = [] + variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag? + variables << { key: :CI_BUILD_NAME, value: name, public: true } + variables << { key: :CI_BUILD_STAGE, value: stage, public: true } + variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request + variables + end end end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 68864edfbbf..e58420d82d4 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -24,6 +24,8 @@ module Ci has_many :builds, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' + scope :ordered, -> { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) } + validates_presence_of :sha validate :valid_commit_sha @@ -89,19 +91,28 @@ module Ci def create_builds(ref, tag, user, trigger_request = nil) return unless config_processor config_processor.stages.any? do |stage| - CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present? + CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request, 'success').present? end end - def create_next_builds(ref, tag, user, trigger_request) + def create_next_builds(build) return unless config_processor - stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage) + # don't create other builds if this one is retried + latest_builds = builds.similar(build).latest + return unless latest_builds.exists?(build.id) - config_processor.stages.any? do |stage| - unless stages.include?(stage) - CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present? - end + # get list of stages after this build + next_stages = config_processor.stages.drop_while { |stage| stage != build.stage } + next_stages.delete(build.stage) + + # get status for all prior builds + prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) } + status = Ci::Status.get_status(prior_builds) + + # create builds for next stages based + next_stages.any? do |stage| + CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present? end end @@ -130,24 +141,7 @@ module Ci return 'failed' end - @status ||= begin - latest = latest_statuses - latest.reject! { |status| status.try(&:allow_failure?) } - - if latest.none? - 'skipped' - elsif latest.all?(&:success?) - 'success' - elsif latest.all?(&:pending?) - 'pending' - elsif latest.any?(&:running?) || latest.any?(&:pending?) - 'running' - elsif latest.all?(&:canceled?) - 'canceled' - else - 'failed' - end - end + @status ||= Ci::Status.get_status(latest_statuses) end def pending? @@ -193,7 +187,7 @@ module Ci end def config_processor - @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil @@ -217,16 +211,6 @@ module Ci update!(committed_at: DateTime.now) end - def should_create_next_builds?(build) - # don't create other builds if this one is retried - other_builds = builds.similar(build).latest - return false unless other_builds.include?(build) - - other_builds.all? do |build| - build.success? || build.ignored? - end - end - private def save_yaml_error(error) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 88ba933a434..4e806ca1a68 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -99,6 +99,7 @@ module Ci def ordered_by_last_commit_date last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)" joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). + joins(:gl_project). order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") end end @@ -115,12 +116,12 @@ module Ci web_url end - def any_runners? - if runners.active.any? + def any_runners?(&block) + if runners.active.any?(&block) return true end - shared_runners_enabled && Ci::Runner.shared.active.any? + shared_runners_enabled && Ci::Runner.shared.active.any?(&block) end def set_default_values @@ -205,7 +206,7 @@ module Ci end def commits - gl_project.ci_commits + gl_project.ci_commits.ordered end def builds diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb index b66f1212f23..2d35aeac225 100644 --- a/app/models/ci/project_status.rb +++ b/app/models/ci/project_status.rb @@ -27,9 +27,5 @@ module Ci def human_status status end - - def last_commit_for_ref(ref) - commits.where(ref: ref).last - end end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 6838ccfaaab..b719ad3c87e 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -20,6 +20,8 @@ module Ci class Runner < ActiveRecord::Base extend Ci::Model + + LAST_CONTACT_TIME = 5.minutes.ago has_many :builds, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' @@ -33,6 +35,8 @@ module Ci scope :shared, ->() { where(is_shared: true) } scope :active, ->() { where(active: true) } scope :paused, ->() { where(active: false) } + scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } + scope :ordered, ->() { order(id: :desc) } acts_as_taggable @@ -56,7 +60,7 @@ module Ci end def display_name - return token unless !description.blank? + return short_sha unless !description.blank? description end @@ -65,6 +69,20 @@ module Ci is_shared end + def online? + contacted_at && contacted_at > LAST_CONTACT_TIME + end + + def status + if contacted_at.nil? + :not_connected + elsif active? + online? ? :online : :offline + else + :paused + end + end + def belongs_to_one_project? runner_projects.count == 1 end @@ -78,7 +96,7 @@ module Ci end def short_sha - token[0...10] + token[0...8] if token end end end diff --git a/app/models/commit.rb b/app/models/commit.rb index d5c50013525..492f6be1ce3 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -2,13 +2,13 @@ class Commit extend ActiveModel::Naming include ActiveModel::Conversion - include Mentionable include Participable + include Mentionable include Referable include StaticModel attr_mentionable :safe_message - participant :author, :committer, :notes, :mentioned_users + participant :author, :committer, :notes attr_accessor :project @@ -164,6 +164,14 @@ class Commit @committer ||= User.find_by_any_email(committer_email) end + def parents + @parents ||= parent_ids.map { |id| project.commit(id) } + end + + def parent + @parent ||= project.commit(self.parent_id) if self.parent_id + end + def notes project.notes.for_commit_id(self.id) end @@ -181,10 +189,6 @@ class Commit @raw.short_id(7) end - def parents - @parents ||= Commit.decorate(super, project) - end - def ci_commit project.ci_commit(sha) end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index b4d91b1b0c3..0b73ab6d2eb 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -16,10 +16,10 @@ class CommitStatus < ActiveRecord::Base scope :success, -> { where(status: 'success') } scope :failed, -> { where(status: 'failed') } scope :running_or_pending, -> { where(status:[:running, :pending]) } + scope :finished, -> { where(status:[:success, :failed, :canceled]) } scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) } scope :ordered, -> { order(:ref, :stage_idx, :name) } scope :for_ref, ->(ref) { where(ref: ref) } - scope :running_or_pending, -> { where(status: [:running, :pending]) } state_machine :status, initial: :pending do event :run do @@ -27,7 +27,7 @@ class CommitStatus < ActiveRecord::Base end event :drop do - transition running: :failed + transition [:pending, :running] => :failed end event :success do @@ -88,4 +88,8 @@ class CommitStatus < ActiveRecord::Base def retry_url nil end + + def show_warning? + false + end end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 4db4ffb2e79..5e964f04ef5 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -6,8 +6,8 @@ # module Issuable extend ActiveSupport::Concern - include Mentionable include Participable + include Mentionable included do belongs_to :author, class_name: "User" @@ -47,7 +47,7 @@ module Issuable prefix: true attr_mentionable :title, :description - participant :author, :assignee, :notes, :mentioned_users + participant :author, :assignee, :notes_with_associations end module ClassMethods @@ -85,6 +85,10 @@ module Issuable assignee_id_changed? end + def open? + opened? || reopened? + end + # # Votes # @@ -176,6 +180,10 @@ module Issuable self.class.to_s.underscore end + def notes_with_associations + notes.includes(:author, :project) + end + private def filter_superceded_votes(votes, notes) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 5b0ae411642..193c91f1742 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -20,6 +20,12 @@ module Mentionable end end + included do + if self < Participable + participant ->(current_user) { mentioned_users(current_user, load_lazy_references: false) } + end + end + # Returns the text used as the body of a Note when this object is referenced # # By default this will be the class name and the result of calling @@ -41,55 +47,49 @@ module Mentionable self end - # Determine whether or not a cross-reference Note has already been created between this Mentionable and - # the specified target. - def has_mentioned?(target) - SystemNoteService.cross_reference_exists?(target, local_reference) + def all_references(current_user = self.author, text = self.mentionable_text, load_lazy_references: true) + ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references) + ext.analyze(text) + ext end - def mentioned_users(current_user = nil) - return [] if mentionable_text.blank? - - ext = Gitlab::ReferenceExtractor.new(self.project, current_user) - ext.analyze(mentionable_text) - ext.users.uniq + def mentioned_users(current_user = nil, load_lazy_references: true) + all_references(current_user, load_lazy_references: load_lazy_references).users end # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. - def references(p = project, current_user = self.author, text = mentionable_text) + def referenced_mentionables(current_user = self.author, text = self.mentionable_text, load_lazy_references: true) return [] if text.blank? - ext = Gitlab::ReferenceExtractor.new(p, current_user) - ext.analyze(text) - - (ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference] + refs = all_references(current_user, text, load_lazy_references: load_lazy_references) + (refs.issues + refs.merge_requests + refs.commits) - [local_reference] end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. - def create_cross_references!(p = project, a = author, without = []) - refs = references(p) - + def create_cross_references!(author = self.author, without = [], text = self.mentionable_text) + refs = referenced_mentionables(author, text) + # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. - refs.reject! { |ref| without.include?(ref) } + refs.reject! { |ref| without.include?(ref) || cross_reference_exists?(ref) } refs.each do |ref| - SystemNoteService.cross_reference(ref, local_reference, a) + SystemNoteService.cross_reference(ref, local_reference, author) end end # When a mentionable field is changed, creates cross-reference notes that # don't already exist - def create_new_cross_references!(p = project, a = author) + def create_new_cross_references!(author = self.author) changes = detect_mentionable_changes return if changes.empty? original_text = changes.collect { |_, vals| vals.first }.join(' ') - preexisting = references(p, self.author, original_text) - create_cross_references!(p, a, preexisting) + preexisting = referenced_mentionables(author, original_text) + create_cross_references!(author, preexisting) end private @@ -111,4 +111,10 @@ module Mentionable # Only include changed fields that are mentionable source.select { |key, val| mentionable.include?(key) } end + + # Determine whether or not a cross-reference Note has already been created between this Mentionable and + # the specified target. + def cross_reference_exists?(target) + SystemNoteService.cross_reference_exists?(target, local_reference) + end end diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index 7c9597333dd..85367f89f4f 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -12,7 +12,7 @@ # # # ... # -# participant :author, :assignee, :mentioned_users, :notes +# participant :author, :assignee, :notes, ->(current_user) { mentioned_users(current_user) } # end # # issue = Issue.last @@ -27,7 +27,7 @@ module Participable module ClassMethods def participant(*attrs) - participant_attrs.concat(attrs.map(&:to_s)) + participant_attrs.concat(attrs) end def participant_attrs @@ -37,21 +37,21 @@ module Participable # Be aware that this method makes a lot of sql queries. # Save result into variable if you are going to reuse it inside same request - def participants(current_user = self.author, project = self.project) + def participants(current_user = self.author, load_lazy_references: true) participants = self.class.participant_attrs.flat_map do |attr| - meth = method(attr) - value = - if meth.arity == 1 || meth.arity == -1 - meth.call(current_user) + if attr.respond_to?(:call) + instance_exec(current_user, &attr) else - meth.call + send(attr) end - participants_for(value, current_user, project) + participants_for(value, current_user) end.compact.uniq - if project + if load_lazy_references + participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq + participants.select! do |user| user.can?(:read_project, project) end @@ -62,14 +62,14 @@ module Participable private - def participants_for(value, current_user = nil, project = nil) + def participants_for(value, current_user = nil) case value - when User + when User, Gitlab::Markdown::ReferenceFilter::LazyReference [value] when Enumerable, ActiveRecord::Relation - value.flat_map { |v| participants_for(v, current_user, project) } + value.flat_map { |v| participants_for(v, current_user) } when Participable - value.participants(current_user, project) + value.participants(current_user, load_lazy_references: false) end end end diff --git a/app/models/group.rb b/app/models/group.rb index 9cd146bb73b..465c22d23ac 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -64,7 +64,7 @@ class Group < Namespace end def owners - @owners ||= group_members.owners.map(&:user) + @owners ||= group_members.owners.includes(:user).map(&:user) end def add_users(user_ids, access_level, current_user = nil) diff --git a/app/models/group_label.rb b/app/models/group_label.rb new file mode 100644 index 00000000000..0fc39cb8771 --- /dev/null +++ b/app/models/group_label.rb @@ -0,0 +1,9 @@ +class GroupLabel + attr_accessor :title, :labels + alias_attribute :name, :title + + def initialize(title, labels) + @title = title + @labels = labels + end +end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 1dd2be68ebf..91844da62e2 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -1,5 +1,5 @@ class GroupMilestone - + attr_accessor :title, :milestones alias_attribute :name, :title def initialize(title, milestones) @@ -7,18 +7,10 @@ class GroupMilestone @milestones = milestones end - def title - @title - end - def safe_title @title.parameterize end - - def milestones - @milestones - end - + def projects milestones.map { |milestone| milestone.project } end diff --git a/app/models/issue.rb b/app/models/issue.rb index fc7e9abe29e..72183108033 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -95,4 +95,14 @@ class Issue < ActiveRecord::Base def source_project project end + + # From all notes on this issue, we'll select the system notes about linked + # merge requests. Of those, the MRs closing `self` are returned. + def closed_by_merge_requests(current_user = nil) + return [] unless open? + + notes.system.flat_map do |note| + note.all_references(current_user).merge_requests + end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) } + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c83b15c7d39..85f37e49e62 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -40,7 +40,7 @@ class MergeRequest < ActiveRecord::Base after_create :create_merge_request_diff after_update :update_merge_request_diff - delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil + delegate :commits, :diffs, to: :merge_request_diff, prefix: nil # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests @@ -157,6 +157,18 @@ class MergeRequest < ActiveRecord::Base reference end + def last_commit + merge_request_diff ? merge_request_diff.last_commit : compare_commits.last + end + + def first_commit + merge_request_diff ? merge_request_diff.first_commit : compare_commits.first + end + + def last_commit_short_sha + last_commit.short_id + end + def validate_branches if target_project == source_project && target_branch == source_branch errors.add :branch_conflict, "You can not use same project/branch for source and target" @@ -222,10 +234,6 @@ class MergeRequest < ActiveRecord::Base self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last end - def open? - opened? || reopened? - end - def work_in_progress? !!(title =~ /\A\[?WIP\]?:? /i) end @@ -249,7 +257,7 @@ class MergeRequest < ActiveRecord::Base Note.where( "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" + - "(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))", + "((project_id = :source_project_id OR project_id = :target_project_id) AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids, target_project_id: target_project_id, @@ -294,6 +302,10 @@ class MergeRequest < ActiveRecord::Base target_project end + def closes_issue?(issue) + closes_issues.include?(issue) + end + # Return the set of issues that will be closed if this merge request is accepted. def closes_issues(current_user = self.author) if target_branch == project.default_branch @@ -458,4 +470,10 @@ class MergeRequest < ActiveRecord::Base unlock_mr if locked? end end + + def ci_commit + if last_commit + source_project.ci_commit(last_commit.id) + end + end end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index c9ef8023aea..6575d0bc81f 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -55,6 +55,10 @@ class MergeRequestDiff < ActiveRecord::Base commits.first end + def first_commit + commits.last + end + def last_commit_short_sha @last_commit_short_sha ||= last_commit.short_id end @@ -163,7 +167,8 @@ class MergeRequestDiff < ActiveRecord::Base merge_request.fetch_ref # Get latest sha of branch from source project - source_sha = merge_request.source_project.commit(source_branch).sha + source_commit = merge_request.source_project.commit(source_branch) + source_sha = source_commit.try(:sha) Gitlab::CompareResult.new( Gitlab::Git::Compare.new( diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 84acba30b6b..2ff16e2825c 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -105,4 +105,36 @@ class Milestone < ActiveRecord::Base def author_id nil end + + # Sorts the issues for the given IDs. + # + # This method runs a single SQL query using a CASE statement to update the + # position of all issues in the current milestone (scoped to the list of IDs). + # + # Given the ids [10, 20, 30] this method produces a SQL query something like + # the following: + # + # UPDATE issues + # SET position = CASE + # WHEN id = 10 THEN 1 + # WHEN id = 20 THEN 2 + # WHEN id = 30 THEN 3 + # ELSE position + # END + # WHERE id IN (10, 20, 30); + # + # This method expects that the IDs given in `ids` are already Fixnums. + def sort_issues(ids) + pairs = [] + + ids.each_with_index do |id, index| + pairs << id + pairs << index + 1 + end + + conditions = 'WHEN id = ? THEN ? ' * ids.length + + issues.where(id: ids). + update_all(["position = CASE #{conditions} ELSE position END", *pairs]) + end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index bc8525df5a5..5782e649f8b 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -118,6 +118,8 @@ class Namespace < ActiveRecord::Base gitlab_shell.add_namespace(path_was) if gitlab_shell.mv_namespace(path_was, path) + Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) + # If repositories moved successfully we need to # send update instructions to users. # However we cannot allow rollback since we moved namespace dir diff --git a/app/models/note.rb b/app/models/note.rb index de3b6df88f7..0b3aa30abd7 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -22,14 +22,14 @@ require 'carrierwave/orm/activerecord' require 'file_size_validator' class Note < ActiveRecord::Base - include Mentionable include Gitlab::CurrentSettings include Participable + include Mentionable default_value_for :system, false attr_mentionable :note - participant :author, :mentioned_users + participant :author belongs_to :project belongs_to :noteable, polymorphic: true @@ -60,9 +60,13 @@ class Note < ActiveRecord::Base scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author, ->{ includes(:author) } + scope :with_associations, -> do + includes(:author, :noteable, :updated_by, + project: [:project_members, { group: [:group_members] }]) + end + serialize :st_diff before_create :set_diff, if: ->(n) { n.line_code.present? } - after_update :set_references class << self def discussions_from_notes(notes) @@ -333,15 +337,13 @@ class Note < ActiveRecord::Base end def noteable_type_name - if noteable_type.present? - noteable_type.downcase - end + noteable_type.downcase if noteable_type.present? end # FIXME: Hack for polymorphic associations with STI # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations - def noteable_type=(sType) - super(sType.to_s.classify.constantize.base_class.to_s) + def noteable_type=(noteable_type) + super(noteable_type.to_s.classify.constantize.base_class.to_s) end # Reset notes events cache @@ -357,10 +359,6 @@ class Note < ActiveRecord::Base Event.reset_event_cache_for(self) end - def set_references - create_new_cross_references!(project, author) - end - def system? read_attribute(:system) end diff --git a/app/models/project.rb b/app/models/project.rb index 021920008ad..74b89aad499 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -119,7 +119,7 @@ class Project < ActiveRecord::Base has_many :deploy_keys, through: :deploy_keys_projects has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user - has_many :ci_commits, ->() { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) }, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id + has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" @@ -243,11 +243,12 @@ class Project < ActiveRecord::Base # Use of unscoped ensures we're not secretly adding any ORDER BYs, which # have a negative impact on performance (and aren't needed for this # query). - unscoped. + projects = unscoped. joins(:namespace). - iwhere('namespaces.path' => namespace_path). - iwhere('projects.path' => project_path). - take + iwhere('namespaces.path' => namespace_path) + + projects.where('projects.path' => project_path).take || + projects.iwhere('projects.path' => project_path).take end def visibility_levels @@ -567,7 +568,7 @@ class Project < ActiveRecord::Base end def empty_repo? - !repository.exists? || repository.empty? + !repository.exists? || !repository.has_visible_content? end def repo @@ -656,6 +657,8 @@ class Project < ActiveRecord::Base # db changes in order to prevent out of sync between db and fs raise Exception.new('repository cannot be renamed') end + + Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) end def hook_attrs diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 5f5255ab487..d31b12f539e 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -48,7 +48,7 @@ class BambooService < CiService end def reset_password - if prop_updated?(:bamboo_url) + if bamboo_url_changed? && !password_touched? self.password = nil end end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index fb11cad352e..0b022461250 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -45,7 +45,7 @@ class TeamcityService < CiService end def reset_password - if prop_updated?(:teamcity_url) + if teamcity_url_changed? && !password_touched? self.password = nil end end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index f602a965364..9f380a382cb 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -139,15 +139,28 @@ class ProjectTeam Gitlab::Access.options.key max_member_access(user_id) end + # This method assumes project and group members are eager loaded for optimal + # performance. def max_member_access(user_id) access = [] - access << project.project_members.find_by(user_id: user_id).try(:access_field) + + project.project_members.each do |member| + if member.user_id == user_id + access << member.access_field if member.access_field + break + end + end if group - access << group.group_members.find_by(user_id: user_id).try(:access_field) + group.group_members.each do |member| + if member.user_id == user_id + access << member.access_field if member.access_field + break + end + end end - access.compact.max + access.max end private diff --git a/app/models/repository.rb b/app/models/repository.rb index 8b51602bc23..9266ba27f0a 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -8,6 +8,14 @@ class Repository attr_accessor :raw_repository, :path_with_namespace, :project + def self.clean_old_archives + repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path + + return unless File.directory?(repository_downloads_path) + + Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete)) + end + def initialize(path_with_namespace, default_branch = nil, project = nil) @path_with_namespace = path_with_namespace @project = project @@ -36,6 +44,19 @@ class Repository raw_repository.empty? end + # + # Git repository can contains some hidden refs like: + # /refs/notes/* + # /refs/git-as-svn/* + # /refs/pulls/* + # This refs by default not visible in project page and not cloned to client side. + # + # This method return true if repository contains some content visible in project page. + # + def has_visible_content? + !raw_repository.branches.empty? + end + def commit(id = 'HEAD') return nil unless raw_repository commit = Gitlab::Git::Commit.find(raw_repository, id) @@ -46,13 +67,16 @@ class Repository end def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false) - commits = Gitlab::Git::Commit.where( + options = { repo: raw_repository, ref: ref, path: path, limit: limit, offset: offset, - ) + follow: path.present? + } + + commits = Gitlab::Git::Commit.where(options) commits = Commit.decorate(commits, @project) if commits.present? commits end @@ -63,6 +87,15 @@ class Repository commits end + def find_commits_by_message(query) + # Limited to 1000 commits for now, could be parameterized? + args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) + + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) + commits = git_log_results.map { |c| commit(c) } + commits + end + def find_branch(name) branches.find { |branch| branch.name == name } end @@ -269,14 +302,6 @@ class Repository end # Remove archives older than 2 hours - def clean_old_archives - repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path - - return unless File.directory?(repository_downloads_path) - - Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete)) - end - def branches_sorted_by(value) case value when 'recently_updated' @@ -312,13 +337,7 @@ class Repository end def blob_for_diff(commit, diff) - file = blob_at(commit.id, diff.new_path) - - unless file - file = prev_blob_for_diff(commit, diff) - end - - file + blob_at(commit.id, diff.file_path) end def prev_blob_for_diff(commit, diff) @@ -480,9 +499,13 @@ class Repository end end + def merge_base(first_commit_id, second_commit_id) + rugged.merge_base(first_commit_id, second_commit_id) + end + def search_files(query, ref) offset = 2 - args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref}) + args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end @@ -514,7 +537,7 @@ class Repository end def fetch_ref(source_path, source_ref, target_ref) - args = %W(git fetch #{source_path} #{source_ref}:#{target_ref}) + args = %W(git fetch -f #{source_path} #{source_ref}:#{target_ref}) Gitlab::Popen.popen(args, path_to_repo) end diff --git a/app/models/service.rb b/app/models/service.rb index 7e845d565b1..d610abd1683 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -33,6 +33,8 @@ class Service < ActiveRecord::Base after_initialize :initialize_properties + after_commit :reset_updated_properties + belongs_to :project has_one :service_hook @@ -103,6 +105,7 @@ class Service < ActiveRecord::Base # Provide convenient accessor methods # for each serialized property. + # Also keep track of updated properties in a similar way as ActiveModel::Dirty def self.prop_accessor(*args) args.each do |arg| class_eval %{ @@ -111,21 +114,39 @@ class Service < ActiveRecord::Base end def #{arg}=(value) + updated_properties['#{arg}'] = #{arg} unless #{arg}_changed? self.properties['#{arg}'] = value end + + def #{arg}_changed? + #{arg}_touched? && #{arg} != #{arg}_was + end + + def #{arg}_touched? + updated_properties.include?('#{arg}') + end + + def #{arg}_was + updated_properties['#{arg}'] + end } end end - # ActiveRecord does not provide a mechanism to track changes in serialized keys. - # This is why we need to perform extra query to do it mannually. - def prop_updated?(prop_name) - relation_name = self.type.underscore - previous_value = project.send(relation_name).send(prop_name) - return false if previous_value.nil? - previous_value != send(prop_name) + # Returns a hash of the properties that have been assigned a new value since last save, + # indicating their original values (attr => original value). + # ActiveRecord does not provide a mechanism to track changes in serialized keys, + # so we need a specific implementation for service properties. + # This allows to track changes to properties set with the accessor methods, + # but not direct manipulation of properties hash. + def updated_properties + @updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new end + def reset_updated_properties + @updated_properties = nil + end + def async_execute(data) return unless supported_events.include?(data[:object_kind]) diff --git a/app/models/user.rb b/app/models/user.rb index 889d2d3b867..67fef1c1e6a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -68,6 +68,7 @@ class User < ActiveRecord::Base include Referable include Sortable include TokenAuthenticatable + include CaseSensitivity default_value_for :admin, false default_value_for :can_create_group, gitlab_config.default_can_create_group @@ -182,7 +183,7 @@ class User < ActiveRecord::Base # User's Project preference # Note: When adding an option, it MUST go on the end of the array. - enum project_view: [:readme, :activity] + enum project_view: [:readme, :activity, :files] alias_attribute :private_token, :authentication_token @@ -234,21 +235,16 @@ class User < ActiveRecord::Base # Find a User by their primary email or any associated secondary email def find_by_any_email(email) - user_table = arel_table - email_table = Email.arel_table - - # Use ARel to build a query: - query = user_table. - # SELECT "users".* FROM "users" - project(user_table[Arel.star]). - # LEFT OUTER JOIN "emails" - join(email_table, Arel::Nodes::OuterJoin). - # ON "users"."id" = "emails"."user_id" - on(user_table[:id].eq(email_table[:user_id])). - # WHERE ("user"."email" = '<email>' OR "emails"."email" = '<email>') - where(user_table[:email].eq(email).or(email_table[:email].eq(email))) - - find_by_sql(query.to_sql).first + sql = 'SELECT * + FROM users + WHERE id IN ( + SELECT id FROM users WHERE email = :email + UNION + SELECT emails.user_id FROM emails WHERE email = :email + ) + LIMIT 1;' + + User.find_by_sql([sql, { email: email }]).first end def filter(filter_name) @@ -273,8 +269,13 @@ class User < ActiveRecord::Base end def by_login(login) - where('lower(username) = :value OR lower(email) = :value', - value: login.to_s.downcase).first + return nil unless login + + if login.include?('@'.freeze) + unscoped.iwhere(email: login).take + else + unscoped.iwhere(username: login).take + end end def find_by_username!(username) @@ -395,15 +396,17 @@ class User < ActiveRecord::Base end end + def authorized_projects_id + @authorized_projects_id ||= begin + project_ids = personal_projects.pluck(:id) + project_ids.push(*groups_projects.pluck(:id)) + project_ids.push(*projects.pluck(:id).uniq) + end + end # Projects user has access to def authorized_projects - @authorized_projects ||= begin - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.pluck(:id)) - project_ids.push(*projects.pluck(:id).uniq) - Project.where(id: project_ids) - end + @authorized_projects ||= Project.where(id: authorized_projects_id) end def owned_projects @@ -700,12 +703,15 @@ class User < ActiveRecord::Base end def toggle_star(project) - user_star_project = users_star_projects. - where(project: project, user: self).take - if user_star_project - user_star_project.destroy - else - UsersStarProject.create!(project: project, user: self) + UsersStarProject.transaction do + user_star_project = users_star_projects. + where(project: project, user: self).lock(true).first + + if user_star_project + user_star_project.destroy + else + UsersStarProject.create!(project: project, user: self) + end end end @@ -759,11 +765,14 @@ class User < ActiveRecord::Base end def ci_authorized_projects - @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects) + @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id) end def ci_authorized_runners - Ci::Runner.specific.includes(:runner_projects). - where(ci_runner_projects: { project_id: ci_authorized_projects } ) + @ci_authorized_runners ||= begin + runner_ids = Ci::RunnerProject.joins(:project). + where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id) + Ci::Runner.specific.where(id: runner_ids) + end end end diff --git a/app/services/archive_repository_service.rb b/app/services/archive_repository_service.rb index e1b41527d8d..2160bf13e6d 100644 --- a/app/services/archive_repository_service.rb +++ b/app/services/archive_repository_service.rb @@ -7,19 +7,12 @@ class ArchiveRepositoryService end def execute(options = {}) - project.repository.clean_old_archives + RepositoryArchiveCacheWorker.perform_async - raise "No archive file path" unless file_path + metadata = project.repository.archive_metadata(ref, storage_path, format) + raise "Repository or ref not found" if metadata.empty? - return file_path if archived? - - unless archiving? - RepositoryArchiveWorker.perform_async(project.id, ref, format) - end - - archived = wait_until_archived(options[:timeout] || 5.0) - - file_path if archived + metadata end private @@ -27,36 +20,4 @@ class ArchiveRepositoryService def storage_path Gitlab.config.gitlab.repository_downloads_path end - - def file_path - @file_path ||= project.repository.archive_file_path(ref, storage_path, format) - end - - def pid_file_path - @pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format) - end - - def archived? - File.exist?(file_path) - end - - def archiving? - File.exist?(pid_file_path) - end - - def wait_until_archived(timeout = 5.0) - return archived? if timeout == 0.0 - - t1 = Time.now - - begin - sleep 0.1 - - success = archived? - - t2 = Time.now - end until success || t2 - t1 >= timeout - - success - end end diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb index c420f3268fd..912eb6258a4 100644 --- a/app/services/ci/create_builds_service.rb +++ b/app/services/ci/create_builds_service.rb @@ -1,8 +1,20 @@ module Ci class CreateBuildsService - def execute(commit, stage, ref, tag, user, trigger_request) + def execute(commit, stage, ref, tag, user, trigger_request, status) builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag) + # check when to create next build + builds_attrs = builds_attrs.select do |build_attrs| + case build_attrs[:when] + when 'on_success' + status == 'success' + when 'on_failure' + status == 'failed' + when 'always' + %w(success failed).include?(status) + end + end + builds_attrs.map do |build_attrs| # don't create the same build twice unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name]) diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb index b95835ba093..b8d24193035 100644 --- a/app/services/ci/image_for_build_service.rb +++ b/app/services/ci/image_for_build_service.rb @@ -1,17 +1,15 @@ module Ci class ImageForBuildService def execute(project, params) - image_name = - if params[:sha] - commit = project.commits.find_by(sha: params[:sha]) - image_for_commit(commit) - elsif params[:ref] - commit = project.last_commit_for_ref(params[:ref]) - image_for_commit(commit) - else - 'build-unknown.svg' + sha = params[:sha] + sha ||= + if params[:ref] + project.gl_project.commit(params[:ref]).try(:sha) end + commit = project.commits.ordered.find_by(sha: sha) + image_name = image_for_commit(commit) + image_path = Rails.root.join('public/ci', image_name) OpenStruct.new( diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 71b61bbe389..7beb098659c 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -17,7 +17,7 @@ module Ci builds = builds.order('created_at ASC') build = builds.find do |build| - (build.tag_list - current_runner.tag_list).empty? + build.can_be_served?(current_runner) end diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb index 71272fb5707..6107254a34e 100644 --- a/app/services/files/create_dir_service.rb +++ b/app/services/files/create_dir_service.rb @@ -5,5 +5,16 @@ module Files def commit repository.commit_dir(current_user, @file_path, @commit_message, @target_branch) end + + def validate + super + + unless @file_path =~ Gitlab::Regex.file_path_regex + raise_error( + 'Your changes could not be committed, because the file path ' + + Gitlab::Regex.file_path_regex_message + ) + end + end end end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index c8e3a910bba..2348920cc58 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -9,12 +9,17 @@ module Files def validate super - file_name = File.basename(@file_path) + if @file_path =~ Gitlab::Regex.directory_traversal_regex + raise_error( + 'Your changes could not be committed, because the file name ' + + Gitlab::Regex.directory_traversal_regex_message + ) + end - unless file_name =~ Gitlab::Regex.file_name_regex + unless @file_path =~ Gitlab::Regex.file_path_regex raise_error( 'Your changes could not be committed, because the file name ' + - Gitlab::Regex.file_name_regex_message + Gitlab::Regex.file_path_regex_message ) end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index f9a8265d2d4..3de7bb9dcaa 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -49,10 +49,13 @@ class GitPushService elsif push_to_existing_branch?(ref, oldrev) # Collect data for this git push @push_commits = project.repository.commits_between(oldrev, newrev) - project.update_merge_requests(oldrev, newrev, ref, @user) process_commit_messages(ref) end + # Update merge requests that may be affected by this push. A new branch + # could cause the last commit of a merge request to change. + project.update_merge_requests(oldrev, newrev, ref, @user) + @push_data = build_push_data(oldrev, newrev, ref) # If CI was disabled but .gitlab-ci.yml file was pushed @@ -74,48 +77,30 @@ class GitPushService def process_commit_messages(ref) is_default_branch = is_default_branch?(ref) - @push_commits.each do |commit| - # Close issues if these commits were pushed to the project's default branch and the commit message matches the - # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to - # a different branch. - issues_to_close = commit.closes_issues(user) + authors = Hash.new do |hash, commit| + email = commit.author_email + next hash[email] if hash.has_key?(email) - # Load commit author only if needed. - # For push with 1k commits it prevents 900+ requests in database - author = nil + hash[email] = commit_user(commit) + end + @push_commits.each do |commit| # Keep track of the issues that will be actually closed because they are on a default branch. # Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) # will also have cross-reference. - actually_closed_issues = [] - - if issues_to_close.present? && is_default_branch - author ||= commit_user(commit) - actually_closed_issues = issues_to_close - issues_to_close.each do |issue| - Issues::CloseService.new(project, author, {}).execute(issue, commit) + closed_issues = [] + + if is_default_branch + # Close issues if these commits were pushed to the project's default branch and the commit message matches the + # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to + # a different branch. + closed_issues = commit.closes_issues(user) + closed_issues.each do |issue| + Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) end end - if project.default_issues_tracker? - create_cross_reference_notes(commit, actually_closed_issues) - end - end - end - - def create_cross_reference_notes(commit, issues_to_close) - # Create cross-reference notes for any other references than those given in issues_to_close. - # Omit any issues that were referenced in an issue-closing phrase, or have already been - # mentioned from this commit (probably from this commit being pushed to a different branch). - refs = commit.references(project, user) - issues_to_close - refs.reject! { |r| commit.has_mentioned?(r) } - - if refs.present? - author ||= commit_user(commit) - - refs.each do |r| - SystemNoteService.cross_reference(r, commit, author) - end + commit.create_cross_references!(authors[commit], closed_issues) end end diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index 1ea4b72216c..bcb380d3215 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -10,7 +10,7 @@ module Issues issue.update_attributes(label_ids: label_params) notification_service.new_issue(issue, current_user) event_service.open_issue(issue, current_user) - issue.create_cross_references!(issue.project, current_user) + issue.create_cross_references!(current_user) execute_hooks(issue, 'open') end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 2fc6ef7f356..551325e4cab 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -35,7 +35,7 @@ module Issues create_title_change_note(issue, issue.previous_changes['title'].first) end - issue.create_new_cross_references!(issue.project, current_user) + issue.create_new_cross_references!(current_user) execute_hooks(issue, 'update') end diff --git a/app/services/labels/group_service.rb b/app/services/labels/group_service.rb new file mode 100644 index 00000000000..b26cee24d56 --- /dev/null +++ b/app/services/labels/group_service.rb @@ -0,0 +1,26 @@ +module Labels + class GroupService < ::BaseService + def initialize(project_labels) + @project_labels = project_labels.group_by(&:title) + end + + def execute + build(@project_labels) + end + + def label(title) + if title + group_label = @project_labels[title].group_by(&:title) + build(group_label).first + else + nil + end + end + + private + + def build(label) + label.map { |title, labels| GroupLabel.new(title, labels) } + end + end +end diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb index 9651b16462c..009d5a6867e 100644 --- a/app/services/merge_requests/create_service.rb +++ b/app/services/merge_requests/create_service.rb @@ -18,7 +18,7 @@ module MergeRequests merge_request.update_attributes(label_ids: label_params) event_service.open_mr(merge_request, current_user) notification_service.new_merge_request(merge_request, current_user) - merge_request.create_cross_references!(merge_request.project, current_user) + merge_request.create_cross_references!(current_user) execute_hooks(merge_request) end diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index aceb8cb9021..8f25c5e2496 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -6,6 +6,7 @@ module MergeRequests # class PostMergeService < MergeRequests::BaseService def execute(merge_request) + close_issues(merge_request) merge_request.mark_as_merged create_merge_event(merge_request, current_user) create_note(merge_request) @@ -15,6 +16,15 @@ module MergeRequests private + def close_issues(merge_request) + return unless merge_request.target_branch == project.default_branch + + closed_issues = merge_request.closes_issues(current_user) + closed_issues.each do |issue| + Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request) + end + end + def create_merge_event(merge_request, current_user) EventCreateService.new.merge_mr(merge_request, current_user) end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index e903e48e3cd..d68bc79ecc0 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -5,13 +5,20 @@ module MergeRequests @oldrev, @newrev = oldrev, newrev @branch_name = Gitlab::Git.ref_name(ref) - @fork_merge_requests = @project.fork_merge_requests.opened - @commits = @project.repository.commits_between(oldrev, newrev) - close_merge_requests + find_new_commits reload_merge_requests + + # Leave a system note if a branch was deleted/added + if branch_added? || branch_removed? + comment_mr_branch_presence_changed + comment_mr_with_commits + else + comment_mr_with_commits + close_merge_requests + end + execute_mr_web_hooks - comment_mr_with_commits true end @@ -31,7 +38,6 @@ module MergeRequests commit_ids.include?(merge_request.last_commit.id) end - merge_requests.uniq.select(&:source_project).each do |merge_request| MergeRequests::PostMergeService. new(merge_request.target_project, @current_user). @@ -47,7 +53,7 @@ module MergeRequests # Note: we should update merge requests from forks too def reload_merge_requests merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a - merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a + merge_requests += fork_merge_requests.by_branch(@branch_name).to_a merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| @@ -70,13 +76,48 @@ module MergeRequests end end + def find_new_commits + if branch_added? + @commits = [] + + merge_request = merge_requests_for_source_branch.first + return unless merge_request + + last_commit = merge_request.last_commit + + begin + # Since any number of commits could have been made to the restored branch, + # find the common root to see what has been added. + common_ref = @project.repository.merge_base(last_commit.id, @newrev) + # If the a commit no longer exists in this repo, gitlab_git throws + # a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52 + @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref + rescue + end + elsif branch_removed? + # No commits for a deleted branch. + @commits = [] + else + @commits = @project.repository.commits_between(@oldrev, @newrev) + end + end + + # Add comment about branches being deleted or added to merge requests + def comment_mr_branch_presence_changed + presence = branch_added? ? :add : :delete + + merge_requests_for_source_branch.each do |merge_request| + SystemNoteService.change_branch_presence( + merge_request, merge_request.project, @current_user, + :source, @branch_name, presence) + end + end + # Add comment about pushing new commits to merge requests def comment_mr_with_commits - merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a - merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a - merge_requests = filter_merge_requests(merge_requests) + return unless @commits.present? - merge_requests.each do |merge_request| + merge_requests_for_source_branch.each do |merge_request| mr_commit_ids = Set.new(merge_request.commits.map(&:id)) new_commits, existing_commits = @commits.partition do |commit| @@ -91,14 +132,7 @@ module MergeRequests # Call merge request webhook with update branches def execute_mr_web_hooks - merge_requests = @project.origin_merge_requests.opened - .where(source_branch: @branch_name) - .to_a - merge_requests += @fork_merge_requests.where(source_branch: @branch_name) - .to_a - merge_requests = filter_merge_requests(merge_requests) - - merge_requests.each do |merge_request| + merge_requests_for_source_branch.each do |merge_request| execute_hooks(merge_request, 'update') end end @@ -106,5 +140,25 @@ module MergeRequests def filter_merge_requests(merge_requests) merge_requests.uniq.select(&:source_project) end + + def merge_requests_for_source_branch + @source_merge_requests ||= begin + merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a + merge_requests += fork_merge_requests.where(source_branch: @branch_name).to_a + filter_merge_requests(merge_requests) + end + end + + def fork_merge_requests + @fork_merge_requests ||= @project.fork_merge_requests.opened + end + + def branch_added? + Gitlab::Git.blank_ref?(@oldrev) + end + + def branch_removed? + Gitlab::Git.blank_ref?(@newrev) + end end end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 25d79e22e39..61f7d2bbe89 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -59,7 +59,7 @@ module MergeRequests merge_request.mark_as_unchecked end - merge_request.create_new_cross_references!(merge_request.project, current_user) + merge_request.create_new_cross_references!(current_user) execute_hooks(merge_request, 'update') end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 482c0444049..2001dc89c33 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -11,13 +11,7 @@ module Notes # Skip system notes, like status changes and cross-references. unless note.system event_service.leave_note(note, note.author) - - # Create a cross-reference note if this Note contains GFM that names an - # issue, merge request, or commit. - note.references.each do |mentioned| - SystemNoteService.cross_reference(mentioned, note.noteable, note.author) - end - + note.create_cross_references! execute_hooks(note) end end diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb index c22a9333ef6..72e2f78008d 100644 --- a/app/services/notes/update_service.rb +++ b/app/services/notes/update_service.rb @@ -4,7 +4,7 @@ module Notes return note unless note.editable? note.update_attributes(params.merge(updated_by: current_user)) - + note.create_new_cross_references!(current_user) note.reset_events_cache note diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index c327c244f0d..64ea6dd42eb 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -27,6 +27,7 @@ module Projects def transfer(project, new_namespace) Project.transaction do old_path = project.path_with_namespace + old_namespace = project.namespace new_path = File.join(new_namespace.try(:path) || '', project.path) if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? @@ -51,6 +52,9 @@ module Projects # clear project cached events project.reset_events_cache + # Move uploads + Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) + true end end diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 8253c1f780d..708c2f00486 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -168,6 +168,31 @@ class SystemNoteService create_note(noteable: noteable, project: project, author: author, note: body) end + # Called when a branch in Noteable is added or deleted + # + # noteable - Noteable object + # project - Project owning noteable + # author - User performing the change + # branch_type - :source or :target + # branch - branch name + # presence - :add or :delete + # + # Example Note text: + # + # "Restored target branch `feature`" + # + # Returns the created Note object + def self.change_branch_presence(noteable, project, author, branch_type, branch, presence) + verb = + if presence == :add + 'restored' + else + 'deleted' + end + body = "#{verb} #{branch_type.to_s} branch `#{branch}`".capitalize + create_note(noteable: noteable, project: project, author: author, note: body) + end + # Called when a Mentionable references a Noteable # # noteable - Noteable object being referenced @@ -302,7 +327,7 @@ class SystemNoteService commit_ids = if count == 1 existing_commits.first.short_id else - if oldrev + if oldrev && !Gitlab::Git.blank_ref?(oldrev) "#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}" else "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}" diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index f9673abbfe8..e8211585834 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -26,7 +26,7 @@ class FileUploader < CarrierWave::Uploader::Base end def secure_url - File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename) + File.join("/uploads", @secret, file.filename) end def file_storage? diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml new file mode 100644 index 00000000000..619533e09a7 --- /dev/null +++ b/app/views/abuse_report_mailer/notify.html.haml @@ -0,0 +1,11 @@ +%p + #{link_to @abuse_report.user.name, user_url(@abuse_report.user)} + (@#{@abuse_report.user.username}) was reported for abuse by + #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)} + (@#{@abuse_report.reporter.username}). + +%blockquote + = @abuse_report.message + +%p + = link_to "View details", abuse_reports_url diff --git a/app/views/abuse_report_mailer/notify.text.haml b/app/views/abuse_report_mailer/notify.text.haml new file mode 100644 index 00000000000..7dacf857035 --- /dev/null +++ b/app/views/abuse_report_mailer/notify.text.haml @@ -0,0 +1,5 @@ +#{@abuse_report.user.name} (@#{@abuse_report.user.username}) was reported for abuse by #{@abuse_report.reporter.name} (@#{@abuse_report.reporter.username}). +\ +> #{@abuse_report.message} +\ +View details: #{admin_abuse_reports_url} diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index 2e8746146d1..40a5fe4628b 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -2,16 +2,17 @@ %h3.page-title Abuse Reports %hr - if @abuse_reports.present? - %table.table - %thead - %tr - %th Reported by - %th Reported at - %th Message - %th User - %th Primary action - %th - = render @abuse_reports + .table-holder + %table.table + %thead + %tr + %th Reported by + %th Reported at + %th Message + %th User + %th Primary action + %th + = render @abuse_reports = paginate @abuse_reports - else %h4 There are no abuse reports diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index a36ae0b766c..7a78526e09a 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -47,6 +47,12 @@ = f.label :version_check_enabled do = f.check_box :version_check_enabled Version check enabled + .form-group + = f.label :admin_notification_email, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :admin_notification_email, class: 'form-control' + .help-block + Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area. %fieldset %legend Account and Limit Settings diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 0ea2ffeda99..3eb9d61972b 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -3,25 +3,26 @@ Application: #{@application.name} -%table.table - %tr - %td - Application Id - %td - %code#application_id= @application.uid - %tr - %td - Secret: - %td - %code#secret= @application.secret +.table-holder + %table.table + %tr + %td + Application Id + %td + %code#application_id= @application.uid + %tr + %td + Secret: + %td + %code#secret= @application.secret - %tr - %td - Callback url - %td - - @application.redirect_uri.split.each do |uri| - %div - %span.monospace= uri + %tr + %td + Callback url + %td + - @application.redirect_uri.split.each do |uri| + %div + %span.monospace= uri .form-actions = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left' = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml index 3a01e115109..de5bc050cf0 100644 --- a/app/views/admin/background_jobs/show.html.haml +++ b/app/views/admin/background_jobs/show.html.haml @@ -12,24 +12,25 @@ %i.fa.fa-exclamation-triangle There are no running sidekiq processes. Please restart GitLab - else - %table.table - %thead - %th USER - %th PID - %th CPU - %th MEM - %th STATE - %th START - %th COMMAND - %tbody - - @sidekiq_processes.each do |process| - - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) - - data = process.strip.split(' ') - %tr - %td= gitlab_config.user - - 5.times do - %td= data.shift - %td= data.join(' ') + .table-holder + %table.table + %thead + %th USER + %th PID + %th CPU + %th MEM + %th STATE + %th START + %th COMMAND + %tbody + - @sidekiq_processes.each do |process| + - next unless process.match(/(sidekiq \d+\.\d+\.\d+.+$)/) + - data = process.strip.split(' ') + %tr + %td= gitlab_config.user + - 5.times do + %td= data.shift + %td= data.join(' ') .clearfix %p diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 2bf1689cbc6..841e6971fb2 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -5,22 +5,23 @@ .panel-head-actions = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm" - if @deploy_keys.any? - %table.table - %thead.panel-heading - %tr - %th Title - %th Fingerprint - %th Added at - %th - %tbody - - @deploy_keys.each do |deploy_key| + .table-holder + %table.table + %thead.panel-heading %tr - %td - %strong= deploy_key.title - %td - %code.key-fingerprint= deploy_key.fingerprint - %td - %span.cgray - added #{time_ago_with_tooltip(deploy_key.created_at)} - %td - = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right" + %th Title + %th Fingerprint + %th Added at + %th + %tbody + - @deploy_keys.each do |deploy_key| + %tr + %td + %strong= deploy_key.title + %td + %code.key-fingerprint= deploy_key.fingerprint + %td + %span.cgray + added #{time_ago_with_tooltip(deploy_key.created_at)} + %td + = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right" diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml index ae57e3adc4d..8358a14445b 100644 --- a/app/views/admin/identities/index.html.haml +++ b/app/views/admin/identities/index.html.haml @@ -2,12 +2,13 @@ = render 'admin/users/head' - if @identities.present? - %table.table - %thead - %tr - %th Provider - %th Identifier - %th - = render @identities + .table-holder + %table.table + %thead + %tr + %th Provider + %th Identifier + %th + = render @identities - else %h4 This user has no identities diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml index e2377291142..6a5986f496a 100644 --- a/app/views/admin/services/index.html.haml +++ b/app/views/admin/services/index.html.haml @@ -2,22 +2,23 @@ %h3.page-title Service templates %p.light Service template allows you to set default values for project services -%table.table - %thead - %tr - %th - %th Service - %th Description - %th Last edit - - @services.sort_by(&:title).each do |service| - %tr - %td - = icon("copy", class: 'clgray') - %td - = link_to edit_admin_application_settings_service_path(service.id) do - %strong= service.title - %td - = service.description - %td.light - = time_ago_in_words service.updated_at - ago +.table-holder + %table.table + %thead + %tr + %th + %th Service + %th Description + %th Last edit + - @services.sort_by(&:title).each do |service| + %tr + %td + = icon("copy", class: 'clgray') + %td + = link_to edit_admin_application_settings_service_path(service.id) do + %strong= service.title + %td + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml index 90d9980c85c..90d9980c85c 100644 --- a/app/views/users/_profile.html.haml +++ b/app/views/admin/users/_profile.html.haml diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index a383ea57384..0848504b7a6 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -8,13 +8,13 @@ = @user.name %ul.well-list %li - = image_tag avatar_icon(@user.email, 60), class: "avatar s60" + = image_tag avatar_icon(@user, 60), class: "avatar s60" %li %span.light Profile page: %strong = link_to user_path(@user) do = @user.username - = render 'users/profile', user: @user + = render 'admin/users/profile', user: @user .panel.panel-default .panel-heading diff --git a/app/views/ci/admin/events/index.html.haml b/app/views/ci/admin/events/index.html.haml index f9ab0994304..5a5b4dc7c35 100644 --- a/app/views/ci/admin/events/index.html.haml +++ b/app/views/ci/admin/events/index.html.haml @@ -1,17 +1,18 @@ -%table.table - %thead - %tr - %th User ID - %th Description - %th When - - @events.each do |event| - %tr - %td - = event.user_id - %td - = event.description - %td.light - = time_ago_in_words event.updated_at - ago +.table-holder + %table.table + %thead + %tr + %th User ID + %th Description + %th When + - @events.each do |event| + %tr + %td + = event.user_id + %td + = event.description + %td.light + = time_ago_in_words event.updated_at + ago -= paginate @events
\ No newline at end of file += paginate @events diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml index dc7b041473b..0da8547924b 100644 --- a/app/views/ci/admin/projects/index.html.haml +++ b/app/views/ci/admin/projects/index.html.haml @@ -1,15 +1,16 @@ -%table.table - %thead - %tr - %th ID - %th Name - %th Last build - %th Access - %th Builds - %th +.table-holder + %table.table + %thead + %tr + %th ID + %th Name + %th Last build + %th Access + %th Builds + %th - - @projects.each do |project| - = render "ci/admin/projects/project", project: project + - @projects.each do |project| + = render "ci/admin/projects/project", project: project = paginate @projects diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml index f049b4f4c4e..6b4e3b2cb38 100644 --- a/app/views/ci/admin/runner_projects/index.html.haml +++ b/app/views/ci/admin/runner_projects/index.html.haml @@ -1,5 +1,5 @@ %p.lead - To register new runner visit #{link_to 'this page ', ci_runners_path} + To register a new runner visit #{link_to 'this page ', ci_runners_path} .row .col-md-8 diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index 01ce81b4476..bacaccfbffa 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -1,5 +1,5 @@ %p.lead - %span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. + %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. %code #{GitlabCi::REGISTRATION_TOKEN} .bs-callout @@ -21,7 +21,7 @@ \- run builds from assigned projects %li %span.label.label-danger paused - \- runner will not receive any new build + \- runner will not receive any new builds .append-bottom-20.clearfix .pull-left @@ -35,18 +35,19 @@ %br -%table.table - %thead - %tr - %th Type - %th Runner token - %th Description - %th Projects - %th Builds - %th Tags - %th Last contact - %th +.table-holder + %table.table + %thead + %tr + %th Type + %th Runner token + %th Description + %th Projects + %th Builds + %th Tags + %th Last contact + %th - - @runners.each do |runner| - = render "ci/admin/runners/runner", runner: runner + - @runners.each do |runner| + = render "ci/admin/runners/runner", runner: runner = paginate @runners diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 92787b2e6ac..1498db46a80 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -13,13 +13,13 @@ - if @runner.shared? .bs-callout.bs-callout-success - %h4 This runner will process build from ALL UNASSIGNED projects + %h4 This runner will process builds from ALL UNASSIGNED projects %p If you want runners to build only specific projects, enable them in the table below. Keep in mind that this is a one way transition. - else .bs-callout.bs-callout-info - %h4 This runner will process build only from ASSIGNED projects + %h4 This runner will process builds only from ASSIGNED projects %p You can't make this a shared runner. %hr = form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| @@ -53,13 +53,14 @@ %th - @runner.runner_projects.each do |runner_project| - project = runner_project.project - %tr.alert-info - %td - %strong - = project.name - %td - .pull-right - = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + - if project.gl_project + %tr.alert-info + %td + %strong + = project.name + %td + .pull-right + = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' %table.table %thead @@ -103,21 +104,26 @@ %th Finished at - @builds.each do |build| + - gl_project = build.gl_project %tr.build %td.id - - gl_project = build.project.gl_project - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do + - if gl_project + = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do + = build.id + - else = build.id %td.status = ci_status_with_icon(build.status) %td.status - = build.project.name + - if gl_project + = gl_project.name_with_namespace %td.build-link - = link_to ci_status_path(build.commit) do - %strong #{build.commit.short_sha} + - if gl_project + = link_to ci_status_path(build.commit) do + %strong #{build.commit.short_sha} %td.timestamp - if build.finished_at diff --git a/app/views/ci/events/index.html.haml b/app/views/ci/events/index.html.haml deleted file mode 100644 index 779f49b3d3a..00000000000 --- a/app/views/ci/events/index.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -%h3.page-title Events - -%table.table - %thead - %tr - %th User ID - %th Description - %th When - - @events.each do |event| - %tr - %td - = event.user_id - %td - = event.description - %td.light - = time_ago_in_words event.updated_at - ago - -= paginate @events
\ No newline at end of file diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml index e2179e60f3e..77f78caa8d8 100644 --- a/app/views/ci/lints/_create.html.haml +++ b/app/views/ci/lints/_create.html.haml @@ -4,29 +4,35 @@ syntax is correct %i.fa.fa-ok.correct-syntax - %table.table.table-bordered - %thead - %tr - %th Parameter - %th Value - %tbody - - @stages.each do |stage| - - @builds.select { |build| build[:stage] == stage }.each do |build| - %tr - %td #{stage.capitalize} Job - #{build[:name]} - %td - %pre - = simple_format build[:script] + .table-holder + %table.table.table-bordered + %thead + %tr + %th Parameter + %th Value + %tbody + - @stages.each do |stage| + - @builds.select { |build| build[:stage] == stage }.each do |build| + %tr + %td #{stage.capitalize} Job - #{build[:name]} + %td + %pre + = simple_format build[:commands] - %br - %b Tag list: - = build[:tags] - %br - %b Refs only: - = build[:only] && build[:only].join(", ") - %br - %b Refs except: - = build[:except] && build[:except].join(", ") + %br + %b Tag list: + = build[:tags] + %br + %b Refs only: + = build[:only] && build[:only].join(", ") + %br + %b Refs except: + = build[:except] && build[:except].join(", ") + %br + %b When: + = build[:when] + - if build[:allow_failure] + %b Allowed to fail -else %p diff --git a/app/views/ci/projects/index.html.haml b/app/views/ci/projects/index.html.haml new file mode 100644 index 00000000000..9c2290bc4a5 --- /dev/null +++ b/app/views/ci/projects/index.html.haml @@ -0,0 +1,20 @@ +.wiki + %h1 + GitLab CI is now integrated in GitLab UI + %h2 For existing projects + + %p + Check the following pages to find the CI status you're looking for: + + %ul + %li Projects page - shows CI status for each project. + %li Project commits page - show CI status for each commit. + + + + %h2 For new projects + + %p + If you want to enable CI for a new project it is easy as adding + = link_to ".gitlab-ci.yml", "http://doc.gitlab.com/ce/ci/yaml/README.html" + file to your repository diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml index 308b217ea78..b8d9a1d7089 100644 --- a/app/views/ci/user_sessions/new.html.haml +++ b/app/views/ci/user_sessions/new.html.haml @@ -1,8 +1,7 @@ .login-block %h2 Login using GitLab account %p.light - Make sure you have account on GitLab server + Make sure you have an account on the GitLab server = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink %hr = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' ) - diff --git a/app/views/dashboard/milestones/_issue.html.haml b/app/views/dashboard/milestones/_issue.html.haml index f689b9698eb..1408ebdd5dc 100644 --- a/app/views/dashboard/milestones/_issue.html.haml +++ b/app/views/dashboard/milestones/_issue.html.haml @@ -7,4 +7,4 @@ = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .pull-right.assignee-icon - if issue.assignee - = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" + = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_merge_request.html.haml b/app/views/dashboard/milestones/_merge_request.html.haml index 8f5c4cce529..77c46de030b 100644 --- a/app/views/dashboard/milestones/_merge_request.html.haml +++ b/app/views/dashboard/milestones/_merge_request.html.haml @@ -7,4 +7,4 @@ = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16" + = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 0d204ced7ea..2fe14c6388c 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -13,26 +13,28 @@ %span All issues for this milestone are closed. You may close the milestone now. .description -%table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @dashboard_milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at + +.table-holder + %table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - @dashboard_milestone.milestones.each do |milestone| + %tr + %td + = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) + %td + = milestone.issues.opened.count + %td + - if milestone.closed? + Closed + - else + Open + %td + = milestone.expires_at .context %p.lead @@ -79,7 +81,7 @@ - @dashboard_milestone.participants.each do |user| %li = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user.email, 32), class: "avatar s32" + = image_tag avatar_icon(user, 32), class: "avatar s32" %strong= truncate(user.name, lenght: 40) %br %small.cgray= user.username diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index d0194a17b01..81a5909e2d2 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -5,6 +5,7 @@ - if current_user.can_create_project? %span.input-group-btn = link_to new_project_path, class: 'btn btn-green' do - New project + %i.fa.fa-plus + New Project = render 'shared/projects/list', projects: @projects, ci: true diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index d3908062f43..07b6d57932e 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -6,33 +6,29 @@ .gray-content-block .pull-right = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet + = icon('plus') + New Snippet - .oneline - Share code pastes with others out of git repository - -%ul.nav.nav-tabs.prepend-top-20 - = nav_tab :scope, nil do - = link_to dashboard_snippets_path do + .btn-group.btn-group-next.snippet-scope-menu + = link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do All %span.badge = current_user.snippets.count - = nav_tab :scope, 'are_private' do - = link_to dashboard_snippets_path(scope: 'are_private') do + + = link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do Private %span.badge = current_user.snippets.are_private.count - = nav_tab :scope, 'are_internal' do - = link_to dashboard_snippets_path(scope: 'are_internal') do + + = link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do Internal %span.badge = current_user.snippets.are_internal.count - = nav_tab :scope, 'are_public' do - = link_to dashboard_snippets_path(scope: 'are_public') do + + = link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do Public %span.badge = current_user.snippets.are_public.count -.my-snippets - = render 'snippets/snippets' += render 'snippets/snippets' diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml index 3b0b19107ca..ba4c5b86efb 100644 --- a/app/views/doorkeeper/applications/index.html.haml +++ b/app/views/doorkeeper/applications/index.html.haml @@ -1,17 +1,19 @@ - page_title "Applications" %h3.page-title Your applications %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' -%table.table.table-striped - %thead - %tr - %th Name - %th Callback URL - %th - %th - %tbody - - @applications.each do |application| - %tr{:id => "application_#{application.id}"} - %td= link_to application.name, oauth_application_path(application) - %td= application.redirect_uri - %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link' - %td= render 'delete_form', application: application + +.table-holder + %table.table.table-striped + %thead + %tr + %th Name + %th Callback URL + %th + %th + %tbody + - @applications.each do |application| + %tr{:id => "application_#{application.id}"} + %td= link_to application.name, oauth_application_path(application) + %td= application.redirect_uri + %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link' + %td= render 'delete_form', application: application diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index 80340aca54c..47442b78d48 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -2,26 +2,26 @@ %h3.page-title Application: #{@application.name} +.table-holder + %table.table + %tr + %td + Application Id + %td + %code#application_id= @application.uid + %tr + %td + Secret: + %td + %code#secret= @application.secret -%table.table - %tr - %td - Application Id - %td - %code#application_id= @application.uid - %tr - %td - Secret: - %td - %code#secret= @application.secret - - %tr - %td - Callback url - %td - - @application.redirect_uri.split.each do |uri| - %div - %span.monospace= uri + %tr + %td + Callback url + %td + - @application.redirect_uri.split.each do |uri| + %div + %span.monospace= uri .form-actions = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml index 814cdc987ef..b184b9c01d4 100644 --- a/app/views/doorkeeper/authorized_applications/index.html.haml +++ b/app/views/doorkeeper/authorized_applications/index.html.haml @@ -1,16 +1,17 @@ %header.page-header %h1 Your authorized applications %main{:role => "main"} - %table.table.table-striped - %thead - %tr - %th Application - %th Created At - %th - %th - %tbody - - @applications.each do |application| + .table-holder + %table.table.table-striped + %thead %tr - %td= application.name - %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S') - %td= render 'delete_form', application: application
\ No newline at end of file + %th Application + %th Created At + %th + %th + %tbody + - @applications.each do |application| + %tr + %td= application.name + %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S') + %td= render 'delete_form', application: application diff --git a/app/views/events/_event_issue.atom.haml b/app/views/events/_event_issue.atom.haml index 4259f64c191..fad65310021 100644 --- a/app/views/events/_event_issue.atom.haml +++ b/app/views/events/_event_issue.atom.haml @@ -1,3 +1,2 @@ %div{xmlns: "http://www.w3.org/1999/xhtml"} - - if issue.description.present? - = markdown(issue.description, xhtml: true, reference_only_path: false, project: issue.project) + = markdown(issue.description, pipeline: :atom, project: issue.project) diff --git a/app/views/events/_event_merge_request.atom.haml b/app/views/events/_event_merge_request.atom.haml index e8ed13df783..19bdc7b9ca5 100644 --- a/app/views/events/_event_merge_request.atom.haml +++ b/app/views/events/_event_merge_request.atom.haml @@ -1,3 +1,2 @@ %div{xmlns: "http://www.w3.org/1999/xhtml"} - - if merge_request.description.present? - = markdown(merge_request.description, xhtml: true, reference_only_path: false, project: merge_request.project) + = markdown(merge_request.description, pipeline: :atom, project: merge_request.project) diff --git a/app/views/events/_event_note.atom.haml b/app/views/events/_event_note.atom.haml index cfbfba50202..b730ebbd5f9 100644 --- a/app/views/events/_event_note.atom.haml +++ b/app/views/events/_event_note.atom.haml @@ -1,2 +1,2 @@ %div{xmlns: "http://www.w3.org/1999/xhtml"} - = markdown(note.note, xhtml: true, reference_only_path: false, project: note.project) + = markdown(note.note, pipeline: :atom, project: note.project) diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml index 3625cb49d8b..b271b9daff1 100644 --- a/app/views/events/_event_push.atom.haml +++ b/app/views/events/_event_push.atom.haml @@ -6,7 +6,7 @@ %i at = commit[:timestamp].to_time.to_s(:short) - %blockquote= markdown(escape_once(commit[:message]), xhtml: true, reference_only_path: false, project: event.project) + %blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project) - if event.commits_count > 15 %p %i diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index 7e4fa7d4873..0f100c39ffb 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -10,7 +10,8 @@ - if current_user .pull-right = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet + = icon('plus') + New Snippet .oneline Public snippets created by you and other users are listed here diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 133f3e2d5a8..11d69977ef9 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -5,6 +5,7 @@ - if can? current_user, :create_projects, @group %span.input-group-btn = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do - New project + %i.fa.fa-plus + New Project = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index b5f359279d5..3c19381321a 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -5,7 +5,7 @@ %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %span{class: ("list-item-name" if show_controls)} - if member.user - = image_tag avatar_icon(user.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml index 09f9b4b8969..9b85d83d6d8 100644 --- a/app/views/groups/milestones/_issue.html.haml +++ b/app/views/groups/milestones/_issue.html.haml @@ -7,4 +7,4 @@ = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .pull-right.assignee-icon - if issue.assignee - = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml index d0d1426762b..e3aa4aad198 100644 --- a/app/views/groups/milestones/_merge_request.html.haml +++ b/app/views/groups/milestones/_merge_request.html.haml @@ -7,4 +7,4 @@ = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 0c213f42186..a92ad5d751b 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -21,26 +21,28 @@ %span All issues for this milestone are closed. You may close the milestone now. .description -%table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @group_milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at + +.table-holder + %table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - @group_milestone.milestones.each do |milestone| + %tr + %td + = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) + %td + = milestone.issues.opened.count + %td + - if milestone.closed? + Closed + - else + Open + %td + = milestone.expires_at .context %p.lead @@ -87,7 +89,7 @@ - @group_milestone.participants.each do |user| %li = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user.email, 32), class: "avatar s32" + = image_tag avatar_icon(user, 32), class: "avatar s32" %strong= truncate(user.name, lenght: 40) %br %small.cgray= user.username diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index e809d99ba71..67349fcbd78 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -102,6 +102,12 @@ %tr %td.shortcut .key g + .key b + %td + Go to builds + %tr + %td.shortcut + .key g .key n %td Go to network graph diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 777eb482714..30bcdb86827 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -14,45 +14,46 @@ = button_tag 'Import all projects', class: "btn btn-success js-import-all" -%table.table.import-jobs - %thead - %tr - %th From Bitbucket - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank" - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From Bitbucket + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} - %td - = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" - %td.import-target - = "#{repo["owner"]}/#{repo["slug"]}" - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" - - @incompatible_repos.each do |repo| - %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} - %td - = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" - %td.import-target - %td.import-actions-job-status - = label_tag "Incompatible Project", nil, class: "label label-danger" + - @repos.each do |repo| + %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} + %td + = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" + %td.import-target + = "#{repo["owner"]}/#{repo["slug"]}" + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" + - @incompatible_repos.each do |repo| + %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"} + %td + = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank" + %td.import-target + %td.import-actions-job-status + = label_tag "Incompatible Project", nil, class: "label label-danger" - if @incompatible_repos.any? %p diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index 25cebfb3665..a701e49ac56 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -25,22 +25,23 @@ of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also associate and/or assign these issues and comments with the selected user. - %table.table - %thead - %tr - %th ID - %th Name - %th Email - %th GitLab User - %tbody - - @user_map.each do |id, user| + .table-holder + %table.table + %thead %tr - %td= id - %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control' - %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control' - %td - = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control', - scope: :all, email_user: true, selected: user[:gitlab_user]) + %th ID + %th Name + %th Email + %th GitLab User + %tbody + - @user_map.each do |id, user| + %tr + %td= id + %td= text_field_tag "users[#{id}][name]", user[:name], class: 'form-control' + %td= text_field_tag "users[#{id}][email]", user[:email], class: 'form-control' + %td + = users_select_tag("users[#{id}][gitlab_user]", class: 'custom-form-control', + scope: :all, email_user: true, selected: user[:gitlab_user]) .form-actions = submit_tag 'Continue to the next step', class: 'btn btn-create' diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index f179ece402d..beca6ab1423 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -14,38 +14,39 @@ %p = button_tag 'Import all projects', class: 'btn btn-success js-import-all' -%table.table.import-jobs - %thead - %tr - %th From FogBugz - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = project.import_source - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From FogBugz + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = project.import_source + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = repo.name - %td.import-target - = "#{current_user.username}/#{repo.name}" - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = repo.name + %td.import-target + = "#{current_user.username}/#{repo.name}" + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" :coffeescript new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}") diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index ef552498239..0669b05adca 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -9,38 +9,39 @@ %p = button_tag 'Import all projects', class: "btn btn-success js-import-all" -%table.table.import-jobs - %thead - %tr - %th From GitHub - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank" - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From GitHub + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank" - %td.import-target - = repo.full_name - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank" + %td.import-target + = repo.full_name + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" :coffeescript new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}") diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 727f3c7e7fa..3bc85059e7d 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -9,38 +9,39 @@ %p = button_tag 'Import all projects', class: "btn btn-success js-import-all" -%table.table.import-jobs - %thead - %tr - %th From GitLab.com - %th To this GitLab instance - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From GitLab.com + %th To this GitLab instance + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo["id"]}"} - %td - = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" - %td.import-target - = repo["path_with_namespace"] - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + - @repos.each do |repo| + %tr{id: "repo_#{repo["id"]}"} + %td + = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank" + %td.import-target + = repo["path_with_namespace"] + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" :coffeescript new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}") diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index bff7ee7c85d..2e3a535737f 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -9,38 +9,39 @@ %p = button_tag 'Import all projects', class: "btn btn-success js-import-all" -%table.table.import-jobs - %thead - %tr - %th From Gitorious.org - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank" - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From Gitorious.org + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank" - %td.import-target - = repo.full_name - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank" + %td.import-target + = repo.full_name + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" :coffeescript new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}") diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index e8ec79e72f7..c5af06edf87 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -17,45 +17,46 @@ - else = button_tag 'Import all projects', class: "btn btn-success js-import-all" -%table.table.import-jobs - %thead - %tr - %th From Google Code - %th To GitLab - %th Status - %tbody - - @already_added_projects.each do |project| - %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} - %td - = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank" - %td - %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] - %td.job-status - - if project.import_status == 'finished' - %span - %i.fa.fa-check - done - - elsif project.import_status == 'started' - %i.fa.fa-spinner.fa-spin - started - - else - = project.human_import_status_name +.table-holder + %table.table.import-jobs + %thead + %tr + %th From Google Code + %th To GitLab + %th Status + %tbody + - @already_added_projects.each do |project| + %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} + %td + = link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank" + %td + %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] + %td.job-status + - if project.import_status == 'finished' + %span + %i.fa.fa-check + done + - elsif project.import_status == 'started' + %i.fa.fa-spinner.fa-spin + started + - else + = project.human_import_status_name - - @repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" - %td.import-target - = "#{current_user.username}/#{repo.name}" - %td.import-actions.job-status - = button_tag "Import", class: "btn js-add-to-import" - - @incompatible_repos.each do |repo| - %tr{id: "repo_#{repo.id}"} - %td - = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" - %td.import-target - %td.import-actions-job-status - = label_tag "Incompatible Project", nil, class: "label label-danger" + - @repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" + %td.import-target + = "#{current_user.username}/#{repo.name}" + %td.import-actions.job-status + = button_tag "Import", class: "btn js-add-to-import" + - @incompatible_repos.each do |repo| + %tr{id: "repo_#{repo.id}"} + %td + = link_to repo.name, "https://code.google.com/p/#{repo.name}", target: "_blank" + %td.import-target + %td.import-actions-job-status + = label_tag "Incompatible Project", nil, class: "label label-danger" - if @incompatible_repos.any? %p diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 1a883e20e89..352b8040cf4 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -18,7 +18,7 @@ = render partial: 'layouts/collapse_button' - if current_user = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36' + = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index ceb64ce3157..d1aa8f62463 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -11,6 +11,8 @@ = hidden_field_tag :scope, 'merge_requests' - elsif current_controller?(:wikis) = hidden_field_tag :scope, 'wiki_blobs' + - elsif current_controller?(:commits) + = hidden_field_tag :scope, 'commits' - else = hidden_field_tag :search_code, true diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml index bb5ec727bff..ab3e29c3f42 100644 --- a/app/views/layouts/ci/_page.html.haml +++ b/app/views/layouts/ci/_page.html.haml @@ -15,7 +15,7 @@ = render partial: 'layouts/collapse_button' - if current_user = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user.email, 60), alt: 'User activity', class: 'avatar avatar s36' + = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml deleted file mode 100644 index 15478c3f5bc..00000000000 --- a/app/views/layouts/ci/project.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title @project.name, ci_project_path(@project) - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page', sidebar: 'nav_project' diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index eb35af22b93..319352876b4 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index 8075fe32fbc..c8411521f36 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to group_path(@group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to group + Go to group %li.separate-item diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 5a47b8e6db2..0f3a793e30b 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index e4c285d8023..20db2866d1f 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,16 +1,16 @@ %ul.nav.nav-sidebar - if @project.group = nav_link do - = link_to group_path(@project.group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to group + Go to group - else = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item @@ -32,12 +32,20 @@ Files - if project_nav_tab? :commits - = nav_link(controller: %w(commit commits compare repositories tags branches builds)) do + = nav_link(controller: %w(commit commits compare repositories tags branches)) do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do = icon('history fw') %span Commits + - if project_nav_tab? :builds + = nav_link(controller: %w(builds)) do + = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds', data: {placement: 'right'} do + = icon('cubes fw') + %span + Builds + %span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all) + - if project_nav_tab? :network = nav_link(controller: %w(network)) do = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 954dbe5d2b9..a59939ccd31 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do + = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to project + Go to project %li.separate-item @@ -65,8 +65,3 @@ = icon('share fw') %span CI Services - = nav_link path: 'events#index' do - = link_to ci_project_events_path(@project.gitlab_ci_project) do - = icon('book fw') - %span - CI Events diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 2f7d7e86f56..854cda57c39 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -41,4 +41,8 @@ #{link_to "view it on GitLab", @target_url}. - else #{link_to "View it on GitLab", @target_url} + %br + You're receiving this email because of your account on #{link_to Gitlab.config.gitlab.host, root_url}. + If you'd like to receive fewer emails, you can adjust your notification settings. + = email_action @target_url diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 3fd4b04ac84..00cb4aa24cc 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,2 +1,2 @@ %div - = markdown(@note.note, reference_only_path: false) + = markdown(@note.note, pipeline: :email) diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index 53a068be52e..d3b799fca23 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,5 +1,5 @@ -if @issue.description - = markdown(@issue.description, reference_only_path: false) + = markdown(@issue.description, pipeline: :email) - if @issue.assignee_id.present? %p diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 5b7dd117c16..90ebdfc3fe2 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -6,4 +6,4 @@ Assignee: #{@merge_request.author_name} → #{@merge_request.assignee_name} -if @merge_request.description - = markdown(@merge_request.description, reference_only_path: false) + = markdown(@merge_request.description, pipeline: :email) diff --git a/app/views/profiles/_event_table.html.haml b/app/views/profiles/_event_table.html.haml index c19ac429d52..58af79716a7 100644 --- a/app/views/profiles/_event_table.html.haml +++ b/app/views/profiles/_event_table.html.haml @@ -1,16 +1,17 @@ -%table.table#audits - %thead - %tr - %th Action - %th When - - %tbody - - events.each do |event| +.table-holder + %table.table#audits + %thead %tr - %td - %span - Signed in with - %b= event.details[:with] - authentication - %td #{time_ago_in_words event.created_at} ago + %th Action + %th When + + %tbody + - events.each do |event| + %tr + %td + %span + Signed in with + %b= event.details[:with] + authentication + %td #{time_ago_in_words event.created_at} ago = paginate events, theme: "gitlab" diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 01e285a8dfa..cc41d7dd813 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -38,7 +38,7 @@ .col-sm-10 = f.select :layout, layout_choices, {}, class: 'form-control' .help-block - Choose between fixed (max. 1200px) and fluid (100%) application layout + Choose between fixed (max. 1200px) and fluid (100%) application layout. .form-group = f.label :dashboard, class: 'control-label' do Default Dashboard @@ -52,6 +52,6 @@ .col-sm-10 = f.select :project_view, project_view_choices, {}, class: 'form-control' .help-block - Choose what content you want to see when visit project page + Choose what content you want to see on a project's home page. .panel-footer = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 47412e2ef0c..ac7355dde1f 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -68,7 +68,7 @@ .col-md-5 .light-well - = image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160' + = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160' .clearfix .profile-avatar-form-option diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index c2683bc6219..012858f70b4 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -1,4 +1,3 @@ -= render 'projects/last_push' .gray-content-block.activity-filter-block - if current_user .pull-right diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml new file mode 100644 index 00000000000..fa978325ddd --- /dev/null +++ b/app/views/projects/_files.html.haml @@ -0,0 +1,6 @@ +#tree-holder.tree-holder.clearfix + .gray-content-block.second-block + = render 'projects/tree/tree_header', tree: @tree + + = render 'projects/tree/tree_content', tree: @tree + diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml new file mode 100644 index 00000000000..d7b20bfc6b1 --- /dev/null +++ b/app/views/projects/_last_commit.html.haml @@ -0,0 +1,12 @@ +.project-last-commit + - ci_commit = project.ci_commit(commit.sha) + - if ci_commit + = link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do + = ci_status_icon(ci_commit) + = ci_commit.status + + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" + = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" + · + #{time_ago_with_tooltip(commit.committed_date, skip_js: true)} by + = commit_author_link(commit, avatar: true, size: 24) diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 507757f6a2b..7b21095ea3e 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -2,10 +2,10 @@ .md-header.clearfix %ul.center-top-menu %li.active - = link_to '#md-write-holder', class: 'js-md-write-button', tabindex: '-1' do + %a.js-md-write-button(href="#md-write-holder" tabindex="-1") Write %li - = link_to '#md-preview-holder', class: 'js-md-preview-button', tabindex: '-1' do + %a.js-md-preview-button(href="md-preview-holder" tabindex="-1") Preview - if defined?(referenced_users) && referenced_users diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index 5bc1999ec9d..b5ef0aca540 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -1,12 +1,9 @@ - if readme = @repository.readme - %article.readme-holder#README - .clearfix - .pull-right - - - if can?(current_user, :push_code, @project) - = link_to namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light' do - %i.fa-align.fa.fa-pencil - .wiki + %article.readme-holder + .pull-right + - if can?(current_user, :push_code, @project) + = link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme' + .file-content.wiki = cache(readme_cache_key) do = render_readme(readme) - else diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml index 6a41cdbc907..63ebfc9381f 100644 --- a/app/views/projects/_zen.html.haml +++ b/app/views/projects/_zen.html.haml @@ -1,10 +1,10 @@ .zennable - %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' } + %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") .zen-backdrop - classes << ' js-gfm-input markdown-area' = f.text_area attr, class: classes, placeholder: '' - = link_to nil, class: 'zen-enter-link', tabindex: '-1' do + %a.zen-enter-link(tabindex="-1" href="#") %i.fa.fa-expand Edit in fullscreen - = link_to nil, class: 'zen-leave-link' do + %a.zen-leave-link(href="#") %i.fa.fa-compress diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index 555ed76426d..69fa4ad37c4 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1,4 +1,6 @@ - page_title "Activity" - header_title project_title(@project, "Activity", activity_project_path(@project)) += render 'projects/last_push' + = render 'projects/activity' diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index a1ae1397584..42f632b38ef 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -1,19 +1,22 @@ -%ul.breadcrumb.repo-breadcrumb - %li - %i.fa.fa-angle-right - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do - = @project.path - - tree_breadcrumbs(@tree, 6) do |title, path| +.gray-content-block.top-block + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'blob', path: @path + + %ul.breadcrumb.repo-breadcrumb %li - - if path - - if path.end_with?(@path) - = link_to namespace_project_blob_path(@project.namespace, @project, path) do - %strong - = truncate(title, length: 40) + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = @project.path + - tree_breadcrumbs(@tree, 6) do |title, path| + %li + - if path + - if path.end_with?(@path) + = link_to namespace_project_blob_path(@project.namespace, @project, path) do + %strong + = truncate(title, length: 40) + - else + = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - else - = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - - else - = link_to title, '#' + = link_to title, '#' %ul.blob-commit-info.hidden-xs - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index fa4be4a1bc4..f52b89f6921 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -3,9 +3,6 @@ = render 'projects/last_push' -%div.tree-ref-holder - = render 'shared/ref_switcher', destination: 'blob', path: @path - %div#tree-holder.tree-holder = render 'blob', blob: @blob diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml new file mode 100644 index 00000000000..4ce4ed63b40 --- /dev/null +++ b/app/views/projects/builds/_build.html.haml @@ -0,0 +1,53 @@ +%tr.build + %td.status + = ci_status_with_icon(build.status) + + %td.commit_status-link + - if build.target_url + = link_to build.target_url do + %strong Build ##{build.id} + - else + %strong Build ##{build.id} + + - if build.show_warning? + %i.fa.fa-warning.text-warning + + %td + = link_to build.short_sha, namespace_project_commit_path(@project.namespace, @project, build.sha) + + %td + = link_to build.ref, namespace_project_commits_path(@project.namespace, @project, build.ref) + + %td + - if build.runner + = runner_link(build.runner) + - else + .light none + + %td + = build.name + + .pull-right + - if build.tags.any? + - build.tags.each do |tag| + %span.label.label-primary + = tag + - if build.trigger_request + %span.label.label-info triggered + - if build.allow_failure + %span.label.label-danger allowed to fail + + %td.duration + - if build.duration + #{duration_in_words(build.finished_at, build.started_at)} + + %td.timestamp + - if build.finished_at + %span #{time_ago_in_words build.finished_at} ago + + %td + .pull-right + - if current_user && can?(current_user, :manage_builds, @project) + - if build.cancel_url + = link_to build.cancel_url, title: 'Cancel' do + %i.fa.fa-remove.cred diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml new file mode 100644 index 00000000000..e08556673ed --- /dev/null +++ b/app/views/projects/builds/index.html.haml @@ -0,0 +1,53 @@ +- page_title "Builds" +- header_title project_title(@project, "Builds", project_builds_path(@project)) + +.project-issuable-filter + .controls + - if @ci_project && current_user && can?(current_user, :manage_builds, @project) + .pull-left.hidden-xs + - if @all_builds.running_or_pending.any? + = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' + + %ul.center-top-menu + %li{class: ('active' if @scope.nil?)} + = link_to project_builds_path(@project) do + Running + %span.badge.js-running-count= @all_builds.running_or_pending.count(:id) + + %li{class: ('active' if @scope == 'finished')} + = link_to project_builds_path(@project, scope: :finished) do + Finished + %span.badge.js-running-count= @all_builds.finished.count(:id) + + %li{class: ('active' if @scope == 'all')} + = link_to project_builds_path(@project, scope: :all) do + All + %span.badge.js-totalbuilds-count= @all_builds.count(:id) + +.gray-content-block + List of #{@scope || 'running'} builds from this project + +%ul.content-list + - if @builds.blank? + %li + .nothing-here-block No builds to show + - else + .table-holder + %table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Commit + %th Ref + %th Runner + %th Name + %th Duration + %th Finished at + %th + + - @builds.each do |build| + = render 'projects/builds/build', build: build + + = paginate @builds + diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 9c3ae622b72..e3d8d734913 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -25,7 +25,7 @@ %a Build ##{@build.id} · - %i.fa.fa-warning-sign + %i.fa.fa-warning This build was retried. .gray-content-block.second-block @@ -39,6 +39,25 @@ .pull-right = @build.updated_at.stamp('19:00 Aug 27') + - if @build.show_warning? + - unless @build.any_runners_online? + .bs-callout.bs-callout-warning + %p + - if no_runners_for_project?(@build.project) + This build is stuck, because the project doesn't have any runners online assigned to it. + - elsif @build.tags.any? + This build is stuck, because you don't have any active runners online with any of these tags assigned to them: + - @build.tags.each do |tag| + %span.label.label-primary + = tag + - else + This build is stuck, because you don't have any active runners that can run this build. + + %br + Go to + = link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do + Runners page + .row.prepend-top-default .col-md-9 .clearfix @@ -136,7 +155,7 @@ - if @builds.present? .build-widget - %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}: + %h4.title #{pluralize(@builds.count(:id), "other build")} for #{@build.short_sha}: %table.table.builds - @builds.each_with_index do |build, i| %tr.build @@ -156,4 +175,4 @@ :javascript - new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}") + new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}") diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 4580c912692..bed2b16249e 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -21,6 +21,10 @@ - if can?(current_user, :push_code, @project) %li.divider %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do + = icon('file fw') + New file + %li = link_to new_namespace_project_branch_path(@project.namespace, @project) do = icon('code-fork fw') New branch diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 3bc2daeec4e..3e83ec3912f 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,14 +1,20 @@ -- return unless @membership +- case @membership +- when ProjectMember + = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do + = hidden_field_tag :notification_type, 'project' + = hidden_field_tag :notification_id, @membership.id + = hidden_field_tag :notification_level + %span.dropdown + %a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"} + = icon('bell') + = notification_label(@membership) + = icon('angle-down') + %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown + - Notification.project_notification_levels.each do |level| + = notification_list_item(level, @membership) -= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do - = hidden_field_tag :notification_type, 'project' - = hidden_field_tag :notification_id, @membership.id - = hidden_field_tag :notification_level - %span.dropdown - %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} - = icon('bell') - = notification_label(@membership) - = icon('angle-down') - %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - - Notification.project_notification_levels.each do |level| - = notification_list_item(level, @membership) +- when GroupMember + .btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} + = icon('bell') + = notification_label(@membership) + = icon('angle-down') diff --git a/app/views/projects/ci_services/index.html.haml b/app/views/projects/ci_services/index.html.haml index c78b21884a3..c164b2d4bc0 100644 --- a/app/views/projects/ci_services/index.html.haml +++ b/app/views/projects/ci_services/index.html.haml @@ -6,7 +6,7 @@ %tr %th %th Service - %th Desription + %th Description %th Last edit - @services.sort_by(&:title).each do |service| %tr diff --git a/app/views/projects/ci_settings/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml index 33038c52978..1374e6680f9 100644 --- a/app/views/projects/ci_settings/_no_runners.html.haml +++ b/app/views/projects/ci_settings/_no_runners.html.haml @@ -5,4 +5,4 @@ You can add Specific runner for this project on Runners page - if current_user.admin - or add Shared runner for whole application in admin are. + or add Shared runner for whole application in admin area. diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml index 6aebd7cfc4d..369086b39ed 100644 --- a/app/views/projects/ci_web_hooks/index.html.haml +++ b/app/views/projects/ci_web_hooks/index.html.haml @@ -20,17 +20,18 @@ -if @web_hooks.any? %h4 Activated web hooks (#{@web_hooks.count}) - %table.table - - @web_hooks.each do |hook| - %tr - %td - .clearfix - %span.monospace= hook.url - %td - .pull-right - - if @ci_project.commits.any? - = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" + .table-holder + %table.table + - @web_hooks.each do |hook| + %tr + %td + .clearfix + %span.monospace= hook.url + %td + .pull-right + - if @ci_project.commits.any? + = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" + = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" %h4 Web Hook data example diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index 4a1ef378a30..43033cad24c 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -3,11 +3,6 @@ = render "commit_box" = render "ci_menu" -- if @ci_project && current_user && can?(current_user, :manage_builds, @project) - .pull-right - - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-sm btn-danger' - - if @ci_commit.yaml_errors.present? .bs-callout.bs-callout-danger @@ -22,31 +17,19 @@ .gray-content-block.second-block Latest builds - - if @ci_commit.duration > 0 - %small.pull-right + + .pull-right + - if @ci_commit.duration > 0 %i.fa.fa-time #{time_interval_in_words @ci_commit.duration} -%table.table.builds - %thead - %tr - %th Status - %th Build ID - %th Ref - %th Stage - %th Name - %th Duration - %th Finished at - - if @ci_project && @ci_project.coverage_enabled? - %th Coverage - %th - - @ci_commit.refs.each do |ref| - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, coverage: @ci_project.try(:coverage_enabled?), controls: true + -- if @ci_commit.retried.any? - .gray-content-block.second-block - Retried builds + - if @ci_project && current_user && can?(current_user, :manage_builds, @project) + - if @ci_commit.builds.running_or_pending.any? + = link_to "Cancel all", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger' +.table-holder %table.table.builds %thead %tr @@ -60,4 +43,27 @@ - if @ci_project && @ci_project.coverage_enabled? %th Coverage %th - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?) + - @ci_commit.refs.each do |ref| + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, + locals: { coverage: @ci_project.try(:coverage_enabled?), allow_retry: true } + +- if @ci_commit.retried.any? + .gray-content-block.second-block + Retried builds + + .table-holder + %table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Ref + %th Stage + %th Name + %th Duration + %th Finished at + - if @ci_project && @ci_project.coverage_enabled? + %th Coverage + %th + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, + locals: { coverage: @ci_project.try(:coverage_enabled?) } diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index e3a17faf0bd..637154f56aa 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -9,6 +9,9 @@ - else %strong Build ##{commit_status.id} + - if commit_status.show_warning? + %i.fa.fa-warning.text-warning + %td = commit_status.ref @@ -41,11 +44,11 @@ #{commit_status.coverage}% %td - - if defined?(controls) && controls && current_user && can?(current_user, :manage_builds, gl_project) - .pull-right + .pull-right + - if current_user && can?(current_user, :manage_builds, commit_status.gl_project) - if commit_status.cancel_url = link_to commit_status.cancel_url, title: 'Cancel' do %i.fa.fa-remove.cred - - elsif commit_status.retry_url + - elsif defined?(allow_retry) && allow_retry && commit_status.retry_url = link_to commit_status.retry_url, method: :post, title: 'Retry' do %i.fa.fa-repeat diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index cddd5aa3a83..805be332e64 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -18,10 +18,10 @@ .pull-right - if ci_commit - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do - = ci_status_icon(ci_commit) + = render_ci_status(ci_commit) - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" + = clipboard_button + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id} .notes_count - if note_count > 0 diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 4f1965bfb39..56b51f038ba 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -15,8 +15,8 @@ .files - diff_files.each_with_index do |diff_file, index| - - diff_commit = commit_for_diff(diff_file.diff) - - blob = project.repository.blob_for_diff(diff_commit, diff_file.diff) + - diff_commit = commit_for_diff(diff_file) + - blob = project.repository.blob_for_diff(diff_commit, diff_file) - next unless blob = render 'projects/diffs/file', i: index, project: project, diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 9698921f6da..410ff6abb43 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,5 +1,5 @@ .diff-file{id: "diff-#{i}", data: diff_file_html_data(project, diff_commit, diff_file)} - .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"} + .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"} - if diff_file.diff.submodule? %span - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) @@ -38,7 +38,7 @@ - else = render "projects/diffs/text_file", diff_file: diff_file, index: i - elsif blob.image? - - old_file = project.repository.prev_blob_for_diff(@commit, diff_file) + - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file) = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i - else .nothing-here-block No preview for this file type diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 1882a82fba5..afbf88b5507 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -189,6 +189,21 @@ - else .nothing-here-block Only the project owner can transfer a project + - if @project.forked? + - if can?(current_user, :remove_fork_project, @project) + = form_for([@project.namespace.becomes(Namespace), @project], url: remove_fork_namespace_project_path(@project.namespace, @project), method: :delete, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f| + .panel.panel-default.panel.panel-danger + .panel-heading Remove fork relationship + .panel-body + %p + This will remove the fork relationship to source project + #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}. + %br + %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. + = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } + - else + .nothing-here-block Only the project owner can remove the fork relationship. + - if can?(current_user, :remove_project, @project) .panel.panel-default.panel.panel-danger .panel-heading Remove project @@ -201,7 +216,8 @@ = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } - else - .nothing-here-block Only project owner can remove a project + .nothing-here-block Only the project owner can remove a project. + .save-project-loader.hide .center diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index e06454fd148..c3858e78cad 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -2,53 +2,56 @@ - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' = render 'shared/no_password' - + = render "home_panel" .gray-content-block.center %h3.page-title The repository for this project is empty - %p - If you already have files you can push them using command line instructions below. - %br - Otherwise you can start with - = link_to "adding README", new_readme_path, class: 'underlined-link' - file to this project. + - if can?(current_user, :download_code, @project) + %p + If you already have files you can push them using command line instructions below. + %br + - if can?(current_user, :push_code, @project) + Otherwise you can start with + = link_to "adding README", new_readme_path, class: 'underlined-link' + file to this project. -.prepend-top-20 -.empty_wrapper - %h3.page-title-empty - Command line instructions - %div.git-empty - %fieldset - %h5 Git global setup - %pre.light-well - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" +- if can?(current_user, :download_code, @project) + .prepend-top-20 + .empty_wrapper + %h3.page-title-empty + Command line instructions + %div.git-empty + %fieldset + %h5 Git global setup + %pre.light-well + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" - %fieldset - %h5 Create a new repository - %pre.light-well - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} - cd #{h @project.path} - touch README.md - git add README.md - git commit -m "add README" - git push -u origin master + %fieldset + %h5 Create a new repository + %pre.light-well + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} + cd #{h @project.path} + touch README.md + git add README.md + git commit -m "add README" + git push -u origin master - %fieldset - %h5 Existing folder or Git repository - %pre.light-well - :preserve - cd existing_folder - git init - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git add . - git commit - git push -u origin master + %fieldset + %h5 Existing folder or Git repository + %pre.light-well + :preserve + cd existing_folder + git init + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git add . + git commit + git push -u origin master - - if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" + - if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index f8f2e192e29..92a87690c54 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -17,6 +17,6 @@ This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. %br The import will time out after 4 minutes. For big repositories, use a clone/push combination. - For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"} + For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"} .form-actions = f.submit 'Start import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml new file mode 100644 index 00000000000..aef352029d0 --- /dev/null +++ b/app/views/projects/issues/_closed_by_box.html.haml @@ -0,0 +1,3 @@ +.issue-closed-by-widget + = icon('check') + This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted. diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index d4a98eca473..c5fd863ae99 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -17,8 +17,10 @@ - @participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) .col-md-3 - %span.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @issue) + .input-group.cross-project-reference + %span.slead.has_tooltip{title: 'Cross-project reference'} + = cross_project_reference(@project, @issue) + = clipboard_button .row %section.col-md-9 diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index a3399c57aa2..ca5b1a8386d 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -5,8 +5,9 @@ .nothing-here-block No issues to show - if @issues.present? - .pull-right - %span.issue_counter #{@issues.total_count} - issues for this filter + .issuable-filter-count + %span.pull-right + = @issues.total_count + issues for this filter = paginate @issues, theme: "gitlab" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 5cb814c9ea8..f01bf2505da 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -46,6 +46,7 @@ = markdown(@issue.description) %textarea.hidden.js-task-list-field = @issue.description - + - if @closed_by_merge_requests.present? + = render 'projects/issues/closed_by_box' .issue-discussion = render 'projects/issues/discussion' diff --git a/app/views/projects/labels/destroy.js.haml b/app/views/projects/labels/destroy.js.haml index 1b4c83ab097..d59563b122a 100644 --- a/app/views/projects/labels/destroy.js.haml +++ b/app/views/projects/labels/destroy.js.haml @@ -1,2 +1,2 @@ - if @project.labels.size == 0 - $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000) + $('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 97175f8232b..fb784ee5f4f 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -14,8 +14,8 @@ = render @labels = paginate @labels, theme: 'gitlab' - else - .light-well + .nothing-here-block - if can? current_user, :admin_label, @project - .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels + Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels - else - .nothing-here-block No labels created + No labels created diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 25e4e8ba80d..300a3715292 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,3 +1,4 @@ +- ci_commit = merge_request.ci_commit %li{ class: mr_css_classes(merge_request) } .merge-request-title %span.merge-request-title-text @@ -6,6 +7,8 @@ - merge_request.labels.each do |label| = link_to_label(label, project: merge_request.project) .pull-right.light + - if ci_commit + = render_ci_status(ci_commit) - if merge_request.merged? %span %i.fa.fa-check diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml index d86707b3d97..0af970e4b92 100644 --- a/app/views/projects/merge_requests/_merge_requests.html.haml +++ b/app/views/projects/merge_requests/_merge_requests.html.haml @@ -5,8 +5,10 @@ .nothing-here-block No merge requests to show - if @merge_requests.present? - .pull-right - %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter + .issuable-filter-count + %span.pull-right + = @merge_requests.total_count + merge requests for this filter = paginate @merge_requests, theme: "gitlab" diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index e7ac7a0eaa4..eeaa72ed21b 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -36,7 +36,8 @@ - if @merge_request.open? && @merge_request.can_be_merged? .light.append-bottom-20 You can also accept this merge request manually using the - = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" + = succeed '.' do + = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - if @commits.present? %ul.merge-request-tabs diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index f18cf96c17d..98f0357ce4e 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -3,11 +3,12 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3 Check out, review and merge locally + %h3 Check out, review, and merge locally .modal-body %p - %strong Step 1. + %strong Step 1. Fetch and check out the branch for this merge request + = clipboard_button %pre.dark - if @merge_request.for_fork? :preserve @@ -24,6 +25,7 @@ %p %strong Step 3. Merge the branch and fix any conflicts that come up + = clipboard_button %pre.dark - if @merge_request.for_fork? :preserve @@ -36,6 +38,7 @@ %p %strong Step 4. Push the result of the merge to GitLab + = clipboard_button %pre.dark :preserve git push origin #{h @merge_request.target_branch} diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 68dda1424cf..a3551516bfe 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,44 +1,43 @@ -- if @merge_request.has_ci? - - ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha) - - if ci_commit - - status = ci_commit.status - .mr-widget-heading - .ci_widget{class: "ci-#{status}"} - = ci_status_icon(ci_commit) +- ci_commit = @merge_request.ci_commit +- if ci_commit + - status = ci_commit.status + .mr-widget-heading + .ci_widget{class: "ci-#{status}"} + = ci_status_icon(ci_commit) + %span CI build #{status} + for #{@merge_request.last_commit_short_sha}. + %span.ci-coverage + = link_to "View build details", ci_status_path(ci_commit) + +- elsif @merge_request.has_ci? + - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX + - # Remove in later versions when services like Jenkins will set CI status via Commit status API + .mr-widget-heading + - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status| + .ci_widget{class: "ci-#{status}", style: "display:none"} + - if status == :success + - status = "passed" + = icon("check-circle") + - else + = icon("circle") %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_status_path(ci_commit) - - - else - - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX - - # Remove in later versions when services like Jenkins will set CI status via Commit status API - .mr-widget-heading - - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status| - .ci_widget{class: "ci-#{status}", style: "display:none"} - - if status == :success - - status = "passed" - = icon("check-circle") - - else - = icon("circle") - %span CI build #{status} - for #{@merge_request.last_commit_short_sha}. - %span.ci-coverage - - if ci_build_details_path(@merge_request) - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + - if ci_build_details_path(@merge_request) + = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" - .ci_widget - = icon("spinner spin") - Checking CI status for #{@merge_request.last_commit_short_sha}… + .ci_widget + = icon("spinner spin") + Checking CI status for #{@merge_request.last_commit_short_sha}… - .ci_widget.ci-not_found{style: "display:none"} - = icon("times-circle") - Could not find CI status for #{@merge_request.last_commit_short_sha}. + .ci_widget.ci-not_found{style: "display:none"} + = icon("times-circle") + Could not find CI status for #{@merge_request.last_commit_short_sha}. - .ci_widget.ci-error{style: "display:none"} - = icon("times-circle") - Could not connect to the CI server. Please check your settings and try again. + .ci_widget.ci-error{style: "display:none"} + = icon("times-circle") + Could not connect to the CI server. Please check your settings and try again. - :coffeescript - $ -> - merge_request_widget.getCiStatus() + :coffeescript + $ -> + merge_request_widget.getCiStatus() diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml index 88fccfe4981..133d802aaca 100644 --- a/app/views/projects/milestones/_issue.html.haml +++ b/app/views/projects/milestones/_issue.html.haml @@ -1,7 +1,7 @@ %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } .pull-right.assignee-icon - if issue.assignee - = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: '' %span = link_to [@project.namespace.becomes(Namespace), @project, issue] do %span.cgray ##{issue.iid} diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml index 0d7a118569a..a1033607c5d 100644 --- a/app/views/projects/milestones/_merge_request.html.haml +++ b/app/views/projects/milestones/_merge_request.html.haml @@ -5,4 +5,4 @@ = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 4eeb0621e52..3a898dfbcfd 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -104,7 +104,7 @@ - @users.each do |user| %li = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user.email, 32), class: "avatar s32" + = image_tag avatar_icon(user, 32), class: "avatar s32" %strong= truncate(user.name, lenght: 40) %br %small.cgray= user.username diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 1638ad6891a..5d184730796 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -1,8 +1,8 @@ %li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } } .timeline-entry-inner .timeline-icon - = link_to user_path(note.author) do - = image_tag avatar_icon(note.author_email), class: 'avatar s40', alt: '' + %a{href: user_path(note.author)} + %img.avatar.s40{src: avatar_icon(note.author), alt: ''} .timeline-content .note-header - if note_editable?(note) @@ -25,7 +25,7 @@ = '@' + note.author.username %span.note-last-update - = link_to "##{dom_id(note)}", name: dom_id(note), title: "Link here" do + %a{name: dom_id(note), href: "##{dom_id(note)}", title: 'Link here'} = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note_created_ago') - if note.updated_at != note.created_at %span diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml index 860a997cff8..76c46d1d806 100644 --- a/app/views/projects/project_members/_project_member.html.haml +++ b/app/views/projects/project_members/_project_member.html.haml @@ -4,7 +4,7 @@ %li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)} %span.list-item-name - if member.user - = image_tag avatar_icon(user.email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml index bb49f4de873..f68449b1863 100644 --- a/app/views/projects/protected_branches/_branches_list.html.haml +++ b/app/views/projects/protected_branches/_branches_list.html.haml @@ -1,34 +1,35 @@ - unless @branches.empty? %br %h4 Already Protected: - %table.table.protected-branches-list - %thead - %tr.no-border - %th Branch - %th Developers can push - %th Last commit - %th + .table-holder + %table.table.protected-branches-list + %thead + %tr.no-border + %th Branch + %th Developers can push + %th Last commit + %th - %tbody - - @branches.each do |branch| - - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch) - %tr - %td - = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do - %strong= branch.name - - if @project.root_ref?(branch.name) - %span.label.label-info default + %tbody + - @branches.each do |branch| + - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch) + %tr %td - = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url - %td - - if commit = branch.commit - = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do - = commit.short_id - · - #{time_ago_with_tooltip(commit.committed_date)} - - else - (branch was removed from repository) - %td - .pull-right - - if can? current_user, :admin_project, @project - = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm" + = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do + %strong= branch.name + - if @project.root_ref?(branch.name) + %span.label.label-info default + %td + = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url + %td + - if commit = branch.commit + = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do + = commit.short_id + · + #{time_ago_with_tooltip(commit.committed_date)} + - else + (branch was removed from repository) + %td + .pull-right + - if can? current_user, :admin_project, @project + = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-sm" diff --git a/app/views/projects/remove_fork.js.haml b/app/views/projects/remove_fork.js.haml new file mode 100644 index 00000000000..17b9fecfeb1 --- /dev/null +++ b/app/views/projects/remove_fork.js.haml @@ -0,0 +1,2 @@ +:plain + location.href = "#{edit_namespace_project_path(@project.namespace, @project)}"; diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml index ffec495f85a..c255cd51bd2 100644 --- a/app/views/projects/runners/show.html.haml +++ b/app/views/projects/runners/show.html.haml @@ -9,56 +9,57 @@ %span.runner-state.runner-state-specific Specific -%table.table - %thead +.table-holder + %table.table + %thead + %tr + %th Property Name + %th Value %tr - %th Property Name - %th Value - %tr - %td - Tags - %td - - @runner.tag_list.each do |tag| - %span.label.label-primary - = tag - %tr - %td - Name - %td - = @runner.name - %tr - %td - Version - %td - = @runner.version - %tr - %td - Revision - %td - = @runner.revision - %tr - %td - Platform - %td - = @runner.platform - %tr - %td - Architecture - %td - = @runner.architecture - %tr - %td - Description - %td - = @runner.description - %tr - %td - Last contact - %td - - if @runner.contacted_at - #{time_ago_in_words(@runner.contacted_at)} ago - - else - Never + %td + Tags + %td + - @runner.tag_list.each do |tag| + %span.label.label-primary + = tag + %tr + %td + Name + %td + = @runner.name + %tr + %td + Version + %td + = @runner.version + %tr + %td + Revision + %td + = @runner.revision + %tr + %td + Platform + %td + = @runner.platform + %tr + %td + Architecture + %td + = @runner.architecture + %tr + %td + Description + %td + = @runner.description + %tr + %td + Last contact + %td + - if @runner.contacted_at + #{time_ago_in_words(@runner.contacted_at)} ago + - else + Never - + diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index 1065def693b..c1356f6db02 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -2,22 +2,23 @@ %h3.page-title Project services %p.light Project services allow you to integrate GitLab with other applications -%table.table - %thead - %tr - %th - %th Service - %th Description - %th Last edit - - @services.sort_by(&:title).each do |service| - %tr - %td - = boolean_to_icon service.activated? - %td - = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do - %strong= service.title - %td - = service.description - %td.light - = time_ago_in_words service.updated_at - ago +.table-holder + %table.table + %thead + %tr + %th + %th Service + %th Description + %th Last edit + - @services.sort_by(&:title).each do |service| + %tr + %td + = boolean_to_icon service.activated? + %td + = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do + %strong= service.title + %td + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index e95d987d74c..585caf674c9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -7,8 +7,7 @@ = render 'shared/no_ssh' = render 'shared/no_password' -- if prefer_readme? - = render 'projects/last_push' += render 'projects/last_push' = render "home_panel" @@ -28,7 +27,7 @@ = link_to project_files_path(@project) do = repository_size - - if !prefer_readme? && @repository.readme + - if default_project_view != 'readme' && @repository.readme %li = link_to 'Readme', readme_path(@project) @@ -64,14 +63,12 @@ = icon("exclamation-triangle fw") Archived project! Repository is read-only -%section - - if prefer_readme? - .project-show-readme - = render 'projects/readme' - - else - .project-show-activity - = render 'projects/activity' +- if @repository.commit + .content-block.second-block.white + = render 'projects/last_commit', commit: @repository.commit, project: @project +%div{class: "project-show-#{default_project_view}"} + = render default_project_view - if current_user - access = user_max_access_in_project(current_user, @project) diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml new file mode 100644 index 00000000000..4a515469422 --- /dev/null +++ b/app/views/projects/snippets/_actions.html.haml @@ -0,0 +1,11 @@ += link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do + = icon('plus') + New Snippet +- if can?(current_user, :admin_project_snippet, @snippet) + = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do + = icon('trash-o') + Delete +- if can?(current_user, :update_project_snippet, @snippet) + = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do + = icon('pencil-square-o') + Edit diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 3fed2c9949d..4af963e14da 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,17 +1,13 @@ - page_title "Snippets" = render "header_title" -%h3.page-title - Snippets - - if can? current_user, :create_project_snippet, @project - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do - Add new snippet +.gray-content-block.top-block + .pull-right + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do + = icon('plus') + New Snippet -%p.light - Share code pastes with others out of git repository + .oneline + Share code pastes with others out of git repository -%ul.bordered-list - = render partial: "shared/snippets/snippet", collection: @snippets - - if @snippets.empty? - %li - .nothing-here-block Nothing here. += render 'snippets/snippets' diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index be7d4d486fa..5d706942f2d 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,40 +1,18 @@ - page_title @snippet.title, "Snippets" = render "header_title" -%h3.page-title - = @snippet.title +.snippet-holder + = render 'shared/snippets/header' - .pull-right - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do - Add new snippet + %article.file-holder + .file-title + = blob_icon 0, @snippet.file_name + %strong + = @snippet.file_name + .file-actions.hidden-xs + .btn-group.tree-btn-group + = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" -%hr + = render 'shared/snippets/blob' -.append-bottom-20 - .pull-right - = "##{@snippet.id}" - %span.light - by - = link_to user_path(@snippet.author) do - = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" - = @snippet.author_name - - .back-link - = link_to namespace_project_snippets_path(@project.namespace, @project) do - ← project snippets - -.file-holder - .file-title - %i.fa.fa-file - %strong - = @snippet.file_name - .file-actions - .btn-group - - if can?(current_user, :update_project_snippet, @snippet) - = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet' - = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" - - if can?(current_user, :admin_project_snippet, @snippet) - = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet' - = render 'shared/snippets/blob' - -%div#notes= render "projects/notes/notes_with_form" + %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml index 02ecbade219..2ddc5d504fa 100644 --- a/app/views/projects/tree/_blob_item.html.haml +++ b/app/views/projects/tree/_blob_item.html.haml @@ -4,5 +4,5 @@ %span.str-truncated = link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name)) %td.tree_time_ago.cgray - = render 'spinner' + = render 'projects/tree/spinner' %td.hidden-xs.tree_commit diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml index 7e9af19c8ba..3c5edf4b033 100644 --- a/app/views/projects/tree/_readme.html.haml +++ b/app/views/projects/tree/_readme.html.haml @@ -1,8 +1,8 @@ -%article.file-holder.readme-holder#README +%article.file-holder.readme-holder .file-title - = link_to '#README' do + = blob_icon readme.mode, readme.name + = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do %strong - %i.fa.fa-file = readme.name .file-content.wiki = render_readme(readme) diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree_content.html.haml index 7ff48e32e60..ee4c9d1693d 100644 --- a/app/views/projects/tree/_tree.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -1,36 +1,5 @@ -.gray-content-block - %ul.breadcrumb.repo-breadcrumb - %li - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do - = @project.path - - tree_breadcrumbs(tree, 6) do |title, path| - %li - - if path - = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - - else - = link_to title, '#' - - if allowed_tree_edit? - %li - %span.dropdown - %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} - = icon('plus') - %ul.dropdown-menu - %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do - = icon('pencil fw') - Create file - %li - = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do - = icon('file fw') - Upload file - %li.divider - %li - = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do - = icon('folder fw') - New directory - -%div#tree-content-holder.tree-content-holder - .tree-table-holder +%div.tree-content-holder + .table-holder %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" } %thead %tr @@ -60,8 +29,6 @@ - if tree.readme = render "projects/tree/readme", readme: tree.readme -%div.tree_progress - - if allowed_tree_edit? = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post = render 'projects/blob/new_dir' diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml new file mode 100644 index 00000000000..1115ca6b4ca --- /dev/null +++ b/app/views/projects/tree/_tree_header.html.haml @@ -0,0 +1,32 @@ +.tree-ref-holder + = render 'shared/ref_switcher', destination: 'tree', path: @path + +%ul.breadcrumb.repo-breadcrumb + %li + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = @project.path + - tree_breadcrumbs(tree, 6) do |title, path| + %li + - if path + = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) + - else + = link_to title, '#' + - if allowed_tree_edit? + %li + %span.dropdown + %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} + = icon('plus') + %ul.dropdown-menu + %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do + = icon('pencil fw') + Create file + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do + = icon('file fw') + Upload file + %li.divider + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do + = icon('folder fw') + New directory diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml index e87138bf980..cf65057e704 100644 --- a/app/views/projects/tree/_tree_item.html.haml +++ b/app/views/projects/tree/_tree_item.html.haml @@ -5,5 +5,5 @@ - path = flatten_tree(tree_item) = link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path)) %td.tree_time_ago.cgray - = render 'spinner' + = render 'projects/tree/spinner' %td.hidden-xs.tree_commit diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index dec4677f830..ec14bd7f65a 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -6,12 +6,12 @@ = render 'projects/last_push' -.tree-ref-holder - = render 'shared/ref_switcher', destination: 'tree', path: @path - - if can? current_user, :download_code, @project .tree-download-holder = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true #tree-holder.tree-holder.clearfix - = render "tree", tree: @tree + .gray-content-block.top-block + = render 'projects/tree/tree_header', tree: @tree + + = render 'projects/tree/tree_content', tree: @tree diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index 17dcb78e256..18a37302c3e 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -7,12 +7,13 @@ %hr.clearfix -if @triggers.any? - %table.table - %thead - %th Token - %th Last used - %th - = render partial: 'trigger', collection: @triggers, as: :trigger + .table-holder + %table.table + %thead + %th Token + %th Last used + %th + = render partial: 'trigger', collection: @triggers, as: :trigger - else %h4 No triggers diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index bfbef823b35..4322146ce34 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -7,28 +7,29 @@ %span.light History for = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page) -%table.table - %thead - %tr - %th Page version - %th Author - %th Commit Message - %th Last updated - %th Format - %tbody - - @page.versions.each_with_index do |version, index| - - commit = version +.table-holder + %table.table + %thead %tr - %td - = link_to project_wiki_path_with_version(@project, @page, - commit.id, index == 0) do - = truncate_sha(commit.id) - %td - = commit.author.name - %td - = commit.message - %td - #{time_ago_with_tooltip(version.authored_date)} - %td - %strong - = @page.page.wiki.page(@page.page.name, commit.id).try(:format) + %th Page version + %th Author + %th Commit Message + %th Last updated + %th Format + %tbody + - @page.versions.each_with_index do |version, index| + - commit = version + %tr + %td + = link_to project_wiki_path_with_version(@project, @page, + commit.id, index == 0) do + = truncate_sha(commit.id) + %td + = commit.author.name + %td + = commit.message + %td + #{time_ago_with_tooltip(version.authored_date)} + %td + %strong + = @page.page.wiki.page(@page.page.name, commit.id).try(:format) diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index d637abfa76b..481451edb23 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -42,6 +42,13 @@ Wiki %span.badge = @search_results.wiki_blobs_count + %li{class: ("active" if @scope == 'commits')} + = link_to search_filter_path(scope: 'commits') do + = icon('history fw') + %span + Commits + %span.badge + = @search_results.commits_count - elsif @show_snippets %li{class: ("active" if @scope == 'snippet_blobs')} diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml new file mode 100644 index 00000000000..4e6c3965dc6 --- /dev/null +++ b/app/views/search/results/_commit.html.haml @@ -0,0 +1,2 @@ +.search-result-row + = render 'projects/commits/commit', project: @project, commit: commit diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index b23b2f0d5eb..2e4aab36301 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -6,7 +6,7 @@ type: 'button', | class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | :"data-clone" => project.ssh_url_to_repo, | - :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH", + :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH.", :"data-html" => "true", :"data-container" => "body"} SSH @@ -15,7 +15,7 @@ type: 'button', | class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | :"data-clone" => project.http_url_to_repo, | - :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}", + :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}.", :"data-html" => "true", :"data-container" => "body"} = gitlab_config.protocol.upcase diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg new file mode 100644 index 00000000000..da49c48acd3 --- /dev/null +++ b/app/views/shared/_logo.svg @@ -0,0 +1,21 @@ +<svg width="36px" height="36px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="tanuki-logo"> + <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> + <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)"> + <g id="Page-1" sketch:type="MSShapeGroup"> + <g id="Fill-1-+-Group-24"> + <g id="Group-24"> + <g id="Group"> + <path d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" id="Fill-4" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-8" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" id="Fill-12" fill="#FCA326" class="tanuki-shape"></path> + <path d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" id="Fill-16" fill="#E24329" class="tanuki-shape"></path> + <path d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" id="Fill-18" fill="#FC6D26" class="tanuki-shape"></path> + <path d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" id="Fill-20" fill="#FCA326" class="tanuki-shape"></path> + <path d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" id="Fill-22" fill="#E24329" class="tanuki-shape"></path> + </g> + </g> + </g> + </g> + </g> + </g> +</svg> diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 8f16773077e..0e4e9c0987a 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -42,11 +42,10 @@ class: 'select2 trigger-submit', include_blank: true, data: {placeholder: 'Milestone'}) - - if @project - .filter-item.inline.labels-filter - = select_tag('label_name', project_labels_options(@project), - class: 'select2 trigger-submit', include_blank: true, - data: {placeholder: 'Label'}) + .filter-item.inline.labels-filter + = select_tag('label_name', projects_labels_options, + class: 'select2 trigger-submit', include_blank: true, + data: {placeholder: 'Label'}) .pull-right = render 'shared/sort_dropdown' diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index aee839b44e7..c36995b94d7 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -21,9 +21,7 @@ .project-controls - if ci && !project.empty_repo? && project.commit - if ci_commit = project.ci_commit(project.commit.sha) - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do - = ci_status_icon(ci_commit) + = render_ci_status(ci_commit) - if stars %span diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml new file mode 100644 index 00000000000..0a4a790ec5e --- /dev/null +++ b/app/views/shared/snippets/_header.html.haml @@ -0,0 +1,24 @@ +.snippet-details + .page-title + .snippet-box{class: visibility_level_color(@snippet.visibility_level)} + = visibility_level_icon(@snippet.visibility_level) + = visibility_level_label(@snippet.visibility_level) + %span.snippet-id Snippet ##{@snippet.id} + %span.creator + · created by #{link_to_member(@project, @snippet.author, size: 24)} + · + = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') + - if @snippet.updated_at != @snippet.created_at + %span + · + = icon('edit', title: 'edited') + = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') + + .pull-right + - if @snippet.project_id? + = render "projects/snippets/actions" + - else + = render "snippets/actions" + .gray-content-block.middle-block + %h2.snippet-title + = gfm escape_once(@snippet.title) diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 69a713ad9aa..c6294caddc7 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -18,4 +18,3 @@ = image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: '' = snippet.author_name authored #{time_ago_with_tooltip(snippet.created_at)} - diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml new file mode 100644 index 00000000000..1979ae6d5bc --- /dev/null +++ b/app/views/snippets/_actions.html.haml @@ -0,0 +1,11 @@ += link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do + = icon('plus') + New Snippet +- if can?(current_user, :update_personal_snippet, @snippet) + = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do + = icon('pencil-square-o') + Edit +- if can?(current_user, :admin_personal_snippet, @snippet) + = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do + = icon('trash-o') + Delete diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 97374e073dc..69d8899d4c1 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,41 +1,14 @@ - page_title @snippet.title, "Snippets" -%h4.page-title - = @snippet.title - - if @snippet.private? - %span.label.label-success - %i.fa.fa-lock - private - - .pull-right - = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do - Add new snippet - -.append-bottom-10.prepend-top-10 - .pull-right - %span.light - created by - = link_to user_snippets_path(@snippet.author) do - = @snippet.author_name - - .back-link - - if @snippet.author == current_user - = link_to dashboard_snippets_path do - ← your snippets - - else - = link_to explore_snippets_path do - ← explore snippets - -.file-holder - .file-title - %i.fa.fa-file - %strong - = @snippet.file_name - .file-actions - .btn-group - - if can?(current_user, :update_personal_snippet, @snippet) - = link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet' - = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" - - if can?(current_user, :admin_personal_snippet, @snippet) - = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet' - = render 'shared/snippets/blob' +.snippet-holder + = render 'shared/snippets/header' + + %article.file-holder + .file-title + = blob_icon 0, @snippet.file_name + %strong + = @snippet.file_name + .file-actions.hidden-xs + .btn-group.tree-btn-group + = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" + = render 'shared/snippets/blob' diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml deleted file mode 100644 index a126a858ea8..00000000000 --- a/app/views/users/_projects.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present? - .panel.panel-default.contributed-projects - .panel-heading Projects contributed to - = render 'shared/projects/list', - projects: contributed_projects.sort_by(&:star_count).reverse, - projects_limit: 5, stars: true, avatar: false - -- if local_assigns.has_key?(:projects) && projects.present? - .panel.panel-default - .panel-heading Personal projects - = render 'shared/projects/list', - projects: projects.sort_by(&:star_count).reverse, - projects_limit: 10, stars: true, avatar: false diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 922b0c6cebf..7f29918dba3 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,7 +1,3 @@ -%h4 - Contributions calendar - .pull-right - %small Issues, merge requests and push events #cal-heatmap.calendar :javascript new Calendar( @@ -10,3 +6,5 @@ #{@starting_month}, '#{user_calendar_activities_path}' ); + +.calendar-hint Summary of issues, merge requests and push events diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 11beb3e3239..e22d93aae84 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -6,61 +6,114 @@ = render 'shared/show_aside' -.row - %section.col-md-7 - .header-with-avatar - = link_to avatar_icon(@user.email, 400), target: '_blank' do - = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: '' - %h3 - = @user.name - - if @user == current_user - .pull-right.hidden-xs - = link_to profile_path, class: 'btn btn-sm' do - = icon('user') - Profile settings - - elsif current_user - .report_abuse.pull-right - - if @user.abuse_report - %span#report_abuse_btn.light.btn.btn-sm.btn-close{title: 'Already reported for abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}} - = icon('exclamation-circle') - - else - %a.light.btn.btn-sm{href: new_abuse_report_path(user_id: @user.id), title: 'Report abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}} - = icon('exclamation-circle') +.cover-block + .avatar-holder + = link_to avatar_icon(@user, 400), target: '_blank' do + = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: '' + .cover-title + = @user.name - .username - @#{@user.username} - .description - - if @user.bio.present? - = @user.bio + .cover-desc + %span + @#{@user.username}. + - if @user.bio.present? + %span + #{@user.bio}. + %span + Member since #{@user.created_at.stamp("Aug 21, 2011")} - .clearfix + .cover-desc + - unless @user.public_email.blank? + .profile-link-holder + = link_to @user.public_email, "mailto:#{@user.public_email}" + - unless @user.skype.blank? + .profile-link-holder + = link_to "skype:#{@user.skype}", title: "Skype" do + = icon('skype') + - unless @user.linkedin.blank? + .profile-link-holder + = link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do + = icon('linkedin-square') + - unless @user.twitter.blank? + .profile-link-holder + = link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do + = icon('twitter-square') + - unless @user.website_url.blank? + .profile-link-holder + = link_to @user.short_website_url, @user.full_website_url + - unless @user.location.blank? + .profile-link-holder + = icon('map-marker') + = @user.location - - if @groups.any? - .prepend-top-20 - %h4 Groups - = render 'groups', groups: @groups - %hr - .hidden-xs - .user-calendar - %h4.center.light - %i.fa.fa-spinner.fa-spin - .user-calendar-activities - %hr - %h4 - User Activity + .cover-controls + - if @user == current_user + = link_to profile_path, class: 'btn btn-gray' do + = icon('pencil') + - elsif current_user + %span.report-abuse + - if @user.abuse_report + %button.btn.btn-danger{ title: 'Already reported for abuse', + data: { toggle: 'tooltip', placement: 'left', container: 'body' }} + = icon('exclamation-circle') + - else + = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray', + title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do + = icon('exclamation-circle') + - if current_user + + = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do + = icon('rss') - - if current_user - %span.rss-icon.pull-right - = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do - %strong - %i.fa.fa-rss +.gray-content-block.second-block + .user-calendar + %h4.center.light + %i.fa.fa-spinner.fa-spin + .user-calendar-activities + +%ul.nav.center-middle-menu + %li.active + = link_to "#activity", 'data-toggle' => 'tab' do + Activity + - if @groups.any? + %li + = link_to "#groups", 'data-toggle' => 'tab' do + Groups + - if @contributed_projects.present? + %li + = link_to "#contributed", 'data-toggle' => 'tab' do + Contributed projects + - if @projects.present? + %li + = link_to "#personal", 'data-toggle' => 'tab' do + Personal projects + +.tab-content + .tab-pane.active#activity .content_list = spinner - %aside.col-md-5 - = render 'profile', user: @user - = render 'projects', projects: @projects, contributed_projects: @contributed_projects + + - if @groups.any? + .tab-pane#groups + %ul.content-list + - @groups.each do |group| + = render 'shared/groups/group', group: group + + - if @contributed_projects.present? + .tab-pane#contributed + .contributed-projects + = render 'shared/projects/list', + projects: @contributed_projects.sort_by(&:star_count).reverse, + projects_limit: 5, stars: true, avatar: false + + - if @projects.present? + .tab-pane#personal + .personal-projects + = render 'shared/projects/list', + projects: @projects.sort_by(&:star_count).reverse, + projects_limit: 10, stars: true, avatar: false :coffeescript $(".user-calendar").load("#{user_calendar_path}") diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb new file mode 100644 index 00000000000..47c5a670ed4 --- /dev/null +++ b/app/workers/repository_archive_cache_worker.rb @@ -0,0 +1,9 @@ +class RepositoryArchiveCacheWorker + include Sidekiq::Worker + + sidekiq_options queue: :default + + def perform + Repository.clean_old_archives + end +end diff --git a/app/workers/repository_archive_worker.rb b/app/workers/repository_archive_worker.rb deleted file mode 100644 index 021c1139568..00000000000 --- a/app/workers/repository_archive_worker.rb +++ /dev/null @@ -1,43 +0,0 @@ -class RepositoryArchiveWorker - include Sidekiq::Worker - - sidekiq_options queue: :archive_repo - - attr_accessor :project, :ref, :format - - def perform(project_id, ref, format) - @project = Project.find(project_id) - @ref, @format = ref, format.downcase - - repository = project.repository - - repository.clean_old_archives - - return unless file_path - return if archived? || archiving? - - repository.archive_repo(ref, storage_path, format) - end - - private - - def storage_path - Gitlab.config.gitlab.repository_downloads_path - end - - def file_path - @file_path ||= project.repository.archive_file_path(ref, storage_path, format) - end - - def pid_file_path - @pid_file_path ||= project.repository.archive_pid_file_path(ref, storage_path, format) - end - - def archived? - File.exist?(file_path) - end - - def archiving? - File.exist?(pid_file_path) - end -end diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb new file mode 100644 index 00000000000..ad02a3b16d9 --- /dev/null +++ b/app/workers/stuck_ci_builds_worker.rb @@ -0,0 +1,18 @@ +class StuckCiBuildsWorker + include Sidekiq::Worker + include Sidetiq::Schedulable + + BUILD_STUCK_TIMEOUT = 1.day + + recurrence { daily } + + def perform + Rails.logger.info 'Cleaning stuck builds' + + builds = Ci::Build.running_or_pending.where('updated_at < ?', BUILD_STUCK_TIMEOUT.ago) + builds.find_each(batch_size: 50).each do |build| + Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}" + build.drop + end + end +end |