summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG7
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/api.js.coffee3
-rw-r--r--app/assets/javascripts/application.js.coffee72
-rw-r--r--app/assets/javascripts/issuable_context.js.coffee14
-rw-r--r--app/assets/javascripts/project_select.js.coffee3
-rw-r--r--app/assets/stylesheets/framework/buttons.scss2
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss2
-rw-r--r--app/assets/stylesheets/framework/mobile.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss31
-rw-r--r--app/assets/stylesheets/framework/variables.scss19
-rw-r--r--app/assets/stylesheets/pages/explore.scss8
-rw-r--r--app/assets/stylesheets/pages/issuable.scss117
-rw-r--r--app/controllers/dashboard/projects_controller.rb1
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb9
-rw-r--r--app/helpers/application_helper.rb70
-rw-r--r--app/helpers/explore_helper.rb13
-rw-r--r--app/helpers/nav_helper.rb23
-rw-r--r--app/helpers/projects_helper.rb12
-rw-r--r--app/helpers/search_helper.rb4
-rw-r--r--app/models/merge_request.rb5
-rw-r--r--app/views/admin/dashboard/index.html.haml5
-rw-r--r--app/views/dashboard/_projects_head.html.haml3
-rw-r--r--app/views/dashboard/milestones/index.html.haml8
-rw-r--r--app/views/explore/projects/_dropdown.html.haml12
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/explore/projects/_nav.html.haml10
-rw-r--r--app/views/explore/projects/index.html.haml6
-rw-r--r--app/views/explore/projects/starred.html.haml12
-rw-r--r--app/views/explore/projects/trending.html.haml10
-rw-r--r--app/views/groups/milestones/index.html.haml16
-rw-r--r--app/views/help/ui.html.haml24
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml51
-rw-r--r--app/views/projects/builds/index.html.haml23
-rw-r--r--app/views/projects/issues/show.html.haml7
-rw-r--r--app/views/projects/issues/update.js.haml6
-rw-r--r--app/views/projects/labels/index.html.haml13
-rw-r--r--app/views/projects/merge_requests/_show.html.haml7
-rw-r--r--app/views/projects/merge_requests/update.js.haml4
-rw-r--r--app/views/projects/milestones/index.html.haml12
-rw-r--r--app/views/projects/wikis/_nav.html.haml18
-rw-r--r--app/views/shared/_milestones_filter.html.haml21
-rw-r--r--app/views/shared/_new_project_item_select.html.haml2
-rw-r--r--app/views/shared/issuable/_participants.html.haml4
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml190
-rw-r--r--config/newrelic.yml16
-rw-r--r--db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb80
-rw-r--r--db/schema.rb2
-rw-r--r--doc/customization/issue_closing.md2
-rw-r--r--features/steps/project/builds/summary.rb2
-rw-r--r--lib/banzai/filter/sanitization_filter.rb9
-rw-r--r--lib/banzai/pipeline/description_pipeline.rb13
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/database.rb38
-rw-r--r--lib/gitlab/regex.rb4
-rw-r--r--spec/controllers/projects_controller_spec.rb8
-rw-r--r--spec/features/builds_spec.rb8
-rw-r--r--spec/helpers/search_helper_spec.rb4
-rw-r--r--spec/lib/banzai/filter/sanitization_filter_spec.rb22
-rw-r--r--spec/lib/banzai/pipeline/description_pipeline_spec.rb37
-rw-r--r--spec/lib/gitlab/database_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb13
64 files changed, 853 insertions, 316 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 5f081236c10..b7e8822fdd6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,8 @@ v 8.5.0 (unreleased)
- Don't vendor minified JS
- Display 404 error on group not found
- Track project import failure
+ - Support Two-factor Authentication for LDAP users
+ - Display database type and version in Administration dashboard
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
@@ -24,9 +26,13 @@ v 8.5.0 (unreleased)
- Fix API to keep request parameters in Link header (Michael Potthoff)
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
+ - Prevent parse error when name of project ends with .atom and prevent path issues
- Mark inline difference between old and new paths when a file is renamed
- Support Akismet spam checking for creation of issues via API (Stan Hu)
- Improve UI consistency between projects and groups lists
+ - Add sort dropdown to dashboard projects page
+ - Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
+ - In seach autocomplete show only groups and projects you are member of
v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger
@@ -179,6 +185,7 @@ v 8.3.0
- Handle and report SSL errors in Web hook test (Stan Hu)
- Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu)
- Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
+ - WIP identifier on merge requests no longer requires trailing space
- Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
- Fix 500 error when update group member permission
- Fix: As an admin, cannot add oneself as a member to a group/project
diff --git a/Gemfile b/Gemfile
index a06dbe8e061..ff119a6d687 100644
--- a/Gemfile
+++ b/Gemfile
@@ -303,8 +303,6 @@ group :production do
gem "gitlab_meta", '7.0'
end
-gem "newrelic_rpm", '~> 3.14'
-
gem 'octokit', '~> 3.8.0'
gem "mail_room", "~> 0.6.1"
diff --git a/Gemfile.lock b/Gemfile.lock
index bd767016108..a7a5db29e35 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -479,7 +479,6 @@ GEM
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
- newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nprogress-rails (0.1.6.7)
@@ -960,7 +959,6 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
- newrelic_rpm (~> 3.14)
nokogiri (= 1.6.7.2)
nprogress-rails (~> 0.1.6.7)
oauth2 (~> 1.0.0)
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 746fa3cea87..3e0fdb3f795 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -47,7 +47,7 @@
callback(namespaces)
# Return projects list. Filtered by query
- projects: (query, callback) ->
+ projects: (query, order, callback) ->
url = Api.buildUrl(Api.projects_path)
$.ajax(
@@ -55,6 +55,7 @@
data:
private_token: gon.api_token
search: query
+ order_by: order
per_page: 20
dataType: "json"
).done (projects) ->
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index d5e6ff0717a..e54bfce058a 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -215,4 +215,76 @@ $ ->
$this = $(this)
$this.attr 'value', $this.val()
+ $(document).on 'breakpoint:change', (e, breakpoint) ->
+ if breakpoint is 'sm' or breakpoint is 'xs'
+ $gutterIcon = $('.gutter-toggle').find('i')
+ if $gutterIcon.hasClass('fa-angle-double-right')
+ $gutterIcon.closest('a').trigger('click')
+
+
+ $(document).on 'click', 'aside .gutter-toggle', (e) ->
+ e.preventDefault()
+ $this = $(this)
+ $thisIcon = $this.find 'i'
+ if $thisIcon.hasClass('fa-angle-double-right')
+ $thisIcon.removeClass('fa-angle-double-right')
+ .addClass('fa-angle-double-left')
+ $this
+ .closest('aside')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed')
+ $('.page-with-sidebar')
+ .removeClass('right-sidebar-expanded')
+ .addClass('right-sidebar-collapsed')
+ else
+ $thisIcon.removeClass('fa-angle-double-left')
+ .addClass('fa-angle-double-right')
+ $this
+ .closest('aside')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded')
+ $('.page-with-sidebar')
+ .removeClass('right-sidebar-collapsed')
+ .addClass('right-sidebar-expanded')
+ $.cookie("collapsed_gutter",
+ $('.right-sidebar')
+ .hasClass('right-sidebar-collapsed'), { path: '/' })
+
+ bootstrapBreakpoint = undefined;
+ checkBootstrapBreakpoints = ->
+ if $('.device-xs').is(':visible')
+ bootstrapBreakpoint = "xs"
+ else if $('.device-sm').is(':visible')
+ bootstrapBreakpoint = "sm"
+ else if $('.device-md').is(':visible')
+ bootstrapBreakpoint = "md"
+ else if $('.device-lg').is(':visible')
+ bootstrapBreakpoint = "lg"
+
+ setBootstrapBreakpoints = ->
+ if $('.device-xs').length
+ return
+
+ $("body")
+ .append('<div class="device-xs visible-xs"></div>'+
+ '<div class="device-sm visible-sm"></div>'+
+ '<div class="device-md visible-md"></div>'+
+ '<div class="device-lg visible-lg"></div>')
+ checkBootstrapBreakpoints()
+
+ fitSidebarForSize = ->
+ oldBootstrapBreakpoint = bootstrapBreakpoint
+ checkBootstrapBreakpoints()
+ if bootstrapBreakpoint != oldBootstrapBreakpoint
+ $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+
+ checkInitialSidebarSize = ->
+ if bootstrapBreakpoint is "xs" or "sm"
+ $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+
+ $(window).on "resize", (e) ->
+ fitSidebarForSize()
+
+ setBootstrapBreakpoints()
+ checkInitialSidebarSize()
new Aside()
diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee
index 02232698bc2..d17b1123418 100644
--- a/app/assets/javascripts/issuable_context.js.coffee
+++ b/app/assets/javascripts/issuable_context.js.coffee
@@ -10,19 +10,7 @@ class @IssuableContext
$(".issuable-sidebar .inline-update").on "change", ".js-assignee", ->
$(this).submit()
- $('.issuable-details').waitForImages ->
- $('.issuable-affix').on 'affix.bs.affix', ->
- $(@).width($(@).outerWidth())
- .on 'affixed-top.bs.affix affixed-bottom.bs.affix', ->
- $(@).width('')
-
- $('.issuable-affix').affix offset:
- top: ->
- @top = ($('.issuable-affix').offset().top - 70)
- bottom: ->
- @bottom = $('.footer').outerHeight(true)
-
- $(".edit-link").click (e) ->
+ $(document).on "click",".edit-link", (e) ->
block = $(@).parents('.block')
block.find('.selectbox').show()
block.find('.value').hide()
diff --git a/app/assets/javascripts/project_select.js.coffee b/app/assets/javascripts/project_select.js.coffee
index 0ae274f3363..be8ab9b428d 100644
--- a/app/assets/javascripts/project_select.js.coffee
+++ b/app/assets/javascripts/project_select.js.coffee
@@ -3,6 +3,7 @@ class @ProjectSelect
$('.ajax-project-select').each (i, select) ->
@groupId = $(select).data('group-id')
@includeGroups = $(select).data('include-groups')
+ @orderBy = $(select).data('order-by') || 'id'
placeholder = "Search for project"
placeholder += " or group" if @includeGroups
@@ -28,7 +29,7 @@ class @ProjectSelect
if @groupId
Api.groupProjects @groupId, query.term, projectsCallback
else
- Api.projects query.term, projectsCallback
+ Api.projects query.term, @orderBy, projectsCallback
id: (project) ->
project.web_url
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 11df4c24056..5f193fa7434 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -2,7 +2,7 @@
@include border-radius(3px);
font-size: $gl-font-size;
font-weight: 500;
- padding: $gl-vert-padding $gl-padding;
+ padding: $gl-vert-padding $gl-btn-padding;
&:focus,
&:active {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index e93dbab0c42..08dcb563dce 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -9,7 +9,7 @@
display: block;
float: left;
- padding: 0 $gl-padding;
+ padding: 0 $gl-btn-padding;
font-weight: normal;
margin-right: 10px;
font-size: $gl-font-size;
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 0997dfc287c..3bfac2ad9b5 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -116,7 +116,7 @@
display: none;
}
- aside {
+ aside:not(.right-sidebar){
display: none;
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 540d0b03163..b7f532c0771 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -200,6 +200,14 @@
}
}
+@mixin expanded-gutter {
+ padding-right: $gutter_width;
+}
+
+@mixin collapsed-gutter {
+ padding-right: $sidebar_collapsed_width;
+}
+
@mixin collapsed-sidebar {
padding-left: $sidebar_collapsed_width;
@@ -266,6 +274,7 @@
background: #f2f6f7;
}
+// page is small enough
@media (max-width: $screen-md-max) {
.page-sidebar-collapsed {
@include collapsed-sidebar;
@@ -275,12 +284,32 @@
@include collapsed-sidebar;
}
+ .page-gutter {
+ &.right-sidebar-collapsed {
+ @include collapsed-gutter;
+ }
+ &.right-sidebar-expanded {
+ @include expanded-gutter;
+ }
+ }
+
.collapse-nav {
display: none;
}
}
+// page is large enough
@media(min-width: $screen-md-max) {
+
+ .page-gutter {
+ &.right-sidebar-collapsed {
+ @include collapsed-gutter;
+ }
+ &.right-sidebar-expanded {
+ @include expanded-gutter;
+ }
+ }
+
.page-sidebar-collapsed {
@include collapsed-sidebar;
}
@@ -288,4 +317,4 @@
.page-sidebar-expanded {
@include expanded-sidebar;
}
-}
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 3ec48da9a41..44d3d7715d2 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -12,6 +12,9 @@ $gl-font-size: 15px;
$list-font-size: 15px;
$sidebar_collapsed_width: 62px;
$sidebar_width: 230px;
+$gutter_collapsed_width: 62px;
+$gutter_width: 312px;
+$gutter_inner_width: 280px;
$avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;
@@ -22,6 +25,7 @@ $header-height: 58px;
$fixed-layout-width: 1280px;
$gl-gray: #5a5a5a;
$gl-padding: 16px;
+$gl-btn-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top:10px;
$gl-avatar-size: 46px;
@@ -36,11 +40,12 @@ $white-light: #FFFFFF;
$white-normal: #ededed;
$white-dark: #ededed;
-$gray-light: #f7f7f7;
-$gray-normal: #ededed;
+$gray-light: #faf9f9;
+$gray-normal: #f5f5f5;
$gray-dark: #ededed;
+$gray-darkest: #c9c9c9;
-$green-light: #31AF64;
+$green-light: #38ae67;
$green-normal: #2FAA60;
$green-dark: #2CA05B;
@@ -52,7 +57,7 @@ $blue-medium-light: #3498CB;
$blue-medium: #2F8EBF;
$blue-medium-dark: #2D86B4;
-$orange-light: #FC6443;
+$orange-light: rgba(252, 109, 38, 0.80);
$orange-normal: #E75E40;
$orange-dark: #CE5237;
@@ -64,8 +69,8 @@ $border-white-light: #F1F2F4;
$border-white-normal: #D6DAE2;
$border-white-dark: #C6CACF;
-$border-gray-light: #d1d1d1;
-$border-gray-normal: #D6DAE2;
+$border-gray-light: rgba(0, 0, 0, 0.06);
+$border-gray-normal: rgba(0, 0, 0, 0.10);;
$border-gray-dark: #C6CACF;
$border-green-light: #2FAA60;
@@ -76,7 +81,7 @@ $border-blue-light: #2D9FD8;
$border-blue-normal: #2897CE;
$border-blue-dark: #258DC1;
-$border-orange-light: #ED5C3D;
+$border-orange-light: #fc6d26;
$border-orange-normal: #CE5237;
$border-orange-dark: #C14E35;
diff --git a/app/assets/stylesheets/pages/explore.scss b/app/assets/stylesheets/pages/explore.scss
index da06fe9954e..9b92128624c 100644
--- a/app/assets/stylesheets/pages/explore.scss
+++ b/app/assets/stylesheets/pages/explore.scss
@@ -6,11 +6,3 @@
font-size: 30px;
}
}
-
-.explore-trending-block {
- .lead {
- line-height: 32px;
- font-size: 18px;
- margin-top: 10px;
- }
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 977ada0ff38..3bfbd9e17b7 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -42,8 +42,6 @@
.issuable-details {
section {
- border-right: 1px solid $border-white-light;
-
.issuable-discussion {
margin-right: 1px;
}
@@ -73,11 +71,35 @@
.block {
@include clearfix;
padding: $gl-padding 0;
- border-bottom: 1px solid #F0F0F0;
+ border-bottom: 1px solid $border-gray-light;
+ // This prevents the mess when resizing the sidebar
+ // of elements repositioning themselves..
+ width: $gutter_inner_width;
+ overflow-x: hidden;
+ // --
+
+ &:first-child {
+ padding-top: 5px;
+ }
&:last-child {
border: none;
}
+
+ span {
+ margin-top: 7px;
+ display: inline-block;
+ }
+
+ .issuable-count {
+
+ }
+
+ .gutter-toggle {
+ margin-left: 20px;
+ border-left: 1px solid $border-gray-light;
+ padding-left: 10px;
+ }
}
.title {
@@ -133,3 +155,92 @@
margin-right: 2px;
}
}
+
+
+.right-sidebar {
+ position: fixed;
+ top: 58px;
+ right: 0;
+ height: 100%;
+ transition-duration: .3s;
+ background: $gray-light;
+ overflow: scroll;
+ padding: 10px 20px;
+
+ &.right-sidebar-expanded {
+ width: $gutter_width;
+
+ hr {
+ display: none;
+ }
+ }
+
+ .subscribe-button {
+ span {
+ margin-top: 0;
+ }
+ }
+
+ &.right-sidebar-collapsed {
+ width: $sidebar_collapsed_width;
+ padding-top: 0;
+ overflow-x: hidden;
+
+ hr {
+ margin: 0;
+ color: $gray-normal;
+ border-color: $gray-normal;
+ width: 62px;
+ margin-left: -20px
+ }
+
+ .block {
+ border-bottom: none;
+ padding: 15px 0 0 0;
+ }
+ }
+
+ .btn {
+ background: $gray-normal;
+ border: 1px solid $border-gray-normal;
+ }
+
+ &.right-sidebar-collapsed {
+ .issuable-count,
+ .issuable-nav,
+ .assignee > *,
+ .milestone > *,
+ .labels > *,
+ .participants > *,
+ .light > *,
+ .project-reference > * {
+ display: none;
+ }
+
+ .gutter-toggle {
+ margin-left: -$gutter_inner_width + 4;
+ }
+
+ .sidebar-collapsed-icon {
+ display: block;
+ float: left;
+ width: 62px;
+ text-align: center;
+ margin-left: -19px;
+ padding-bottom: 10px;
+ color: #999999;
+
+ span {
+ display: block;
+ margin-top: 0;
+ }
+ }
+
+ }
+
+ &.right-sidebar-expanded {
+ .sidebar-collapsed-icon {
+ display: none;
+ }
+ }
+} \ No newline at end of file
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 0b7fcdf5e9e..721e2a6bcbd 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -3,6 +3,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
def index
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
+ @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace)
@last_push = current_user.recent_push
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 4c13228fce9..9cf76521a0d 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,4 +1,5 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
+ include AuthenticatesWithTwoFactor
protect_from_forgery except: [:kerberos, :saml, :cas3]
@@ -29,8 +30,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Do additional LDAP checks for the user filter and EE features
if ldap_user.allowed?
- log_audit_event(@user, with: :ldap)
- sign_in_and_redirect(@user)
+ if @user.two_factor_enabled?
+ prompt_for_two_factor(@user)
+ else
+ log_audit_event(@user, with: :ldap)
+ sign_in_and_redirect(@user)
+ end
else
flash[:alert] = "Access denied for your LDAP account."
redirect_to new_user_session_path
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index a2458ad3be0..14f098d8355 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -293,6 +293,76 @@ module ApplicationHelper
end
end
+ def issuable_link_next(project,issuable)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ namespace_project_issue_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
+ elsif current_controller?(:merge_requests)
+ namespace_project_merge_request_path(project.namespace, project, next_issuable_for(project, issuable.id).try(:iid))
+ end
+ end
+
+ def issuable_link_prev(project,issuable)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ namespace_project_issue_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
+ elsif current_controller?(:merge_requests)
+ namespace_project_merge_request_path(project.namespace, project, prev_issuable_for(project, issuable.id).try(:iid))
+ end
+ end
+
+ def issuable_count(entity, project)
+ if project.nil?
+ 0
+ elsif current_controller?(:issues)
+ project.issues.send(entity).count
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.send(entity).count
+ end
+ end
+
+ def next_issuable_for(project, id)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ project.issues.where("id > ?", id).last
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.where("id > ?", id).last
+ end
+ end
+
+ def has_next_issuable?(project, id)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ project.issues.where("id > ?", id).last
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.where("id > ?", id).last
+ end
+ end
+
+ def prev_issuable_for(project, id)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ project.issues.where("id < ?", id).first
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.where("id < ?", id).first
+ end
+ end
+
+ def has_prev_issuable?(project, id)
+ if project.nil?
+ nil
+ elsif current_controller?(:issues)
+ project.issues.where("id < ?", id).first
+ elsif current_controller?(:merge_requests)
+ project.merge_requests.where("id < ?", id).first
+ end
+ end
+
def state_filters_text_for(entity, project)
titles = {
opened: "Open"
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 0d291f9a87e..3648757428b 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -10,8 +10,19 @@ module ExploreHelper
options = exist_opts.merge(options)
- path = explore_projects_path
+ path = if explore_controller?
+ explore_projects_path
+ elsif current_action?(:starred)
+ starred_dashboard_projects_path
+ else
+ dashboard_projects_path
+ end
+
path << "?#{options.to_param}"
path
end
+
+ def explore_controller?
+ controller.class.name.split("::").first == "Explore"
+ end
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index e6fb8670e57..2c299d1d794 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -3,6 +3,18 @@ module NavHelper
cookies[:collapsed_nav] == 'true'
end
+ def sidebar_gutter_collapsed_class
+ if cookies[:collapsed_gutter] == 'true'
+ "right-sidebar-collapsed"
+ else
+ "right-sidebar-expanded"
+ end
+ end
+
+ def sidebar_gutter_collapsed?
+ cookies[:collapsed_gutter] == 'true'
+ end
+
def nav_sidebar_class
if nav_menu_collapsed?
"sidebar-collapsed"
@@ -19,6 +31,17 @@ module NavHelper
end
end
+ def page_gutter_class
+
+ if current_path?('merge_requests#show') || current_path?('issues#show')
+ if cookies[:collapsed_gutter] == 'true'
+ "page-gutter right-sidebar-collapsed"
+ else
+ "page-gutter right-sidebar-expanded"
+ end
+ end
+ end
+
def nav_header_class
if nav_menu_collapsed?
"header-collapsed"
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index e7e472cbb5b..2e9741a8622 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -20,6 +20,12 @@ module ProjectsHelper
end
end
+ def link_to_member_avatar(author, opts = {})
+ default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
+ opts = default_opts.merge(opts)
+ image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
+ end
+
def link_to_member(project, author, opts = {})
default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
opts = default_opts.merge(opts)
@@ -57,7 +63,11 @@ module ProjectsHelper
link_output = simple_sanitize(project.name)
link_output += content_tag :span, nil, { class: "fa fa-chevron-down dropdown-toggle-caret" } if current_user
- link_output += project_select_tag :project_path, class: "project-item-select js-projects-dropdown", data: { include_groups: false } if current_user
+ if current_user
+ link_output += project_select_tag :project_path,
+ class: "project-item-select js-projects-dropdown",
+ data: { include_groups: false, order_by: 'last_activity_at' }
+ end
link_output
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index d4f78258626..1eb790b1796 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -70,7 +70,7 @@ module SearchHelper
# Autocomplete results for the current user's groups
def groups_autocomplete(term, limit = 5)
- Group.search(term).limit(limit).map do |group|
+ current_user.authorized_groups.search(term).limit(limit).map do |group|
{
label: "group: #{search_result_sanitize(group.name)}",
url: group_path(group)
@@ -80,7 +80,7 @@ module SearchHelper
# Autocomplete results for the current user's projects
def projects_autocomplete(term, limit = 5)
- ProjectsFinder.new.execute(current_user).search_by_title(term).
+ current_user.authorized_projects.search_by_title(term).
sorted_by_stars.non_archived.limit(limit).map do |p|
{
label: "project: #{search_result_sanitize(p.name_with_namespace)}",
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 89b6c49b362..ddc476447ca 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -258,7 +258,7 @@ class MergeRequest < ActiveRecord::Base
end
def work_in_progress?
- !!(title =~ /\A\[?WIP\]?:? /i)
+ !!(title =~ /\A\[?WIP(\]|:| )/i)
end
def mergeable?
@@ -284,7 +284,8 @@ class MergeRequest < ActiveRecord::Base
def can_remove_source_branch?(current_user)
!source_project.protected_branch?(source_branch) &&
!source_project.root_ref?(source_branch) &&
- Ability.abilities.allowed?(current_user, :push_code, source_project)
+ Ability.abilities.allowed?(current_user, :push_code, source_project) &&
+ last_commit == source_project.commit(source_branch)
end
def mr_and_commit_notes
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index cc389c3ae08..3274ba5377b 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -92,6 +92,11 @@
Rails
%span.pull-right
#{Rails::VERSION::STRING}
+
+ %p
+ = Gitlab::Database.adapter_name
+ %span.pull-right
+ = Gitlab::Database.version
%hr
.row
.col-sm-4
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index 726f669b1d2..d865a2c6fae 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -13,7 +13,8 @@
Explore Projects
.nav-controls
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs', spellcheck: false
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name...', class: 'projects-list-filter form-control hidden-xs input-short', spellcheck: false
+ = render 'explore/projects/dropdown'
- if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do
= icon('plus')
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index eb2979fc13e..917bfbd47e9 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -1,12 +1,12 @@
- page_title "Milestones"
- header_title "Milestones", dashboard_milestones_path
-.project-issuable-filter
- .controls
- = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true
-
+.top-area
= render 'shared/milestones_filter'
+ .nav-controls
+ = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true
+
.milestones
%ul.content-list
- if @milestones.blank?
diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml
index a988d4c8154..87c556adc7d 100644
--- a/app/views/explore/projects/_dropdown.html.haml
+++ b/app/views/explore/projects/_dropdown.html.haml
@@ -3,19 +3,13 @@
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- - elsif current_page?(trending_explore_projects_path) || current_page?(explore_root_path)
- Trending projects
- - elsif current_page?(starred_explore_projects_path)
- Most stars
- else
- = sort_title_recently_created
+ = sort_title_recently_updated
%b.caret
%ul.dropdown-menu
%li
- = link_to trending_explore_projects_path do
- Trending projects
- = link_to starred_explore_projects_path do
- Most stars
+ = link_to explore_projects_filter_path(sort: sort_value_name) do
+ = sort_title_name
= link_to explore_projects_filter_path(sort: sort_value_recently_created) do
= sort_title_recently_created
= link_to explore_projects_filter_path(sort: sort_value_oldest_created) do
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 28b12c8dca8..66a4b535ae5 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -2,6 +2,7 @@
= form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
+ = hidden_field_tag :sort, @sort
.form-group
= button_tag 'Search', class: "btn"
@@ -46,4 +47,3 @@
= link_to explore_projects_filter_path(tag: tag.name) do
%i.fa.fa-tag
= tag.name
- = render 'explore/projects/dropdown'
diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml
new file mode 100644
index 00000000000..614b5431779
--- /dev/null
+++ b/app/views/explore/projects/_nav.html.haml
@@ -0,0 +1,10 @@
+%ul.nav-links
+ = nav_link(page: [trending_explore_projects_path, explore_root_path]) do
+ = link_to trending_explore_projects_path do
+ Trending
+ = nav_link(page: starred_explore_projects_path) do
+ = link_to starred_explore_projects_path do
+ Most stars
+ = nav_link(page: explore_projects_path) do
+ = link_to explore_projects_path do
+ All
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index b9a958fbe7b..bee8518d57a 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -6,7 +6,11 @@
- else
= render 'explore/head'
-.gray-content-block.clearfix.second-block
+.top-area
+ = render 'explore/projects/nav'
+
+.gray-content-block.second-block.clearfix
= render 'filter'
+
= render 'projects', projects: @projects
= paginate @projects, theme: "gitlab"
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 95d46e331f8..16f52f7a530 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -6,12 +6,6 @@
- else
= render 'explore/head'
-.explore-trending-block
- .gray-content-block.second-block
- .pull-right
- = render 'explore/projects/dropdown'
- .oneline
- %i.fa.fa-star
- See most starred projects
- = render 'projects', projects: @starred_projects
- = paginate @starred_projects, theme: 'gitlab'
+= render 'explore/projects/nav'
+= render 'projects', projects: @starred_projects
+= paginate @starred_projects, theme: 'gitlab'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index fa0b718e48b..adcda810061 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -6,11 +6,5 @@
- else
= render 'explore/head'
-.explore-trending-block
- .gray-content-block.second-block
- .pull-right
- = render 'explore/projects/dropdown'
- .oneline
- %i.fa.fa-comments-o
- See most discussed projects for last month
- = render 'projects', projects: @trending_projects
+= render 'explore/projects/nav'
+= render 'projects', projects: @trending_projects
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index b221d3a89a4..ab307708b75 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -1,17 +1,15 @@
- page_title "Milestones"
- header_title group_title(@group, "Milestones", group_milestones_path(@group))
-.project-issuable-filter
- .controls
- - if can?(current_user, :admin_milestones, @group)
- .pull-right
- %span.pull-right.hidden-xs
- = link_to new_group_milestone_path(@group), class: "btn btn-new" do
- = icon('plus')
- New Milestone
-
+.top-area
= render 'shared/milestones_filter'
+ .nav-controls
+ - if can?(current_user, :admin_milestones, @group)
+ = link_to new_group_milestone_path(@group), class: "btn btn-new" do
+ = icon('plus')
+ New Milestone
+
.gray-content-block
Only milestones from
%strong #{@group.name}
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 7b45bd09050..746386cab58 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -139,7 +139,31 @@
%h2#navs Navigation
%h4
+ %code .top-area
+ %p Holder for top page navigation. Includes navigation, search field, sorting and button
+
+ .example
+ .top-area
+ %ul.nav-links
+ %li.active
+ %a Open
+ %li
+ %a Closed
+ .nav-controls
+ = text_field_tag 'sample', nil, class: 'form-control'
+ .dropdown
+ %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %span Sort by name
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ %a Sort by date
+
+ = link_to 'New issue', '#', class: 'btn btn-new'
+
+ %h4
%code .nav-links
+ %p Only nav links without button and search
.example
%ul.nav-links
%li.active
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 26159989777..0c1b5eec95a 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,4 +1,4 @@
-.page-with-sidebar{ class: page_sidebar_class }
+.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
= render "layouts/broadcast"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 52bfc595fda..9fa96084f94 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -31,34 +31,33 @@
- else
= f.submit 'Generate', class: "btn btn-default"
- - unless current_user.ldap_user?
- .panel.panel-default
- .panel-heading
- Two-factor Authentication
- .panel-body
- - if current_user.two_factor_enabled?
- .pull-right
- = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
- data: { confirm: 'Are you sure?' }
- %p.text-success
- %strong
- Two-factor Authentication is enabled
- %p
- If you lose your recovery codes you can
- %strong
- = succeed ',' do
- = link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
- invalidating all previous codes.
+ .panel.panel-default
+ .panel-heading
+ Two-factor Authentication
+ .panel-body
+ - if current_user.two_factor_enabled?
+ .pull-right
+ = link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
+ data: { confirm: 'Are you sure?' }
+ %p.text-success
+ %strong
+ Two-factor Authentication is enabled
+ %p
+ If you lose your recovery codes you can
+ %strong
+ = succeed ',' do
+ = link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
+ invalidating all previous codes.
- - else
- %p
- Increase your account's security by enabling two-factor authentication (2FA).
- %p
- Each time you log in you’ll be required to provide your username and
- password as usual, plus a randomly-generated code from your phone.
+ - else
+ %p
+ Increase your account's security by enabling two-factor authentication (2FA).
+ %p
+ Each time you log in you’ll be required to provide your username and
+ password as usual, plus a randomly-generated code from your phone.
- .form-actions
- = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
+ .form-actions
+ = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if button_based_providers.any?
.panel.panel-default
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index bbb6944a65a..630d0286f25 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -1,18 +1,7 @@
- page_title "Builds"
= render "header_title"
-.project-issuable-filter
- .controls
- - if can?(current_user, :manage_builds, @project)
- .pull-left.hidden-xs
- - if @all_builds.running_or_pending.any?
- = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
- data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
-
- = link_to ci_lint_path, class: 'btn btn-default' do
- = icon('wrench')
- %span CI Lint
-
+.top-area
%ul.nav-links
%li{class: ('active' if @scope.nil?)}
= link_to project_builds_path(@project) do
@@ -32,6 +21,16 @@
%span.badge.js-running-count
= number_with_delimiter(@all_builds.finished.count(:id))
+ .nav-controls
+ - if can?(current_user, :manage_builds, @project)
+ - if @all_builds.running_or_pending.any?
+ = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
+ data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+
+ = link_to ci_lint_path, class: 'btn btn-default' do
+ = icon('wrench')
+ %span CI Lint
+
.gray-content-block
#{(@scope || 'running').capitalize} builds from this project
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 51dcca7a1ab..030f4a2e644 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -54,11 +54,8 @@
= render 'votes/votes_block', votable: @issue
.row
- %section.col-md-9
+ %section.col-md-12
.issuable-discussion
= render 'projects/issues/discussion'
- %aside.col-md-3
- = render 'shared/issuable/sidebar', issuable: @issue
-
- = render 'shared/show_aside'
+= render 'shared/issuable/sidebar', issuable: @issue \ No newline at end of file
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index 2f0f3fcfb06..a54733883b4 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -1,3 +1,3 @@
-$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}");
-$('.issuable-sidebar').parent().effect('highlight')
-new Issue();
+$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}";
+$('aside.right-sidebar').effect('highlight');
+new Issue(); \ No newline at end of file
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 9081bcfe9b3..cc41130a9dc 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,13 +1,14 @@
- page_title "Labels"
= render "header_title"
-.gray-content-block.top-block
- - if can? current_user, :admin_label, @project
- = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
- = icon('plus')
- New label
- .oneline
+.top-area
+ .nav-text
Labels can be applied to issues and merge requests.
+ .nav-controls
+ - if can? current_user, :admin_label, @project
+ = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
+ = icon('plus')
+ New label
.labels
- if @labels.present?
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 8641c3d8b4b..da67645bc2b 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -70,12 +70,9 @@
= render 'votes/votes_block', votable: @merge_request
.row
- %section.col-md-9
+ %section.col-md-12
.issuable-discussion
= render "projects/merge_requests/discussion"
- %aside.col-md-3
- = render 'shared/issuable/sidebar', issuable: @merge_request
- = render 'shared/show_aside'
#commits.commits.tab-pane
- # This tab is always loaded via AJAX
@@ -87,6 +84,8 @@
.mr-loading-status
= spinner
+= render 'shared/issuable/sidebar', issuable: @merge_request
+
:javascript
var merge_request;
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index 93db65ddf79..ce5157d69a2 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -1,3 +1,3 @@
-$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}");
-$('.issuable-sidebar').parent().effect('highlight')
+$('aside.right-sidebar')[0].outerHTML= "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
+$('aside.right-sidebar').effect('highlight')
merge_request = new MergeRequest();
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index aa185126b56..abe567af1dd 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -2,15 +2,15 @@
= render "header_title"
-.project-issuable-filter
- .controls
+.top-area
+ = render 'shared/milestones_filter'
+
+ .nav-controls
- if can?(current_user, :admin_milestone, @project)
- = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
- %i.fa.fa-plus
+ = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
+ = icon('plus')
New Milestone
- = render 'shared/milestones_filter'
-
.milestones
%ul.content-list
= render @milestones
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 69ba301e231..56a53ffff2a 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,12 +1,4 @@
-.project-issuable-filter
- .controls
- - if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
- %i.fa.fa-plus
- New Page
-
- = render 'projects/wikis/new'
-
+.top-area
%ul.nav-links
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
@@ -17,3 +9,11 @@
= nav_link(path: 'wikis#git_access') do
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
Git Access
+
+ .nav-controls
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ = icon('plus')
+ New Page
+
+ = render 'projects/wikis/new'
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index f77feeb79cd..cf16c203f9c 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,11 +1,10 @@
-.milestones-filters
- %ul.nav-links
- %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
- = link_to milestones_filter_path(state: 'opened') do
- Open
- %li{class: ("active" if params[:state] == 'closed')}
- = link_to milestones_filter_path(state: 'closed') do
- Closed
- %li{class: ("active" if params[:state] == 'all')}
- = link_to milestones_filter_path(state: 'all') do
- All
+%ul.nav-links
+ %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
+ = link_to milestones_filter_path(state: 'opened') do
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to milestones_filter_path(state: 'closed') do
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to milestones_filter_path(state: 'all') do
+ All
diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml
index 46095912821..1c58345278a 100644
--- a/app/views/shared/_new_project_item_select.html.haml
+++ b/app/views/shared/_new_project_item_select.html.haml
@@ -1,6 +1,6 @@
- if @projects.any?
.prepend-left-10.project-item-select-holder
- = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups] }
+ = project_select_tag :project_path, class: "project-item-select", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at' }
%a.btn.btn-new.new-project-item-select-button
= icon('plus')
= local_assigns[:label]
diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml
index da6bacbb74a..ea61935487c 100644
--- a/app/views/shared/issuable/_participants.html.haml
+++ b/app/views/shared/issuable/_participants.html.haml
@@ -1,4 +1,8 @@
.block.participants
+ .sidebar-collapsed-icon
+ = icon('users')
+ %span
+ = participants.count
.title
= pluralize participants.count, "participant"
- participants.each do |participant|
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 3092ff54242..cab500d7244 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -1,88 +1,132 @@
-.issuable-sidebar.issuable-affix
- = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
- .block.assignee
- .title
- %label
- Assignee
- - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- .pull-right
- = link_to 'Edit', '#', class: 'edit-link'
- .value
- - if issuable.assignee
- %strong= link_to_member(@project, issuable.assignee, size: 24)
- - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
- %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
- = icon('exclamation-triangle')
+%aside.right-sidebar{ class: sidebar_gutter_collapsed_class }
+ .issuable-sidebar
+ .block
+ %span.issuable-count.pull-left
+ = issuable.iid
+ of
+ = issuable_count(:all, @project)
+ %span.pull-right
+ %a.gutter-toggle{href: '#'}
+ - if sidebar_gutter_collapsed?
+ = icon('angle-double-left')
+ - else
+ = icon('angle-double-right')
+ .issuable-nav.pull-right.btn-group{role: 'group', "aria-label" => '...'}
+ - if has_prev_issuable?(@project, issuable.id)
+ = link_to 'Prev', issuable_link_prev(@project, issuable), class: 'btn btn-default'
- else
- .light None
+ %a.btn.btn-default.disabled{href: '#'}
+ Prev
+ - if has_next_issuable?(@project, issuable.id)
+ = link_to 'Next', issuable_link_next(@project, issuable), class: 'btn btn-default'
+ - else
+ %a.btn.btn-default.disabled{href: '#'}
+ Next
- .selectbox
- = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
+ = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
+ .block.assignee
+ .sidebar-collapsed-icon
+ - if issuable.assignee
+ = link_to_member_avatar(issuable.assignee, size: 24)
+ - else
+ = icon('user')
+ .title
+ %label
+ Assignee
+ - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
+ .pull-right
+ = link_to 'Edit', '#', class: 'edit-link'
+ .value
+ - if issuable.assignee
+ %strong= link_to_member(@project, issuable.assignee, size: 24)
+ - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
+ %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
+ = icon('exclamation-triangle')
+ - else
+ .light None
- .block.milestone
- .title
- %label
- Milestone
- - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
- .pull-right
- = link_to 'Edit', '#', class: 'edit-link'
- .value
- - if issuable.milestone
- %span.back-to-milestone
- = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
- %strong
- = icon('clock-o')
- = issuable.milestone.title
- - else
- .light None
- .selectbox
- = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
- = hidden_field_tag :issuable_context
- = f.submit class: 'btn hide'
+ .selectbox
+ = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true)
- - if issuable.project.labels.any?
- .block
+ .block.milestone
+ .sidebar-collapsed-icon
+ = icon('balance-scale')
+ %span
+ - if issuable.milestone
+ = issuable.milestone.title
+ - else
+ No
.title
- %label Labels
+ %label
+ Milestone
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.pull-right
= link_to 'Edit', '#', class: 'edit-link'
- .value.issuable-show-labels
- - if issuable.labels.any?
- - issuable.labels.each do |label|
- = link_to_label(label)
+ .value
+ - if issuable.milestone
+ %span.back-to-milestone
+ = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do
+ %strong
+ = icon('clock-o')
+ = issuable.milestone.title
- else
.light None
.selectbox
- = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
- { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
+ = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }})
+ = hidden_field_tag :issuable_context
+ = f.submit class: 'btn hide'
- = render "shared/issuable/participants", participants: issuable.participants(current_user)
+ - if issuable.project.labels.any?
+ .block.labels
+ .sidebar-collapsed-icon
+ = icon('tags')
+ %span
+ = issuable.labels.count
+ .title
+ %label Labels
+ - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
+ .pull-right
+ = link_to 'Edit', '#', class: 'edit-link'
+ .value.issuable-show-labels
+ - if issuable.labels.any?
+ - issuable.labels.each do |label|
+ = link_to_label(label)
+ - else
+ .light None
+ .selectbox
+ = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
+ { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" }
- - if current_user
- - subscribed = issuable.subscribed?(current_user)
- .block.light
- .title
- %label.light Notifications
- - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
- %button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'}
- %span= subscribed ? 'Unsubscribe' : 'Subscribe'
- .subscription-status{data: {status: subscribtion_status}}
- .unsubscribed{class: ( 'hidden' if subscribed )}
- You're not receiving notifications from this thread.
- .subscribed{class: ( 'hidden' unless subscribed )}
- You're receiving notifications because you're subscribed to this thread.
+ = render "shared/issuable/participants", participants: issuable.participants(current_user)
+ %hr
+ - if current_user
+ - subscribed = issuable.subscribed?(current_user)
+ .block.light
+ .sidebar-collapsed-icon
+ = icon('rss')
+ .title
+ %label.light Notifications
+ - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed'
+ %button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'}
+ %span= subscribed ? 'Unsubscribe' : 'Subscribe'
+ .subscription-status{data: {status: subscribtion_status}}
+ .unsubscribed{class: ( 'hidden' if subscribed )}
+ You're not receiving notifications from this thread.
+ .subscribed{class: ( 'hidden' unless subscribed )}
+ You're receiving notifications because you're subscribed to this thread.
- - project_ref = cross_project_reference(@project, issuable)
- .block
- .title
- .cross-project-reference
- %span
- Reference:
- %cite{title: project_ref}
- = project_ref
- = clipboard_button(clipboard_text: project_ref)
+ - project_ref = cross_project_reference(@project, issuable)
+ .block.project-reference
+ .sidebar-collapsed-icon
+ = icon('clipboard')
+ .title
+ .cross-project-reference
+ %span
+ Reference:
+ %cite{title: project_ref}
+ = project_ref
+ = clipboard_button(clipboard_text: project_ref)
- :javascript
- new Subscription("#{toggle_subscription_path(issuable)}");
- new IssuableContext(); \ No newline at end of file
+ :javascript
+ new Subscription("#{toggle_subscription_path(issuable)}");
+ new IssuableContext(); \ No newline at end of file
diff --git a/config/newrelic.yml b/config/newrelic.yml
deleted file mode 100644
index 9ef922a38d9..00000000000
--- a/config/newrelic.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-# New Relic configuration file
-#
-# This file is here to make sure the New Relic gem stays
-# quiet by default.
-#
-# To enable and configure New Relic, please use
-# environment variables, e.g. NEW_RELIC_ENABLED=true
-
-production:
- enabled: false
-
-development:
- enabled: false
-
-test:
- enabled: false
diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
new file mode 100644
index 00000000000..091de54978b
--- /dev/null
+++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
@@ -0,0 +1,80 @@
+class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
+ include Gitlab::ShellAdapter
+
+ class ProjectPath
+ attr_reader :old_path, :id, :namespace_path
+
+ def initialize(old_path, id, namespace_path, namespace_id)
+ @old_path = old_path
+ @id = id
+ @namespace_path = namespace_path
+ @namespace_id = namespace_id
+ end
+
+ def clean_path
+ @_clean_path ||= PathCleaner.clean(@old_path, @namespace_id)
+ end
+ end
+
+ class PathCleaner
+ def initialize(path, namespace_id)
+ @namespace_id = namespace_id
+ @path = path
+ end
+
+ def self.clean(*args)
+ new(*args).clean
+ end
+
+ def clean
+ path = cleaned_path
+ count = 0
+ while path_exists?(path)
+ path = "#{cleaned_path}#{count}"
+ count += 1
+ end
+ path
+ end
+
+ private
+
+ def cleaned_path
+ @_cleaned_path ||= @path.gsub(/\.atom\z/, '-atom')
+ end
+
+ def path_exists?(path)
+ Project.find_by_path_and_namespace_id(path, @namespace_id)
+ end
+ end
+
+ def projects_with_dot_atom
+ select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE lower(p.path) LIKE '%.atom'")
+ end
+
+ def up
+ projects_with_dot_atom.each do |project|
+ project_path = ProjectPath.new(project['path'], project['id'], project['namespace_path'], project['namespace_id'])
+ clean_path(project_path) if rename_project_repo(project_path)
+ end
+ end
+
+ private
+
+ def clean_path(project_path)
+ execute "UPDATE projects SET path = #{sanitize(project_path.clean_path)} WHERE id = #{project_path.id}"
+ end
+
+ def rename_project_repo(project_path)
+ old_path_with_namespace = File.join(project_path.namespace_path, project_path.old_path)
+ new_path_with_namespace = File.join(project_path.namespace_path, project_path.clean_path)
+
+ gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
+ gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace)
+ rescue
+ false
+ end
+
+ def sanitize(value)
+ ActiveRecord::Base.connection.quote(value)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d546e06cd8a..d4710346b82 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160128233227) do
+ActiveRecord::Schema.define(version: 20160129135155) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 00edfc97ed9..194b8e00299 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -16,7 +16,7 @@ Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that
For example:
```
-git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#2). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
+git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#22). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
```
will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does not match the pattern. It also works with multiline commit messages.
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
index 036bc0a499e..4bc670fdfcb 100644
--- a/features/steps/project/builds/summary.rb
+++ b/features/steps/project/builds/summary.rb
@@ -13,7 +13,7 @@ class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
end
step 'I see button to CI Lint' do
- page.within('.controls') do
+ page.within('.nav-controls') do
ci_lint_tool_link = page.find_link('CI Lint')
expect(ci_lint_tool_link[:href]).to eq ci_lint_path
end
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index d1e11eedec3..04ddfe53ed6 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -8,14 +8,7 @@ module Banzai
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
def whitelist
- # Descriptions are more heavily sanitized, allowing only a few elements.
- # See http://git.io/vkuAN
- if context[:inline_sanitization]
- whitelist = LIMITED
- whitelist[:elements] -= %w(pre code img ol ul li)
- else
- whitelist = super
- end
+ whitelist = super
customize_whitelist(whitelist)
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index 20e24ace352..f2395867658 100644
--- a/lib/banzai/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -4,9 +4,20 @@ module Banzai
def self.transform_context(context)
super(context).merge(
# SanitizationFilter
- inline_sanitization: true
+ whitelist: whitelist
)
end
+
+ private
+
+ def self.whitelist
+ # Descriptions are more heavily sanitized, allowing only a few elements.
+ # See http://git.io/vkuAN
+ whitelist = Banzai::Filter::SanitizationFilter::LIMITED
+ whitelist[:elements] -= %w(pre code img ol ul li)
+
+ whitelist
+ end
end
end
end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 4c15d58d680..f751458ac66 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -47,7 +47,7 @@ module Gitlab
# new_path - new project path with namespace
#
# Ex.
- # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git")
+ # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new")
#
def mv_repository(path, new_path)
Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'mv-project',
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index de77a6fbff1..6ebb8027553 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -1,16 +1,23 @@
module Gitlab
module Database
+ def self.adapter_name
+ connection.adapter_name
+ end
+
def self.mysql?
- ActiveRecord::Base.connection.adapter_name.downcase == 'mysql2'
+ adapter_name.downcase == 'mysql2'
end
def self.postgresql?
- ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
+ adapter_name.downcase == 'postgresql'
+ end
+
+ def self.version
+ database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1]
end
def true_value
- case ActiveRecord::Base.connection.adapter_name.downcase
- when 'postgresql'
+ if self.class.postgresql?
"'t'"
else
1
@@ -18,12 +25,31 @@ module Gitlab
end
def false_value
- case ActiveRecord::Base.connection.adapter_name.downcase
- when 'postgresql'
+ if self.class.postgresql?
"'f'"
else
0
end
end
+
+ private
+
+ def self.connection
+ ActiveRecord::Base.connection
+ end
+
+ def self.database_version
+ row = connection.execute("SELECT VERSION()").first
+
+ if postgresql?
+ row['version']
+ else
+ row.first
+ end
+ end
+
+ def connection
+ self.class.connection
+ end
end
end
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 5c35c5b1450..ace906a6f59 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -34,12 +34,12 @@ module Gitlab
def project_path_regex
- @project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git)\z/.freeze
+ @project_path_regex ||= /\A[a-zA-Z0-9_.][a-zA-Z0-9_\-\.]*(?<!\.git|\.atom)\z/.freeze
end
def project_path_regex_message
"can contain only letters, digits, '_', '-' and '.'. " \
- "Cannot start with '-' or end in '.git'" \
+ "Cannot start with '-', end in '.git' or end in '.atom'" \
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 665526fde93..6eee4dfe229 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -86,6 +86,14 @@ describe ProjectsController do
end
end
end
+
+ context "when the url contains .atom" do
+ let(:public_project_with_dot_atom) { build(:project, :public, name: 'my.atom', path: 'my.atom') }
+
+ it 'expect an error creating the project' do
+ expect(public_project_with_dot_atom).not_to be_valid
+ end
+ end
end
describe "#destroy" do
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index d37bd103714..5b6f3cb3f15 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -18,7 +18,7 @@ describe "Builds" do
visit namespace_project_builds_path(@project.namespace, @project, scope: :running)
end
- it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running') }
+ it { expect(page).to have_selector('.nav-links li.active', text: 'Running') }
it { expect(page).to have_link 'Cancel running' }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
@@ -31,7 +31,7 @@ describe "Builds" do
visit namespace_project_builds_path(@project.namespace, @project, scope: :finished)
end
- it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished') }
+ it { expect(page).to have_selector('.nav-links li.active', text: 'Finished') }
it { expect(page).to have_content 'No builds to show' }
it { expect(page).to have_link 'Cancel running' }
end
@@ -42,7 +42,7 @@ describe "Builds" do
visit namespace_project_builds_path(@project.namespace, @project)
end
- it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') }
+ it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
@@ -57,7 +57,7 @@ describe "Builds" do
click_link "Cancel running"
end
- it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') }
+ it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
it { expect(page).to have_content 'canceled' }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index f0d553f5f1d..601b6915e27 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -42,9 +42,9 @@ describe SearchHelper do
expect(search_autocomplete_opts(project.name).size).to eq(1)
end
- it "includes the public group" do
+ it "should not include the public group" do
group = create(:group)
- expect(search_autocomplete_opts(group.name).size).to eq(1)
+ expect(search_autocomplete_opts(group.name).size).to eq(0)
end
context "with a current project" do
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index 9c63d227044..e14a6dbf922 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -177,26 +177,4 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
expect(act.to_html).to eq exp
end
end
-
- context 'when inline_sanitization is true' do
- it 'uses a stricter whitelist' do
- doc = filter('<h1>Description</h1>', inline_sanitization: true)
- expect(doc.to_html.strip).to eq 'Description'
- end
-
- %w(pre code img ol ul li).each do |elem|
- it "removes '#{elem}' elements" do
- act = "<#{elem}>Description</#{elem}>"
- expect(filter(act, inline_sanitization: true).to_html.strip).
- to eq 'Description'
- end
- end
-
- %w(b i strong em a ins del sup sub p).each do |elem|
- it "still allows '#{elem}' elements" do
- exp = act = "<#{elem}>Description</#{elem}>"
- expect(filter(act, inline_sanitization: true).to_html).to eq exp
- end
- end
- end
end
diff --git a/spec/lib/banzai/pipeline/description_pipeline_spec.rb b/spec/lib/banzai/pipeline/description_pipeline_spec.rb
new file mode 100644
index 00000000000..76f42071810
--- /dev/null
+++ b/spec/lib/banzai/pipeline/description_pipeline_spec.rb
@@ -0,0 +1,37 @@
+require 'rails_helper'
+
+describe Banzai::Pipeline::DescriptionPipeline do
+ def parse(html)
+ # When we pass HTML to Redcarpet, it gets wrapped in `p` tags...
+ # ...except when we pass it pre-wrapped text. Rabble rabble.
+ unwrap = !html.start_with?('<p>')
+
+ output = described_class.to_html(html, project: spy)
+
+ output.gsub!(%r{\A<p>(.*)</p>(.*)\z}, '\1\2') if unwrap
+
+ output
+ end
+
+ it 'uses a limited whitelist' do
+ doc = parse('# Description')
+
+ expect(doc.strip).to eq 'Description'
+ end
+
+ %w(pre code img ol ul li).each do |elem|
+ it "removes '#{elem}' elements" do
+ act = "<#{elem}>Description</#{elem}>"
+
+ expect(parse(act).strip).to eq 'Description'
+ end
+ end
+
+ %w(b i strong em a ins del sup sub p).each do |elem|
+ it "still allows '#{elem}' elements" do
+ exp = act = "<#{elem}>Description</#{elem}>"
+
+ expect(parse(act).strip).to eq exp
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8461e8ce50d..bd8688fefa1 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -14,4 +14,24 @@ describe Gitlab::Database, lib: true do
it { is_expected.to satisfy { |val| val == true || val == false } }
end
+
+ describe '.version' do
+ context "on mysql" do
+ it "extracts the version number" do
+ allow(described_class).to receive(:database_version).
+ and_return("5.7.12-standard")
+
+ expect(described_class.version).to eq '5.7.12-standard'
+ end
+ end
+
+ context "on postgresql" do
+ it "extracts the version number" do
+ allow(described_class).to receive(:database_version).
+ and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
+
+ expect(described_class.version).to eq '9.4.4'
+ end
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 46f2f20b986..c61ddf01118 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -188,6 +188,11 @@ describe MergeRequest, models: true do
expect(subject).to be_work_in_progress
end
+ it "detects the '[WIP]' prefix" do
+ subject.title = "[WIP]#{subject.title}"
+ expect(subject).to be_work_in_progress
+ end
+
it "doesn't detect WIP for words starting with WIP" do
subject.title = "Wipwap #{subject.title}"
expect(subject).not_to be_work_in_progress
@@ -226,9 +231,15 @@ describe MergeRequest, models: true do
expect(subject.can_remove_source_branch?(user2)).to be_falsey
end
- it "is can be removed in all other cases" do
+ it "can be removed if the last commit is the head of the source branch" do
+ allow(subject.source_project).to receive(:commit).and_return(subject.last_commit)
+
expect(subject.can_remove_source_branch?(user)).to be_truthy
end
+
+ it "cannot be removed if the last commit is not also the head of the source branch" do
+ expect(subject.can_remove_source_branch?(user)).to be_falsey
+ end
end
describe "#reset_merge_when_build_succeeds" do