diff options
288 files changed, 2348 insertions, 1216 deletions
diff --git a/.gitignore b/.gitignore index 8f861d76a37..ce6a363fe35 100644 --- a/.gitignore +++ b/.gitignore @@ -4,46 +4,46 @@ .bundle .chef .directory -.envrc -.gitlab_shell_secret +/.envrc +/.gitlab_shell_secret .idea -.rbenv-version +/.rbenv-version .rbx/ -.ruby-gemset -.ruby-version -.rvmrc +/.ruby-gemset +/.ruby-version +/.rvmrc .sass-cache/ -.secret -.vagrant -.byebug_history -Vagrantfile -backups/* -config/aws.yml -config/database.yml -config/gitlab.yml -config/gitlab_ci.yml -config/initializers/rack_attack.rb -config/initializers/smtp_settings.rb -config/initializers/relative_url.rb -config/resque.yml -config/unicorn.rb -config/secrets.yml -config/sidekiq.yml -coverage/* -db/*.sqlite3 -db/*.sqlite3-journal -db/data.yml -doc/code/* -dump.rdb -log/*.log* -nohup.out -public/assets/ -public/uploads.* -public/uploads/ -shared/artifacts/ -rails_best_practices_output.html +/.secret +/.vagrant +/.byebug_history +/Vagrantfile +/backups/* +/config/aws.yml +/config/database.yml +/config/gitlab.yml +/config/gitlab_ci.yml +/config/initializers/rack_attack.rb +/config/initializers/smtp_settings.rb +/config/initializers/relative_url.rb +/config/resque.yml +/config/unicorn.rb +/config/secrets.yml +/config/sidekiq.yml +/coverage/* +/db/*.sqlite3 +/db/*.sqlite3-journal +/db/data.yml +/doc/code/* +/dump.rdb +/log/*.log* +/nohup.out +/public/assets/ +/public/uploads.* +/public/uploads/ +/shared/artifacts/ +/rails_best_practices_output.html /tags -tmp/ -vendor/bundle/* -builds/* -shared/* +/tmp/* +/vendor/bundle/* +/builds/* +/shared/* diff --git a/.rubocop.yml b/.rubocop.yml index 4e6ced4e1ab..2d8eb4077f3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -59,7 +59,7 @@ Style/AndOr: # Use `Array#join` instead of `Array#*`. Style/ArrayJoin: - Enabled: false + Enabled: true # Use only ascii symbols in comments. Style/AsciiComments: @@ -71,7 +71,7 @@ Style/AsciiIdentifiers: # Checks for uses of Module#attr. Style/Attr: - Enabled: false + Enabled: true # Avoid the use of BEGIN blocks. Style/BeginBlock: @@ -83,7 +83,7 @@ Style/BarePercentLiterals: # Do not use block comments. Style/BlockComments: - Enabled: false + Enabled: true # Put end statement of multiline block on its own line. Style/BlockEndNewline: @@ -124,7 +124,7 @@ Style/ClassCheck: # Use self when defining module/class methods. Style/ClassMethods: - Enabled: false + Enabled: true # Avoid the use of class variables. Style/ClassVars: @@ -218,7 +218,7 @@ Style/EmptyLiteral: # Avoid the use of END blocks. Style/EndBlock: - Enabled: false + Enabled: true # Use Unix-style line endings. Style/EndOfLine: @@ -226,7 +226,7 @@ Style/EndOfLine: # Favor the use of Fixnum#even? && Fixnum#odd? Style/EvenOdd: - Enabled: false + Enabled: true # Do not use unnecessary spacing. Style/ExtraSpacing: @@ -234,11 +234,16 @@ Style/ExtraSpacing: # Use snake_case for source file names. Style/FileName: - Enabled: false + Enabled: true + +# Checks for a line break before the first parameter in a multi-line method +# parameter definition. +Style/FirstMethodParameterLineBreak: + Enabled: true # Checks for flip flops. Style/FlipFlop: - Enabled: false + Enabled: true # Checks use of for or each in multiline loops. Style/For: @@ -250,7 +255,7 @@ Style/FormatString: # Do not introduce global variables. Style/GlobalVars: - Enabled: false + Enabled: true # Check for conditionals that can be replaced with guard clauses. Style/GuardClause: @@ -271,7 +276,7 @@ Style/IfUnlessModifier: # Do not use if x; .... Use the ternary operator instead. Style/IfWithSemicolon: - Enabled: false + Enabled: true # Checks that conditional statements do not have an identical line at the # end of each branch, which can validly be moved out of the conditional. @@ -309,7 +314,7 @@ Style/Lambda: # Use lambda.call(...) instead of lambda.(...). Style/LambdaCall: - Enabled: false + Enabled: true # Comments should start with a space. Style/LeadingCommentSpace: @@ -329,7 +334,7 @@ Style/MethodDefParentheses: # Use the configured style when naming methods. Style/MethodName: - Enabled: false + Enabled: true # Checks for usage of `extend self` in modules. Style/ModuleFunction: @@ -370,6 +375,11 @@ Style/MultilineMethodCallBraceLayout: Style/MultilineMethodCallIndentation: Enabled: false +# Checks that the closing brace in a method definition is symmetrical with +# respect to the opening brace and the method parameters. +Style/MultilineMethodDefinitionBraceLayout: + Enabled: false + # Checks indentation of binary operations that span more than one line. Style/MultilineOperationIndentation: Enabled: false @@ -392,7 +402,7 @@ Style/NegatedWhile: # Avoid using nested modifiers. Style/NestedModifier: - Enabled: false + Enabled: true # Parenthesize method calls which are nested inside the argument list of # another parenthesized method call. @@ -429,7 +439,7 @@ Style/OneLineConditional: # When defining binary operators, name the argument other. Style/OpMethod: - Enabled: false + Enabled: true # Check for simple usages of parallel assignment. It will only warn when # the number of variables matches on both sides of the assignment. @@ -509,7 +519,8 @@ Style/Semicolon: # Checks for proper usage of fail and raise. Style/SignalException: - Enabled: false + EnforcedStyle: only_raise + Enabled: true # Enforces the names of some block params. Style/SingleLineBlockParams: @@ -534,11 +545,11 @@ Style/SpaceAfterMethodName: # Tracks redundant space after the ! operator. Style/SpaceAfterNot: - Enabled: false + Enabled: true # Use spaces after semicolons. Style/SpaceAfterSemicolon: - Enabled: false + Enabled: true # Checks that the equals signs in parameter default assignments have or don't # have surrounding space depending on configuration. @@ -572,7 +583,7 @@ Style/SpaceBeforeFirstArg: # No spaces before semicolons. Style/SpaceBeforeSemicolon: - Enabled: false + Enabled: true # Checks that block braces have or don't have surrounding space. # For blocks taking parameters, checks that the left brace has or doesn't @@ -594,11 +605,12 @@ Style/SpaceInsideParens: # No spaces inside range literals. Style/SpaceInsideRangeLiteral: - Enabled: false + Enabled: true # Checks for padding/surrounding spaces inside string interpolation. Style/SpaceInsideStringInterpolation: - Enabled: false + EnforcedStyle: no_space + Enabled: true # Avoid Perl-style global variables. Style/SpecialGlobalVars: @@ -606,7 +618,8 @@ Style/SpecialGlobalVars: # Check for the usage of parentheses around stabby lambda arguments. Style/StabbyLambdaParentheses: - Enabled: false + EnforcedStyle: require_parentheses + Enabled: true # Checks if uses of quotes match the configured preference. Style/StringLiterals: @@ -619,7 +632,9 @@ Style/StringLiteralsInInterpolation: # Checks if configured preferred methods are used over non-preferred. Style/StringMethods: - Enabled: false + PreferredMethods: + intern: to_sym + Enabled: true # Use %i or %I for arrays of symbols. Style/SymbolArray: @@ -677,15 +692,16 @@ Style/UnneededPercentQ: # Don't interpolate global, instance and class variables directly in strings. Style/VariableInterpolation: - Enabled: false + Enabled: true # Use the configured style when naming variables. Style/VariableName: - Enabled: false + EnforcedStyle: snake_case + Enabled: true # Use when x then ... for one-line cases. Style/WhenThen: - Enabled: false + Enabled: true # Checks for redundant do after while or until. Style/WhileUntilDo: @@ -693,7 +709,7 @@ Style/WhileUntilDo: # Favor modifier while/until usage when you have a single-line body. Style/WhileUntilModifier: - Enabled: false + Enabled: true # Use %w or %W for arrays of words. Style/WordArray: @@ -1130,7 +1146,7 @@ RSpec/MultipleDescribes: # Enforces the usage of the same method on all negative message expectations. RSpec/NotToNot: EnforcedStyle: not_to - Enabled: false + Enabled: true # Prefer using verifying doubles over normal doubles. RSpec/VerifiedDoubles: diff --git a/CHANGELOG b/CHANGELOG index b515d2f09b8..3e8b0a2af01 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,18 +1,39 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.9.0 (unreleased) + - Allow forking projects with restricted visibility level + - Improve note validation to prevent errors when creating invalid note via API - Redesign navigation for project pages + - Fix groups API to list only user's accessible projects + - Redesign account and email confirmation emails - Use gitlab-shell v3.0.0 + - Add DB index on users.state + - Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database - Changed the Slack build message to use the singular duration if necessary (Aran Koning) - -v 8.8.2 (unreleased) - - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources - - Fix Error 500 in CI charts by gracefully handling commits with no durations - - Fix concurrent request when updating build log in browser + - Fix issues filter when ordering by milestone + - Todos will display target state if issuable target is 'Closed' or 'Merged' + - Remove 'main language' feature + - Projects pending deletion will render a 404 page + - Measure queue duration between gitlab-workhorse and Rails + +v 8.8.3 + - Fix gitlab importer failing to import new projects due to missing credentials + - Fix import URL migration not rescuing with the correct Error + +v 8.8.2 + - Added remove due date button. !4209 + - Fix Error 500 when accessing application settings due to nil disabled OAuth sign-in sources. !4242 + - Fix Error 500 in CI charts by gracefully handling commits with no durations. !4245 + - Fix table UI on CI builds page. !4249 + - Fix backups if registry is disabled. !4263 + - Fixed issue with merge button color. !4211 + - Fixed issue with enter key selecting wrong option in dropdown. !4210 + - When creating a .gitignore file a dropdown with templates will be provided. !4075 + - Fix concurrent request when updating build log in browser. !4183 v 8.8.1 - Add documentation for the "Health Check" feature - - Allow anonymous users to access a public project's pipelines + - Allow anonymous users to access a public project's pipelines !4233 - Fix MySQL compatibility in zero downtime migrations helpers - Fix the CI login to Container Registry (the gitlab-ci-token user) @@ -29,6 +50,7 @@ v 8.8.0 - Added inline diff styling for `change_title` system notes. (Adam Butler) - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Escape HTML in commit titles in system note messages + - Improve design of Pipeline View - Fix scope used when accessing container registry - Fix creation of Ci::Commit object which can lead to pending, failed in some scenarios - Improve multiple branch push performance by memoizing permission checking @@ -90,10 +112,14 @@ v 8.8.0 - Allows MR authors to have the source branch removed when merging the MR. !2801 (Jeroen Jacobs) - When creating a .gitignore file a dropdown with templates will be provided +v 8.7.7 + - Fix import by `Any Git URL` broken if the URL contains a space + v 8.7.6 - Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko) - Fix import from GitLab.com to a private instance failure. !4181 - Fix external imports not finding the import data. !4106 + - Fix notification delay when changing status of an issue v 8.7.5 - Fix relative links in wiki pages. !4050 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index f38fc5393ff..0a1ffad4b4d 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.7.3 +0.7.4 @@ -121,7 +121,7 @@ group :unicorn do end # State machine -gem "state_machines-activerecord", '~> 0.3.0' +gem "state_machines-activerecord", '~> 0.4.0' # Run events after state machine commits gem 'after_commit_queue' @@ -178,9 +178,6 @@ gem 'ruby-fogbugz', '~> 0.2.1' # d3 gem 'd3_rails', '~> 3.5.0' -#cal-heatmap -gem 'cal-heatmap-rails', '~> 3.6.0' - # underscore-rails gem "underscore-rails", "~> 1.8.0" diff --git a/Gemfile.lock b/Gemfile.lock index 4533aa31d5f..fa2b72b2524 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -102,7 +102,6 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (8.2.1) - cal-heatmap-rails (3.6.0) capybara (2.6.2) addressable mime-types (>= 1.16) @@ -790,11 +789,11 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) state_machines (0.4.0) - state_machines-activemodel (0.3.0) - activemodel (~> 4.1) + state_machines-activemodel (0.4.0) + activemodel (>= 4.1, < 5.1) state_machines (>= 0.4.0) - state_machines-activerecord (0.3.0) - activerecord (~> 4.1) + state_machines-activerecord (0.4.0) + activerecord (>= 4.1, < 5.1) state_machines-activemodel (>= 0.3.0) stringex (2.5.2) systemu (2.6.5) @@ -908,7 +907,6 @@ DEPENDENCIES bullet bundler-audit byebug - cal-heatmap-rails (~> 3.6.0) capybara (~> 2.6.2) capybara-screenshot (~> 1.0.0) carrierwave (~> 0.10.0) @@ -1043,7 +1041,7 @@ DEPENDENCIES spring-commands-spinach (~> 1.1.0) spring-commands-teaspoon (~> 0.0.2) sprockets (~> 3.6.0) - state_machines-activerecord (~> 0.3.0) + state_machines-activerecord (~> 0.4.0) task_list (~> 1.0.2) teaspoon (~> 1.1.0) teaspoon-jasmine (~> 2.2.0) diff --git a/app/assets/images/mailers/gitlab_header_logo.png b/app/assets/images/mailers/gitlab_header_logo.png Binary files differnew file mode 100644 index 00000000000..35ca1860887 --- /dev/null +++ b/app/assets/images/mailers/gitlab_header_logo.png diff --git a/app/assets/images/mailers/gitlab_tanuki_2x.png b/app/assets/images/mailers/gitlab_tanuki_2x.png Binary files differnew file mode 100644 index 00000000000..551dd6ce2ce --- /dev/null +++ b/app/assets/images/mailers/gitlab_tanuki_2x.png diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index bffce5a0c0f..7c547ac843b 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -19,7 +19,6 @@ #= require jquery.scrollTo #= require jquery.turbolinks #= require d3 -#= require cal-heatmap #= require turbolinks #= require autosave #= require bootstrap/affix @@ -246,38 +245,6 @@ $ -> if $navIcon.hasClass('fa-angle-left') $navIconToggle.trigger('click') - $(document) - .off 'click', '.js-sidebar-toggle' - .on 'click', '.js-sidebar-toggle', (e, triggered) -> - e.preventDefault() - $this = $(this) - $thisIcon = $this.find 'i' - $allGutterToggleIcons = $('.js-sidebar-toggle i') - if $thisIcon.hasClass('fa-angle-double-right') - $allGutterToggleIcons - .removeClass('fa-angle-double-right') - .addClass('fa-angle-double-left') - $('aside.right-sidebar') - .removeClass('right-sidebar-expanded') - .addClass('right-sidebar-collapsed') - $('.page-with-sidebar') - .removeClass('right-sidebar-expanded') - .addClass('right-sidebar-collapsed') - else - $allGutterToggleIcons - .removeClass('fa-angle-double-left') - .addClass('fa-angle-double-right') - $('aside.right-sidebar') - .removeClass('right-sidebar-collapsed') - .addClass('right-sidebar-expanded') - $('.page-with-sidebar') - .removeClass('right-sidebar-collapsed') - .addClass('right-sidebar-expanded') - if not triggered - $.cookie("collapsed_gutter", - $('.right-sidebar') - .hasClass('right-sidebar-collapsed'), { path: '/' }) - fitSidebarForSize = -> oldBootstrapBreakpoint = bootstrapBreakpoint bootstrapBreakpoint = bp.getBreakpointSize() diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index d80e0e716ce..26a26061539 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -1,34 +1,198 @@ class @Calendar - constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> - cal = new CalHeatMap() - cal.init - itemName: ["contribution"] - data: timestamps - start: new Date(starting_year, starting_month) - domainLabelFormat: "%b" - id: "cal-heatmap" - domain: "month" - subDomain: "day" - range: 12 - tooltip: true - label: - position: "top" - legend: [ - 0 - 10 - 20 - 30 - ] - legendCellPadding: 3 - cellSize: $('.user-calendar').width() / 73 - onClick: (date, count) -> - formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() - $.ajax - url: calendar_activities_path - data: - date: formated_date - cache: false - dataType: "html" - success: (data) -> - $(".user-calendar-activities").html data + constructor: (timestamps, @calendar_activities_path) -> + @currentSelectedDate = '' + @daySpace = 1 + @daySize = 15 + @daySizeWithSpace = @daySize + (@daySpace * 2) + @monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + @months = [] + @highestValue = 0 + # Get the highest value from the timestampes + _.each timestamps, (count) => + if count > @highestValue + @highestValue = count + + # Loop through the timestamps to create a group of objects + # The group of objects will be grouped based on the day of the week they are + @timestampsTmp = [] + i = 0 + group = 0 + _.each timestamps, (count, date) => + newDate = new Date parseInt(date) * 1000 + day = newDate.getDay() + + # Create a new group array if this is the first day of the week + # or if is first object + if (day is 0 and i isnt 0) or i is 0 + @timestampsTmp.push [] + group++ + + innerArray = @timestampsTmp[group-1] + + # Push to the inner array the values that will be used to render map + innerArray.push + count: count + date: newDate + day: day + + i++ + + # Init color functions + @color = @initColor() + @colorKey = @initColorKey() + + # Init the svg element + @renderSvg(group) + @renderDays() + @renderMonths() + @renderDayTitles() + @renderKey() + + @initTooltips() + + renderSvg: (group) -> + @svg = d3.select '.js-contrib-calendar' + .append 'svg' + .attr 'width', (group + 1) * @daySizeWithSpace + .attr 'height', 167 + .attr 'class', 'contrib-calendar' + + renderDays: -> + @svg.selectAll 'g' + .data @timestampsTmp + .enter() + .append 'g' + .attr 'transform', (group, i) => + _.each group, (stamp, a) => + if a is 0 and stamp.day is 0 + month = stamp.date.getMonth() + x = (@daySizeWithSpace * i + 1) + @daySizeWithSpace + lastMonth = _.last(@months) + if lastMonth? + lastMonthX = lastMonth.x + + if !lastMonth? + @months.push + month: month + x: x + else if month isnt lastMonth.month and x - @daySizeWithSpace isnt lastMonthX + @months.push + month: month + x: x + + "translate(#{(@daySizeWithSpace * i + 1) + @daySizeWithSpace}, 18)" + .selectAll 'rect' + .data (stamp) -> + stamp + .enter() + .append 'rect' + .attr 'x', '0' + .attr 'y', (stamp, i) => + (@daySizeWithSpace * stamp.day) + .attr 'width', @daySize + .attr 'height', @daySize + .attr 'title', (stamp) => + contribText = 'No contributions' + + if stamp.count > 0 + contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}" + + date = dateFormat(stamp.date, 'mmm d, yyyy') + + "#{contribText}<br />#{date}" + .attr 'class', 'user-contrib-cell js-tooltip' + .attr 'fill', (stamp) => + if stamp.count isnt 0 + @color(stamp.count) + else + '#ededed' + .attr 'data-container', 'body' + .on 'click', @clickDay + + renderDayTitles: -> + days = [{ + text: 'M' + y: 29 + (@daySizeWithSpace * 1) + }, { + text: 'W' + y: 29 + (@daySizeWithSpace * 3) + }, { + text: 'F' + y: 29 + (@daySizeWithSpace * 5) + }] + @svg.append 'g' + .selectAll 'text' + .data days + .enter() + .append 'text' + .attr 'text-anchor', 'middle' + .attr 'x', 8 + .attr 'y', (day) -> + day.y + .text (day) -> + day.text + .attr 'class', 'user-contrib-text' + + renderMonths: -> + @svg.append 'g' + .selectAll 'text' + .data @months + .enter() + .append 'text' + .attr 'x', (date) -> + date.x + .attr 'y', 10 + .attr 'class', 'user-contrib-text' + .text (date) => + @monthNames[date.month] + + renderKey: -> + keyColors = ['#ededed', @colorKey(0), @colorKey(1), @colorKey(2), @colorKey(3)] + @svg.append 'g' + .attr 'transform', "translate(18, #{@daySizeWithSpace * 8 + 16})" + .selectAll 'rect' + .data keyColors + .enter() + .append 'rect' + .attr 'width', @daySize + .attr 'height', @daySize + .attr 'x', (color, i) => + @daySizeWithSpace * i + .attr 'y', 0 + .attr 'fill', (color) -> + color + + initColor: -> + d3.scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, @highestValue]) + + initColorKey: -> + d3.scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, 3]) + + clickDay: (stamp) => + if @currentSelectedDate isnt stamp.date + @currentSelectedDate = stamp.date + formatted_date = @currentSelectedDate.getFullYear() + "-" + (@currentSelectedDate.getMonth()+1) + "-" + @currentSelectedDate.getDate() + + $.ajax + url: @calendar_activities_path + data: + date: formatted_date + cache: false + dataType: 'html' + beforeSend: -> + $('.user-calendar-activities').html '<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>' + success: (data) -> + $('.user-calendar-activities').html data + else + $('.user-calendar-activities').html '' + + initTooltips: -> + $('.js-contrib-calendar .js-tooltip').tooltip + html: true diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index b10f389d94c..cd418ad244a 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -16,7 +16,6 @@ class Dispatcher shortcut_handler = null switch page when 'projects:issues:index' - Issues.init() Issuable.init() shortcut_handler = new ShortcutsNavigation() when 'projects:issues:show' diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee index afffed63ac5..6504e481102 100644 --- a/app/assets/javascripts/issuable.js.coffee +++ b/app/assets/javascripts/issuable.js.coffee @@ -1,7 +1,11 @@ +issuable_created = false @Issuable = init: -> - Issuable.initTemplates() - Issuable.initSearch() + unless issuable_created + issuable_created = true + Issuable.initTemplates() + Issuable.initSearch() + Issuable.initChecks() initTemplates: -> Issuable.labelRow = _.template( @@ -19,7 +23,16 @@ .on 'keyup', -> clearTimeout(@timer) @timer = setTimeout( -> - Issuable.filterResults $('#issue_search_form') + $search = $('#issue_search') + $form = $('.js-filter-form') + $input = $("input[name='#{$search.attr('name')}']", $form) + + if $input.length is 0 + $form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>" + else + $input.val $search.val() + + Issuable.filterResults $form , 500) toggleLabelFilters: -> @@ -59,15 +72,22 @@ dataType: "json" reload: -> - if Issues.created - Issues.initChecks() + if Issuable.created + Issuable.initChecks() $('#filter_issue_search').val($('#issue_search').val()) + initChecks: -> + $('.check_all_issues').on 'click', -> + $('.selected_issue').prop('checked', @checked) + Issuable.checkChanged() + + $('.selected_issue').on 'change', Issuable.checkChanged + updateStateFilters: -> - stateFilters = $('.issues-state-filters') + stateFilters = $('.issues-state-filters, .dropdown-menu-sort') newParams = {} - paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search'] + paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search', 'issue_search'] for paramKey in paramKeys newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or '' @@ -82,3 +102,17 @@ else newUrl = gl.utils.mergeUrlParams(newParams, initialUrl) $(this).attr 'href', newUrl + + checkChanged: -> + checked_issues = $('.selected_issue:checked') + if checked_issues.length > 0 + ids = $.map checked_issues, (value) -> + $(value).data('id') + + $('#update_issues_ids').val ids + $('.issues-other-filters').hide() + $('.issues_bulk_update').show() + else + $('#update_issues_ids').val [] + $('.issues_bulk_update').hide() + $('.issues-other-filters').show() diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee index 72ae3bde81e..898506fde32 100644 --- a/app/assets/javascripts/issuable_form.js.coffee +++ b/app/assets/javascripts/issuable_form.js.coffee @@ -19,6 +19,7 @@ class @IssuableForm @form.on "click", ".btn-cancel", @resetAutosave @initWip() + @initMoveDropdown() $issuableDueDate = $('#issuable-due-date') @@ -89,3 +90,19 @@ class @IssuableForm addWip: -> @titleField.val "WIP: #{@titleField.val()}" + + initMoveDropdown: -> + $moveDropdown = $('.js-move-dropdown') + + if $moveDropdown.length + $('.js-move-dropdown').select2 + ajax: + url: $moveDropdown.data('projects-url') + results: (data) -> + return { + results: data + } + formatResult: (project) -> + project.name_with_namespace + formatSelection: (project) -> + project.name_with_namespace diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee deleted file mode 100644 index 3330e6c68ad..00000000000 --- a/app/assets/javascripts/issues.js.coffee +++ /dev/null @@ -1,38 +0,0 @@ -@Issues = - init: -> - Issues.created = true - Issues.initChecks() - - $("body").on "ajax:success", ".close_issue, .reopen_issue", -> - t = $(this) - totalIssues = undefined - reopen = t.hasClass("reopen_issue") - $(".issue_counter").each -> - issue = $(this) - totalIssues = parseInt($(this).html(), 10) - if reopen and issue.closest(".main_menu").length - $(this).html totalIssues + 1 - else - $(this).html totalIssues - 1 - - initChecks: -> - $(".check_all_issues").click -> - $(".selected_issue").prop("checked", @checked) - Issues.checkChanged() - - $(".selected_issue").bind "change", Issues.checkChanged - - checkChanged: -> - checked_issues = $(".selected_issue:checked") - if checked_issues.length > 0 - ids = [] - $.each checked_issues, (index, value) -> - ids.push $(value).attr("data-id") - - $("#update_issues_ids").val ids - $(".issues-other-filters").hide() - $(".issues_bulk_update").show() - else - $("#update_issues_ids").val [] - $(".issues_bulk_update").hide() - $(".issues-other-filters").show() diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee new file mode 100644 index 00000000000..6adac6dac97 --- /dev/null +++ b/app/assets/javascripts/layout_nav.js.coffee @@ -0,0 +1,14 @@ +class @LayoutNav + $ -> + $('.fade-left').addClass('end-scroll') + $('.scrolling-tabs').on 'scroll', (event) -> + $this = $(this) + $el = $(event.target) + currentPosition = $this.scrollLeft() + size = bp.getBreakpointSize() + controlBtnWidth = $('.controls').width() + maxPosition = $this.get(0).scrollWidth - $this.parent().width() + maxPosition += controlBtnWidth if size isnt 'xs' and $('.nav-control').length + + $el.find('.fade-left').toggleClass('end-scroll', currentPosition is 0) + $el.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition) diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/url_utility.js.coffee index 6a00932c028..e8085e1c2e4 100644 --- a/app/assets/javascripts/lib/url_utility.js.coffee +++ b/app/assets/javascripts/lib/url_utility.js.coffee @@ -26,10 +26,19 @@ newUrl = decodeURIComponent(url) for paramName, paramValue of params pattern = new RegExp "\\b(#{paramName}=).*?(&|$)" - if url.search(pattern) >= 0 + if not paramValue? + newUrl = newUrl.replace pattern, '' + else if url.search(pattern) isnt -1 newUrl = newUrl.replace pattern, "$1#{paramValue}$2" else newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}" + + # Remove a trailing ampersand + lastChar = newUrl[newUrl.length - 1] + + if lastChar is '&' + newUrl = newUrl.slice 0, -1 + newUrl # removes parameter query string from url. returns the modified url diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index b6d590f681c..779f536d9f0 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -10,6 +10,7 @@ class @MergeRequestWidget $('#modal_merge_info').modal(show: false) @firstCICheck = true @readyForCICheck = false + @cancel = false clearInterval @fetchBuildStatusInterval @clearEventListeners() @@ -21,10 +22,16 @@ class @MergeRequestWidget clearEventListeners: -> $(document).off 'page:change.merge_request' + cancelPolling: -> + @cancel = true + addEventListeners: -> + allowedPages = ['show', 'commits', 'builds', 'changes'] $(document).on 'page:change.merge_request', => - if $('body').data('page') isnt 'projects:merge_requests:show' + page = $('body').data('page').split(':').last() + if allowedPages.indexOf(page) < 0 clearInterval @fetchBuildStatusInterval + @cancelPolling() @clearEventListeners() mergeInProgress: (deleteSourceBranch = false)-> @@ -67,6 +74,7 @@ class @MergeRequestWidget $('.ci-widget-fetching').show() $.getJSON @opts.ci_status_url, (data) => + return if @cancel @readyForCICheck = true if data.status is '' @@ -106,6 +114,7 @@ class @MergeRequestWidget @firstCICheck = false showCIStatus: (state) -> + return if not state? $('.ci_widget').hide() allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"] if state in allowed_states @@ -126,6 +135,6 @@ class @MergeRequestWidget $('.ci_widget:visible .ci-coverage').text(text) setMergeButtonClass: (css_class) -> - $('.js-merge-button') + $('.js-merge-button,.accept-action .dropdown-toggle') .removeClass('btn-danger btn-warning btn-create') .addClass(css_class) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b8359c31dfa..7c3d57fc194 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -114,9 +114,9 @@ class @Notes @refresh() , @pollingInterval - refresh: -> + refresh: => return if @refreshing is true - refreshing = true + @refreshing = true if not document.hidden and document.URL.indexOf(@noteable_url) is 0 @getContent() @@ -134,8 +134,8 @@ class @Notes @renderDiscussionNote(note) else @renderNote(note) - always: => - @refreshing = false + .always () => + @refreshing = false ### Increase @pollingInterval up to 120 seconds on every function call, @@ -329,7 +329,7 @@ class @Notes @renderDiscussionNote(note) # cleanup after successfully creating a diff/discussion note - @removeDiscussionNoteForm($("#new-discussion-note-form-#{note.discussion_id}")) + @removeDiscussionNoteForm($(xhr.target)) ### Called in response to the edit note form being submitted diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 2d084b76cfe..c9cb0f4bb32 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -10,6 +10,40 @@ class @Sidebar $('.dropdown').on('loading.gl.dropdown', @sidebarDropdownLoading) $('.dropdown').on('loaded.gl.dropdown', @sidebarDropdownLoaded) + + $(document) + .off 'click', '.js-sidebar-toggle' + .on 'click', '.js-sidebar-toggle', (e, triggered) -> + e.preventDefault() + $this = $(this) + $thisIcon = $this.find 'i' + $allGutterToggleIcons = $('.js-sidebar-toggle i') + if $thisIcon.hasClass('fa-angle-double-right') + $allGutterToggleIcons + .removeClass('fa-angle-double-right') + .addClass('fa-angle-double-left') + $('aside.right-sidebar') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed') + $('.page-with-sidebar') + .removeClass('right-sidebar-expanded') + .addClass('right-sidebar-collapsed') + else + $allGutterToggleIcons + .removeClass('fa-angle-double-left') + .addClass('fa-angle-double-right') + $('aside.right-sidebar') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded') + $('.page-with-sidebar') + .removeClass('right-sidebar-collapsed') + .addClass('right-sidebar-expanded') + if not triggered + $.cookie("collapsed_gutter", + $('.right-sidebar') + .hasClass('right-sidebar-collapsed'), { path: '/' }) + + sidebarDropdownLoading: (e) -> $sidebarCollapsedIcon = $(@).closest('.block').find('.sidebar-collapsed-icon') img = $sidebarCollapsedIcon.find('img') @@ -76,7 +110,7 @@ class @Sidebar @triggerOpenSidebar() if not @isOpen() if action is 'hide' - @triggerOpenSidebar() is @isOpen() + @triggerOpenSidebar() if @isOpen() isOpen: -> @sidebar.is('.right-sidebar-expanded') diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index b80b1b861cc..519618aa617 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -93,7 +93,9 @@ class @UsersSelect $dropdown.glDropdown( data: (term, callback) => - @users term, (users) => + isAuthorFilter = $('.js-author-search') + + @users term, term is '' and isAuthorFilter, (users) => if term.length is 0 showDivider = 0 @@ -138,7 +140,7 @@ class @UsersSelect toggleLabel: (selected) -> if selected && 'id' of selected - selected.name + if selected.text then selected.text else selected.name else defaultLabel @@ -219,7 +221,7 @@ class @UsersSelect multiple: $(select).hasClass('multiselect') minimumInputLength: 0 query: (query) => - @users query.term, (users) => + @users query.term, @projectId?, (users) => data = { results: users } if query.term.length == 0 @@ -302,7 +304,7 @@ class @UsersSelect # Return users list. Filtered by query # Only active users retrieved - users: (query, callback) => + users: (query, fromProject, callback) => url = @buildUrl(@usersPath) $.ajax( @@ -311,7 +313,7 @@ class @UsersSelect search: query per_page: 20 active: true - project_id: @projectId + project_id: @projectId if fromProject group_id: @groupId current_user: @showCurrentUser author_id: @authorId diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e2d590f4df4..8b93665d085 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -8,7 +8,6 @@ *= require select2 *= require_self *= require dropzone/basic - *= require cal-heatmap *= require cropper.css */ diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 11f39d583bd..8642b7530e2 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -1,70 +1,44 @@ .calender-block { + padding-left: 0; + padding-right: 0; + @media (min-width: $screen-sm-min) and (max-width: $screen-lg-min) { overflow-x: scroll; } } .user-calendar-activities { - .calendar_onclick_hr { - padding: 0; - margin: 10px 0; - } - .str-truncated { max-width: 70%; } - .text-expander { - background: #eee; - color: #555; - padding: 0 5px; - cursor: pointer; - margin-left: 4px; - &:hover { - background-color: #ddd; - } + .user-calendar-activities-loading { + font-size: 24px; } } -/** -* This overwrites the default values of the cal-heatmap gem -*/ -.calendar { - .qi { - fill: #fff; - } - - .q1 { - fill: #ededed !important; - } +.user-calendar { + text-align: center; - .q2 { - fill: #acd5f2 !important; - } - - .q3 { - fill: #7fa8d1 !important; - } - - .q4 { - fill: #49729b !important; - } - - .q5 { - fill: #254e77 !important; + .calendar { + display: inline-block; } +} - .future { - visibility: hidden; +.user-contrib-cell { + &:hover { + cursor: pointer; + stroke: #000; } +} - .domain-background { - fill: none; - shape-rendering: crispedges; - } +.user-contrib-text { + font-size: 12px; + fill: #959494; +} - .ch-tooltip { - padding: 3px; - font-weight: 550; - } +.calendar-hint { + margin-top: -23px; + float: right; + font-size: 12px; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 79a37d87e82..93c63c69843 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -154,7 +154,7 @@ color: $dropdown-header-color; font-size: 13px; line-height: 22px; - padding: 0 10px 10px; + padding: 0 10px; } .separator + .dropdown-header { diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index c70be58f48f..7eb7a8e4544 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -1,3 +1,34 @@ +@mixin fade($gradient-direction, $rgba, $gradient-color) { + visibility: visible; + opacity: 1; + position: absolute; + bottom: 12px; + width: 43px; + height: 30px; + transition-duration: .3s; + -webkit-transform: translateZ(0); + background: -webkit-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: -o-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%); + + &.end-scroll { + visibility: hidden; + opacity: 0; + transition-duration: .3s; + } +} + +@mixin scrolling-links() { + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + &::-webkit-scrollbar { + display: none; + } +} + .nav-links { padding: 0; margin: 0; @@ -119,7 +150,7 @@ } input { - height: 34px; + height: 35px; display: inline-block; position: relative; top: 2px; @@ -196,7 +227,7 @@ position: fixed; top: $header-height; width: 100%; - z-index: 3; + z-index: 11; background: $background-color; border-bottom: 1px solid $border-color; transition-duration: .3s; @@ -209,13 +240,8 @@ float: right; padding: 7px 0 0; - @media (max-width: $screen-xs-min) { - float: none; - padding: 0 9px; - - .dropdown-new { - width: 100%; - } + @media (max-width: $screen-xs-max) { + display: none; } i { @@ -246,14 +272,18 @@ } .nav-links { + @include scrolling-links(); border-bottom: none; height: 51px; - white-space: nowrap; - overflow-x: auto; - overflow-y: hidden; - -webkit-overflow-scrolling: touch; - &::-webkit-scrollbar { - display: none; + + .fade-right { + @include fade(left, rgba(250, 250, 250, 0.4), $background-color); + right: 0; + } + + .fade-left { + @include fade(right, rgba(250, 250, 250, 0.4), $background-color); + left: 0; } li { @@ -278,18 +308,62 @@ } } + .nav-control { + .fade-right { + + @media (min-width: $screen-xs-max) { + right: 67px; + } + @media (max-width: $screen-xs-min) { + right: 0; + } + } + } } -.page-with-layout-nav { - margin-top: 50px; +.nav-block { + position: relative; - &.controls-dropdown-visible { - @media (max-width: $screen-xs-min) { - margin-top: 96px; + .nav-links { + @include scrolling-links(); + + .fade-right { + @include fade(left, rgba(255, 255, 255, 0.4), $white-light); + right: 0; + } + + .fade-left { + @include fade(right, rgba(255, 255, 255, 0.4), $white-light); + left: 0; + } + + &.event-filter { + .fade-right { + visibility: hidden; + + @media (max-width: $screen-xs-max) { + visibility: visible; + } + } } } +} + +.page-with-layout-nav { + margin-top: $header-height + 2; .right-sidebar { top: ($header-height * 2) + 2; } } + +.activities { + + .nav-block { + border-bottom: 1px solid $border-color; + + .nav-links { + border-bottom: none; + } + } +} diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index f90d7a806d3..67f491b6d9c 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -324,7 +324,7 @@ .layout-nav { @media (max-width: $screen-xs-min) { - padding-right: 0;; + padding-right: 0; } @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) { diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss new file mode 100644 index 00000000000..28611a5ec81 --- /dev/null +++ b/app/assets/stylesheets/mailers/devise.scss @@ -0,0 +1,134 @@ +// NOTE: This stylesheet is for the exclusive use of the `devise_mailer` layout +// used for Devise email templates, and _should not_ be included in any +// application stylesheets. +// +// Styles defined here are embedded directly into the resulting email HTML via +// the `premailer` gem. + +$body-background-color: #363636; +$message-background-color: #fafafa; + +$header-color: #6b4fbb; +$body-color: #444; +$cta-color: #e14329; +$footer-link-color: #7e7e7e; + +$font-family: Helvetica, Arial, sans-serif; + +body { + background-color: $body-background-color; + font-family: $font-family; + margin: 0; + padding: 0; +} + +table { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + + border: 0; + border-collapse: separate; + + &#wrapper { + background-color: $body-background-color; + width: 100%; + } + + &#header { + margin: 0 auto; + text-align: left; + width: 600px; + } + + &#body { + background-color: $message-background-color; + border: 1px solid #000; + border-radius: 4px; + margin: 0 auto; + width: 600px; + } + + &#footer { + color: $footer-link-color; + font-size: 14px; + text-align: center; + width: 100%; + } + + td { + &#body-container { + padding: 20px 40px; + } + } +} + +.center { + text-align: center; +} + +#logo { + border: none; + outline: none; + min-height: 88px; + width: 134px; +} + +#content { + h2 { + color: $header-color; + font-size: 30px; + font-weight: 400; + line-height: 34px; + margin-top: 0; + } + + p { + color: $body-color; + font-size: 17px; + line-height: 24px; + margin-bottom: 0; + } +} + +#cta { + border: 1px solid $cta-color; + border-radius: 3px; + display: inline-block; + margin: 20px 0; + padding: 12px 24px; + + a { + background-color: $message-background-color; + color: $cta-color; + display: inline-block; + text-decoration: none; + } +} + +#tanuki { + padding: 40px 0 0; + + img { + border: none; + outline: none; + width: 37px; + min-height: 36px; + } +} + +#tagline { + font-size: 22px; + font-weight: 100; + padding: 4px 0 40px; +} + +#social { + padding: 0 10px 20px; + width: 600px; + word-spacing: 20px; + + a { + color: $footer-link-color; + text-decoration: none; + } +} diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index c2cd227571f..fc3f214aba5 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -26,8 +26,28 @@ .commit-info-row { margin-bottom: 10px; + + &.commit-info-row-header { + line-height: 34px; + + @media (min-width: $screen-sm-min) { + margin-bottom: 0; + } + + .commit-options-dropdown-caret { + @media (max-width: $screen-sm) { + margin-left: 0; + } + } + } + .avatar { @extend .avatar-inline; + margin-left: 0; + + @media (min-width: $screen-sm-min) { + margin-left: 4px; + } } .commit-committer-link, .commit-author-link { @@ -35,10 +55,6 @@ font-weight: bold; } - .time_ago { - margin-left: 8px; - } - .fa-clipboard { color: $dropdown-title-btn-color; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index fc9db97132d..4e35ca329e4 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -40,11 +40,6 @@ } } -.issue-search-form { - margin: 0; - height: 24px; -} - form.edit-issue { margin: 0; } @@ -96,8 +91,3 @@ form.edit-issue { .issue-form .select2-container { width: 250px !important; } - -.issue-closed-by-widget { - color: $gl-text-color; - margin-left: 52px; -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index c4005ba1e69..4f8a8748d3f 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -280,11 +280,5 @@ background-color: $white-light; color: $gl-placeholder-color; } - - th, - td { - padding: 16px; - } } } - diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 546176b65e4..6128868b670 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -1,4 +1,24 @@ -.pipeline-stage { - overflow: hidden; - text-overflow: ellipsis; +.pipelines { + .stage { + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .duration, .finished_at { + margin: 4px 0; + } + + .commit-title { + margin: 0; + } + + .controls { + white-space: nowrap; + } + + .btn { + margin: 4px; + } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8040dd0a1d8..167ab40d881 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -66,12 +66,6 @@ } } -.calendar-hint { - margin-top: -12px; - float: right; - font-size: 12px; -} - .profile-link-holder { display: inline; diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index e51c3491dae..afc00a68572 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -29,6 +29,17 @@ .todo-item { .todo-title { @include str-truncated(calc(100% - 174px)); + overflow: visible; + } + + .status-box { + margin: 0; + float: none; + display: inline-block; + font-weight: normal; + padding: 0 5px; + line-height: inherit; + font-size: 14px; } .todo-body { @@ -76,12 +87,11 @@ @media (max-width: $screen-xs-max) { .todo-item { - padding-left: $gl-padding; - .todo-title { white-space: normal; overflow: visible; max-width: 100%; + margin-bottom: 10px; } .avatar { diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index eb0abc80ab4..3865b2d61fd 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -31,6 +31,24 @@ class AutocompleteController < ApplicationController render json: @user, only: [:name, :username, :id], methods: [:avatar_url] end + def projects + project = Project.find_by_id(params[:project_id]) + + projects = current_user.authorized_projects + projects = projects.select do |project| + current_user.can?(:admin_issue, project) + end + + no_project = { + id: 0, + name_with_namespace: 'No project', + } + projects.unshift(no_project) + projects.delete(project) + + render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace) + end + private def find_users diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index be872a93fee..776ba92c9ab 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController project_path = "#{namespace}/#{id}" @project = Project.find_with_namespace(project_path) - if @project && can?(current_user, :read_project, @project) + if can?(current_user, :read_project, @project) && !@project.pending_delete? if @project.path_with_namespace != project_path redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index c48e4679473..883f02e106e 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -230,6 +230,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController if ci_commit status = ci_commit.status coverage = ci_commit.try(:coverage) + + status ||= "preparing" else ci_service = @merge_request.source_project.ci_service status = ci_service.commit_status(merge_request.last_commit.sha, merge_request.source_branch) if ci_service @@ -239,8 +241,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end - status = "preparing" if status.nil? - response = { title: merge_request.title, sha: merge_request.last_commit_short_sha, diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 799421c185b..a99632454d9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -74,8 +74,6 @@ class UsersController < ApplicationController def calendar calendar = contributions_calendar @timestamps = calendar.timestamps - @starting_year = calendar.starting_year - @starting_month = calendar.starting_month render 'calendar', layout: false end diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb index 3b9a421b118..aa8f4c1d0e4 100644 --- a/app/finders/group_projects_finder.rb +++ b/app/finders/group_projects_finder.rb @@ -18,7 +18,7 @@ class GroupProjectsFinder < UnionFinder projects = [] if current_user - if @group.users.include?(current_user) + if @group.users.include?(current_user) || current_user.admin? projects << @group.projects unless only_shared projects << @group.shared_projects unless only_owned else diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 5849e00662b..8ed3ccf1c02 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -250,12 +250,12 @@ class IssuableFinder def by_milestone(items) if milestones? if filter_by_no_milestone? - items = items.where(milestone_id: [-1, nil]) + items = items.left_joins_milestones.where(milestone_id: [-1, nil]) elsif filter_by_upcoming_milestone? upcoming_ids = Milestone.upcoming_ids_by_projects(projects) - items = items.joins(:milestone).where(milestone_id: upcoming_ids) + items = items.left_joins_milestones.where(milestone_id: upcoming_ids) else - items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) + items = items.with_milestone(params[:milestone_title]) if projects items = items.where(milestones: { project_id: projects }) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e6e2546b92f..439b015b3b8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -262,6 +262,8 @@ module ApplicationHelper assignee_id: params[:assignee_id], author_id: params[:author_id], sort: params[:sort], + issue_search: params[:issue_search], + label_name: params[:label_name] } options = exist_opts.merge(options) @@ -272,16 +274,11 @@ module ApplicationHelper end end - path = request.path - path << "?#{options.to_param}" - if add_label - if params[:label_name].present? and params[:label_name].respond_to?('any?') - params[:label_name].each do |label| - path << "&label_name[]=#{label}" - end - end - end - path + params = options.compact + + params.delete(:label_name) unless add_label + + "#{request.path}?#{params.to_param}" end def outdated_browser? diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index b59c3982edd..d328f56c80c 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -123,13 +123,14 @@ module CommitsHelper ) end - def revert_commit_link(commit, continue_to_path, btn_class: nil) + def revert_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) return unless current_user - tooltip = "Revert this #{commit.change_type_title} in a new merge request" + tooltip = "Revert this #{commit.change_type_title} in a new merge request" if has_tooltip if can_collaborate_with_project? - link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip" + btn_class = "btn btn-grouped btn-close btn-#{btn_class}" unless btn_class.nil? + link_to 'Revert', '#modal-revert-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { to: continue_to_path, @@ -140,17 +141,20 @@ module CommitsHelper namespace_key: current_user.namespace.id, continue: continue_params) - link_to 'Revert', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip + btn_class = "btn btn-grouped btn-close" unless btn_class.nil? + + link_to 'Revert', fork_path, class: btn_class, method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end - def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil) + def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) return unless current_user tooltip = "Cherry-pick this #{commit.change_type_title} in a new merge request" if can_collaborate_with_project? - link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: tooltip, class: "btn btn-default btn-grouped btn-#{btn_class} has-tooltip" + btn_class = "btn btn-default btn-grouped btn-#{btn_class}" unless btn_class.nil? + link_to 'Cherry-pick', '#modal-cherry-pick-commit', 'data-toggle' => 'modal', 'data-container' => 'body', title: (tooltip if has_tooltip), class: "#{btn_class} #{'has-tooltip' if has_tooltip}" elsif can?(current_user, :fork_project, @project) continue_params = { to: continue_to_path, @@ -161,7 +165,8 @@ module CommitsHelper namespace_key: current_user.namespace.id, continue: continue_params) - link_to 'Cherry-pick', fork_path, class: 'btn btn-grouped btn-close', method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: tooltip + btn_class = "btn btn-grouped btn-close" unless btn_class.nil? + link_to 'Cherry-pick', fork_path, class: "#{btn_class}", method: :post, 'data-toggle' => 'tooltip', 'data-container' => 'body', title: (tooltip if has_tooltip) end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index e1489381706..bfedcb1c42b 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -159,28 +159,6 @@ module EventsHelper "--broken encoding" end - def event_to_atom(xml, event) - if event.visible_to_user?(current_user) - xml.entry do - event_link = event_feed_url(event) - event_title = event_feed_title(event) - event_summary = event_feed_summary(event) - - xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" - xml.link href: event_link - xml.title truncate(event_title, length: 80) - xml.updated event.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email)) - xml.author do |author| - xml.name event.author_name - xml.email event.author_email - end - - xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? } - end - end - end - def event_row_class(event) if event.body? "event-block" diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 39474217286..fe84ee3de44 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -50,14 +50,10 @@ module IssuablesHelper end def user_dropdown_label(user_id, default_label) + return default_label if user_id.nil? return "Unassigned" if user_id == "0" - if @project - member = @project.team.find_member(user_id) - user = member.user if member - else - user = User.find_by(id: user_id) - end + user = User.find_by(id: user_id) if user user.name @@ -76,7 +72,7 @@ module IssuablesHelper def issuable_meta(issuable, project, text) output = content_tag :strong, "#{text} #{issuable.to_reference}", class: "identifier" - output << " opened #{time_ago_with_tooltip(issuable.created_at)} by".html_safe + output << " opened #{time_ago_with_tooltip(issuable.created_at)} by ".html_safe output << content_tag(:strong) do author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 0ea712c8266..72bd1fbbd81 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -105,23 +105,6 @@ module IssuesHelper return 'hidden' if issue.closed? == closed end - def issue_to_atom(xml, issue) - xml.entry do - xml.id namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.link href: namespace_project_issue_url(issue.project.namespace, - issue.project, issue) - xml.title truncate(issue.title, length: 80) - xml.updated issue.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) - xml.author do |author| - xml.name issue.author_name - xml.email issue.author_email - end - xml.summary issue.title - end - end - def merge_requests_sentence(merge_requests) # Sorting based on the `!123` or `group/project!123` reference will sort # local merge requests first. diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index fbb799eecd3..f685e547537 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -48,7 +48,7 @@ module NavHelper "page-with-layout-nav" if defined?(nav) && nav end - def layout_dropdown_class - "controls-dropdown-visible" if current_user + def nav_control_class + "nav-control" if current_user end end diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 81b9b5d7052..b9d7edb4185 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -37,6 +37,16 @@ module TodosHelper end end + def todo_target_state_pill(todo) + return unless show_todo_state?(todo) + + content_tag(:span, nil, class: 'target-status') do + content_tag(:span, nil, class: "status-box status-box-#{todo.target.state.dasherize}") do + todo.target.state.capitalize + end + end + end + def todos_filter_params { state: params[:state], @@ -95,4 +105,10 @@ module TodosHelper options_from_collection_for_select(types, 'name', 'title', params[:type]) end + + private + + def show_todo_state?(todo) + (todo.target.is_a?(MergeRequest) || todo.target.is_a?(Issue)) && ['closed', 'merged'].include?(todo.target.state) + end end diff --git a/app/mailers/devise_mailer.rb b/app/mailers/devise_mailer.rb index b616add283a..415f6e12885 100644 --- a/app/mailers/devise_mailer.rb +++ b/app/mailers/devise_mailer.rb @@ -1,4 +1,6 @@ class DeviseMailer < Devise::Mailer default from: "#{Gitlab.config.gitlab.email_display_name} <#{Gitlab.config.gitlab.email_from}>" default reply_to: Gitlab.config.gitlab.email_reply_to + + layout 'devise_mailer' end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 83d3bfe8e17..57ded0f91ad 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -32,18 +32,21 @@ module Issuable scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } scope :of_milestones, ->(ids) { where(milestone_id: ids) } + scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) } scope :opened, -> { with_state(:opened, :reopened) } scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } scope :closed, -> { with_state(:closed) } - scope :order_milestone_due_desc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date DESC, milestones.id DESC') } - scope :order_milestone_due_asc, -> { outer_join_milestone.reorder('milestones.due_date IS NULL ASC, milestones.due_date ASC, milestones.id ASC') } - scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } + scope :left_joins_milestones, -> { joins("LEFT OUTER JOIN milestones ON #{table_name}.milestone_id = milestones.id") } + scope :order_milestone_due_desc, -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date DESC') } + scope :order_milestone_due_asc, -> { left_joins_milestones.reorder('milestones.due_date IS NULL, milestones.id IS NULL, milestones.due_date ASC') } + + scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } scope :non_archived, -> { join_project.where(projects: { archived: false }) } - scope :outer_join_milestone, -> { joins("LEFT OUTER JOIN milestones ON milestones.id = #{table_name}.milestone_id") } + delegate :name, :email, diff --git a/app/models/note.rb b/app/models/note.rb index 2b1a3f90697..bbe5545dc80 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -28,10 +28,17 @@ class Note < ActiveRecord::Base # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } - validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } - validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } + validates :noteable_type, presence: true + validates :noteable_id, presence: true, unless: :for_commit? + validates :commit_id, presence: true, if: :for_commit? validates :author, presence: true + validate unless: :for_commit? do |note| + unless note.noteable.try(:project) == note.project + errors.add(:invalid_project, 'Note and noteable project mismatch') + end + end + mount_uploader :attachment, AttachmentUploader # Scopes diff --git a/app/models/project.rb b/app/models/project.rb index 37de1dfe4d5..f3a56b86d5a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,7 +431,13 @@ class Project < ActiveRecord::Base def check_limit unless creator.can_create_project? or namespace.kind == 'group' - self.errors.add(:limit_reached, "Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") + projects_limit = creator.projects_limit + + if projects_limit == 0 + self.errors.add(:limit_reached, "Personal project creation is not allowed. Please contact your administrator with questions") + else + self.errors.add(:limit_reached, "Your project limit is #{projects_limit} projects! Please contact your administrator to increase it") + end end rescue self.errors.add(:base, "Can't check your ability to create project") diff --git a/app/models/repository.rb b/app/models/repository.rb index ecc8795c954..1ab163510bf 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -972,12 +972,6 @@ class Repository end end - def main_language - return unless head_exists? - - Linguist::Repository.new(rugged, rugged.head.target_id).language - end - def avatar return nil unless exists? diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 66136b62617..a886f35981f 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -53,10 +53,6 @@ class GitPushService < BaseService # could cause the last commit of a merge request to change. update_merge_requests - # Checks if the main language has changed in the project and if so - # it updates it accordingly - update_main_language - perform_housekeeping end @@ -64,19 +60,6 @@ class GitPushService < BaseService @project.repository.copy_gitattributes(params[:ref]) end - def update_main_language - # Performance can be bad so for now only check main_language once - # See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937 - return if @project.main_language.present? - - return unless is_default_branch? - return unless push_to_new_branch? || push_to_existing_branch? - - current_language = @project.repository.main_language - @project.update_attributes(main_language: current_language) - true - end - protected def update_merge_requests diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 44ff96f9bd8..02fca5c0ea3 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -12,8 +12,6 @@ module Notes return noteable.create_award_emoji(note.award_emoji_name, current_user) end - return unless valid_project?(note) - if note.save # Finish the harder work in the background NewNoteWorker.perform_in(2.seconds, note.id, params) @@ -22,14 +20,5 @@ module Notes note end - - private - - def valid_project?(note) - return false unless project - return true if note.for_commit? - - note.noteable.try(:project) == project - end end end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 0577ae778d5..de6dc38cc8e 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -3,7 +3,7 @@ module Projects def execute new_params = { forked_from_project_id: @project.id, - visibility_level: @project.visibility_level, + visibility_level: allowed_visibility_level, description: @project.description, name: @project.name, path: @project.path, @@ -19,5 +19,17 @@ module Projects new_project = CreateService.new(current_user, new_params).execute new_project end + + private + + def allowed_visibility_level + project_level = @project.visibility_level + + if Gitlab::VisibilityLevel.non_restricted_level?(project_level) + project_level + else + Gitlab::VisibilityLevel.highest_allowed_level + end + end end end diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index ed24757087b..d74cf8598e8 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -47,4 +47,3 @@ = render "admin/builds/build", build: build = paginate @builds, theme: 'gitlab' - diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 0d7b1b30dc3..83c0c6da21b 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -6,8 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id issues_dashboard_url xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end - diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index d4daf07c6c0..fb5be63b472 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id dashboard_projects_url xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(partial: 'events/event', collection: @events) if @events.any? end diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index 539f1dc6036..98f302d2f93 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -3,6 +3,8 @@ = image_tag avatar_icon(todo.author_email, 40), class: 'avatar s40', alt:'' .todo-title.title - unless todo.build_failed? + = todo_target_state_pill(todo) + %span.author-name - if todo.author = link_to_author(todo) diff --git a/app/views/devise/mailer/confirmation_instructions.html.erb b/app/views/devise/mailer/confirmation_instructions.html.erb deleted file mode 100644 index c6fa8f0ee36..00000000000 --- a/app/views/devise/mailer/confirmation_instructions.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<p>Welcome <%= @resource.name %>!</p> - -<% if @resource.unconfirmed_email.present? %> - <p>You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below:</p> -<% else %> - <p>You can confirm your account through the link below:</p> -<% end %> - -<p><%= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) %></p> diff --git a/app/views/devise/mailer/confirmation_instructions.html.haml b/app/views/devise/mailer/confirmation_instructions.html.haml new file mode 100644 index 00000000000..086bb8e083d --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.html.haml @@ -0,0 +1,16 @@ +.center + - if @resource.unconfirmed_email.present? + #content + %h2= @resource.unconfirmed_email + %p Click the link below to confirm your email address. + #cta + = link_to 'Confirm your email address', confirmation_url(@resource, confirmation_token: @token) + - else + #content + - if Gitlab.com? + %h2 Thanks for signing up to GitLab! + - else + %h2 Welcome, #{@resource.name}! + %p To get started, click the link below to confirm your account. + #cta + = link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) diff --git a/app/views/devise/mailer/confirmation_instructions.text.erb b/app/views/devise/mailer/confirmation_instructions.text.erb new file mode 100644 index 00000000000..9f76edb76a4 --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.text.erb @@ -0,0 +1,9 @@ +Welcome, <%= @resource.name %>! + +<% if @resource.unconfirmed_email.present? %> +You can confirm your email (<%= @resource.unconfirmed_email %>) through the link below: +<% else %> +You can confirm your account through the link below: +<% end %> + +<%= confirmation_url(@resource, confirmation_token: @token) %> diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml index eae80e5210f..ce050007204 100644 --- a/app/views/doorkeeper/authorizations/new.html.haml +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -1,4 +1,4 @@ -%h3.page-title Authorize required +%h3.page-title Authorization required %main{:role => "main"} %p.h4 Authorize diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder new file mode 100644 index 00000000000..7890e717aa7 --- /dev/null +++ b/app/views/events/_event.atom.builder @@ -0,0 +1,20 @@ +return unless event.visible_to_user?(current_user) + +xml.entry do + xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" + xml.link href: event_feed_url(event) + xml.title truncate(event_feed_title(event), length: 80) + xml.updated event.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email)) + + xml.author do + xml.name event.author_name + xml.email event.author_email + end + + xml.summary(type: "xhtml") do |summary| + event_summary = event_feed_summary(event) + + summary << event_summary unless event_summary.nil? + end +end diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index a6eb9abada6..c19671295af 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -6,8 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id issues_group_url xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end - diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index c66b82bb484..b68bf444d27 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id group_url(@group) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index e3a356b5379..aedb8468eca 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -47,7 +47,7 @@ %td.import-target = repo["path_with_namespace"] %td.import-actions.job-status - = button_tag class: "btn js-add-to-import" do + = button_tag class: "btn btn-import js-add-to-import" do Import = icon("spinner spin", class: "loading-icon") diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder new file mode 100644 index 00000000000..68a2d19e58d --- /dev/null +++ b/app/views/issues/_issue.atom.builder @@ -0,0 +1,14 @@ +xml.entry do + xml.id namespace_project_issue_url(issue.project.namespace, issue.project, issue) + xml.link href: namespace_project_issue_url(issue.project.namespace, issue.project, issue) + xml.title truncate(issue.title, length: 80) + xml.updated issue.created_at.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) + + xml.author do |author| + xml.name issue.author_name + xml.email issue.author_email + end + + xml.summary issue.title +end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 5be0b546a62..1e961853c70 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -25,7 +25,7 @@ .layout-nav .container-fluid = render "layouts/nav/#{nav}" - .content-wrapper{ class: "#{layout_nav_class} #{layout_dropdown_class}" } + .content-wrapper{ class: "#{layout_nav_class}" } = render "layouts/broadcast" = render "layouts/flash" = yield :flash_message diff --git a/app/views/layouts/devise_mailer.html.haml b/app/views/layouts/devise_mailer.html.haml new file mode 100644 index 00000000000..c258eafdd51 --- /dev/null +++ b/app/views/layouts/devise_mailer.html.haml @@ -0,0 +1,34 @@ +!!! 5 +%html + %head + %meta(content='text/html; charset=UTF-8' http-equiv='Content-Type') + = stylesheet_link_tag 'mailers/devise' + + %body + %table#wrapper + %tr + %td + %table#header + %td{valign: "top"} + = image_tag('mailers/gitlab_header_logo.png', id: 'logo', alt: 'GitLab Wordmark') + + %table#body + %tr + %td#body-container + = yield + + - if Gitlab.com? + %table#footer + %tr + %td#tanuki + = image_tag('mailers/gitlab_tanuki_2x.png', alt: 'GitLab Logo') + %tr + %td#tagline + Everyone can contribute + %tr + %td#social + = link_to 'Blog', 'https://about.gitlab.com/blog/' + = link_to 'Twitter', 'https://twitter.com/gitlab' + = link_to 'Facebook', 'https://www.facebook.com/gitlab/' + = link_to 'YouTube', 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg' + = link_to 'LinkedIn', 'https://www.linkedin.com/company/gitlab-com' diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 3438005863a..de15add3617 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,37 +1,40 @@ -= render 'layouts/nav/group_settings' +%div{ class: nav_control_class } + = render 'layouts/nav/group_settings' -%ul.nav-links - = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: 'Home' do - = icon('group fw') - %span - Group - = nav_link(path: 'groups#activity') do - = link_to activity_group_path(@group), title: 'Activity' do - = icon('dashboard fw') - %span - Activity - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones' do - = icon('clock-o fw') - %span - Milestones - = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues' do - = icon('exclamation-circle fw') - %span - Issues - - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(issues.count) - = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests' do - = icon('tasks fw') - %span - Merge Requests - - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute - %span.badge.count= number_with_delimiter(merge_requests.count) - = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members' do - = icon('users fw') - %span - Members + %ul.nav-links.scrolling-tabs + .fade-left + = nav_link(path: 'groups#show', html_options: {class: 'home'}) do + = link_to group_path(@group), title: 'Home' do + = icon('group fw') + %span + Group + = nav_link(path: 'groups#activity') do + = link_to activity_group_path(@group), title: 'Activity' do + = icon('dashboard fw') + %span + Activity + = nav_link(controller: [:group, :milestones]) do + = link_to group_milestones_path(@group), title: 'Milestones' do + = icon('clock-o fw') + %span + Milestones + = nav_link(path: 'groups#issues') do + = link_to issues_group_path(@group), title: 'Issues' do + = icon('exclamation-circle fw') + %span + Issues + - issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.badge.count= number_with_delimiter(issues.count) + = nav_link(path: 'groups#merge_requests') do + = link_to merge_requests_group_path(@group), title: 'Merge Requests' do + = icon('tasks fw') + %span + Merge Requests + - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute + %span.badge.count= number_with_delimiter(merge_requests.count) + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do + = icon('users fw') + %span + Members + .fade-right diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index d730840d63a..2efc6c48a48 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,4 +1,5 @@ -%ul.nav-links +%ul.nav-links.scrolling-tabs + .fade-left = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: 'Profile Settings' do = icon('user fw') @@ -47,3 +48,4 @@ = icon('history fw') %span Audit Log + .fade-right diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 33ba654bbee..087b7472701 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -19,113 +19,117 @@ data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do Leave Project -%ul.nav-links - = nav_link(path: 'projects#show', html_options: {class: 'home'}) do - = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do - = icon('bookmark fw') - %span - Project - = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do - = icon('dashboard fw') - %span - Activity - - if project_nav_tab? :files - = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do - = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do - = icon('files-o fw') +%div{ class: nav_control_class } + %ul.nav-links.scrolling-tabs + .fade-left + = nav_link(path: 'projects#show', html_options: {class: 'home'}) do + = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do + = icon('bookmark fw') %span - Files - - - if project_nav_tab? :commits - = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do - = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do - = icon('history fw') + Project + = nav_link(path: 'projects#activity') do + = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do + = icon('dashboard fw') %span - Commits + Activity + - if project_nav_tab? :files + = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do + = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do + = icon('files-o fw') + %span + Files - - if project_nav_tab? :pipelines - = nav_link(controller: :pipelines) do - = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do - = icon('ship fw') - %span - Pipelines - %span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count) + - if project_nav_tab? :commits + = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do + = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' 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' do - = icon('cubes fw') - %span - Builds - %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) + - if project_nav_tab? :pipelines + = nav_link(controller: :pipelines) do + = link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do + = icon('ship fw') + %span + Pipelines + %span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count) - - if project_nav_tab? :container_registry - = nav_link(controller: %w(container_registry)) do - = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do - = icon('hdd-o fw') - %span - Container Registry + - if project_nav_tab? :builds + = nav_link(controller: %w(builds)) do + = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do + = icon('cubes fw') + %span + Builds + %span.badge.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) - - if project_nav_tab? :graphs - = nav_link(controller: %w(graphs)) do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do - = icon('area-chart fw') - %span - Graphs + - if project_nav_tab? :container_registry + = nav_link(controller: %w(container_registry)) do + = link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do + = icon('hdd-o fw') + %span + Container Registry - - if project_nav_tab? :milestones - = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do - = icon('clock-o fw') - %span - Milestones + - if project_nav_tab? :graphs + = nav_link(controller: %w(graphs)) do + = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do + = icon('area-chart fw') + %span + Graphs - - if project_nav_tab? :issues - = nav_link(controller: :issues) do - = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do - = icon('exclamation-circle fw') - %span - Issues - - if @project.default_issues_tracker? - %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) + - if project_nav_tab? :milestones + = nav_link(controller: :milestones) do + = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do + = icon('clock-o fw') + %span + Milestones - - if project_nav_tab? :merge_requests - = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do - = icon('tasks fw') - %span - Merge Requests - %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) + - if project_nav_tab? :issues + = nav_link(controller: :issues) do + = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do + = icon('exclamation-circle fw') + %span + Issues + - if @project.default_issues_tracker? + %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count) - - if project_nav_tab? :labels - = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do - = icon('tags fw') - %span - Labels + - if project_nav_tab? :merge_requests + = nav_link(controller: :merge_requests) do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do + = icon('tasks fw') + %span + Merge Requests + %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - - if project_nav_tab? :wiki - = nav_link(controller: :wikis) do - = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do - = icon('book fw') - %span - Wiki + - if project_nav_tab? :labels + = nav_link(controller: :labels) do + = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do + = icon('tags fw') + %span + Labels - - if project_nav_tab? :snippets - = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do - = icon('clipboard fw') - %span - Snippets + - if project_nav_tab? :wiki + = nav_link(controller: :wikis) do + = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do + = icon('book fw') + %span + Wiki + + - if project_nav_tab? :snippets + = nav_link(controller: :snippets) do + = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do + = icon('clipboard fw') + %span + Snippets + + -# Global shortcut to network page for compatibility + - if project_nav_tab? :network + %li.hidden + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do + Network - -# Global shortcut to network page for compatibility - - if project_nav_tab? :network + -# Shortcut to create a new issue %li.hidden - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do - Network + = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do + Create a new issue - -# Shortcut to create a new issue - %li.hidden - = link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do - Create a new issue + .fade-right diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index bfe53be6854..1b1b16d656f 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -5,7 +5,7 @@ %h4.prepend-top-0 Application theme %p - This setting allows you to customize the appearance of the site, ex. sidebar. + This setting allows you to customize the appearance of the site, e.g. the sidebar. .col-lg-9.application-theme - Gitlab::Themes.each do |theme| = label_tag do diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml index 69fa4ad37c4..3c0f01cbf6f 100644 --- a/app/views/projects/activity.html.haml +++ b/app/views/projects/activity.html.haml @@ -1,5 +1,4 @@ - page_title "Activity" -- header_title project_title(@project, "Activity", activity_project_path(@project)) = render 'projects/last_push' diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml index 49f95ff37db..ede01dcc1aa 100644 --- a/app/views/projects/artifacts/browse.html.haml +++ b/app/views/projects/artifacts/browse.html.haml @@ -1,5 +1,4 @@ - page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds' -= render 'projects/builds/header_title' .top-block.row-content-block.clearfix .pull-right diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml index c22384ddf46..ee63bc55a30 100644 --- a/app/views/projects/badges/index.html.haml +++ b/app/views/projects/badges/index.html.haml @@ -1,6 +1,5 @@ - page_title 'Badges' - badges_path = namespace_project_badges_path(@project.namespace, @project) -- header_title project_title(@project, 'Badges', badges_path) .prepend-top-10 .panel.panel-default diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 5f9a92ff93f..377665b096f 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -1,5 +1,4 @@ - page_title "Blame", @blob.path, @ref -- header_title project_title(@project, "Files", project_files_path(@project)) %h3.page-title Blame view diff --git a/app/views/projects/blob/_header_title.html.haml b/app/views/projects/blob/_header_title.html.haml deleted file mode 100644 index 78c5ef20a5f..00000000000 --- a/app/views/projects/blob/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Files", project_files_path(@project)) diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index effcce5a1c4..e4f04ca7764 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @blob.path, @ref -= render "header_title" .file-editor %ul.nav-links.no-bottom.js-edit-mode diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 0459699432e..c952bc7e5db 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,5 +1,4 @@ - page_title "New File", @path.presence, @ref -= render "header_title" %h3.page-title New File diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 6988039b6c7..ed670dae88d 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -1,5 +1,4 @@ - page_title @blob.path, @ref -= render "header_title" = render 'projects/last_push' diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index ac7790421a4..08148b1a18b 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -1,5 +1,4 @@ - page_title "Branches" -= render "projects/commits/header_title" = render "projects/commits/head" .row-content-block .pull-right diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index c659af6338c..5a6c8c243fa 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Branch" -= render "projects/commits/header_title" - if @error .alert.alert-danger diff --git a/app/views/projects/builds/_header_title.html.haml b/app/views/projects/builds/_header_title.html.haml deleted file mode 100644 index 082dab1f5b0..00000000000 --- a/app/views/projects/builds/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Builds", project_builds_path(@project)) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 98f4a9416e5..8fb9ebc1b8b 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -1,5 +1,4 @@ - page_title "Builds" -= render "header_title" .top-area %ul.nav-links @@ -35,9 +34,6 @@ = icon('wrench') %span CI Lint -.row-content-block - #{(@scope || 'all').capitalize} builds from this project - %ul.content-list - if @builds.blank? %li diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index c7b9c36a3ab..16017c994ba 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -1,5 +1,4 @@ - page_title "#{@build.name} (##{@build.id})", "Builds" -= render "header_title" - trace_with_state = @build.trace_with_state .build-page diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index e23a3782c6b..5bd6e3f0ebc 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -57,14 +57,10 @@ %td.duration - if build.duration - = icon("clock-o") - #{duration_in_words(build.finished_at, build.started_at)} %td.timestamp - if build.finished_at - = icon("calendar") - %span #{time_ago_with_tooltip(build.finished_at)} - if defined?(coverage) && coverage diff --git a/app/views/projects/ci/commits/_commit.html.haml b/app/views/projects/ci/commits/_commit.html.haml index 13162b41f9b..5b6b940a0c4 100644 --- a/app/views/projects/ci/commits/_commit.html.haml +++ b/app/views/projects/ci/commits/_commit.html.haml @@ -12,10 +12,10 @@ · = link_to commit.short_sha, namespace_project_commit_path(@project.namespace, @project, commit.sha), class: "commit-id monospace" - - if commit.latest? - %span.label.label-success latest - if commit.tag? %span.label.label-primary tag + - elsif commit.latest? + %span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest - if commit.triggered? %span.label.label-primary triggered - if commit.yaml_errors.present? @@ -23,33 +23,29 @@ - if commit.builds.any?(&:stuck?) %span.label.label-warning stuck - %p - %span - - if commit_data = commit.commit_data - = link_to_gfm commit_data.title, namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message" - - else - Cant find HEAD commit for this branch + %p.commit-title + - if commit_data = commit.commit_data + = link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message" + - else + Cant find HEAD commit for this branch - stages_status = commit.statuses.stages_status - stages.each do |stage| %td - - if status = stages_status[stage] - - tooltip = "#{stage.titleize}: #{status}" - %span.has-tooltip{ title: "#{tooltip}", class: "ci-status-icon-#{status}" } + - status = stages_status[stage] + - tooltip = "#{stage.titleize}: #{status || 'not found'}" + - if status + = link_to namespace_project_pipeline_path(@project.namespace, @project, commit.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do = ci_icon_for_status(status) + - else + .light.has-tooltip{ title: tooltip } + \- %td - if commit.started_at && commit.finished_at - %p - = icon("clock-o") - + %p.duration #{duration_in_words(commit.finished_at, commit.started_at)} - - if commit.finished_at - %p - = icon("calendar") - - #{time_ago_with_tooltip(commit.finished_at)} %td .controls.hidden-xs.pull-right @@ -67,11 +63,9 @@ %span #{build.name} - if can?(current_user, :update_pipeline, @project) - - if commit.retryable? && commit.builds.failed.any? = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn has-tooltip', title: "Retry", method: :post do = icon("repeat") - - if commit.active? = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do = icon("remove") diff --git a/app/views/projects/commit/_ci_stage.html.haml b/app/views/projects/commit/_ci_stage.html.haml index aaa318e1eb3..ae7bb01223e 100644 --- a/app/views/projects/commit/_ci_stage.html.haml +++ b/app/views/projects/commit/_ci_stage.html.haml @@ -1,6 +1,7 @@ %tr %th{colspan: 10} %strong + %a{name: stage} - status = statuses.latest.status %span{class: "ci-status-link ci-status-icon-#{status}"} = ci_icon_for_status(status) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 028564c9305..429bf041248 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -1,32 +1,35 @@ -.pull-right.commit-action-buttons - %div +.commit-info-row.commit-info-row-header + %span.hidden-xs Authored by + %strong + = commit_author_link(@commit, avatar: true, size: 24) + #{time_ago_with_tooltip(@commit.authored_date)} + + .pull-right.commit-action-buttons - if defined?(@notes_count) && @notes_count > 0 - %span.btn.disabled.btn-grouped - %i.fa.fa-comment + %span.btn.disabled.btn-grouped.hidden-xs + = icon('comment') = @notes_count - .pull-left.btn-group - %a.btn.btn-grouped.dropdown-toggle{ data: {toggle: :dropdown} } - %i.fa.fa-download - Download as - %span.caret - %ul.dropdown-menu + = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped hidden-xs hidden-sm" do + Browse Files + .dropdown.inline + %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } } + %span.hidden-xs Options + %span.caret.commit-options-dropdown-caret + %ul.dropdown-menu.dropdown-menu-align-right + %li.visible-xs-block.visible-sm-block + = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do + Browse Files + - unless @commit.has_been_reverted?(current_user) + %li.clearfix + = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) + %li.clearfix + = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false) + %li.divider + %li.dropdown-header + Download - unless @commit.parents.length > 1 %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) - = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped" do - = icon('files-o') - Browse Files - - unless @commit.has_been_reverted?(current_user) - = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id)) - = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id)) - %div - -%p -.commit-info-row - %span.light Authored by - %strong - = commit_author_link(@commit, avatar: true, size: 24) - #{time_ago_with_tooltip(@commit.authored_date)} - if @commit.different_committer? .commit-info-row @@ -36,8 +39,9 @@ #{time_ago_with_tooltip(@commit.committed_date)} .commit-info-row - %span.light Commit - = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" + %span.hidden-xs.hidden-sm Commit + = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace hidden-xs hidden-sm" + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace visible-xs-inline visible-sm-inline" = clipboard_button(clipboard_text: @commit.id) %span.cgray= pluralize(@commit.parents.count, "parent") - @commit.parents.each do |parent| diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml index 7118a4846c6..2f051fb90e0 100644 --- a/app/views/projects/commit/builds.html.haml +++ b/app/views/projects/commit/builds.html.haml @@ -1,7 +1,7 @@ - page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits" -= render "projects/commits/header_title" + .prepend-top-default = render "commit_box" -= render "ci_menu" += render "ci_menu" = render "builds" diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index e5e3d696035..401cb4f7e30 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,8 +1,6 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description -= render "projects/commits/header_title" - .prepend-top-default = render "commit_box" - if @commit.status diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder new file mode 100644 index 00000000000..1657fb46163 --- /dev/null +++ b/app/views/projects/commits/_commit.atom.builder @@ -0,0 +1,14 @@ +xml.entry do + xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) + xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) + xml.title truncate(commit.title, length: 80) + xml.updated commit.committed_date.xmlschema + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) + + xml.author do |author| + xml.name commit.author_name + xml.email commit.author_email + end + + xml.summary markdown(commit.description, pipeline: :single_line) +end diff --git a/app/views/projects/commits/_header_title.html.haml b/app/views/projects/commits/_header_title.html.haml deleted file mode 100644 index e4385893dd9..00000000000 --- a/app/views/projects/commits/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Commits", project_commits_path(@project)) diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index e310fafd82c..30bb7412073 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -6,18 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_commits_url(@project.namespace, @project, @ref) xml.updated @commits.first.committed_date.xmlschema if @commits.any? - @commits.each do |commit| - xml.entry do - xml.id namespace_project_commit_url(@project.namespace, @project, id: commit.id) - xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) - xml.title truncate(commit.title, length: 80) - xml.updated commit.committed_date.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) - xml.author do |author| - xml.name commit.author_name - xml.email commit.author_email - end - xml.summary markdown(commit.description, pipeline: :single_line) - end - end + xml << render(@commits) if @commits.any? end diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 088eaa28013..2c21923ed4f 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -1,5 +1,4 @@ - page_title "Commits", @ref -= render "header_title" = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 5e188dd0f3c..0b8ed23b305 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -1,5 +1,4 @@ - page_title "Compare" -= render "projects/commits/header_title" = render "projects/commits/head" .row-content-block diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 62525168239..cdc34f51d6d 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -1,5 +1,4 @@ - page_title "#{params[:from]}...#{params[:to]}" -= render "projects/commits/header_title" = render "projects/commits/head" diff --git a/app/views/projects/container_registry/_header_title.html.haml b/app/views/projects/container_registry/_header_title.html.haml deleted file mode 100644 index f1863c52a3e..00000000000 --- a/app/views/projects/container_registry/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Container Registry", project_container_registry_path(@project)) diff --git a/app/views/projects/container_registry/index.html.haml b/app/views/projects/container_registry/index.html.haml index e1e762410f2..993da27310f 100644 --- a/app/views/projects/container_registry/index.html.haml +++ b/app/views/projects/container_registry/index.html.haml @@ -1,5 +1,4 @@ - page_title "Container Registry" -= render "header_title" %hr @@ -37,4 +36,4 @@ %th - @tags.each do |tag| - = render 'tag', tag: tag
\ No newline at end of file + = render 'tag', tag: tag diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml index 1fe1d98bf13..9322c82904f 100644 --- a/app/views/projects/find_file/show.html.haml +++ b/app/views/projects/find_file/show.html.haml @@ -1,5 +1,4 @@ - page_title "Find File", @ref -- header_title project_title(@project, "Files", project_files_path(@project)) .file-finder-holder.tree-holder.clearfix .nav-block diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 8129514964a..5bc5c71283e 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -40,24 +40,20 @@ %td = generic_commit_status.name - .pull-right - - if generic_commit_status.tags.any? - - generic_commit_status.tags.each do |tag| - %span.label.label-primary - = tag - - if defined?(retried) && retried - %span.label.label-warning retried + %td + - if generic_commit_status.tags.any? + - generic_commit_status.tags.each do |tag| + %span.label.label-primary + = tag + - if defined?(retried) && retried + %span.label.label-warning retried %td.duration - if generic_commit_status.duration - = icon("clock-o") - #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)} %td.timestamp - if generic_commit_status.finished_at - = icon("calendar") - %span #{time_ago_with_tooltip(generic_commit_status.finished_at)} - if defined?(coverage) && coverage diff --git a/app/views/projects/graphs/_header_title.html.haml b/app/views/projects/graphs/_header_title.html.haml deleted file mode 100644 index 1e2f61cd22b..00000000000 --- a/app/views/projects/graphs/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Graphs", namespace_project_graph_path(@project.namespace, @project, current_ref)) diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml index 9f05be9982b..19ccc125ea8 100644 --- a/app/views/projects/graphs/ci.html.haml +++ b/app/views/projects/graphs/ci.html.haml @@ -1,5 +1,4 @@ - page_title "Continuous Integration", "Graphs" -= render "header_title" = render 'head' .row-content-block.append-bottom-default .oneline diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index da9f648cc9c..d9b2fb6c065 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,5 +1,4 @@ - page_title "Commits", "Graphs" -= render "header_title" = render 'head' .row-content-block.append-bottom-default diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml index ebecab1dbfc..249c16f4709 100644 --- a/app/views/projects/graphs/languages.html.haml +++ b/app/views/projects/graphs/languages.html.haml @@ -1,5 +1,4 @@ - page_title "Languages", "Graphs" -= render "header_title" = render 'head' .row-content-block.append-bottom-default diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index ad4a932d391..33970e7b909 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,5 +1,4 @@ - page_title "Contributors", "Graphs" -= render "header_title" = render 'head' .row-content-block.append-bottom-default @@ -19,7 +18,7 @@ .header.clearfix %h3#date_header.page-title %p.light - Commits to #{@ref}, excluding merge commits. Limited by 6,000 commits + Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits. %input#brush_change{:type => "hidden"} .graphs #contributors-master diff --git a/app/views/projects/issues/_header_title.html.haml b/app/views/projects/issues/_header_title.html.haml deleted file mode 100644 index 99f03549c44..00000000000 --- a/app/views/projects/issues/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project)) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index dec3e8809ce..4701429215b 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,7 +6,7 @@ .issue-title.title %span.issue-title-text = confidential_icon(issue) - = link_to_gfm issue.title, issue_path(issue) + = link_to issue.title, issue_path(issue) %ul.controls - if issue.closed? %li diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index e953353567e..2f9dc867d0d 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -24,5 +24,8 @@ MERGED - elsif merge_request.closed? CLOSED - - if @closed_by_merge_requests.present? + %li = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} + - if @closed_by_merge_requests.present? + %li + = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml index 20216297d25..7cf1923456e 100644 --- a/app/views/projects/issues/edit.html.haml +++ b/app/views/projects/issues/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues" -= render "header_title" %h3.page-title Edit Issue ##{@issue.iid} diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index ee8a9414657..7ad7c9c87e8 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_issues_url(@project.namespace, @project) xml.updated @issues.first.created_at.xmlschema if @issues.any? - @issues.each do |issue| - issue_to_atom(xml, issue) - end + xml << render(partial: 'issues/issue', collection: @issues) if @issues.any? end diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index efa7642b2dc..19a6f4a91f6 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,5 +1,4 @@ - page_title "Issues" -= render "header_title" = content_for :meta_tags do - if current_user diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index b317a0c1cf4..e8aae0f47e2 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Issue" -= render "header_title" %h3.page-title New Issue diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6623028c02e..cfffa63dd9a 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,7 +1,6 @@ - page_title "#{@issue.title} (##{@issue.iid})", "Issues" - page_description @issue.description - page_card_attributes @issue.card_attributes -- header_title project_title(@project, "Issues", namespace_project_issues_path(@project.namespace, @project)) .clearfix.detail-page-header .issuable-header diff --git a/app/views/projects/labels/_header_title.html.haml b/app/views/projects/labels/_header_title.html.haml deleted file mode 100644 index abe28da483b..00000000000 --- a/app/views/projects/labels/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Labels", namespace_project_labels_path(@project.namespace, @project)) diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index 675a805e12f..6901ba13ab7 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @label.name, "Labels" -= render "header_title" %h3.page-title Edit Label diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index cc41130a9dc..2557d1a4d5b 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -1,5 +1,4 @@ - page_title "Labels" -= render "header_title" .top-area .nav-text @@ -18,6 +17,6 @@ - else .nothing-here-block - if can? current_user, :admin_label, @project - Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels + Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}. - else No labels created diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index e20fd7d6891..49ddf901619 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Label" -= render "header_title" %h3.page-title New Label diff --git a/app/views/projects/merge_requests/_header_title.html.haml b/app/views/projects/merge_requests/_header_title.html.haml deleted file mode 100644 index 669a9b06bdf..00000000000 --- a/app/views/projects/merge_requests/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project)) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 8edeb120ba5..1ec180235ce 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,7 +1,7 @@ %li{ class: mr_css_classes(merge_request) } .merge-request-title.title %span.merge-request-title-text - = link_to_gfm merge_request.title, merge_request_path(merge_request) + = link_to merge_request.title, merge_request_path(merge_request) %ul.controls - if merge_request.merged? %li diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 23525318400..a73d0063be2 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,7 +1,6 @@ - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", "Merge Requests" - page_description @merge_request.description - page_card_attributes @merge_request.card_attributes -- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project)) - if diff_view == 'parallel' - fluid_layout true diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index b31ea5e5321..03159f123f3 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" -= render "header_title" %h3.page-title Edit Merge Request #{@merge_request.to_reference} diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index e56a44e0a79..b517e874b0f 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,5 +1,4 @@ - page_title "Merge Requests" -= render "header_title" = render 'projects/last_push' diff --git a/app/views/projects/merge_requests/invalid.html.haml b/app/views/projects/merge_requests/invalid.html.haml index f5bf16ef3ad..a00d3128ffe 100644 --- a/app/views/projects/merge_requests/invalid.html.haml +++ b/app/views/projects/merge_requests/invalid.html.haml @@ -1,5 +1,4 @@ - page_title "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests" -= render "header_title" .merge-request = render "projects/merge_requests/show/mr_title" diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml index d259968030e..2e798ce780a 100644 --- a/app/views/projects/merge_requests/new.html.haml +++ b/app/views/projects/merge_requests/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Merge Request" -= render "header_title" - if @merge_request.can_be_created && !params[:change_branches] = render 'new_submit' diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml index 3c68d61c4b5..b79508bdc34 100644 --- a/app/views/projects/merge_requests/widget/_show.html.haml +++ b/app/views/projects/merge_requests/widget/_show.html.haml @@ -13,7 +13,7 @@ check_enable: #{@merge_request.unchecked? ? "true" : "false"}, ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", gitlab_icon: "#{asset_path 'gitlab_logo.png'}", - ci_status: "", + ci_status: "#{@merge_request.ci_commit ? @merge_request.ci_commit.status : ''}", ci_message: { normal: "Build {{status}} for \"{{title}}\"", preparing: "{{status}} build for \"{{title}}\"" @@ -26,4 +26,10 @@ builds_path: "#{builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}" }; + if (typeof merge_request_widget !== 'undefined') { + clearInterval(merge_request_widget.fetchBuildStatusInterval); + merge_request_widget.cancelPolling(); + merge_request_widget.clearEventListeners(); + } + merge_request_widget = new MergeRequestWidget(opts); diff --git a/app/views/projects/milestones/_header_title.html.haml b/app/views/projects/milestones/_header_title.html.haml deleted file mode 100644 index 5f4b6982a6d..00000000000 --- a/app/views/projects/milestones/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Milestones", namespace_project_milestones_path(@project.namespace, @project)) diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index 43f8863163d..be682226ab6 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @milestone.title, "Milestones" -= render "header_title" %h3.page-title Edit Milestone ##{@milestone.iid} diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index abe567af1dd..e6133b22f96 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,6 +1,4 @@ - page_title "Milestones" -= render "header_title" - .top-area = render 'shared/milestones_filter' diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 0d016f78313..7f372b41698 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Milestone" -= render "header_title" %h3.page-title New Milestone diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 6ec84660157..19944e3e023 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,8 +1,6 @@ - page_title @milestone.title, "Milestones" - page_description @milestone.description -= render "header_title" - .detail-page-header .status-box{ class: status_box_class(@milestone) } - if @milestone.closed? diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 8065663ca2a..326180ebe4e 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,5 +1,4 @@ - page_title "Network", @ref -= render "projects/commits/header_title" = render "projects/commits/head" = render "head" .project-network diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index a4c6094c69a..f9ac16b32f3 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,5 +1,5 @@ - page_title 'New Project' -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path %h3.page-title New Project diff --git a/app/views/projects/pipelines/_header_title.html.haml b/app/views/projects/pipelines/_header_title.html.haml deleted file mode 100644 index faf63d64a79..00000000000 --- a/app/views/projects/pipelines/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Pipelines", project_pipelines_path(@project)) diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 9d5b6d367c9..8788db09dbe 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -1,5 +1,4 @@ - page_title "Pipelines" -= render "header_title" .top-area %ul.nav-links @@ -36,15 +35,7 @@ = icon('wrench') %span CI Lint -.row-content-block - - if @scope == 'running' - Running pipelines for this project - - elsif @scope.nil? - Pipelines for this project - - else - #{@scope.titleize} for this project - -%ul.content-list +%ul.content-list.pipelines - stages = @pipelines.stages - if @pipelines.blank? %li @@ -56,10 +47,10 @@ %th ID %th Commit - stages.each do |stage| - %th - %span.pipeline-stage.has-tooltip{ title: "#{stage.titleize}" } + %th.stage + %span.has-tooltip{ title: "#{stage.titleize}" } = stage.titleize.pluralize - %th + %th Duration %th = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml index b97c9f5f3b6..5f4ec2e40c8 100644 --- a/app/views/projects/pipelines/new.html.haml +++ b/app/views/projects/pipelines/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Pipeline" -= render "header_title" %h3.page-title New Pipeline diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml index b082d4d5da8..2aad5602414 100644 --- a/app/views/projects/pipelines/show.html.haml +++ b/app/views/projects/pipelines/show.html.haml @@ -1,6 +1,5 @@ - page_title "Pipeline" -= render "header_title" .prepend-top-default - if @commit = render "projects/pipelines/info" diff --git a/app/views/projects/project_members/_header_title.html.haml b/app/views/projects/project_members/_header_title.html.haml deleted file mode 100644 index a31f0a37fa2..00000000000 --- a/app/views/projects/project_members/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Members", namespace_project_project_members_path(@project.namespace, @project)) diff --git a/app/views/projects/project_members/import.html.haml b/app/views/projects/project_members/import.html.haml index 189906498cb..eef97107d77 100644 --- a/app/views/projects/project_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -1,5 +1,4 @@ - page_title "Import members" -= render "header_title" %h3.page-title Import members from another project diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index ebcfc907ebb..15dc064e7ea 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,5 +1,4 @@ - page_title "Members" -= render "header_title" .project-members-page.prepend-top-default - if can?(current_user, :admin_project_member, @project) diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 0d59cec322c..835398b6f98 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @tag.name, "Tags" -= render "projects/commits/header_title" = render "projects/commits/head" .row-content-block diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index 30cd1263a12..8ae9f0d95f7 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -8,7 +8,7 @@ Install GitLab Runner software. Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it %li - Specify following URL during runner setup: + Specify the following URL during runner setup: %code #{ci_root_url(only_path: false)} %li Use the following registration token during setup: diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 1b70880043a..1f13ea28b4e 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -1,18 +1,16 @@ -%h3.page-title - = @service.title - = boolean_to_icon @service.activated? +.row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0 + = @service.title + = boolean_to_icon @service.activated? -%p= @service.description - -%hr - -= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| - = render 'shared/service_settings', form: form - - .form-actions - = form.submit 'Save changes', class: 'btn btn-save' - - - if @service.valid? && @service.activated? - - disabled = @service.can_test? ? '':'disabled' - = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" - = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" + %p= @service.description + .col-lg-9 + = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| + = render 'shared/service_settings', form: form + = form.submit 'Save changes', class: 'btn btn-save' + + - if @service.valid? && @service.activated? + - disabled = @service.can_test? ? '':'disabled' + = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" + = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml index c1356f6db02..4a33a5bc6f6 100644 --- a/app/views/projects/services/index.html.haml +++ b/app/views/projects/services/index.html.haml @@ -1,24 +1,32 @@ - page_title "Services" -%h3.page-title Project services -%p.light Project services allow you to integrate GitLab with other applications -.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 +.row.prepend-top-default.append-bottom-default + .col-lg-3 + %h4.prepend-top-0 + Project services + %p Project services allow you to integrate GitLab with other applications + .col-lg-9 + %table.table + %colgroup + %col + %col + %col.hidden-xs + %col{ width: "120" } + %thead + %tr + %th + %th Service + %th.hidden-xs 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.hidden-xs + = service.description + %td.light + = time_ago_in_words service.updated_at + ago diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 9b3d3f069d9..11310d5e1e1 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id namespace_project_url(@project.namespace, @project) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/projects/snippets/_header_title.html.haml b/app/views/projects/snippets/_header_title.html.haml deleted file mode 100644 index 04f0bbe9853..00000000000 --- a/app/views/projects/snippets/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, "Snippets", namespace_project_snippets_path(@project.namespace, @project)) diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index dc3ea1fcf12..216f70f5605 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @snippet.title, "Snippets" -= render "header_title" %h3.page-title Edit Snippet diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 103ff447464..96fee3b17b2 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,5 +1,4 @@ - page_title "Snippets" -= render "header_title" .row-content-block.top-block .pull-right diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index e57237991b4..772a594269c 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Snippets" -= render "header_title" %h3.page-title New Snippet diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 6f7bdcb29e8..bae4d8f349f 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,5 +1,4 @@ - page_title @snippet.title, "Snippets" -= render "header_title" .snippet-holder = render 'shared/snippets/header' diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index dc6ece30dd2..8f381663e6e 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -1,5 +1,4 @@ - page_title "Tags" -= render "projects/commits/header_title" = render "projects/commits/head" .row-content-block diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index f9306453297..3a097750d6e 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -1,5 +1,4 @@ - page_title "New Tag" -= render "projects/commits/header_title" - if @error .alert.alert-danger diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 9f1424aecc7..b7d7d5c5382 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -1,5 +1,4 @@ - page_title @tag.name, "Tags" -= render "projects/commits/header_title" = render "projects/commits/head" .row-content-block diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 91fb2a44594..7e9ba09c720 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,5 +1,4 @@ - page_title @path.presence || "Files", @ref -- header_title project_title(@project, "Files", project_files_path(@project)) = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index d73ac987161..7f3de47d7df 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -5,7 +5,7 @@ %h4.prepend-top-0 = page_title %p - Triggers can be used to force a rebuild of a specific branch or tag with an API call. + Triggers can force a specific branch or tag to rebuild with an API call. .col-lg-9 %h5.prepend-top-0 Your triggers @@ -19,7 +19,7 @@ = render partial: 'trigger', collection: @triggers, as: :trigger - else %p.settings-message.text-center.append-bottom-default - There are no triggers to use, add one by the button below. + No triggers have been created yet. Add one using the button below. = form_for @trigger, url: url_for(controller: 'projects/triggers', action: 'create') do |f| = f.submit "Add Trigger", class: 'btn btn-success' @@ -28,8 +28,7 @@ Use CURL %p.light - Copy the token above and set your branch or tag name. This is the reference that will be rebuild. - + Copy the token above, set your branch or tag name, and that reference will be rebuilt. %pre :plain @@ -41,10 +40,10 @@ Use .gitlab-ci.yml %p.light - Copy the snippet to - %i .gitlab-ci.yml - of dependent project. - At the end of your build it will trigger this project to rebuilt. + In the + %code .gitlab-ci.yml + of the dependent project, include the following snippet. + The project will rebuild at the end of the build. %pre :plain @@ -57,9 +56,8 @@ %p.light Add - %strong variables[VARIABLE]=VALUE - to API request. - The value of variable could then be used to distinguish triggered build from normal one. + %code variables[VARIABLE]=VALUE + to an API request. Variable values can be used to distinguish between triggered builds and normal builds. %pre.append-bottom-0 :plain diff --git a/app/views/projects/wikis/_header_title.html.haml b/app/views/projects/wikis/_header_title.html.haml deleted file mode 100644 index 408adc36ca6..00000000000 --- a/app/views/projects/wikis/_header_title.html.haml +++ /dev/null @@ -1 +0,0 @@ -- header_title project_title(@project, 'Wiki', get_project_wiki_path(@project)) diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 4dd818c7f67..aaa15dd3bbe 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,5 +1,4 @@ - page_title "Edit", @page.title.capitalize, "Wiki" -= render "header_title" = render 'nav' .top-area diff --git a/app/views/projects/wikis/empty.html.haml b/app/views/projects/wikis/empty.html.haml index c7e490c3cd1..7dfa405d063 100644 --- a/app/views/projects/wikis/empty.html.haml +++ b/app/views/projects/wikis/empty.html.haml @@ -1,5 +1,4 @@ - page_title "Wiki" -= render "header_title" %h3.page-title Empty page %hr diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index ba3f2cadc48..ccceab6155e 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -1,5 +1,4 @@ - page_title "Git Access", "Wiki" -= render "header_title" = render 'nav' .row-content-block diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml index dcaddae2b04..45460ed9f41 100644 --- a/app/views/projects/wikis/history.html.haml +++ b/app/views/projects/wikis/history.html.haml @@ -1,5 +1,4 @@ - page_title "History", @page.title.capitalize, "Wiki" -= render "header_title" = render 'nav' .top-area diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 92b494a513c..2f6162fa3c5 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,5 +1,4 @@ - page_title "Pages", "Wiki" -= render "header_title" = render 'nav' diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 067fb7f8f54..1cb48a1e85d 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,5 +1,4 @@ - page_title @page.title.capitalize, "Wiki" -= render "header_title" = render 'nav' .top-area diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index c38d9313dba..30055002213 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -1,5 +1,7 @@ -%ul.nav-links.event-filter +%ul.nav-links.event-filter.scrolling-tabs + .fade-left = event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.team, 'Team' + .fade-right diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index d327bd0a96f..1e0f075b303 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -6,7 +6,7 @@ - else = sort_title_recently_created %b.caret - %ul.dropdown-menu.dropdown-menu-align-right + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort %li = link_to page_filter_path(sort: sort_value_recently_created) do = sort_title_recently_created diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 9474462cbd1..cedff4af2e0 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -1,6 +1,8 @@ .issues-filters .issues-details-filters.row-content-block.second-block - = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do + = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :issue_search]), method: :get, class: 'filter-form js-filter-form' do + - if params[:issue_search].present? + = hidden_field_tag :issue_search, params[:issue_search] - if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project) .check-all-holder = check_box_tag "check_all_issues", nil, false, @@ -10,7 +12,7 @@ - if params[:author_id].present? = hidden_field_tag(:author_id, params[:author_id]) = dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit", - placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: "author_id", default_label: "Author" } }) + placeholder: "Search authors", data: { any_user: "Any Author", first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author], field_name: "author_id", default_label: "Author" } }) .filter-item.inline - if params[:assignee_id].present? diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index fc3410f425d..b430251dbf6 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -98,9 +98,7 @@ = label_tag :move_to_project_id, 'Move', class: 'control-label' .col-sm-10 .issuable-form-select-holder - - projects = project_options(issuable, current_user, ability: :admin_issue) - = select_tag(:move_to_project_id, projects, include_blank: true, - class: 'select2', data: { placeholder: 'Select project' }) + = hidden_field_tag :move_to_project_id, nil, class: 'js-move-dropdown', data: { placeholder: 'Select project', projects_url: autocomplete_projects_path(project_id: @project.id) } %span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default', title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' } diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml index afad48499b7..186963b32b8 100644 --- a/app/views/shared/issuable/_search_form.html.haml +++ b/app/views/shared/issuable/_search_form.html.haml @@ -1,8 +1,2 @@ = form_tag(path, method: :get, id: "issue_search_form", class: 'issue-search-form') do = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input input-short', spellcheck: false } - = hidden_field_tag :state, params['state'] - = hidden_field_tag :scope, params['scope'] - = hidden_field_tag :assignee_id, params['assignee_id'] - = hidden_field_tag :author_id, params['author_id'] - = hidden_field_tag :milestone_id, params['milestone_id'] - = hidden_field_tag :label_id, params['label_id'] diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 9ef021747a5..b8b66d08db8 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -12,9 +12,6 @@ %li.project-row{ class: css_class } = cache(cache_key) do .controls - - if project.main_language - %span - = project.main_language - if project.commit.try(:status) %span = render_commit_status(project.commit) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 897aa5b4462..e0d0d967da6 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -4,7 +4,7 @@ = visibility_level_label(@snippet.visibility_level) = visibility_level_icon(@snippet.visibility_level, fw: false) %strong.item-title - Snippet ##{@snippet.id} + Snippet $#{@snippet.id} %span.creator created by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title")} = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 1de71f37d1a..77f2ddefb1e 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,10 +1,9 @@ -#cal-heatmap.calendar - :javascript - new Calendar( - #{@timestamps.to_json}, - #{@starting_year}, - #{@starting_month}, - '#{user_calendar_activities_path}' - ); - -.calendar-hint Summary of issues, merge requests, and push events +.clearfix.calendar + .js-contrib-calendar + .calendar-hint + Summary of issues, merge requests, and push events +:javascript + new Calendar( + #{@timestamps.to_json}, + '#{user_calendar_activities_path}' + ); diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index 027a93a75fc..630d97e339d 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -1,23 +1,27 @@ %h4.prepend-top-20 - %span.light Contributions for + Contributions for %strong #{@calendar_date.to_s(:short)} -%ul.bordered-list - - @events.sort_by(&:created_at).each do |event| - %li - %span.light - %i.fa.fa-clock-o - = event.created_at.to_s(:time) - - if event.push? - #{event.action_name} #{event.ref_type} #{event.ref_name} - - else - = event_action_name(event) - - if event.target - %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] - - at - %strong - - if event.project - = link_to_project event.project +- if @events.any? + %ul.bordered-list + - @events.sort_by(&:created_at).each do |event| + %li + %span.light + %i.fa.fa-clock-o + = event.created_at.to_s(:time) + - if event.push? + #{event.action_name} #{event.ref_type} #{event.ref_name} - else - = event.project_name + = event_action_name(event) + - if event.target + %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + + at + %strong + - if event.project + = link_to_project event.project + - else + = event.project_name +- else + %p + No contributions found for #{@calendar_date.to_s(:short)} diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index e9e466c6350..6c85e5f9fbd 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -6,7 +6,5 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.id user_url(@user) xml.updated @events[0].updated_at.xmlschema if @events[0] - @events.each do |event| - event_to_atom(xml, event) - end + xml << render(@events) if @events.any? end diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 9017fd54fcc..0c513308308 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -89,10 +89,9 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - %div{ class: container_class } - .user-calendar{data: {href: user_calendar_path}} - %h4.center.light - %i.fa.fa-spinner.fa-spin + .user-calendar{data: {href: user_calendar_path}} + %h4.center.light + %i.fa.fa-spinner.fa-spin .user-calendar-activities .content_list{ data: {href: user_path} } diff --git a/config/application.rb b/config/application.rb index 0e5a77285e5..de2bb08c978 100644 --- a/config/application.rb +++ b/config/application.rb @@ -80,7 +80,7 @@ module Gitlab config.assets.precompile << "*.png" config.assets.precompile << "print.css" config.assets.precompile << "notify.css" - config.assets.precompile << "mailers/repository_push_email.css" + config.assets.precompile << "mailers/*.css" # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' diff --git a/config/environments/development.rb b/config/environments/development.rb index 4f39016bfa4..8cca0039b4a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -39,6 +39,7 @@ Rails.application.configure do config.action_mailer.delivery_method = :letter_opener_web # Don't make a mess when bootstrapping a development environment config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') + config.action_mailer.preview_path = 'spec/mailers/previews' config.eager_load = false end diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index 2338916e9da..0c788714714 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -12,6 +12,7 @@ if Gitlab::Metrics.enabled? Gitlab::Application.configure do |config| config.middleware.use(Gitlab::Metrics::RackMiddleware) + config.middleware.use(Gitlab::Middleware::RailsQueueDuration) end Sidekiq.configure_server do |config| diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb index b9176688bc4..cb00d3cfe95 100644 --- a/config/initializers/premailer.rb +++ b/config/initializers/premailer.rb @@ -3,6 +3,6 @@ Premailer::Rails.config.merge!( generate_text_part: false, preserve_styles: true, remove_comments: true, - remove_ids: true, + remove_ids: false, remove_scripts: false ) diff --git a/config/routes.rb b/config/routes.rb index 1471bca710e..85760409c1f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,6 +56,7 @@ Rails.application.routes.draw do # Autocomplete get '/autocomplete/users' => 'autocomplete#users' get '/autocomplete/users/:id' => 'autocomplete#user' + get '/autocomplete/projects' => 'autocomplete#projects' # Emojis resources :emojis, only: :index diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb index 561c18a5776..6aed0fe03d2 100644 --- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb +++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb @@ -28,7 +28,7 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration update_import_url(import_url, project) update_import_data(import_url, project) - rescue URI::InvalidURIError + rescue Addressable::URI::InvalidURIError nullify_import_url(project) end end diff --git a/db/migrate/20160525205328_remove_main_language_from_projects.rb b/db/migrate/20160525205328_remove_main_language_from_projects.rb new file mode 100644 index 00000000000..0f9d60c385f --- /dev/null +++ b/db/migrate/20160525205328_remove_main_language_from_projects.rb @@ -0,0 +1,21 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveMainLanguageFromProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + remove_column :projects, :main_language + end +end diff --git a/db/migrate/20160528043124_add_users_state_index.rb b/db/migrate/20160528043124_add_users_state_index.rb new file mode 100644 index 00000000000..e77a5460737 --- /dev/null +++ b/db/migrate/20160528043124_add_users_state_index.rb @@ -0,0 +1,9 @@ +class AddUsersStateIndex < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + disable_ddl_transaction! + + def change + add_concurrent_index :users, :state + end +end diff --git a/db/schema.rb b/db/schema.rb index 4127b7a47e9..9ef68efc4e5 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: 20160516174813) do +ActiveRecord::Schema.define(version: 20160528043124) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -771,7 +771,6 @@ ActiveRecord::Schema.define(version: 20160516174813) do t.integer "build_timeout", default: 3600, null: false t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false - t.string "main_language" t.integer "pushes_since_gc", default: 0 t.boolean "last_repository_check_failed" t.datetime "last_repository_check_at" @@ -1012,6 +1011,7 @@ ActiveRecord::Schema.define(version: 20160516174813) do add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + add_index "users", ["state"], name: "index_users_on_state", using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree add_index "users", ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"} diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md index 43d85ffb775..d74a786ac24 100644 --- a/doc/administration/high_availability/README.md +++ b/doc/administration/high_availability/README.md @@ -19,6 +19,8 @@ Components/Servers Required: - 2 servers/virtual machines (one active/one passive) +![Active/Passive HA Diagram](../img/high_availability/active-passive-diagram.png) + ### Active/Active This architecture scales easily because all application servers handle @@ -26,6 +28,8 @@ user requests simultaneously. The database, Redis, and GitLab application are all deployed on separate servers. The configuration is **only** highly-available if the database, Redis and storage are also configured as such. +![Active/Active HA Diagram](../img/high_availability/active-active-diagram.png) + **Steps to configure active/active:** 1. [Configure the database](database.md) diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md index d89a1e582ca..f6153216f33 100644 --- a/doc/administration/high_availability/redis.md +++ b/doc/administration/high_availability/redis.md @@ -26,7 +26,7 @@ that runs Redis. ```ruby external_url 'https://gitlab.example.com' - # Disable all components except PostgreSQL + # Disable all components except Redis redis['enable'] = true bootstrap['enable'] = false nginx['enable'] = false diff --git a/doc/administration/img/high_availability/active-active-diagram.png b/doc/administration/img/high_availability/active-active-diagram.png Binary files differnew file mode 100644 index 00000000000..81259e0ae93 --- /dev/null +++ b/doc/administration/img/high_availability/active-active-diagram.png diff --git a/doc/administration/img/high_availability/active-passive-diagram.png b/doc/administration/img/high_availability/active-passive-diagram.png Binary files differnew file mode 100644 index 00000000000..f69ff1d0357 --- /dev/null +++ b/doc/administration/img/high_availability/active-passive-diagram.png diff --git a/doc/development/testing.md b/doc/development/testing.md index 33eed29ba5c..513457d203a 100644 --- a/doc/development/testing.md +++ b/doc/development/testing.md @@ -65,7 +65,7 @@ the command line via `bundle exec teaspoon`, or via a web browser at - Use `context` to test branching logic. - Don't `describe` symbols (see [Gotchas](gotchas.md#dont-describe-symbols)). - Don't supply the `:each` argument to hooks since it's the default. -- Prefer `not_to` to `to_not`. +- Prefer `not_to` to `to_not` (_this is enforced by Rubocop_). - Try to match the ordering of tests to the ordering within the class. - Try to follow the [Four-Phase Test][four-phase-test] pattern, using newlines to separate phases. diff --git a/doc/development/ui_guide.md b/doc/development/ui_guide.md index a3e260a5f89..b4dcb748351 100644 --- a/doc/development/ui_guide.md +++ b/doc/development/ui_guide.md @@ -6,3 +6,31 @@ We created a page inside GitLab where you can check commonly used html and css e When you run GitLab instance locally - just visit http://localhost:3000/help/ui page to see UI examples you can use during GitLab development. + +## Design repository + +All design files are stored in the [gitlab-design](https://gitlab.com/gitlab-org/gitlab-design) +repository and maintained by GitLab UX designers. + +## Navigation + +GitLab's layout contains 2 sections: the left sidebar and the content. The left sidebar contains a static navigation menu. +This menu will be visible regardless of what page you visit. The left sidebar also contains the GitLab logo +and the current user's profile picture. The content section contains a header and the content itself. +The header describes the current GitLab page and what navigation is +available to user in this area. Depending on the area (project, group, profile setting) the header name and navigation may change. For example when user visits one of the +project pages the header will contain a project name and navigation for that project. When the user visits a group page it will contain a group name and navigation related to this group. + +### Adding new tab to header navigation + +We try to keep the amount of tabs in the header navigation between 5 and 10 so that it fits on a typical laptop screen. We also try not to confuse the user with too many options. Ideally each +tab should represent separate functionality. Everything related to the issue +tracker should be under the 'Issues' tab while everything related to the wiki should +be under 'Wiki' tab and so on and so forth. + +## Mobile screen size + +We want GitLab to work well on small mobile screens as well. Size limitations make it is impossible to fit everything on a mobile screen. In this case it is OK to hide +part of the UI for smaller resolutions in favor of a better user experience. +However core functionality like browsing files, creating issues, writing comments, should +be available on all resolutions.
\ No newline at end of file diff --git a/doc/install/installation.md b/doc/install/installation.md index 108ceb593d0..1318b3d1fa5 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -394,7 +394,7 @@ GitLab Shell is an SSH access and repository management software developed speci cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse - sudo -u git -H git checkout v0.7.3 + sudo -u git -H git checkout v0.7.4 sudo -u git -H make ### Initialize Database and Activate Advanced Features diff --git a/features/steps/admin/users.rb b/features/steps/admin/users.rb index 4bc290b6bdf..8fb8a86d58b 100644 --- a/features/steps/admin/users.rb +++ b/features/steps/admin/users.rb @@ -158,7 +158,7 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps step 'I should not see twitter details' do expect(page).to have_content 'Pete' - expect(page).to_not have_content 'twitter' + expect(page).not_to have_content 'twitter' end step 'click on ssh keys tab' do diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index 2b23df6764b..bd8a270202e 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -20,7 +20,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps step 'I have todos' do create(:todo, user: current_user, project: project, author: mary_jane, target: issue, action: Todo::MENTIONED) create(:todo, user: current_user, project: project, author: john_doe, target: issue, action: Todo::ASSIGNED) - note = create(:note, author: john_doe, noteable: issue, note: "#{current_user.to_reference} Wdyt?") + note = create(:note, author: john_doe, noteable: issue, note: "#{current_user.to_reference} Wdyt?", project: project) create(:todo, user: current_user, project: project, author: john_doe, target: issue, action: Todo::MENTIONED, note: note) create(:todo, user: current_user, project: project, author: john_doe, target: merge_request, action: Todo::ASSIGNED) end @@ -106,7 +106,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps if pending expect(page).to have_link 'Done' else - expect(page).to_not have_link 'Done' + expect(page).not_to have_link 'Done' end end end diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index f33f37be951..e1b29f1e57a 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -105,7 +105,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps end step 'I should not see button to create a new merge request' do - expect(page).to_not have_link 'Create Merge Request' + expect(page).not_to have_link 'Create Merge Request' end step 'I should see button to the merge request' do diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index c5d45709b44..1b14659b4df 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -39,8 +39,8 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps step 'I can see the activity and food categories' do page.within '.emoji-menu' do - expect(page).to_not have_selector 'Activity' - expect(page).to_not have_selector 'Food' + expect(page).not_to have_selector 'Activity' + expect(page).not_to have_selector 'Food' end end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index 530ba604300..439363e6f14 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -216,7 +216,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps page.within 'li.issue:nth-child(3)' do expect(page).to have_content 'Bugfix' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -235,7 +235,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps page.within 'li.issue:nth-child(3)' do expect(page).to have_content 'Bugfix' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -348,7 +348,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps step 'another user adds a comment with text "Yay!" to issue "Release 0.4"' do issue = Issue.find_by!(title: 'Release 0.4') - create(:note_on_issue, noteable: issue, note: 'Yay!') + create(:note_on_issue, noteable: issue, project: project, note: 'Yay!') end step 'I should see a new comment with text "Yay!"' do diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index 0ca2d6257c3..8d87f6a7a58 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -24,8 +24,8 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I should see labels help message' do page.within '.labels' do - expect(page).to have_content 'Create first label or generate default set of '\ - 'labels' + expect(page).to have_content 'Create a label or generate a default set '\ + 'of labels' end end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 692a01c7f74..f6fafe81194 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -203,7 +203,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps page.within 'li.merge-request:nth-child(3)' do expect(page).to have_content 'Bug NS-05' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -222,7 +222,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps page.within 'li.merge-request:nth-child(3)' do expect(page).to have_content 'Bug NS-05' - expect(page).to_not have_content '0 0' + expect(page).not_to have_content '0 0' end end end @@ -273,7 +273,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps step 'user "John Doe" leaves a comment like "Line is wrong" on diff' do mr = MergeRequest.find_by(title: "Bug NS-05") create(:note_on_merge_request_diff, project: project, - noteable_id: mr.id, + noteable: mr, author: user_exists("John Doe"), line_code: sample_commit.line_code, note: 'Line is wrong') diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index a3c3887ab46..3d7c6ef9d2d 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -107,7 +107,7 @@ module SharedNote end step 'I should see no notes at all' do - expect(page).to_not have_css('.note') + expect(page).not_to have_css('.note') end # Markdown diff --git a/features/steps/user.rb b/features/steps/user.rb index b1d088f07f9..59385a6ab59 100644 --- a/features/steps/user.rb +++ b/features/steps/user.rb @@ -34,7 +34,7 @@ class Spinach::Features::User < Spinach::FeatureSteps end step 'I should see contributions calendar' do - expect(page).to have_css('.cal-heatmap-container') + expect(page).to have_css('.js-contrib-calendar') end def contributed_project diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 91e420832f3..9d8b8d737a9 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -95,8 +95,7 @@ module API # GET /groups/:id/projects get ":id/projects" do group = find_group(params[:id]) - projects = group.projects - projects = filter_projects(projects) + projects = GroupProjectsFinder.new(group).execute(current_user) projects = paginate projects present projects, with: Entities::Project end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 504d3df9d34..e4b4760c53b 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -1,6 +1,6 @@ module Ci class GitlabCiYamlProcessor - class ValidationError < StandardError;end + class ValidationError < StandardError; end DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb index 85583dce9ee..9dc2602867e 100644 --- a/lib/gitlab/contributions_calendar.rb +++ b/lib/gitlab/contributions_calendar.rb @@ -19,7 +19,7 @@ module Gitlab select('date(created_at) as date, count(id) as total_amount'). map(&:attributes) - dates = (1.year.ago.to_date..(Date.today + 1.day)).to_a + dates = (1.year.ago.to_date..Date.today).to_a dates.each do |date| date_id = date.to_time.to_i.to_s diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index 3e51c06877e..e32ef8a44c2 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -5,9 +5,9 @@ module Gitlab def initialize(project) @project = project - credentials = project.import_data - if credentials && credentials[:password] - @client = Client.new(credentials[:password]) + import_data = project.import_data + if import_data && import_data.credentials && import_data.credentials[:password] + @client = Client.new(import_data.credentials[:password]) @formatter = Gitlab::ImportFormatter.new else raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" diff --git a/lib/gitlab/middleware/rails_queue_duration.rb b/lib/gitlab/middleware/rails_queue_duration.rb new file mode 100644 index 00000000000..56608b1b276 --- /dev/null +++ b/lib/gitlab/middleware/rails_queue_duration.rb @@ -0,0 +1,24 @@ +# This Rack middleware is intended to measure the latency between +# gitlab-workhorse forwarding a request to the Rails application and the +# time this middleware is reached. + +module Gitlab + module Middleware + class RailsQueueDuration + def initialize(app) + @app = app + end + + def call(env) + trans = Gitlab::Metrics.current_transaction + proxy_start = env['HTTP_GITLAB_WORHORSE_PROXY_START'].presence + if trans && proxy_start + # Time in milliseconds since gitlab-workhorse started the request + trans.set(:rails_queue_duration, Time.now.to_f * 1_000 - proxy_start.to_f / 1_000_000) + end + + @app.call(env) + end + end + end +end diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb index c59d53b941a..7d02fe3c971 100644 --- a/lib/gitlab/url_sanitizer.rb +++ b/lib/gitlab/url_sanitizer.rb @@ -7,7 +7,7 @@ module Gitlab end def initialize(url, credentials: nil) - @url = Addressable::URI.parse(URI.encode(url)) + @url = Addressable::URI.parse(url) @credentials = credentials end diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index a1ee1cba216..9462f3368e6 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -32,6 +32,13 @@ module Gitlab } end + def highest_allowed_level + restricted_levels = current_application_settings.restricted_visibility_levels + + allowed_levels = self.values - restricted_levels + allowed_levels.max || PRIVATE + end + def allowed_for?(user, level) user.is_admin? || allowed_level?(level.to_i) end diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake index e473b756023..86f5d65f128 100644 --- a/lib/tasks/gitlab/db.rake +++ b/lib/tasks/gitlab/db.rake @@ -36,5 +36,15 @@ namespace :gitlab do # Add `IF EXISTS` because cascade could have already deleted a table. tables.each { |t| connection.execute("DROP TABLE IF EXISTS #{t} CASCADE") } end + + desc 'Configures the database by running migrate, or by loading the schema and seeding if needed' + task configure: :environment do + if ActiveRecord::Base.connection.tables.any? + Rake::Task['db:migrate'].invoke + else + Rake::Task['db:schema:load'].invoke + Rake::Task['db:seed_fu'].invoke + end + end end end diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb index 2ba0d489197..4cb8b8da150 100644 --- a/spec/controllers/admin/projects_controller_spec.rb +++ b/spec/controllers/admin/projects_controller_spec.rb @@ -17,7 +17,7 @@ describe Admin::ProjectsController do it 'does not retrieve the project' do get :index, visibility_levels: [Gitlab::VisibilityLevel::INTERNAL] - expect(response.body).to_not match(project.name) + expect(response.body).not_to match(project.name) end end end diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 788a609ee40..4018dac95a2 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -19,7 +19,7 @@ describe Projects::CompareController do to: ref_to) expect(response).to be_success - expect(assigns(:diffs).first).to_not be_nil + expect(assigns(:diffs).first).not_to be_nil expect(assigns(:commits).length).to be >= 1 end @@ -32,7 +32,7 @@ describe Projects::CompareController do w: 1) expect(response).to be_success - expect(assigns(:diffs).first).to_not be_nil + expect(assigns(:diffs).first).not_to be_nil expect(assigns(:commits).length).to be >= 1 # without whitespace option, there are more than 2 diff_splits diff_splits = assigns(:diffs).first.diff.split("\n") diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb index 1bd1fc5189e..fbe8758dda7 100644 --- a/spec/controllers/projects/group_links_controller_spec.rb +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -43,7 +43,7 @@ describe Projects::GroupLinksController do end it 'does not share project with that group' do - expect(group.shared_projects).to_not include project + expect(group.shared_projects).not_to include project end end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 13d9a498789..849816b9caf 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -56,7 +56,7 @@ describe Projects::IssuesController do move_issue expect(response).to have_http_status :found - expect(another_project.issues).to_not be_empty + expect(another_project.issues).not_to be_empty end end diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index ed64e7cf9af..750fbecdd07 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -38,7 +38,7 @@ describe Projects::ProjectMembersController do include_context 'import applied' it 'does not import team members' do - expect(project.team_members).to_not include member + expect(project.team_members).not_to include member end it 'responds with not found' do diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 91b46c4d65c..fba545560c7 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -115,6 +115,17 @@ describe ProjectsController do expect(public_project_with_dot_atom).not_to be_valid end end + + context 'when the project is pending deletions' do + it 'renders a 404 error' do + project = create(:project, pending_delete: true) + sign_in(user) + + get :show, namespace_id: project.namespace.path, id: project.path + + expect(response.status).to eq 404 + end + end end describe "#update" do diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb index df70a589a89..fbe50d10ec5 100644 --- a/spec/controllers/registrations_controller_spec.rb +++ b/spec/controllers/registrations_controller_spec.rb @@ -16,7 +16,7 @@ describe RegistrationsController do it 'logs user in directly' do post(:create, user_params) expect(ActionMailer::Base.deliveries.last).to be_nil - expect(subject.current_user).to_not be_nil + expect(subject.current_user).not_to be_nil end end diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 83cc8ec6d26..ab57c52c7cd 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -47,7 +47,7 @@ describe SessionsController do authenticate_2fa(login: another_user.username, otp_attempt: another_user.current_otp) - expect(subject.current_user).to_not eq another_user + expect(subject.current_user).not_to eq another_user end end @@ -56,7 +56,7 @@ describe SessionsController do authenticate_2fa(login: another_user.username, otp_attempt: 'invalid') - expect(subject.current_user).to_not eq another_user + expect(subject.current_user).not_to eq another_user end end @@ -73,7 +73,7 @@ describe SessionsController do before { authenticate_2fa(otp_attempt: 'invalid') } it 'does not authenticate' do - expect(subject.current_user).to_not eq user + expect(subject.current_user).not_to eq user end it 'warns about invalid OTP code' do diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 2603b4fb0ce..696cf276e57 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -7,6 +7,7 @@ FactoryGirl.define do project note "Note" author + on_issue factory :note_on_commit, traits: [:on_commit] factory :note_on_commit_diff, traits: [:on_commit, :on_diff], class: LegacyDiffNote @@ -17,29 +18,26 @@ FactoryGirl.define do factory :system_note, traits: [:system] trait :on_commit do - project + noteable nil + noteable_id nil + noteable_type 'Commit' commit_id RepoHelpers.sample_commit.id - noteable_type "Commit" end trait :on_diff do line_code "0_184_184" end - trait :on_merge_request do - project - noteable_id 1 - noteable_type "MergeRequest" + trait :on_issue do + noteable { create(:issue, project: project) } end - trait :on_issue do - noteable_id 1 - noteable_type "Issue" + trait :on_merge_request do + noteable { create(:merge_request, source_project: project) } end trait :on_project_snippet do - noteable_id 1 - noteable_type "Snippet" + noteable { create(:snippet, project: project) } end trait :system do diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index a073a1f9d7c..675d9bd18b7 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -6,7 +6,7 @@ describe 'factories' do let(:entity) { build(factory.name) } it 'does not raise error when created' do - expect { entity }.to_not raise_error + expect { entity }.not_to raise_error end it 'should be valid', if: factory.build_class < ActiveRecord::Base do diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 26d03944b8a..8ebd4a6808e 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -79,7 +79,7 @@ describe "Admin Runners" do end it 'changes registration token' do - expect(page_token).to_not eq token + expect(page_token).not_to eq token end end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 6dee0cd8d47..96621843b30 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -152,7 +152,7 @@ describe "Admin::Users", feature: true do it 'sees impersonation log out icon' do icon = first('.fa.fa-user-secret') - expect(icon).to_not eql nil + expect(icon).not_to eql nil end it 'can log out of impersonated user back to original user' do diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index f83a78308e3..7a05d30e8b5 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -43,11 +43,10 @@ describe "Builds" do end it { expect(page).to have_selector('.nav-links li.active', text: 'All') } - it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') } 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 } - it { expect(page).to_not have_link 'Cancel running' } + it { expect(page).not_to have_link 'Cancel running' } end end @@ -63,7 +62,7 @@ describe "Builds" do 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 } - it { expect(page).to_not have_link 'Cancel running' } + it { expect(page).not_to have_link 'Cancel running' } end describe "GET /:project/builds/:id" do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index dacaa96d760..20f0b27bcc1 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -137,8 +137,8 @@ describe 'Commits' do expect(page).to have_content commit.git_commit_message expect(page).to have_content commit.git_author_name expect(page).to have_link('Download artifacts') - expect(page).to_not have_link('Cancel running') - expect(page).to_not have_link('Retry failed') + expect(page).not_to have_link('Cancel running') + expect(page).not_to have_link('Retry failed') end end @@ -155,9 +155,9 @@ describe 'Commits' do expect(page).to have_content commit.sha[0..7] expect(page).to have_content commit.git_commit_message expect(page).to have_content commit.git_author_name - expect(page).to_not have_link('Download artifacts') - expect(page).to_not have_link('Cancel running') - expect(page).to_not have_link('Retry failed') + expect(page).not_to have_link('Download artifacts') + expect(page).not_to have_link('Cancel running') + expect(page).not_to have_link('Retry failed') end end end diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb index 192e3619375..7efbaaa048c 100644 --- a/spec/features/issues/filter_issues_spec.rb +++ b/spec/features/issues/filter_issues_spec.rb @@ -154,4 +154,180 @@ describe 'Filter issues', feature: true do end end end + + describe 'filter issues by text' do + before do + create(:issue, title: "Bug", project: project) + + bug_label = create(:label, project: project, title: 'bug') + milestone = create(:milestone, title: "8", project: project) + + issue = create(:issue, + title: "Bug 2", + project: project, + milestone: milestone, + author: user, + assignee: user) + issue.labels << bug_label + + visit namespace_project_issues_path(project.namespace, project) + end + + context 'only text', js: true do + it 'should filter issues by searched text' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + end + + it 'should not show any issues' do + fill_in 'issue_search', with: 'testing' + + page.within '.issues-list' do + expect(page).not_to have_selector('.issue') + end + end + end + + context 'text and dropdown options', js: true do + it 'should filter by text and label' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Label' + page.within '.labels-filter' do + click_link 'bug' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and milestone' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Milestone' + page.within '.milestone-filter' do + click_link '8' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and assignee' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Assignee' + page.within '.dropdown-menu-assignee' do + click_link user.name + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + + it 'should filter by text and author' do + fill_in 'issue_search', with: 'Bug' + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Author' + page.within '.dropdown-menu-author' do + click_link user.name + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 1) + end + end + end + end + + describe 'filter issues and sort', js: true do + before do + bug_label = create(:label, project: project, title: 'bug') + bug_one = create(:issue, title: "Frontend", project: project) + bug_two = create(:issue, title: "Bug 2", project: project) + + bug_one.labels << bug_label + bug_two.labels << bug_label + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'should be able to filter and sort issues' do + click_button 'Label' + page.within '.labels-filter' do + click_link 'bug' + end + + page.within '.issues-list' do + expect(page).to have_selector('.issue', count: 2) + end + + click_button 'Last created' + page.within '.dropdown-menu-sort' do + click_link 'Oldest created' + end + + page.within '.issues-list' do + expect(first('.issue')).to have_content('Frontend') + end + end + end + + describe 'filter by any author', js: true do + before do + user2 = create(:user, name: "tester") + create(:issue, project: project, author: user) + create(:issue, project: project, author: user2) + + visit namespace_project_issues_path(project.namespace, project) + end + + it 'should show filter by any author link' do + click_button "Author" + fill_in "Search authors", with: "tester" + + page.within ".dropdown-menu-author" do + expect(page).to have_content "tester" + end + end + + it 'should show filter issues by any author' do + page.within '.issues-list' do + expect(page).to have_selector ".issue", count: 2 + end + + click_button "Author" + fill_in "Search authors", with: "tester" + + page.within ".dropdown-menu-author" do + click_link "tester" + end + + page.within '.issues-list' do + expect(page).to have_selector ".issue", count: 1 + end + end + end end diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 84c8e20ebaa..c7019c5aea1 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -19,7 +19,7 @@ feature 'issue move to another project' do end scenario 'moving issue to another project not allowed' do - expect(page).to have_no_select('move_to_project_id') + expect(page).to have_no_selector('#move_to_project_id') end end @@ -37,7 +37,7 @@ feature 'issue move to another project' do end scenario 'moving issue to another project' do - select(new_project.name_with_namespace, from: 'move_to_project_id') + first('#move_to_project_id', visible: false).set(new_project.id) click_button('Save changes') expect(current_url).to include project_path(new_project) @@ -47,14 +47,18 @@ feature 'issue move to another project' do expect(page).to have_content(issue.title) end - context 'projects user does not have permission to move issue to exist' do + context 'user does not have permission to move the issue to a project', js: true do let!(:private_project) { create(:project, :private) } let(:another_project) { create(:project) } background { another_project.team << [user, :guest] } scenario 'browsing projects in projects select' do - options = [ '', 'No project', new_project.name_with_namespace ] - expect(page).to have_select('move_to_project_id', options: options) + click_link 'Select project' + + page.within '.select2-results' do + expect(page).to have_content 'No project' + expect(page).to have_content new_project.name_with_namespace + end end end @@ -65,7 +69,7 @@ feature 'issue move to another project' do end scenario 'user wants to move issue that has already been moved' do - expect(page).to have_no_select('move_to_project_id') + expect(page).to have_no_selector('#move_to_project_id') end end end diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index e4efdbe2421..f5cfe2d666e 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -9,8 +9,11 @@ feature 'Issue notes polling' do end scenario 'Another user adds a comment to an issue', js: true do - note = create(:note_on_issue, noteable: issue, note: 'Looks good!') + note = create(:note, noteable: issue, project: project, + note: 'Looks good!') + page.execute_script('notes.refresh();') + expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') end end diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index b03dd0f666d..466a6f7dfa7 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -95,7 +95,7 @@ feature 'Multiple issue updating from issues#index', feature: true do find('.dropdown-menu-milestone a', text: "No Milestone").click click_update_issues_button - expect(first('.issue')).to_not have_content milestone.title + expect(first('.issue')).not_to have_content milestone.title end end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index e8b78124ea1..de52460f15f 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -19,10 +19,14 @@ describe 'Comments', feature: true do end describe 'On a merge request', js: true, feature: true do - let!(:merge_request) { create(:merge_request) } - let!(:project) { merge_request.source_project } + let!(:project) { create(:project) } + let!(:merge_request) do + create(:merge_request, source_project: project, target_project: project) + end + let!(:note) do - create(:note_on_merge_request, :with_attachment, project: project) + create(:note_on_merge_request, :with_attachment, noteable: merge_request, + project: project) end before do diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb index 1adab7e9c6c..c7c00a3266a 100644 --- a/spec/features/participants_autocomplete_spec.rb +++ b/spec/features/participants_autocomplete_spec.rb @@ -32,7 +32,8 @@ feature 'Member autocomplete', feature: true do context 'adding a new note on a Issue', js: true do before do issue = create(:issue, author: author, project: project) - create(:note, note: 'Ultralight Beam', noteable: issue, author: participant) + create(:note, note: 'Ultralight Beam', noteable: issue, + project: project, author: participant) visit_issue(project, issue) end @@ -47,7 +48,8 @@ feature 'Member autocomplete', feature: true do context 'adding a new note on a Merge Request ', js: true do before do merge = create(:merge_request, source_project: project, target_project: project, author: author) - create(:note, note: 'Ultralight Beam', noteable: merge, author: participant) + create(:note, note: 'Ultralight Beam', noteable: merge, + project: project, author: participant) visit_merge_request(project, merge) end diff --git a/spec/features/pipelines_spec.rb b/spec/features/pipelines_spec.rb index 1d6f4485c81..bef0578a9bb 100644 --- a/spec/features/pipelines_spec.rb +++ b/spec/features/pipelines_spec.rb @@ -41,7 +41,7 @@ describe "Pipelines" do context 'when canceling' do before { click_link('Cancel') } - it { expect(page).to_not have_link('Cancel') } + it { expect(page).not_to have_link('Cancel') } it { expect(page).to have_selector('.ci-canceled') } end end @@ -57,7 +57,7 @@ describe "Pipelines" do context 'when retrying' do before { click_link('Retry') } - it { expect(page).to_not have_link('Retry') } + it { expect(page).not_to have_link('Retry') } it { expect(page).to have_selector('.ci-pending') } end end @@ -75,7 +75,7 @@ describe "Pipelines" do context 'without artifacts' do let!(:without_artifacts) { create(:ci_build, :success, commit: pipeline, name: 'rspec', stage: 'test') } - it { expect(page).to_not have_selector('.build-artifacts') } + it { expect(page).not_to have_selector('.build-artifacts') } end end end @@ -104,23 +104,23 @@ describe "Pipelines" do end context 'retrying builds' do - it { expect(page).to_not have_content('retried') } + it { expect(page).not_to have_content('retried') } context 'when retrying' do before { click_on 'Retry failed' } - it { expect(page).to_not have_content('Retry failed') } + it { expect(page).not_to have_content('Retry failed') } it { expect(page).to have_content('retried') } end end context 'canceling builds' do - it { expect(page).to_not have_selector('.ci-canceled') } + it { expect(page).not_to have_selector('.ci-canceled') } context 'when canceling' do before { click_on 'Cancel running' } - it { expect(page).to_not have_content('Cancel running') } + it { expect(page).not_to have_content('Cancel running') } it { expect(page).to have_selector('.ci-canceled') } end end diff --git a/spec/features/project/shortcuts_spec.rb b/spec/features/project/shortcuts_spec.rb index 2595c4181e5..54aa9c66a08 100644 --- a/spec/features/project/shortcuts_spec.rb +++ b/spec/features/project/shortcuts_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'Project shortcuts', feature: true do - let(:project) { create(:project) } + let(:project) { create(:project, name: 'Victorialand') } let(:user) { create(:user) } describe 'On a project', js: true do @@ -14,7 +14,7 @@ feature 'Project shortcuts', feature: true do describe 'pressing "i"' do it 'redirects to new issue page' do find('body').native.send_key('i') - expect(page).to have_content('New Issue') + expect(page).to have_content('Victorialand') end end end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 49156130ad3..a5ed3595b0a 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -29,8 +29,8 @@ describe "Runners" do end before do - expect(page).to_not have_content(@specific_runner3.display_name) - expect(page).to_not have_content(@specific_runner3.display_name) + expect(page).not_to have_content(@specific_runner3.display_name) + expect(page).not_to have_content(@specific_runner3.display_name) end it "places runners in right places" do diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index b7368cca29d..6ed279ef9be 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -75,7 +75,10 @@ feature 'Task Lists', feature: true do describe 'for Notes' do let!(:issue) { create(:issue, author: user, project: project) } - let!(:note) { create(:note, note: markdown, noteable: issue, author: user) } + let!(:note) do + create(:note, note: markdown, noteable: issue, + project: project, author: user) + end it 'renders for note body' do visit_issue(project, issue) diff --git a/spec/features/todos/target_state_spec.rb b/spec/features/todos/target_state_spec.rb new file mode 100644 index 00000000000..72491ac7e61 --- /dev/null +++ b/spec/features/todos/target_state_spec.rb @@ -0,0 +1,65 @@ +require 'rails_helper' + +feature 'Todo target states', feature: true do + let(:user) { create(:user) } + let(:author) { create(:user) } + let(:project) { create(:project) } + + before do + login_as user + end + + scenario 'on a closed issue todo has closed label' do + issue_closed = create(:issue, state: 'closed') + create_todo issue_closed + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Closed') + end + end + + scenario 'on an open issue todo does not have an open label' do + issue_open = create(:issue) + create_todo issue_open + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).not_to have_content('Open') + end + end + + scenario 'on a merged merge request todo has merged label' do + mr_merged = create(:merge_request, :simple, author: user, state: 'merged') + create_todo mr_merged + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Merged') + end + end + + scenario 'on a closed merge request todo has closed label' do + mr_closed = create(:merge_request, :simple, author: user, state: 'closed') + create_todo mr_closed + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).to have_content('Closed') + end + end + + scenario 'on an open merge request todo does not have an open label' do + mr_open = create(:merge_request, :simple, author: user) + create_todo mr_open + visit dashboard_todos_path + + page.within '.todos-list' do + expect(page).not_to have_content('Open') + end + end + + def create_todo(target) + create(:todo, :mentioned, user: user, project: project, target: target, author: author) + end +end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index 48e2dae4884..a2b8f7b6931 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -34,7 +34,7 @@ describe 'Project variables', js: true do find('.btn-variable-delete').click end - expect(page).to_not have_selector('variables-table') + expect(page).not_to have_selector('variables-table') end it 'should edit variable' do diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb index 16fbb5dcecb..49ea4fa6d3e 100644 --- a/spec/helpers/auth_helper_spec.rb +++ b/spec/helpers/auth_helper_spec.rb @@ -36,7 +36,7 @@ describe AuthHelper do ) expect(helper.enabled_button_based_providers).to include('twitter') - expect(helper.enabled_button_based_providers).to_not include('github') + expect(helper.enabled_button_based_providers).not_to include('github') end end end diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb index 600e1c4e9ec..8e7ed42e883 100644 --- a/spec/helpers/merge_requests_helper_spec.rb +++ b/spec/helpers/merge_requests_helper_spec.rb @@ -17,7 +17,7 @@ describe MergeRequestsHelper do it 'does not include api credentials in a link' do allow(ci_service). to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c") - expect(helper.ci_build_details_path(merge_request)).to_not match("secret") + expect(helper.ci_build_details_path(merge_request)).not_to match("secret") end end diff --git a/spec/javascripts/fixtures/right_sidebar.html.haml b/spec/javascripts/fixtures/right_sidebar.html.haml new file mode 100644 index 00000000000..95efaff4b69 --- /dev/null +++ b/spec/javascripts/fixtures/right_sidebar.html.haml @@ -0,0 +1,13 @@ +%div + %div.page-gutter.page-with-sidebar + + %aside.right-sidebar + %div.block.issuable-sidebar-header + %a.gutter-toggle.pull-right.js-sidebar-toggle + %i.fa.fa-angle-double-left + + %form.issuable-context-form + %div.block.labels + %div.sidebar-collapsed-icon + %i.fa.fa-tags + %span 1 diff --git a/spec/javascripts/right_sidebar_spec.js.coffee b/spec/javascripts/right_sidebar_spec.js.coffee new file mode 100644 index 00000000000..2075cacdb67 --- /dev/null +++ b/spec/javascripts/right_sidebar_spec.js.coffee @@ -0,0 +1,69 @@ +#= require right_sidebar +#= require jquery +#= require jquery.cookie + +@sidebar = null +$aside = null +$toggle = null +$icon = null +$page = null +$labelsIcon = null + + +assertSidebarState = (state) -> + + shouldBeExpanded = state is 'expanded' + shouldBeCollapsed = state is 'collapsed' + + expect($aside.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded + expect($page.hasClass('right-sidebar-expanded')).toBe shouldBeExpanded + expect($icon.hasClass('fa-angle-double-right')).toBe shouldBeExpanded + + expect($aside.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed + expect($page.hasClass('right-sidebar-collapsed')).toBe shouldBeCollapsed + expect($icon.hasClass('fa-angle-double-left')).toBe shouldBeCollapsed + + +describe 'RightSidebar', -> + + fixture.preload 'right_sidebar.html' + + beforeEach -> + fixture.load 'right_sidebar.html' + + @sidebar = new Sidebar + $aside = $ '.right-sidebar' + $page = $ '.page-with-sidebar' + $icon = $aside.find 'i' + $toggle = $aside.find '.js-sidebar-toggle' + $labelsIcon = $aside.find '.sidebar-collapsed-icon' + + + it 'should expand the sidebar when arrow is clicked', -> + + $toggle.click() + assertSidebarState 'expanded' + + + it 'should collapse the sidebar when arrow is clicked', -> + + $toggle.click() + assertSidebarState 'expanded' + + $toggle.click() + assertSidebarState 'collapsed' + + + it 'should float over the page and when sidebar icons clicked', -> + + $labelsIcon.click() + assertSidebarState 'expanded' + + + it 'should collapse when the icon arrow clicked while it is floating on page', -> + + $labelsIcon.click() + assertSidebarState 'expanded' + + $toggle.click() + assertSidebarState 'collapsed' diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 9eef8ea0976..7375539cf17 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -619,19 +619,19 @@ module Ci context 'no dependencies' do let(:dependencies) { } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'dependencies to builds' do let(:dependencies) { ['build1', 'build2'] } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'dependencies to builds defined as symbols' do let(:dependencies) { [:build1, :build2] } - it { expect { subject }.to_not raise_error } + it { expect { subject }.not_to raise_error } end context 'undefined dependency' do diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb index 2638401ae6e..4f3f8b24fc4 100644 --- a/spec/lib/container_registry/registry_spec.rb +++ b/spec/lib/container_registry/registry_spec.rb @@ -10,7 +10,7 @@ describe ContainerRegistry::Registry do it { is_expected.to respond_to(:uri) } it { is_expected.to respond_to(:path) } - it { expect(subject.repository('test')).to_not be_nil } + it { expect(subject.repository('test')).not_to be_nil } context '#path' do subject { registry.path } diff --git a/spec/lib/container_registry/repository_spec.rb b/spec/lib/container_registry/repository_spec.rb index e6d66b11e4e..279709521c9 100644 --- a/spec/lib/container_registry/repository_spec.rb +++ b/spec/lib/container_registry/repository_spec.rb @@ -6,7 +6,7 @@ describe ContainerRegistry::Repository do it { expect(repository).to respond_to(:registry) } it { expect(repository).to delegate_method(:client).to(:registry) } - it { expect(repository.tag('test')).to_not be_nil } + it { expect(repository.tag('test')).not_to be_nil } context '#path' do subject { repository.path } @@ -27,7 +27,7 @@ describe ContainerRegistry::Repository do context '#manifest' do subject { repository.manifest } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context '#valid?' do @@ -39,7 +39,7 @@ describe ContainerRegistry::Repository do context '#tags' do subject { repository.tags } - it { is_expected.to_not be_empty } + it { is_expected.not_to be_empty } end end diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb index 12cf91127ed..858cb0bb134 100644 --- a/spec/lib/container_registry/tag_spec.rb +++ b/spec/lib/container_registry/tag_spec.rb @@ -50,13 +50,13 @@ describe ContainerRegistry::Tag do context '#config' do subject { tag.config } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context '#created_at' do subject { tag.created_at } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end end end diff --git a/spec/lib/gitlab/award_emoji_spec.rb b/spec/lib/gitlab/award_emoji_spec.rb index 4e6c04a11b9..0f3852b1729 100644 --- a/spec/lib/gitlab/award_emoji_spec.rb +++ b/spec/lib/gitlab/award_emoji_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::AwardEmoji do subject { Gitlab::AwardEmoji.urls } it { is_expected.to be_an_instance_of(Array) } - it { is_expected.to_not be_empty } + it { is_expected.not_to be_empty } context 'every Hash in the Array' do it 'has the correct keys and values' do diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index af839f42421..7718689e6d4 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -59,7 +59,7 @@ describe Gitlab::BitbucketImport::Client, lib: true do bitbucket_access_token_secret: "test" } }) project.import_url = "ssh://git@bitbucket.org/test/test.git" - expect { described_class.from_project(project) }.to_not raise_error + expect { described_class.from_project(project) }.not_to raise_error end end end diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb index 46a5b7fce65..711a3e1c7d4 100644 --- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb +++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb @@ -122,7 +122,7 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do describe 'empty path', path: '' do subject { |example| path(example) } - it { is_expected.to_not have_parent } + it { is_expected.not_to have_parent } describe '#children' do subject { |example| path(example).children } diff --git a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb index 0a7ca3ec848..0af249d8690 100644 --- a/spec/lib/gitlab/gfm/reference_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/reference_rewriter_spec.rb @@ -33,8 +33,8 @@ describe Gitlab::Gfm::ReferenceRewriter do end it { is_expected.to include issue_first.to_reference(new_project) } - it { is_expected.to_not include issue_second.to_reference(new_project) } - it { is_expected.to_not include merge_request.to_reference(new_project) } + it { is_expected.not_to include issue_second.to_reference(new_project) } + it { is_expected.not_to include merge_request.to_reference(new_project) } end context 'description ambigous elements' do diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb index eda956e6f0a..6eca33f9fee 100644 --- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb +++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb @@ -32,13 +32,13 @@ describe Gitlab::Gfm::UploadsRewriter do let(:new_paths) { new_files.map(&:path) } it 'rewrites content' do - expect(new_text).to_not eq text + expect(new_text).not_to eq text expect(new_text.length).to eq text.length end it 'copies files' do expect(new_files).to all(exist) - expect(old_paths).to_not match_array new_paths + expect(old_paths).not_to match_array new_paths expect(old_paths).to all(include(old_project.path_with_namespace)) expect(new_paths).to all(include(new_project.path_with_namespace)) end @@ -48,8 +48,8 @@ describe Gitlab::Gfm::UploadsRewriter do end it 'generates a new secret for each file' do - expect(new_paths).to_not include image_uploader.secret - expect(new_paths).to_not include zip_uploader.secret + expect(new_paths).not_to include image_uploader.secret + expect(new_paths).not_to include zip_uploader.secret end end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 3325190789b..88814bc474d 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -368,7 +368,7 @@ describe Gitlab::Lfs::Router, lib: true do expect(response['objects']).to be_kind_of(Array) expect(response['objects'].first['oid']).to eq(sample_oid) expect(response['objects'].first['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).not_to include(project.id) expect(lfs_object.projects.pluck(:id)).to include(public_project.id) expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}") expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) @@ -430,7 +430,7 @@ describe Gitlab::Lfs::Router, lib: true do expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(response_body['objects'].last['size']).to eq(sample_size) - expect(response_body['objects'].last).to_not have_key('actions') + expect(response_body['objects'].last).not_to have_key('actions') end end end diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index 7b86450a223..220e86924a2 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -67,7 +67,7 @@ describe Gitlab::Metrics::Instrumentation do allow(Gitlab::Metrics).to receive(:method_call_threshold). and_return(100) - expect(transaction).to_not receive(:add_metric) + expect(transaction).not_to receive(:add_metric) @dummy.foo end @@ -147,7 +147,7 @@ describe Gitlab::Metrics::Instrumentation do allow(Gitlab::Metrics).to receive(:method_call_threshold). and_return(100) - expect(transaction).to_not receive(:add_metric) + expect(transaction).not_to receive(:add_metric) @dummy.new.bar end @@ -220,7 +220,7 @@ describe Gitlab::Metrics::Instrumentation do described_class.instrument_methods(@dummy) - expect(@dummy).to_not respond_to(:_original_kittens) + expect(@dummy).not_to respond_to(:_original_kittens) end it 'can take a block to determine if a method should be instrumented' do @@ -228,7 +228,7 @@ describe Gitlab::Metrics::Instrumentation do false end - expect(@dummy).to_not respond_to(:_original_foo) + expect(@dummy).not_to respond_to(:_original_foo) end end diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb index 38da77adc9f..59db127674a 100644 --- a/spec/lib/gitlab/metrics/sampler_spec.rb +++ b/spec/lib/gitlab/metrics/sampler_spec.rb @@ -130,7 +130,7 @@ describe Gitlab::Metrics::Sampler do 100.times do interval = sampler.sleep_interval - expect(interval).to_not eq(last) + expect(interval).not_to eq(last) last = interval end diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index e3293a01207..49699ffe28f 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -13,7 +13,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do describe 'without a current transaction' do it 'simply returns' do expect_any_instance_of(Gitlab::Metrics::Transaction). - to_not receive(:increment) + not_to receive(:increment) subscriber.sql(event) end diff --git a/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb new file mode 100644 index 00000000000..fd6f684db0c --- /dev/null +++ b/spec/lib/gitlab/middleware/rails_queue_duration_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe Gitlab::Middleware::RailsQueueDuration do + let(:app) { double(:app) } + let(:middleware) { described_class.new(app) } + let(:env) { {} } + let(:transaction) { double(:transaction) } + + before { expect(app).to receive(:call).with(env).and_return('yay') } + + describe '#call' do + it 'calls the app when metrics are disabled' do + expect(Gitlab::Metrics).to receive(:current_transaction).and_return(nil) + expect(middleware.call(env)).to eq('yay') + end + + context 'when metrics are enabled' do + before { allow(Gitlab::Metrics).to receive(:current_transaction).and_return(transaction) } + + it 'calls the app when metrics are enabled but no timing header is found' do + expect(middleware.call(env)).to eq('yay') + end + + it 'sets proxy_flight_time and calls the app when the header is present' do + env['HTTP_GITLAB_WORHORSE_PROXY_START'] = '123' + expect(transaction).to receive(:set).with(:rails_queue_duration, an_instance_of(Float)) + expect(middleware.call(env)).to eq('yay') + end + end + end +end diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb index f093d0a0d8b..e848d88182f 100644 --- a/spec/lib/gitlab/note_data_builder_spec.rb +++ b/spec/lib/gitlab/note_data_builder_spec.rb @@ -9,7 +9,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do before(:each) do expect(data).to have_key(:object_attributes) expect(data[:object_attributes]).to have_key(:url) - expect(data[:object_attributes][:url]).to eq(Gitlab::UrlBuilder.build(note)) + expect(data[:object_attributes][:url]) + .to eq(Gitlab::UrlBuilder.build(note)) expect(data[:object_kind]).to eq('note') expect(data[:user]).to eq(user.hook_attrs) end @@ -37,13 +38,21 @@ describe 'Gitlab::NoteDataBuilder', lib: true do end describe 'When asking for a note on issue' do - let(:issue) { create(:issue, created_at: fixed_time, updated_at: fixed_time) } - let(:note) { create(:note_on_issue, noteable_id: issue.id, project: project) } + let(:issue) do + create(:issue, created_at: fixed_time, updated_at: fixed_time, + project: project) + end + + let(:note) do + create(:note_on_issue, noteable: issue, project: project) + end it 'returns the note and issue-specific data' do expect(data).to have_key(:issue) - expect(data[:issue].except('updated_at')).to eq(issue.hook_attrs.except('updated_at')) - expect(data[:issue]['updated_at']).to be > issue.hook_attrs['updated_at'] + expect(data[:issue].except('updated_at')) + .to eq(issue.reload.hook_attrs.except('updated_at')) + expect(data[:issue]['updated_at']) + .to be > issue.hook_attrs['updated_at'] end include_examples 'project hook data' @@ -51,13 +60,23 @@ describe 'Gitlab::NoteDataBuilder', lib: true do end describe 'When asking for a note on merge request' do - let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) } - let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id, project: project) } + let(:merge_request) do + create(:merge_request, created_at: fixed_time, + updated_at: fixed_time, + source_project: project) + end + + let(:note) do + create(:note_on_merge_request, noteable: merge_request, + project: project) + end it 'returns the note and merge request data' do expect(data).to have_key(:merge_request) - expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at')) - expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at'] + expect(data[:merge_request].except('updated_at')) + .to eq(merge_request.reload.hook_attrs.except('updated_at')) + expect(data[:merge_request]['updated_at']) + .to be > merge_request.hook_attrs['updated_at'] end include_examples 'project hook data' @@ -65,13 +84,22 @@ describe 'Gitlab::NoteDataBuilder', lib: true do end describe 'When asking for a note on merge request diff' do - let(:merge_request) { create(:merge_request, created_at: fixed_time, updated_at: fixed_time) } - let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id, project: project) } + let(:merge_request) do + create(:merge_request, created_at: fixed_time, updated_at: fixed_time, + source_project: project) + end + + let(:note) do + create(:note_on_merge_request_diff, noteable: merge_request, + project: project) + end it 'returns the note and merge request diff data' do expect(data).to have_key(:merge_request) - expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at')) - expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at'] + expect(data[:merge_request].except('updated_at')) + .to eq(merge_request.reload.hook_attrs.except('updated_at')) + expect(data[:merge_request]['updated_at']) + .to be > merge_request.hook_attrs['updated_at'] end include_examples 'project hook data' @@ -79,13 +107,22 @@ describe 'Gitlab::NoteDataBuilder', lib: true do end describe 'When asking for a note on project snippet' do - let!(:snippet) { create(:project_snippet, created_at: fixed_time, updated_at: fixed_time) } - let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id, project: project) } + let!(:snippet) do + create(:project_snippet, created_at: fixed_time, updated_at: fixed_time, + project: project) + end + + let!(:note) do + create(:note_on_project_snippet, noteable: snippet, + project: project) + end it 'returns the note and project snippet data' do expect(data).to have_key(:snippet) - expect(data[:snippet].except('updated_at')).to eq(snippet.hook_attrs.except('updated_at')) - expect(data[:snippet]['updated_at']).to be > snippet.hook_attrs['updated_at'] + expect(data[:snippet].except('updated_at')) + .to eq(snippet.reload.hook_attrs.except('updated_at')) + expect(data[:snippet]['updated_at']) + .to be > snippet.hook_attrs['updated_at'] end include_examples 'project hook data' diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb index de6bb86c5dd..2ae79b50e77 100644 --- a/spec/lib/gitlab/sherlock/collection_spec.rb +++ b/spec/lib/gitlab/sherlock/collection_spec.rb @@ -11,13 +11,13 @@ describe Gitlab::Sherlock::Collection, lib: true do it 'adds a new transaction' do collection.add(transaction) - expect(collection).to_not be_empty + expect(collection).not_to be_empty end it 'is aliased as <<' do collection << transaction - expect(collection).to_not be_empty + expect(collection).not_to be_empty end end @@ -47,7 +47,7 @@ describe Gitlab::Sherlock::Collection, lib: true do it 'returns false for a collection with a transaction' do collection.add(transaction) - expect(collection).to_not be_empty + expect(collection).not_to be_empty end end diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb index 05da915ccfd..0a620428138 100644 --- a/spec/lib/gitlab/sherlock/query_spec.rb +++ b/spec/lib/gitlab/sherlock/query_spec.rb @@ -85,7 +85,7 @@ FROM users; frames = query.application_backtrace expect(frames).to be_an_instance_of(Array) - expect(frames).to_not be_empty + expect(frames).not_to be_empty frames.each do |frame| expect(frame.path).to start_with(Rails.root.to_s) diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index 7553f2a045f..9fe18f253f0 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -203,7 +203,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do end it 'only tracks queries triggered from the transaction thread' do - expect(transaction).to_not receive(:track_query) + expect(transaction).not_to receive(:track_query) Thread.new { subscription.publish('test', time, time, nil, query_data) }. join @@ -226,7 +226,7 @@ describe Gitlab::Sherlock::Transaction, lib: true do end it 'only tracks views rendered from the transaction thread' do - expect(transaction).to_not receive(:track_view) + expect(transaction).not_to receive(:track_view) Thread.new { subscription.publish('test', time, time, nil, view_data) }. join diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb index 0c3d3ea7019..18726754517 100644 --- a/spec/lib/json_web_token/rsa_token_spec.rb +++ b/spec/lib/json_web_token/rsa_token_spec.rb @@ -23,7 +23,7 @@ describe JSONWebToken::RSAToken do subject { JWT.decode(rsa_encoded, rsa_key) } - it { expect{subject}.to_not raise_error } + it { expect{subject}.not_to raise_error } it { expect(subject.first).to include('key' => 'value') } it do expect(subject.second).to eq( diff --git a/spec/mailers/previews/devise_mailer_preview.rb b/spec/mailers/previews/devise_mailer_preview.rb new file mode 100644 index 00000000000..dc3062a4332 --- /dev/null +++ b/spec/mailers/previews/devise_mailer_preview.rb @@ -0,0 +1,11 @@ +class DeviseMailerPreview < ActionMailer::Preview + def confirmation_instructions_for_signup + user = User.new(name: 'Jane Doe', email: 'signup@example.com') + DeviseMailer.confirmation_instructions(user, 'faketoken', {}) + end + + def confirmation_instructions_for_new_email + user = User.last + DeviseMailer.confirmation_instructions(user, 'faketoken', {}) + end +end diff --git a/spec/mailers/shared/notify.rb b/spec/mailers/shared/notify.rb index 5a85cb501dd..93de5850ba2 100644 --- a/spec/mailers/shared/notify.rb +++ b/spec/mailers/shared/notify.rb @@ -146,8 +146,8 @@ shared_examples 'it should have Gmail Actions links' do end shared_examples 'it should not have Gmail Actions links' do - it { is_expected.to_not have_body_text '<script type="application/ld+json">' } - it { is_expected.to_not have_body_text /ViewAction/ } + it { is_expected.not_to have_body_text '<script type="application/ld+json">' } + it { is_expected.not_to have_body_text /ViewAction/ } end shared_examples 'it should show Gmail Actions View Issue link' do diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index abae3271a5c..55b7af441d6 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -90,7 +90,7 @@ describe Ci::Build, models: true do build.update_attributes(trace: token) end - it { is_expected.to_not include(token) } + it { is_expected.not_to include(token) } end end @@ -317,7 +317,7 @@ describe Ci::Build, models: true do context 'when build does not have tags' do subject { create(:ci_build, tag_list: []) } - it { is_expected.to_not have_tags } + it { is_expected.not_to have_tags } end end @@ -534,7 +534,7 @@ describe Ci::Build, models: true do end it 'should set erase date' do - expect(build.erased_at).to_not be_falsy + expect(build.erased_at).not_to be_falsy end end @@ -606,7 +606,7 @@ describe Ci::Build, models: true do describe '#erase' do it 'should not raise error' do - expect { build.erase }.to_not raise_error + expect { build.erase }.not_to raise_error end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 1b5940ad5a8..22f8639e5ab 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -247,7 +247,7 @@ describe Ci::Commit, models: true do expect(commit.builds.pluck(:status)).to contain_exactly('pending') commit.builds.running_or_pending.each(&:success) - expect(commit.builds.running_or_pending).to_not be_empty + expect(commit.builds.running_or_pending).not_to be_empty expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb deleted file mode 100644 index 95fc160b238..00000000000 --- a/spec/models/ci/runner_project_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'spec_helper' - -describe Ci::RunnerProject, models: true do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 7c6e39419ed..5d04d8ffcff 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -140,7 +140,7 @@ describe Ci::Runner, models: true do context 'when runner does not have tags' do subject { create(:ci_runner, tag_list: []) } - it { is_expected.to_not have_tags } + it { is_expected.not_to have_tags } end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 424b6f5e27f..c41d2a330f7 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -118,6 +118,35 @@ describe Issue, "Issuable" do end end + describe "#sort" do + let(:project) { build_stubbed(:empty_project) } + + context "by milestone due date" do + #Correct order is: + #Issues/MRs with milestones ordered by date + #Issues/MRs with milestones without dates + #Issues/MRs without milestones + + let!(:issue) { create(:issue, project: project) } + let!(:early_milestone) { create(:milestone, project: project, due_date: 10.days.from_now) } + let!(:late_milestone) { create(:milestone, project: project, due_date: 30.days.from_now) } + let!(:issue1) { create(:issue, project: project, milestone: early_milestone) } + let!(:issue2) { create(:issue, project: project, milestone: late_milestone) } + let!(:issue3) { create(:issue, project: project) } + + it "sorts desc" do + issues = project.issues.sort('milestone_due_desc') + expect(issues).to match_array([issue2, issue1, issue, issue3]) + end + + it "sorts asc" do + issues = project.issues.sort('milestone_due_asc') + expect(issues).to match_array([issue1, issue2, issue, issue3]) + end + end + end + + describe '#subscribed?' do context 'user is not a participant in the issue' do before { allow(issue).to receive(:participants).with(user).and_return([]) } @@ -164,12 +193,11 @@ describe Issue, "Issuable" do let(:data) { issue.to_hook_data(user) } let(:project) { issue.project } - it "returns correct hook data" do expect(data[:object_kind]).to eq("issue") expect(data[:user]).to eq(user.hook_attrs) expect(data[:object_attributes]).to eq(issue.hook_attrs) - expect(data).to_not have_key(:assignee) + expect(data).not_to have_key(:assignee) end context "issue is assigned" do @@ -204,6 +232,8 @@ describe Issue, "Issuable" do end describe "votes" do + let(:project) { issue.project } + before do create(:award_emoji, :upvote, awardable: issue) create(:award_emoji, :downvote, awardable: issue) diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index b6adc2bf247..9e8ebc56a31 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -28,14 +28,14 @@ describe ApplicationSetting, 'TokenAuthenticatable' do context 'token is not generated yet' do describe 'token field accessor' do subject { described_class.new.send(token_field) } - it { is_expected.to_not be_blank } + it { is_expected.not_to be_blank } end describe 'ensured token' do subject { described_class.new.send("ensure_#{token_field}") } it { is_expected.to be_a String } - it { is_expected.to_not be_blank } + it { is_expected.not_to be_blank } end describe 'ensured! token' do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 0caf5869c24..d0e02618b6b 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -27,13 +27,13 @@ describe GenericCommitStatus, models: true do describe :context do subject { generic_commit_status.context } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end describe :stage do subject { generic_commit_status.stage } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 8ab00c70f9d..6540d77fbc0 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -192,7 +192,7 @@ describe Issue, models: true do source_project: subject.project, source_branch: "#{subject.iid}-branch" }) merge_request.create_cross_references!(user) - expect(subject.referenced_merge_requests).to_not be_empty + expect(subject.referenced_merge_requests).not_to be_empty expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb index 7c29bef54e4..b2d06853886 100644 --- a/spec/models/legacy_diff_note_spec.rb +++ b/spec/models/legacy_diff_note_spec.rb @@ -63,7 +63,9 @@ describe LegacyDiffNote, models: true do code = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos) # We're persisting in order to trigger the set_diff callback - note = create(:note_on_merge_request_diff, noteable: merge, line_code: code) + note = create(:note_on_merge_request_diff, noteable: merge, + line_code: code, + project: merge.source_project) # Make sure we don't get a false positive from a guard clause expect(note).to receive(:find_noteable_diff).and_call_original diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index e269ff26a04..4b67c2facf3 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -119,7 +119,8 @@ describe MergeRequest, models: true do before do allow(merge_request).to receive(:commits) { [merge_request.source_project.repository.commit] } - create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.project) + create(:note_on_commit, commit_id: merge_request.commits.first.id, + project: merge_request.project) create(:note, noteable: merge_request, project: merge_request.project) end @@ -129,7 +130,9 @@ describe MergeRequest, models: true do end it "should include notes for commits from target project as well" do - create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project) + create(:note_on_commit, commit_id: merge_request.commits.first.id, + project: merge_request.target_project) + expect(merge_request.commits).not_to be_empty expect(merge_request.mr_and_commit_notes.count).to eq(3) end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index a3ab1b796be..457bf337b52 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -12,6 +12,34 @@ describe Note, models: true do describe 'validation' do it { is_expected.to validate_presence_of(:note) } it { is_expected.to validate_presence_of(:project) } + + context 'when note is on commit' do + before { allow(subject).to receive(:for_commit?).and_return(true) } + + it { is_expected.to validate_presence_of(:commit_id) } + it { is_expected.not_to validate_presence_of(:noteable_id) } + end + + context 'when note is not on commit' do + before { allow(subject).to receive(:for_commit?).and_return(false) } + + it { is_expected.not_to validate_presence_of(:commit_id) } + it { is_expected.to validate_presence_of(:noteable_id) } + end + + context 'when noteable and note project differ' do + subject do + build(:note, noteable: build_stubbed(:issue), + project: build_stubbed(:project)) + end + + it { is_expected.to be_invalid } + end + + context 'when noteable and note project are the same' do + subject { create(:note) } + it { is_expected.to be_valid } + end end describe "Commit notes" do @@ -89,8 +117,8 @@ describe Note, models: true do end describe "#all_references" do - let!(:note1) { create(:note) } - let!(:note2) { create(:note) } + let!(:note1) { create(:note_on_issue) } + let!(:note2) { create(:note_on_issue) } it "reads the rendered note body from the cache" do expect(Banzai::Renderer).to receive(:render).with(note1.note, pipeline: :note, cache_key: [note1, "note"], project: note1.project) @@ -102,7 +130,7 @@ describe Note, models: true do end describe '.search' do - let(:note) { create(:note, note: 'WoW') } + let(:note) { create(:note_on_issue, note: 'WoW') } it 'returns notes with matching content' do expect(described_class.search(note.note)).to eq([note]) diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 6fb5cad5011..5f618322aab 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -176,86 +176,117 @@ describe HipchatService, models: true do context "Note events" do let(:user) { create(:user) } let(:project) { create(:project, creator_id: user.id) } - let(:issue) { create(:issue, project: project) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:snippet) { create(:project_snippet, project: project) } - let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } - let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") } - let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")} - let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") } - - it "should call Hipchat API for commit comment events" do - data = Gitlab::NoteDataBuilder.build(commit_note, user) - hipchat.execute(data) - expect(WebMock).to have_requested(:post, api_url).once + context 'when commit comment event triggered' do + let(:commit_note) do + create(:note_on_commit, author: user, project: project, + commit_id: project.repository.commit.id, + note: 'a comment on a commit') + end + + it "should call Hipchat API for commit comment events" do + data = Gitlab::NoteDataBuilder.build(commit_note, user) + hipchat.execute(data) - message = hipchat.send(:create_message, data) + expect(WebMock).to have_requested(:post, api_url).once - obj_attr = data[:object_attributes] - commit_id = Commit.truncate_sha(data[:commit][:id]) - title = hipchat.send(:format_title, data[:commit][:message]) + message = hipchat.send(:create_message, data) - expect(message).to eq("#{user.name} commented on " \ - "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \ - "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ - "#{title}" \ - "<pre>a comment on a commit</pre>") + obj_attr = data[:object_attributes] + commit_id = Commit.truncate_sha(data[:commit][:id]) + title = hipchat.send(:format_title, data[:commit][:message]) + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "#{title}" \ + "<pre>a comment on a commit</pre>") + end end - it "should call Hipchat API for merge request comment events" do - data = Gitlab::NoteDataBuilder.build(merge_request_note, user) - hipchat.execute(data) + context 'when merge request comment event triggered' do + let(:merge_request) do + create(:merge_request, source_project: project, + target_project: project) + end - expect(WebMock).to have_requested(:post, api_url).once + let(:merge_request_note) do + create(:note_on_merge_request, noteable: merge_request, + project: project, + note: "merge request note") + end - message = hipchat.send(:create_message, data) + it "should call Hipchat API for merge request comment events" do + data = Gitlab::NoteDataBuilder.build(merge_request_note, user) + hipchat.execute(data) - obj_attr = data[:object_attributes] - merge_id = data[:merge_request]['iid'] - title = data[:merge_request]['title'] + expect(WebMock).to have_requested(:post, api_url).once - expect(message).to eq("#{user.name} commented on " \ - "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \ - "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ - "<b>#{title}</b>" \ - "<pre>merge request note</pre>") + message = hipchat.send(:create_message, data) + + obj_attr = data[:object_attributes] + merge_id = data[:merge_request]['iid'] + title = data[:merge_request]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>merge request note</pre>") + end end - it "should call Hipchat API for issue comment events" do - data = Gitlab::NoteDataBuilder.build(issue_note, user) - hipchat.execute(data) + context 'when issue comment event triggered' do + let(:issue) { create(:issue, project: project) } + let(:issue_note) do + create(:note_on_issue, noteable: issue, project: project, + note: "issue note") + end - message = hipchat.send(:create_message, data) + it "should call Hipchat API for issue comment events" do + data = Gitlab::NoteDataBuilder.build(issue_note, user) + hipchat.execute(data) - obj_attr = data[:object_attributes] - issue_id = data[:issue]['iid'] - title = data[:issue]['title'] + message = hipchat.send(:create_message, data) - expect(message).to eq("#{user.name} commented on " \ - "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \ - "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ - "<b>#{title}</b>" \ - "<pre>issue note</pre>") + obj_attr = data[:object_attributes] + issue_id = data[:issue]['iid'] + title = data[:issue]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>issue note</pre>") + end end - it "should call Hipchat API for snippet comment events" do - data = Gitlab::NoteDataBuilder.build(snippet_note, user) - hipchat.execute(data) + context 'when snippet comment event triggered' do + let(:snippet) { create(:project_snippet, project: project) } + let(:snippet_note) do + create(:note_on_project_snippet, noteable: snippet, + project: project, + note: "snippet note") + end - expect(WebMock).to have_requested(:post, api_url).once + it "should call Hipchat API for snippet comment events" do + data = Gitlab::NoteDataBuilder.build(snippet_note, user) + hipchat.execute(data) - message = hipchat.send(:create_message, data) + expect(WebMock).to have_requested(:post, api_url).once - obj_attr = data[:object_attributes] - snippet_id = data[:snippet]['id'] - title = data[:snippet]['title'] + message = hipchat.send(:create_message, data) - expect(message).to eq("#{user.name} commented on " \ - "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \ - "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ - "<b>#{title}</b>" \ - "<pre>snippet note</pre>") + obj_attr = data[:object_attributes] + snippet_id = data[:snippet]['id'] + title = data[:snippet]['title'] + + expect(message).to eq("#{user.name} commented on " \ + "<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \ + "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ + "<b>#{title}</b>" \ + "<pre>snippet note</pre>") + end end end @@ -303,7 +334,7 @@ describe HipchatService, models: true do it "should notify only broken" do hipchat.notify_only_broken_builds = true hipchat.execute(data) - expect(WebMock).to_not have_requested(:post, api_url).once + expect(WebMock).not_to have_requested(:post, api_url).once end end end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index a97b7560137..155f3e74e0d 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -142,13 +142,6 @@ describe SlackService, models: true do let(:slack) { SlackService.new } let(:user) { create(:user) } let(:project) { create(:project, creator_id: user.id) } - let(:issue) { create(:issue, project: project) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project) } - let(:snippet) { create(:project_snippet, project: project) } - let(:commit_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } - let(:merge_request_note) { create(:note_on_merge_request, noteable_id: merge_request.id, note: "merge request note") } - let(:issue_note) { create(:note_on_issue, noteable_id: issue.id, note: "issue note")} - let(:snippet_note) { create(:note_on_project_snippet, noteable_id: snippet.id, note: "snippet note") } let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } before do @@ -162,32 +155,61 @@ describe SlackService, models: true do WebMock.stub_request(:post, webhook_url) end - it "should call Slack API for commit comment events" do - data = Gitlab::NoteDataBuilder.build(commit_note, user) - slack.execute(data) + context 'when commit comment event executed' do + let(:commit_note) do + create(:note_on_commit, author: user, + project: project, + commit_id: project.repository.commit.id, + note: 'a comment on a commit') + end - expect(WebMock).to have_requested(:post, webhook_url).once + it "should call Slack API for commit comment events" do + data = Gitlab::NoteDataBuilder.build(commit_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end end - it "should call Slack API for merge request comment events" do - data = Gitlab::NoteDataBuilder.build(merge_request_note, user) - slack.execute(data) + context 'when merge request comment event executed' do + let(:merge_request_note) do + create(:note_on_merge_request, project: project, + note: "merge request note") + end - expect(WebMock).to have_requested(:post, webhook_url).once + it "should call Slack API for merge request comment events" do + data = Gitlab::NoteDataBuilder.build(merge_request_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end end - it "should call Slack API for issue comment events" do - data = Gitlab::NoteDataBuilder.build(issue_note, user) - slack.execute(data) + context 'when issue comment event executed' do + let(:issue_note) do + create(:note_on_issue, project: project, note: "issue note") + end - expect(WebMock).to have_requested(:post, webhook_url).once + it "should call Slack API for issue comment events" do + data = Gitlab::NoteDataBuilder.build(issue_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end end - it "should call Slack API for snippet comment events" do - data = Gitlab::NoteDataBuilder.build(snippet_note, user) - slack.execute(data) + context 'when snippet comment event executed' do + let(:snippet_note) do + create(:note_on_project_snippet, project: project, + note: "snippet note") + end - expect(WebMock).to have_requested(:post, webhook_url).once + it "should call Slack API for snippet comment events" do + data = Gitlab::NoteDataBuilder.build(snippet_note, user) + slack.execute(data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 60e1ec43f2b..6c1b0393682 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -60,7 +60,7 @@ describe Project, models: true do project2 = build(:project) allow(project2).to receive(:creator).and_return(double(can_create_project?: false, projects_limit: 0).as_null_object) expect(project2).not_to be_valid - expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) + expect(project2.errors[:limit_reached].first).to match(/Personal project creation is not allowed/) end end @@ -791,7 +791,7 @@ describe Project, models: true do subject { project.container_registry_repository } - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end describe '#container_registry_repository_url' do @@ -809,7 +809,7 @@ describe Project, models: true do } end - it { is_expected.to_not be_nil } + it { is_expected.not_to be_nil } end context 'for disabled registry' do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 583151023b6..8c2347992f1 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -443,7 +443,7 @@ describe Repository, models: true do end it 'does nothing' do - expect(repository.raw_repository).to_not receive(:autocrlf=). + expect(repository.raw_repository).not_to receive(:autocrlf=). with(:input) repository.update_autocrlf_option @@ -511,7 +511,7 @@ describe Repository, models: true do it 'does not expire the emptiness caches for a non-empty repository' do expect(repository).to receive(:empty?).and_return(false) - expect(repository).to_not receive(:expire_emptiness_caches) + expect(repository).not_to receive(:expire_emptiness_caches) repository.expire_cache end @@ -674,7 +674,7 @@ describe Repository, models: true do end it 'does not flush caches that depend on repository data' do - expect(repository).to_not receive(:expire_cache) + expect(repository).not_to receive(:expire_cache) repository.before_delete end @@ -829,18 +829,6 @@ describe Repository, models: true do end end - describe "#main_language" do - it 'shows the main language of the project' do - expect(repository.main_language).to eq("Ruby") - end - - it 'returns nil when the repository is empty' do - allow(repository).to receive(:empty?).and_return(true) - - expect(repository.main_language).to be_nil - end - end - describe '#before_remove_tag' do it 'flushes the tag cache' do expect(repository).to receive(:expire_tag_count_cache) @@ -951,7 +939,7 @@ describe Repository, models: true do expect(repository.avatar).to eq('logo.png') - expect(repository).to_not receive(:blob_at_branch) + expect(repository).not_to receive(:blob_at_branch) expect(repository.avatar).to eq('logo.png') end end @@ -1045,7 +1033,7 @@ describe Repository, models: true do and_return(true) repository.cache_keys.each do |key| - expect(repository).to_not receive(key) + expect(repository).not_to receive(key) end repository.build_cache diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 37ddab83c30..7ecefce80d6 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -12,6 +12,7 @@ describe API::API, api: true do let!(:group2) { create(:group, :private) } let!(:project1) { create(:project, namespace: group1) } let!(:project2) { create(:project, namespace: group2) } + let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) } before do group1.add_owner(user1) @@ -147,9 +148,11 @@ describe API::API, api: true do context "when authenticated as user" do it "should return the group's projects" do get api("/groups/#{group1.id}/projects", user1) + expect(response.status).to eq(200) - expect(json_response.length).to eq(1) - expect(json_response.first['name']).to eq(project1.name) + expect(json_response.length).to eq(2) + project_names = json_response.map { |proj| proj['name' ] } + expect(project_names).to match_array([project1.name, project3.name]) end it "should not return a non existing group" do @@ -162,6 +165,16 @@ describe API::API, api: true do expect(response.status).to eq(404) end + + it "should only return projects to which user has access" do + project3.team << [user3, :developer] + + get api("/groups/#{group1.id}/projects", user3) + + expect(response.status).to eq(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project3.name) + end end context "when authenticated as admin" do @@ -181,8 +194,10 @@ describe API::API, api: true do context 'when using group path in URL' do it 'should return any existing group' do get api("/groups/#{group1.path}/projects", admin) + expect(response.status).to eq(200) - expect(json_response.first['name']).to eq(project1.name) + project_names = json_response.map { |proj| proj['name' ] } + expect(project_names).to match_array([project1.name, project3.name]) end it 'should not return a non existing group' do diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index ed1ed5aeb95..beb29a68692 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -258,8 +258,8 @@ describe API::API, api: true do body: 'Hi!' end - it 'responds with 500' do - expect(response.status).to eq 500 + it 'responds with resource not found error' do + expect(response.status).to eq 404 end it 'does not create new note' do diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index c112ca5e3ca..44b532b10e1 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -133,7 +133,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/members/#{user3.id}", user) expect do delete api("/projects/#{project.id}/members/#{user3.id}", user) - end.to_not change { ProjectMember.count } + end.not_to change { ProjectMember.count } expect(response.status).to eq(200) end diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 3e676515488..94eebc48ec8 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -49,7 +49,7 @@ describe API::API, api: true do it "should not create new hook without url" do expect do post api("/hooks", admin) - end.to_not change { SystemHook.count } + end.not_to change { SystemHook.count } end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7ebf8e41f3b..e5124ea5ea7 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -253,13 +253,13 @@ describe Ci::API::API do it "using token as parameter" do post authorize_url, { token: build.token }, headers expect(response.status).to eq(200) - expect(json_response["TempPath"]).to_not be_nil + expect(json_response["TempPath"]).not_to be_nil end it "using token as header" do post authorize_url, {}, headers_with_token expect(response.status).to eq(200) - expect(json_response["TempPath"]).to_not be_nil + expect(json_response["TempPath"]).not_to be_nil end end diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb index 3f4a1ced2b6..98ef9d21035 100644 --- a/spec/services/auth/container_registry_authentication_service_spec.rb +++ b/spec/services/auth/container_registry_authentication_service_spec.rb @@ -52,12 +52,12 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do shared_examples 'an unauthorized' do it { is_expected.to include(http_status: 401) } - it { is_expected.to_not include(:token) } + it { is_expected.not_to include(:token) } end shared_examples 'a forbidden' do it { is_expected.to include(http_status: 403) } - it { is_expected.to_not include(:token) } + it { is_expected.not_to include(:token) } end describe '#full_access_token' do diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index ea5dcfa068a..9ae8f31b372 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -78,7 +78,7 @@ describe CreateCommitBuildsService, services: true do expect(commit).to be_persisted expect(commit.builds.any?).to be false expect(commit.status).to eq('failed') - expect(commit.yaml_errors).to_not be_nil + expect(commit.yaml_errors).not_to be_nil end describe :ci_skip? do diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index eeab540c2fd..18692f1279a 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -158,49 +158,6 @@ describe GitPushService, services: true do end end - describe "Updates main language" do - context "before push" do - it { expect(project.main_language).to eq(nil) } - end - - context "after push" do - def execute - execute_service(project, user, @oldrev, @newrev, ref) - end - - context "to master" do - let(:ref) { @ref } - - context 'when main_language is nil' do - it 'obtains the language from the repository' do - expect(project.repository).to receive(:main_language) - execute - end - - it 'sets the project main language' do - execute - expect(project.main_language).to eq("Ruby") - end - end - - context 'when main_language is already set' do - it 'does not check the repository' do - execute # do an initial run to simulate lang being preset - expect(project.repository).not_to receive(:main_language) - execute - end - end - end - - context "to other branch" do - let(:ref) { 'refs/heads/feature/branch' } - - it { expect(project.main_language).to eq(nil) } - end - end - end - - describe "Updates git attributes" do context "for default branch" do it "calls the copy attributes method for the first push to the default branch" do diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index 6aefb48a4e8..a97ac3ead45 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -14,7 +14,7 @@ describe Groups::CreateService, services: true do context "cannot create group with restricted visibility level" do before { allow(current_application_settings).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC]) } - it { is_expected.to_not be_persisted } + it { is_expected.not_to be_persisted } end end end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 6aa0a89f893..1ee9f3aae4d 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -55,7 +55,7 @@ describe Issues::CreateService, services: true do end it 'does not assign label' do - expect(issue.labels).to_not include label + expect(issue.labels).not_to include label end end @@ -69,7 +69,7 @@ describe Issues::CreateService, services: true do end it 'does not assign milestone' do - expect(issue.milestone).to_not eq milestone + expect(issue.milestone).not_to eq milestone end end end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index c15e26189a5..95fe6c2400a 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -194,10 +194,10 @@ describe Issues::MoveService, services: true do include_context 'issue move executed' it 'rewrites uploads in description' do - expect(new_issue.description).to_not eq description + expect(new_issue.description).not_to eq description expect(new_issue.description) .to match(/Text and #{FileUploader::MARKDOWN_PATTERN}/) - expect(new_issue.description).to_not include uploader.secret + expect(new_issue.description).not_to include uploader.secret end end end @@ -231,7 +231,7 @@ describe Issues::MoveService, services: true do context 'user is reporter in both projects' do include_context 'user can move issue' - it { expect { move }.to_not raise_error } + it { expect { move }.not_to raise_error } end context 'user is reporter only in new project' do diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index 52a302e0e1a..0861d74aede 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe MergeRequests::MergeWhenBuildSucceedsService do - let(:user) { create(:user) } - let(:merge_request) { create(:merge_request) } + let(:user) { create(:user) } + let(:project) { create(:project) } let(:mr_merge_if_green_enabled) do create(:merge_request, merge_when_build_succeeds: true, merge_user: user, @@ -10,11 +10,15 @@ describe MergeRequests::MergeWhenBuildSucceedsService do source_project: project, target_project: project, state: "opened") end - let(:project) { create(:project) } let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) } let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') } describe "#execute" do + let(:merge_request) do + create(:merge_request, target_project: project, source_project: project, + source_branch: "feature", target_branch: 'master') + end + context 'first time enabling' do before do allow(merge_request).to receive(:ci_commit).and_return(ci_commit) @@ -75,7 +79,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do allow(ci_commit).to receive(:success?).and_return(true) allow(old_build).to receive(:sha).and_return('1234abcdef') - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) service.trigger(old_build) end end @@ -88,7 +92,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do it "doesn't merge a requests for status on other branch" do allow(project.repository).to receive(:branch_names_contains).with(commit_status.sha).and_return([]) - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) service.trigger(commit_status) end @@ -122,7 +126,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do end it "doesn't merge if some stages failed" do - expect(MergeWorker).to_not receive(:perform_async) + expect(MergeWorker).not_to receive(:perform_async) build.success test.drop end diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index d1ee60a0aea..31bb7120d84 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -42,6 +42,33 @@ describe Projects::ForkService, services: true do expect(@to_project.builds_enabled?).to be_truthy end end + + context "when project has restricted visibility level" do + context "and only one visibility level is restricted" do + before do + @from_project.update_attributes(visibility_level: Gitlab::VisibilityLevel::INTERNAL) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL]) + end + + it "creates fork with highest allowed level" do + forked_project = fork_project(@from_project, @to_user) + + expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) + end + end + + context "and all visibility levels are restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PRIVATE]) + end + + it "creates fork with private visibility levels" do + forked_project = fork_project(@from_project, @to_user) + + expect(forked_project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + end + end end describe :fork_to_namespace do diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index ee976eb2926..29e0a63d8ce 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -208,8 +208,10 @@ describe SystemNoteService, services: true do end describe '.merge_when_build_succeeds' do - let(:ci_commit) { build :ci_commit_without_jobs } - let(:noteable) { create :merge_request } + let(:ci_commit) { build(:ci_commit_without_jobs )} + let(:noteable) do + create(:merge_request, source_project: project, target_project: project) + end subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.last_commit) } @@ -221,8 +223,10 @@ describe SystemNoteService, services: true do end describe '.cancel_merge_when_build_succeeds' do - let(:ci_commit) { build :ci_commit_without_jobs } - let(:noteable) { create :merge_request } + let(:ci_commit) { build(:ci_commit_without_jobs) } + let(:noteable) do + create(:merge_request, source_project: project, target_project: project) + end subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) } diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb new file mode 100644 index 00000000000..36d03a224e4 --- /dev/null +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require 'rake' + +describe 'gitlab:db namespace rake task' do + before :all do + Rake.application.rake_require 'active_record/railties/databases' + Rake.application.rake_require 'tasks/seed_fu' + Rake.application.rake_require 'tasks/gitlab/db' + + # empty task as env is already loaded + Rake::Task.define_task :environment + end + + before do + # Stub out db tasks + allow(Rake::Task['db:migrate']).to receive(:invoke).and_return(true) + allow(Rake::Task['db:schema:load']).to receive(:invoke).and_return(true) + allow(Rake::Task['db:seed_fu']).to receive(:invoke).and_return(true) + end + + describe 'configure' do + it 'should invoke db:migrate when schema has already been loaded' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return(['default']) + expect(Rake::Task['db:migrate']).to receive(:invoke) + expect(Rake::Task['db:schema:load']).not_to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.not_to raise_error + end + + it 'should invoke db:shema:load and db:seed_fu when schema is not loaded' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return([]) + expect(Rake::Task['db:schema:load']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).to receive(:invoke) + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.not_to raise_error + end + + it 'should not invoke any other rake tasks during an error' do + allow(ActiveRecord::Base).to receive(:connection).and_raise(RuntimeError, 'error') + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect(Rake::Task['db:schema:load']).not_to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error') + # unstub connection so that the database cleaner still works + allow(ActiveRecord::Base).to receive(:connection).and_call_original + end + + it 'should not invoke seed after a failed schema_load' do + allow(ActiveRecord::Base.connection).to receive(:tables).and_return([]) + allow(Rake::Task['db:schema:load']).to receive(:invoke).and_raise(RuntimeError, 'error') + expect(Rake::Task['db:schema:load']).to receive(:invoke) + expect(Rake::Task['db:seed_fu']).not_to receive(:invoke) + expect(Rake::Task['db:migrate']).not_to receive(:invoke) + expect { run_rake_task('gitlab:db:configure') }.to raise_error(RuntimeError, 'error') + end + end + + def run_rake_task(task_name) + Rake::Task[task_name].reenable + Rake.application.invoke_task task_name + end +end diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb index 2f465bcf1e3..20d3dfb42b3 100644 --- a/spec/workers/post_receive_spec.rb +++ b/spec/workers/post_receive_spec.rb @@ -61,7 +61,7 @@ describe PostReceive do context "does not create a Ci::Commit" do before { stub_ci_commit_yaml_file(nil) } - it { expect{ subject }.to_not change{ Ci::Commit.count } } + it { expect{ subject }.not_to change{ Ci::Commit.count } } end end end |