Commit 9d491712 authored by Z.J. van de Weg's avatar Z.J. van de Weg

Merge branch 'master' into awardables

parents fab69546 ca3c5c29
......@@ -222,7 +222,7 @@ Style/EndBlock:
# Use Unix-style line endings.
Style/EndOfLine:
Enabled: false
Enabled: true
# Favor the use of Fixnum#even? && Fixnum#odd?
Style/EvenOdd:
......@@ -247,7 +247,7 @@ Style/FlipFlop:
# Checks use of for or each in multiline loops.
Style/For:
Enabled: false
Enabled: true
# Enforce the use of Kernel#sprintf, Kernel#format or String#%.
Style/FormatString:
......@@ -286,7 +286,7 @@ Style/IdenticalConditionalBranches:
# Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment.
Style/IndentAssignment:
Enabled: false
Enabled: true
# Keep indentation straight.
Style/IndentationConsistency:
......@@ -318,7 +318,7 @@ Style/LambdaCall:
# Comments should start with a space.
Style/LeadingCommentSpace:
Enabled: false
Enabled: true
# Use \ instead of + or << to concatenate two string literals at line end.
Style/LineEndConcatenation:
......@@ -330,7 +330,7 @@ Style/MethodCallParentheses:
# Checks if the method definitions have or don't have parentheses.
Style/MethodDefParentheses:
Enabled: false
Enabled: true
# Use the configured style when naming methods.
Style/MethodName:
......@@ -362,7 +362,7 @@ Style/MultilineHashBraceLayout:
# Do not use then for multi-line if/unless.
Style/MultilineIfThen:
Enabled: false
Enabled: true
# Checks that the closing brace in a method call is either on the same line as
# the last method argument, or a new line.
......@@ -394,7 +394,7 @@ Style/MutableConstant:
# Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf:
Enabled: false
Enabled: true
# Favor until over while for negative conditions.
Style/NegatedWhile:
......@@ -486,10 +486,9 @@ Style/RedundantException:
Style/RedundantFreeze:
Enabled: false
# TODO: Enable RedundantParentheses Cop.
# Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses:
Enabled: false
Enabled: true
# Don't use return where it's not required.
Style/RedundantReturn:
......@@ -515,7 +514,7 @@ Style/SelfAssignment:
# Don't use semicolons to terminate expressions.
Style/Semicolon:
Enabled: false
Enabled: true
# Checks for proper usage of fail and raise.
Style/SignalException:
......@@ -541,7 +540,7 @@ Style/SpaceAfterComma:
# Do not put a space between a method name and the opening parenthesis in a
# method definition.
Style/SpaceAfterMethodName:
Enabled: false
Enabled: true
# Tracks redundant space after the ! operator.
Style/SpaceAfterNot:
......@@ -570,11 +569,11 @@ Style/SpaceBeforeBlockBraces:
# No spaces before commas.
Style/SpaceBeforeComma:
Enabled: false
Enabled: true
# Checks for missing space between code and a comment on the same line.
Style/SpaceBeforeComment:
Enabled: false
Enabled: true
# Checks that exactly one space is used between a method name and the first
# argument for method calls without parentheses.
......@@ -705,7 +704,7 @@ Style/WhenThen:
# Checks for redundant do after while or until.
Style/WhileUntilDo:
Enabled: false
Enabled: true
# Favor modifier while/until usage when you have a single-line body.
Style/WhileUntilModifier:
......@@ -785,7 +784,7 @@ Lint/AssignmentInCondition:
# Align block ends correctly.
Lint/BlockAlignment:
Enabled: false
Enabled: true
# Default values in optional keyword arguments and optional ordinal arguments
# should not refer back to the name of the argument.
......@@ -878,7 +877,7 @@ Lint/InvalidCharacterLiteral:
# Checks of literals used in conditions.
Lint/LiteralInCondition:
Enabled: false
Enabled: true
# Checks for literals used in interpolation.
Lint/LiteralInInterpolation:
......@@ -1027,10 +1026,9 @@ Performance/StartWith:
Performance/StringReplacement:
Enabled: true
# TODO: Enable TimesMap Cop.
# Checks for `.times.map` calls.
Performance/TimesMap:
Enabled: false
Enabled: true
##################### Rails ##################################
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased)
- Allow enabling wiki page events from Webhook management UI
- Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Allow forking projects with restricted visibility level
- Improve note validation to prevent errors when creating invalid note via API
- Reduce number of fog gem dependencies
- Remove project notification settings associated with deleted projects
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects
- 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 `sha` parameter to MR merge API, to ensure only reviewed changes are merged
- Don't allow MRs to be merged when commits were added since the last review / page load
- 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)
- Fix issues filter when ordering by milestone
- Todos will display target state if issuable target is 'Closed' or 'Merged'
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
- Link to blank group icon doesn't throw a 404 anymore
- Remove 'main language' feature
- Pipelines can be canceled only when there are running builds
- Use downcased path to container repository as this is expected path by Docker
- Projects pending deletion will render a 404 page
- Measure queue duration between gitlab-workhorse and Rails
- Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min)
- Cache assigned issue and merge request counts in sidebar nav
- Cache project build count in sidebar nav
- Reduce number of queries needed to render issue labels in the sidebar
- Improve error handling importing projects
- Put project Files and Commits tabs under Code tab
v 8.8.4
- Fix todos page throwing errors when you have a project pending deletion
- Reduce number of SQL queries when rendering user references
v 8.8.4 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds
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
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
- Fixed JS error when trying to remove discussion form. !4303
- Fixed issue with button color when no CI enabled. !4287
- Fixed potential issue with 2 CI status polling events happening. !3869
- Improve design of Pipeline view. !4230
- Fix gitlab importer failing to import new projects due to missing credentials. !4301
- Fix import URL migration not rescuing with the correct Error. !4321
- Fix health check access token changing due to old application settings being used. !4332
- Make authentication service for Container Registry to be compatible with Docker versions before 1.11. !4363
- Add Application Setting to configure Container Registry token expire delay (default 5 min). !4364
- Pass the "Remember me" value to the 2FA token form. !4369
- Fix incorrect links on pipeline page when merge request created from fork. !4376
- Use downcased path to container repository as this is expected path by Docker. !4420
- Fix wiki project clone address error (chujinjin). !4429
- Fix serious performance bug with rendering Markdown with InlineDiffFilter. !4392
- Fix missing number on generated ordered list element. !4437
- Prevent disclosure of notes on confidential issues in search results.
v 8.8.2
- Added remove due date button. !4209
......
......@@ -308,16 +308,14 @@ tests are least likely to receive timely feedback. The workflow to make a merge
request is as follows:
1. Fork the project into your personal space on GitLab.com
1. Create a feature branch
1. Create a feature branch, branch away from `master`.
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you are changing the README, some documentation or other things which
have no effect on the tests, add `[ci skip]` somewhere in the commit message
and make sure to read the [documentation styleguide][doc-styleguide]
1. If you are writing documentation, make sure to read the [documentation styleguide][doc-styleguide]
1. If you have multiple commits please combine them into one commit by
[squashing them][git-squash]
1. Push the commit(s) to your fork
1. Submit a merge request (MR) to the master branch
1. Submit a merge request (MR) to the `master` branch
1. The MR title should describe the change you want to make
1. The MR description should give a motive for your change and the method you
used to achieve it, see the [merge request description format]
......
......@@ -18,9 +18,8 @@ gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries
gem 'devise', '~> 3.5.4'
gem 'devise', '~> 4.0'
gem 'doorkeeper', '~> 3.1'
gem 'devise-async', '~> 0.9.0'
gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
......@@ -43,9 +42,9 @@ gem 'recaptcha', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0'
# Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0'
gem 'devise-two-factor', '~> 3.0.0'
gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 1.3.4'
gem 'attr_encrypted', '~> 3.0.0'
# Browser detection
gem "browser", '~> 1.0.0'
......@@ -73,7 +72,7 @@ gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Pagination
gem "kaminari", "~> 0.16.3"
gem "kaminari", "~> 0.17.0"
# HAML
gem "haml-rails", '~> 0.9.0'
......@@ -84,8 +83,14 @@ gem "carrierwave", '~> 0.10.0'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
gem 'fog-aws', '~> 0.9'
gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3'
gem 'fog-openstack', '~> 0.1'
# for aws storage
gem "fog", "~> 1.36.0"
gem "unf", '~> 0.1.4'
# Authorization
......
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (4.0.2)
actionmailer (4.2.6)
......@@ -60,8 +59,8 @@ GEM
oauth2 (~> 1.0)
asciidoctor (1.5.3)
ast (2.2.0)
attr_encrypted (1.3.4)
encryptor (>= 1.3.0)
attr_encrypted (3.0.1)
encryptor (~> 3.0.0)
attr_required (1.0.0)
autoprefixer-rails (6.2.3)
execjs
......@@ -73,7 +72,7 @@ GEM
thread_safe (~> 0.3, >= 0.3.1)
babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.10)
bcrypt (3.1.11)
benchmark-ips (2.3.0)
better_errors (1.0.1)
coderay (>= 1.0.0)
......@@ -155,21 +154,18 @@ GEM
activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
devise (3.5.4)
devise (4.1.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
railties (>= 4.1.0, < 5.1)
responders
thread_safe (~> 0.1)
warden (~> 1.2.3)
devise-async (0.9.0)
devise (~> 3.2)
devise-two-factor (2.0.1)
devise-two-factor (3.0.0)
activesupport
attr_encrypted (~> 1.3.2)
devise (~> 3.5.0)
attr_encrypted (>= 1.3, < 4, != 2)
devise (~> 4.0)
railties
rotp (~> 2)
rotp (~> 2.0)
diff-lcs (1.2.5)
diffy (3.0.7)
docile (1.1.5)
......@@ -181,12 +177,12 @@ GEM
email_spec (1.6.0)
launchy (~> 2.1)
mail (~> 2.2)
encryptor (1.3.0)
encryptor (3.0.0)
equalizer (0.0.11)
erubis (2.7.0)
escape_utils (1.1.1)
eventmachine (1.0.8)
excon (0.45.4)
excon (0.49.0)
execjs (2.6.0)
expression_parser (0.9.0)
factory_girl (4.5.0)
......@@ -203,8 +199,6 @@ GEM
multi_json
ffaker (2.0.0)
ffi (1.9.10)
fission (0.5.0)
CFPropertyList (~> 2.2)
flay (2.6.1)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
......@@ -214,109 +208,28 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
fog (1.36.0)
fog-aliyun (>= 0.1.0)
fog-atmos
fog-aws (>= 0.6.0)
fog-brightbox (~> 0.4)
fog-core (~> 1.32)
fog-dynect (~> 0.0.2)
fog-ecloud (~> 0.1)
fog-google (<= 0.1.0)
fog-json
fog-local
fog-powerdns (>= 0.1.1)
fog-profitbricks
fog-radosgw (>= 0.0.2)
fog-riakcs
fog-sakuracloud (>= 0.0.4)
fog-serverlove
fog-softlayer
fog-storm_on_demand
fog-terremark
fog-vmfusion
fog-voxel
fog-xenserver
fog-xml (~> 0.1.1)
ipaddress (~> 0.5)
nokogiri (~> 1.5, >= 1.5.11)
fog-aliyun (0.1.0)
fog-core (~> 1.27)
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
fog-atmos (0.1.0)
fog-core
fog-xml
fog-aws (0.8.1)
fog-aws (0.9.2)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
fog-brightbox (0.10.1)
fog-core (~> 1.22)
fog-json
inflecto (~> 0.0.2)
fog-core (1.35.0)
fog-core (1.40.0)
builder
excon (~> 0.45)
excon (~> 0.49)
formatador (~> 0.2)
fog-dynect (0.0.2)
fog-core
fog-json
fog-xml
fog-ecloud (0.3.0)
fog-core
fog-xml
fog-google (0.1.0)
fog-google (0.3.2)
fog-core
fog-json
fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
fog-local (0.2.1)
fog-core (~> 1.27)
fog-powerdns (0.1.1)
fog-local (0.3.0)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
fog-profitbricks (0.0.5)
fog-core
fog-xml
nokogiri
fog-radosgw (0.0.5)
fog-core (>= 1.21.0)
fog-json
fog-xml (>= 0.0.1)
fog-riakcs (0.1.0)
fog-core
fog-json
fog-xml
fog-sakuracloud (1.7.5)
fog-core
fog-json
fog-serverlove (0.1.2)
fog-core
fog-json
fog-softlayer (1.0.3)
fog-core
fog-json
fog-storm_on_demand (0.1.1)
fog-core
fog-json
fog-terremark (0.1.0)
fog-core
fog-xml
fog-vmfusion (0.1.0)
fission
fog-core
fog-voxel (0.1.0)
fog-core
fog-xml
fog-xenserver (0.2.2)
fog-core
fog-xml
fog-openstack (0.1.6)
fog-core (>= 1.39)
fog-json (>= 1.0)
ipaddress (>= 0.8)
fog-xml (0.1.2)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
......@@ -425,11 +338,10 @@ GEM
httpclient (2.7.0.1)
i18n (0.7.0)
ice_nine (0.11.1)
inflecto (0.0.2)
influxdb (0.2.3)
cause
json
ipaddress (0.8.2)
ipaddress (0.8.3)
jquery-atwho-rails (1.3.2)
jquery-rails (4.1.1)
rails-dom-testing (>= 1, < 3)
......@@ -442,7 +354,7 @@ GEM
railties (>= 3.2.16)
json (1.8.3)
jwt (1.5.2)
kaminari (0.16.3)
kaminari (0.17.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
kgio (2.10.0)
......@@ -656,7 +568,7 @@ GEM
responders (2.1.1)
railties (>= 4.2.0, < 5.1)
rinku (1.7.3)
rotp (2.1.1)
rotp (2.1.2)
rouge (1.10.1)
rqrcode (0.7.0)
chunky_png
......@@ -859,7 +771,7 @@ GEM
coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9)
warden (1.2.4)
warden (1.2.6)
rack (>= 1.0)
web-console (2.3.0)
activemodel (>= 4.0)
......@@ -876,7 +788,6 @@ GEM
builder
expression_parser
rinku
xml-simple (1.1.5)
xpath (2.0.0)
nokogiri (~> 1.3)
......@@ -894,7 +805,7 @@ DEPENDENCIES
allocations (~> 1.0)
asana (~> 0.4.0)
asciidoctor (~> 1.5.2)
attr_encrypted (~> 1.3.4)
attr_encrypted (~> 3.0.0)
awesome_print (~> 1.2.0)
babosa (~> 1.0.2)
base32 (~> 0.3.0)
......@@ -919,9 +830,8 @@ DEPENDENCIES
d3_rails (~> 3.5.0)
database_cleaner (~> 1.4.0)
default_value_for (~> 3.0.0)
devise (~> 3.5.4)
devise-async (~> 0.9.0)
devise-two-factor (~> 2.0.0)
devise (~> 4.0)
devise-two-factor (~> 3.0.0)
diffy (~> 3.0.3)
doorkeeper (~> 3.1)
dropzonejs-rails (~> 0.7.1)
......@@ -931,7 +841,11 @@ DEPENDENCIES
ffaker (~> 2.0.0)
flay
flog
fog (~> 1.36.0)
fog-aws (~> 0.9)
fog-core (~> 1.40)
fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
font-awesome-rails (~> 4.2)
foreman
fuubar (~> 2.0.0)
......@@ -959,7 +873,7 @@ DEPENDENCIES
jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.16.3)
kaminari (~> 0.17.0)
letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0)
loofah (~> 2.0.3)
......
# GitLab
[![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
## Canonical source
......
......@@ -18,7 +18,6 @@
#= require jquery.atwho
#= require jquery.scrollTo
#= require jquery.turbolinks
#= require d3
#= require turbolinks
#= require autosave
#= require bootstrap/affix
......@@ -51,7 +50,13 @@
#= require shortcuts_network
#= require jquery.nicescroll
#= require date.format
#= require_tree .
#= require_directory ./behaviors
#= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib
#= require_directory .
#= require fuzzaldrin-plus
#= require cropper
......
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require_tree .
#= require d3
#= require stat_graph_contributors_util
class @ContributorsStatGraph
init: (log) ->
......
......@@ -20,8 +20,7 @@ class @SearchAutocomplete
@dropdown = @wrap.find('.dropdown')
@dropdownContent = @dropdown.find('.dropdown-content')
@locationBadgeEl = @getElement('.search-location-badge')
@locationText = @getElement('.location-text')
@locationBadgeEl = @getElement('.location-badge')
@scopeInputEl = @getElement('#scope')
@searchInput = @getElement('.search-input')
@projectInputEl = @getElement('#search_project_id')
......@@ -133,7 +132,7 @@ class @SearchAutocomplete
scope: @scopeInputEl.val()
# Location badge
_location: @locationText.text()
_location: @locationBadgeEl.text()
}
bindEvents: ->
......@@ -143,12 +142,14 @@ class @SearchAutocomplete
@searchInput.on 'click', @onSearchInputClick
@searchInput.on 'focus', @onSearchInputFocus
@clearInput.on 'click', @onClearInputClick
@locationBadgeEl.on 'click', =>
@searchInput.focus()
onDocumentClick: (e) =>
# If clicking outside the search box
# And search input is not focused
# And we are not clicking inside a suggestion
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).parents('ul').length
if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length
@onSearchInputBlur()
enableAutocomplete: ->
......@@ -221,10 +222,8 @@ class @SearchAutocomplete
category = if item.category? then "#{item.category}: " else ''
value = if item.value? then item.value else ''
html = "<span class='location-badge'>
<i class='location-text'>#{category}#{value}</i>
</span>"
@locationBadgeEl.html(html)
badgeText = "#{category}#{value}"
@locationBadgeEl.text(badgeText).show()
@wrap.addClass('has-location-badge')
restoreOriginalState: ->
......@@ -233,9 +232,8 @@ class @SearchAutocomplete
for input in inputs
@getElement("##{input}").val(@originalState[input])
if @originalState._location is ''
@locationBadgeEl.empty()
@locationBadgeEl.hide()
else
@addLocationBadge(
value: @originalState._location
......@@ -244,7 +242,7 @@ class @SearchAutocomplete
@dropdown.removeClass 'open'
badgePresent: ->
@locationBadgeEl.children().length
@locationBadgeEl.length
resetSearchState: ->
inputs = Object.keys @originalState
......@@ -257,7 +255,7 @@ class @SearchAutocomplete
@getElement("##{input}").val('')
removeLocationBadge: ->
@locationBadgeEl.empty()
@locationBadgeEl.hide()
# Reset state
@resetSearchState()
......
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
#= require d3
#= require_tree .
......@@ -16,6 +16,19 @@
@include btn-default;
}
@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
background-color: $background;
color: $text;
border-color: $border;
&:hover,
&:focus {
background-color: $hover-background;
color: $hover-text;
border-color: $hover-border;;
}
}
@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
background-color: $light;
border-color: $border-light;
......@@ -106,11 +119,14 @@
@include btn-blue;
}
&.btn-close,
&.btn-warning {
@include btn-orange;
}
&.btn-close {
@include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
}
&.btn-danger,
&.btn-remove,
&.btn-red {
......
......@@ -63,7 +63,8 @@ $gl-padding-top: 10px;
/*
* Misc
*/
$row-hover: #f4f8fe;
$row-hover: #f7faff;
$row-hover-border: #b2d7ff;
$progress-color: #c0392b;
$avatar_radius: 50%;
$header-height: 50px;
......@@ -104,7 +105,7 @@ $blue-medium-light: #3498cb;
$blue-medium: #2f8ebf;
$blue-medium-dark: #2d86b4;
$orange-light: rgba(252, 109, 38, 0.80);
$orange-light: #fc8a51;
$orange-normal: #e75e40;
$orange-dark: #ce5237;
......
......@@ -29,8 +29,6 @@
margin-top: 6px;
p {
overflow-x: auto;
&:last-child {
margin-bottom: 0;
}
......
......@@ -41,7 +41,7 @@
margin: 0;
margin-left: 20px;
padding: 5px;
padding-top: 12px;
padding-top: 8px;
line-height: 20px;
&.right {
......@@ -110,6 +110,29 @@
p:last-child {
margin-bottom: 0;
}
@media (max-width: $screen-sm-max) {
h4 {
font-size: 15px;
}
p {
font-size: 13px;
}
.btn,
.btn-group,
.accept-action {
width: 100%;
margin-bottom: 4px;
}
.accept-control {
width: 100%;
text-align: center;
margin: 0;
}
}
}
.mr-widget-footer {
......
......@@ -28,6 +28,7 @@
}
.search-input {
padding-right: 20px;
border: none;
font-size: 14px;
outline: none;
......@@ -47,6 +48,7 @@
display: inline-block;
background-color: $location-badge-bg;
vertical-align: top;
cursor: default;
}
.search-input-container {
......@@ -55,7 +57,7 @@
position: relative;
}
.search-location-badge, .search-input-wrap {
.search-input-wrap {
// Fallback if flexbox is not supported
display: inline-block;
}
......
......@@ -15,16 +15,23 @@
margin-bottom: 0;
tr {
> td, > th {
border-bottom: 1px solid $table-border-gray;
border-top: 1px solid $table-border-gray;
td, th {
line-height: 23px;
}
&:hover {
cursor: pointer;
td {
background: $row-hover;
background-color: $row-hover;
border-top: 1px solid $row-hover-border;
border-bottom: 1px solid $row-hover-border;
}
cursor: pointer;
}
&.selected {
td {
background: $gray-dark;
......
......@@ -107,6 +107,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:repository_checks_enabled,
:metrics_packet_size,
:send_user_confirmation_email,
:container_registry_token_expire_delay,
restricted_visibility_levels: [],
import_sources: [],
disabled_oauth_sign_in_sources: []
......
......@@ -232,7 +232,7 @@ class ApplicationController < ActionController::Base
end
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me, :otp_attempt) }
devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
end
def hexdigest(string)
......@@ -263,7 +263,7 @@ class ApplicationController < ActionController::Base
# internal repos where you are not a member. Enable this filter
# or improve current implementation to filter only issues you
# created or assigned or mentioned
#@filter_params[:authorized_only] = true
# @filter_params[:authorized_only] = true
end
@filter_params
......
......@@ -32,7 +32,7 @@ class JwtController < ApplicationController
end
def auth_params
params.permit(:service, :scope, :offline_token, :account, :client_id)
params.permit(:service, :scope, :account, :client_id)
end
def authenticate_project(login, password)
......
......@@ -97,7 +97,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_signup_error
end
def handle_service_ticket provider, ticket
def handle_service_ticket(provider, ticket)
Gitlab::OAuth::Session.create provider, ticket
session[:service_tickets] ||= {}
session[:service_tickets][provider] = ticket
......
......@@ -50,7 +50,7 @@ class Projects::BranchesController < Projects::ApplicationController
redirect_to namespace_project_branches_path(@project.namespace,
@project), status: 303
end
format.js { render status: status[:return_code] }
format.js { render nothing: true, status: status[:return_code] }
end
end
......
# Controller for viewing a repository's file structure
class Projects::FindFileController < Projects::ApplicationController
include ExtractsPath
include ActionView::Helpers::SanitizeHelper
include TreeHelper
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
def show
return render_404 unless @repository.commit(@ref)
respond_to do |format|
format.html
end
end
def list
file_paths = @repo.ls_files(@ref)
respond_to do |format|
format.json { render json: file_paths }
end
end
end
# Controller for viewing a repository's file structure
class Projects::FindFileController < Projects::ApplicationController
include ExtractsPath
include ActionView::Helpers::SanitizeHelper
include TreeHelper
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_download_code!
def show
return render_404 unless @repository.commit(@ref)
respond_to do |format|
format.html
end
end
def list
file_paths = @repo.ls_files(@ref)
respond_to do |format|
format.json { render json: file_paths }
end
end
end
......@@ -63,7 +63,8 @@ class Projects::HooksController < Projects::ApplicationController
:push_events,
:tag_push_events,
:token,
:url
:url,
:wiki_page_events
)
end
end
......@@ -191,6 +191,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return
end
if params[:sha] != @merge_request.source_sha
@status = :sha_mismatch
return
end
TodoService.new.merge_merge_request(merge_request, current_user)
@merge_request.update(merge_error: nil)
......@@ -206,7 +211,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def branch_from
#This is always source
# This is always source
@source_project = @merge_request.nil? ? @project : @merge_request.source_project
@commit = @repository.commit(params[:ref]) if params[:ref].present?
render layout: false
......
......@@ -91,8 +91,8 @@ class Projects::WikisController < Projects::ApplicationController
def markdown_preview
text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
ext.analyze(text)
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
ext.analyze(text, author: current_user)
render json: {
body: view_context.markdown(text, pipeline: :wiki, project_wiki: @project_wiki),
......
......@@ -197,8 +197,8 @@ class ProjectsController < Projects::ApplicationController
def markdown_preview
text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user, current_user)
ext.analyze(text)
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
ext.analyze(text, author: current_user)
render json: {
body: view_context.markdown(text),
......
class SessionsController < Devise::SessionsController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
include Recaptcha::ClientHelper
skip_before_action :check_2fa_requirement, only: [:destroy]
......@@ -96,6 +97,7 @@ class SessionsController < Devise::SessionsController
# Remove any lingering user data from login
session.delete(:otp_user_id)
remember_me(user) if user_params[:remember_me] == '1'
sign_in(user) and return
else
flash.now[:alert] = 'Invalid two-factor code.'
......
......@@ -271,7 +271,7 @@ class IssuableFinder
if filter_by_no_label?
items = items.without_label
else
items = items.with_label(label_names)
items = items.with_label(label_names, params[:sort])
if projects
items = items.where(labels: { project_id: projects })
end
......
......@@ -30,7 +30,7 @@ class TodosFinder
items = by_state(items)
items = by_type(items)
items
items.reorder(id: :desc)
end
private
......@@ -78,6 +78,16 @@ class TodosFinder
@project
end
def projects
return @projects if defined?(@projects)
if project?
@projects = project
else
@projects = ProjectsFinder.new.execute(current_user)
end
end
def type?
type.present? && ['Issue', 'MergeRequest'].include?(type)
end
......@@ -105,6 +115,8 @@ class TodosFinder
def by_project(items)
if project?
items = items.where(project: project)
elsif projects
items = items.merge(projects).joins(:project)
end
items
......
......@@ -30,7 +30,7 @@ module ButtonHelper
content_tag :a, protocol,
class: klass,
href: @project.http_url_to_repo,
href: project.http_url_to_repo,
data: {
html: true,
placement: 'right',
......
......@@ -39,11 +39,11 @@ module DiffHelper
end
def unfold_bottom_class(bottom)
(bottom) ? 'js-unfold-bottom' : ''
bottom ? 'js-unfold-bottom' : ''
end
def unfold_class(unfold)
(unfold) ? 'unfold js-unfold' : ''
unfold ? 'unfold js-unfold' : ''
end
def diff_line_content(line, line_type = nil)
......
......@@ -31,7 +31,7 @@ module GroupsHelper
if group && group.avatar.present?
group.avatar.url
else
'no_group_avatar.png'
image_path('no_group_avatar.png')
end
end
......
module JavascriptHelper
def page_specific_javascripts(js = nil)
@page_specific_javascripts = js unless js.nil?
@page_specific_javascripts
end
end
......@@ -17,7 +17,9 @@ module TodosHelper
def todo_target_link(todo)
target = todo.target_type.titleize.downcase
link_to "#{target} #{todo.target_reference}", todo_target_path(todo), { title: todo.target.title }
link_to "#{target} #{todo.target_reference}", todo_target_path(todo),
class: 'has-tooltip',
title: todo.target.title
end
def todo_target_path(todo)
......
......@@ -23,20 +23,41 @@ class Ability
end.concat(global_abilities(user))
end
# Given a list of users and a project this method returns the users that can
# read the given project.
def users_that_can_read_project(users, project)
if project.public?
users
else
users.select do |user|
if user.admin?
true
elsif project.internal? && !user.external?
true
elsif project.owner == user
true
elsif project.team.members.include?(user)
true
else
false
end
end
end
end
# List of possible abilities for anonymous user
def anonymous_abilities(user, subject)
case true
when subject.is_a?(PersonalSnippet)
if subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject)
when subject.is_a?(ProjectSnippet)
elsif subject.is_a?(ProjectSnippet)
anonymous_project_snippet_abilities(subject)
when subject.is_a?(CommitStatus)
elsif subject.is_a?(CommitStatus)
anonymous_commit_status_abilities(subject)
when subject.is_a?(Project) || subject.respond_to?(:project)
elsif subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group)
elsif subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject)
when subject.is_a?(User)
elsif subject.is_a?(User)
anonymous_user_abilities
else
[]
......
......@@ -51,6 +51,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :container_registry_token_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
......@@ -98,6 +102,10 @@ class ApplicationSetting < ActiveRecord::Base
Rails.cache.delete(CACHE_KEY)
end
def self.cached
Rails.cache.fetch(CACHE_KEY)
end
def self.create_from_defaults
create(
default_projects_limit: Settings.gitlab['default_projects_limit'],
......@@ -121,7 +129,8 @@ class ApplicationSetting < ActiveRecord::Base
akismet_enabled: false,
repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
)
end
......
......@@ -286,7 +286,7 @@ module Ci
project.runners_token
end
def valid_token? token
def valid_token?(token)
project.valid_runners_token? token
end
......@@ -313,6 +313,7 @@ module Ci
build_data = Gitlab::BuildDataBuilder.build(self)
project.execute_hooks(build_data.dup, :build_hooks)
project.execute_services(build_data.dup, :build_hooks)
project.running_or_pending_build_count(force: true)
end
def artifacts?
......
......@@ -66,6 +66,10 @@ module Ci
end
end
def cancelable?
builds.running_or_pending.any?
end
def cancel_running
builds.running_or_pending.each(&:cancel)
end
......
......@@ -60,7 +60,7 @@ module Ci
end
def display_name
return short_sha unless !description.blank?
return short_sha if description.blank?
description
end
......
......@@ -11,6 +11,9 @@ module Ci
format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." }
attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
attr_encrypted :value,
mode: :per_attribute_iv_and_salt,
key: Gitlab::Application.secrets.db_key_base,
algorithm: 'aes-256-cbc'
end
end
......@@ -8,7 +8,10 @@ class Commit
include StaticModel
attr_mentionable :safe_message, pipeline: :single_line
participant :author, :committer, :notes
participant :author
participant :committer
participant :notes_with_associations
attr_accessor :project
......@@ -194,6 +197,10 @@ class Commit
project.notes.for_commit_id(self.id)
end
def notes_with_associations
notes.includes(:author, :project)
end
def method_missing(m, *args, &block)
@raw.send(m, *args, &block)
end
......@@ -219,7 +226,7 @@ class Commit
def revert_branch_name
"revert-#{short_id}"
end
def cherry_pick_branch_name
project.repository.next_branch("cherry-pick-#{short_id}", mild: true)
end
......@@ -251,11 +258,13 @@ class Commit
end
def has_been_reverted?(current_user = nil, noteable = self)
Gitlab::ReferenceExtractor.lazily do
noteable.notes.system.flat_map do |note|
note.all_references(current_user).commits
end
end.any? { |commit_ref| commit_ref.reverts_commit?(self) }
ext = all_references(current_user)
noteable.notes_with_associations.system.each do |note|
note.all_references(current_user, extractor: ext)
end
ext.commits.any? { |commit_ref| commit_ref.reverts_commit?(self) }
end
def change_type_title
......
......@@ -62,7 +62,7 @@ class CommitRange
def initialize(range_string, project)
@project = project
range_string.strip!
range_string = range_string.strip
unless range_string =~ /\A#{PATTERN}\z/
raise ArgumentError, "invalid CommitRange string format: #{range_string}"
......
......@@ -60,11 +60,23 @@ module Issuable
prefix: true
attr_mentionable :title, pipeline: :single_line
attr_mentionable :description, cache: true
participant :author, :assignee, :notes_with_associations
attr_mentionable :description
participant :author
participant :assignee
participant :notes_with_associations
strip_attributes :title
acts_as_paranoid
after_save :update_assignee_cache_counts, if: :assignee_id_changed?
def update_assignee_cache_counts
# make sure we flush the cache for both the old *and* new assignee
User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee
end
end
module ClassMethods
......@@ -104,13 +116,29 @@ module Issuable
end
end
def with_label(title)
def with_label(title, sort = nil)
if title.is_a?(Array) && title.size > 1
joins(:labels).where(labels: { title: title }).group(arel_table[:id]).having("COUNT(DISTINCT labels.title) = #{title.size}")
joins(:labels).where(labels: { title: title }).group(*grouping_columns(sort)).having("COUNT(DISTINCT labels.title) = #{title.size}")
else
joins(:labels).where(labels: { title: title })
end
end
# Includes table keys in group by clause when sorting
# preventing errors in postgres
#
# Returns an array of arel columns
def grouping_columns(sort)
grouping_columns = [arel_table[:id]]
if ["milestone_due_desc", "milestone_due_asc"].include?(sort)
milestone_table = Milestone.arel_table
grouping_columns << milestone_table[:id]
grouping_columns << milestone_table[:due_date]
end
grouping_columns
end
end
def today?
......@@ -121,10 +149,6 @@ module Issuable
today? && created_at == updated_at
end
def is_assigned?
!!assignee_id
end
def is_being_reassigned?
assignee_id_changed?
end
......@@ -155,6 +179,10 @@ module Issuable
hook_data
end
def labels_array
labels.to_a
end
def label_names
labels.order('title ASC').pluck(:title)
end
......
......@@ -23,7 +23,7 @@ module Mentionable
included do
if self < Participable
participant ->(current_user) { mentioned_users(current_user) }
participant -> (user, ext) { all_references(user, extractor: ext) }
end
end
......@@ -43,23 +43,22 @@ module Mentionable
self
end
def all_references(current_user = nil, text = nil)
ext = Gitlab::ReferenceExtractor.new(self.project, current_user || self.author, self.author)
def all_references(current_user = nil, text = nil, extractor: nil)
extractor ||= Gitlab::ReferenceExtractor.
new(project, current_user || author)
if text
ext.analyze(text)
extractor.analyze(text, author: author)
else
self.class.mentionable_attrs.each do |attr, options|
text = send(attr)
text = __send__(attr)
options = options.merge(cache_key: [self, attr], author: author)
context = options.dup
context[:cache_key] = [self, attr] if context.delete(:cache) && self.persisted?
ext.analyze(text, context)
extractor.analyze(text, options)
end
end
ext
extractor
end
def mentioned_users(current_user = nil)
......
......@@ -3,8 +3,6 @@
# Contains functionality related to objects that can have participants, such as
# an author, an assignee and people mentioned in its description or comments.
#
# Used by Issue, Note, MergeRequest, Snippet and Commit.
#
# Usage:
#
# class Issue < ActiveRecord::Base
......@@ -12,22 +10,36 @@
#
# # ...
#
# participant :author, :assignee, :notes, ->(current_user) { mentioned_users(current_user) }
# participant :author
# participant :assignee
# participant :notes
#
# participant -> (current_user, ext) do
# ext.analyze('...')
# end
# end
#
# issue = Issue.last
# users = issue.participants
# # `users` will contain the issue's author, its assignee,
# # all users returned by its #mentioned_users method,
# # as well as all participants to all of the issue's notes,
# # since Note implements Participable as well.
#
module Participable
extend ActiveSupport::Concern
module ClassMethods
def participant(*attrs)
participant_attrs.concat(attrs)
# Adds a list of participant attributes. Attributes can either be symbols or
# Procs.
#
# When using a Proc instead of a Symbol the Proc will be given two
# arguments:
#
# 1. The current user (as an instance of User)
# 2. An instance of `Gitlab::ReferenceExtractor`
#
# It is expected that a Proc populates the given reference extractor
# instance with data. The return value of the Proc is ignored.
#
# attr - The name of the attribute or a Proc
def participant(attr)
participant_attrs << attr
end
def participant_attrs
......@@ -35,42 +47,42 @@ module Participable
end
end
# Be aware that this method makes a lot of sql queries.
# Save result into variable if you are going to reuse it inside same request
def participants(current_user = self.author)
participants =
Gitlab::ReferenceExtractor.lazily do
self.class.participant_attrs.flat_map do |attr|
value =
if attr.respond_to?(:call)
instance_exec(current_user, &attr)
else
send(attr)
end
# Returns the users participating in a discussion.
#
# This method processes attributes of objects in breadth-first order.
#
# Returns an Array of User instances.
def participants(current_user = nil)
current_user ||= author
ext = Gitlab::ReferenceExtractor.new(project, current_user)
participants = Set.new
process = [self]
participants_for(value, current_user)
end.compact.uniq
end
until process.empty?
source = process.pop
unless Gitlab::ReferenceExtractor.lazy?
participants.select! do |user|
user.can?(:read_project, project)
case source
when User
participants << source
when Participable
source.class.participant_attrs.each do |attr|
if attr.respond_to?(:call)
source.instance_exec(current_user, ext, &attr)
else
process << source.__send__(attr)
end
end
when Enumerable, ActiveRecord::Relation
# This uses reverse_each so we can use "pop" to get the next value to
# process (in order). Using unshift instead of pop would require
# moving all Array values one index to the left (which can be
# expensive).
source.reverse_each { |obj| process << obj }
end
end
participants
end
private
participants.merge(ext.users)
def participants_for(value, current_user = nil)
case value
when User, Banzai::LazyReference
[value]
when Enumerable, ActiveRecord::Relation
value.flat_map { |v| participants_for(v, current_user) }
when Participable
value.participants(current_user)
end
Ability.users_that_can_read_project(participants.to_a, project)
end
end
......@@ -95,14 +95,13 @@ class Issue < ActiveRecord::Base
end
def referenced_merge_requests(current_user = nil)
@referenced_merge_requests ||= {}
@referenced_merge_requests[current_user] ||= begin
Gitlab::ReferenceExtractor.lazily do
[self, *notes].flat_map do |note|
note.all_references(current_user).merge_requests
end
end.sort_by(&:iid).uniq
ext = all_references(current_user)
notes_with_associations.each do |object|
object.all_references(current_user, extractor: ext)
end
ext.merge_requests.sort_by(&:iid)
end
# All branches containing the current issue's ID, except for
......@@ -139,9 +138,13 @@ class Issue < ActiveRecord::Base
def closed_by_merge_requests(current_user = nil)
return [] unless open?
notes.system.flat_map do |note|
note.all_references(current_user).merge_requests
end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) }
ext = all_references(current_user)
notes.system.each do |note|
note.all_references(current_user, extractor: ext)
end
ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) }
end
def moved?
......
......@@ -26,7 +26,7 @@ class Key < ActiveRecord::Base
end
def publishable_key
#Removes anything beyond the keytype and key itself
# Removes anything beyond the keytype and key itself
self.key.split[0..1].join(' ')
end
......
......@@ -5,7 +5,6 @@ class ProjectMember < Member
belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
# Make sure project member points only to project as it source
default_value_for :source_type, SOURCE_TYPE
validates_format_of :source_type, with: /\AProject\z/
......@@ -15,6 +14,8 @@ class ProjectMember < Member
scope :in_projects, ->(projects) { where(source_id: projects.pluck(:id)) }
scope :with_user, ->(user) { where(user_id: user.id) }
before_destroy :delete_member_todos
class << self
# Add users to project teams with passed access option
......@@ -102,6 +103,10 @@ class ProjectMember < Member
private
def delete_member_todos
user.todos.where(project_id: source_id).destroy_all if user
end
def send_invite
notification_service.invite_project_member(self, @raw_invite_token)
......
......@@ -22,9 +22,16 @@ module Network
def collect_notes
h = Hash.new(0)
@project.notes.where('noteable_type = ?' ,"Commit").group('notes.commit_id').select('notes.commit_id, count(notes.id) as note_count').each do |item|
h[item.commit_id] = item.note_count.to_i
end
@project
.notes
.where('noteable_type = ?', 'Commit')
.group('notes.commit_id')
.select('notes.commit_id, count(notes.id) as note_count')
.each do |item|
h[item.commit_id] = item.note_count.to_i
end
h
end
......@@ -89,7 +96,7 @@ module Network
end
end
if self.class.max_count / 2 < offset then
if self.class.max_count / 2 < offset
# get max index that commit is displayed in the center.
offset - self.class.max_count / 2
else
......@@ -130,7 +137,7 @@ module Network
commit.parents(@map).each do |parent|
range = commit.time..parent.time
space = if commit.space >= parent.space then
space = if commit.space >= parent.space
find_free_parent_space(range, parent.space, -1, commit.space)
else
find_free_parent_space(range, commit.space, -1, parent.space)
......@@ -144,7 +151,7 @@ module Network
end
def find_free_parent_space(range, space_base, space_step, space_default)
if is_overlap?(range, space_default) then
if is_overlap?(range, space_default)
find_free_space(range, space_step, space_base, space_default)
else
space_default
......@@ -155,9 +162,9 @@ module Network
range.each do |i|
if i != range.first &&
i != range.last &&
@commits[i].spaces.include?(overlap_space) then
@commits[i].spaces.include?(overlap_space)
return true;
return true
end
end
......@@ -198,7 +205,7 @@ module Network
# Visit branching chains
leaves.each do |l|
parents = l.parents(@map).select{|p| p.space.zero?}
for p in parents
parents.each do |p|
place_chain(p, l.time)
end
end
......@@ -216,7 +223,7 @@ module Network
end
def mark_reserved(time_range, space)
for day in time_range
time_range.each do |day|
@reserved[day].push(space)
end
end
......@@ -225,15 +232,15 @@ module Network
space_default ||= space_base
reserved = []
for day in time_range
time_range.each do |day|
reserved.push(*@reserved[day])
end
reserved.uniq!
space = space_default
while reserved.include?(space) do
while reserved.include?(space)
space += space_step
if space < space_base then
if space < space_base
space_step *= -1
space = space_base + space_step
end
......
......@@ -6,7 +6,7 @@ class Note < ActiveRecord::Base
default_value_for :system, false
attr_mentionable :note, cache: true, pipeline: :note
attr_mentionable :note, pipeline: :note
participant :author
belongs_to :project
......@@ -79,14 +79,30 @@ class Note < ActiveRecord::Base
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
#
# query - The search query as a String.
# query - The search query as a String.
# as_user - Limit results to those viewable by a specific user
#
# Returns an ActiveRecord::Relation.
def search(query)
def search(query, as_user: nil)
table = arel_table
pattern = "%#{query}%"
where(table[:note].matches(pattern))
found_notes = joins('LEFT JOIN issues ON issues.id = noteable_id').
where(table[:note].matches(pattern))
if as_user
found_notes.where('
issues.confidential IS NULL
OR issues.confidential IS FALSE
OR (issues.confidential IS TRUE
AND (issues.author_id = :user_id
OR issues.assignee_id = :user_id
OR issues.project_id IN(:project_ids)))',
user_id: as_user.id,
project_ids: as_user.authorized_projects.select(:id))
else
found_notes.where('issues.confidential IS NULL OR issues.confidential IS FALSE')
end
end
end
......
......@@ -309,21 +309,25 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(path_with_namespace, self)
end
def container_registry_path_with_namespace
path_with_namespace.downcase
end
def container_registry_repository
return unless Gitlab.config.registry.enabled
@container_registry_repository ||= begin
token = Auth::ContainerRegistryAuthenticationService.full_access_token(path_with_namespace)
token = Auth::ContainerRegistryAuthenticationService.full_access_token(container_registry_path_with_namespace)
url = Gitlab.config.registry.api_url
host_port = Gitlab.config.registry.host_port
registry = ContainerRegistry::Registry.new(url, token: token, path: host_port)
registry.repository(path_with_namespace)
registry.repository(container_registry_path_with_namespace)
end
end
def container_registry_repository_url
if Gitlab.config.registry.enabled
"#{Gitlab.config.registry.host_port}/#{path_with_namespace}"
"#{Gitlab.config.registry.host_port}/#{container_registry_path_with_namespace}"
end
end
......@@ -946,13 +950,13 @@ class Project < ActiveRecord::Base
shared_runners_enabled? && Ci::Runner.shared.active.any?(&block)
end
def valid_runners_token? token
def valid_runners_token?(token)
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
# TODO (ayufan): For now we use runners_token (backward compatibility)
# In 8.4 every build will have its own individual token valid for time of build
def valid_build_token? token
def valid_build_token?(token)
self.builds_enabled? && self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
......@@ -1007,4 +1011,22 @@ class Project < ActiveRecord::Base
update_attribute(:pending_delete, true)
end
def running_or_pending_build_count(force: false)
Rails.cache.fetch(['projects', id, 'running_or_pending_build_count'], force: force) do
builds.running_or_pending.count(:all)
end
end
def mark_import_as_failed(error_message)
original_errors = errors.dup
sanitized_message = Gitlab::UrlSanitizer.sanitize(error_message)
import_fail
update_column(:import_error, sanitized_message)
rescue ActiveRecord::ActiveRecordError => e
Rails.logger.error("Error setting import status to failed: #{e.message}. Original error: #{sanitized_message}")
ensure
@errors = original_errors
end
end
......@@ -6,7 +6,8 @@ class ProjectImportData < ActiveRecord::Base
key: Gitlab::Application.secrets.db_key_base,
marshal: true,
encode: true,
mode: :per_attribute_iv_and_salt
mode: :per_attribute_iv_and_salt,
algorithm: 'aes-256-cbc'
serialize :data, JSON
......
......@@ -70,7 +70,7 @@ class IrkerService < Service
private
def get_channels
return true unless :activated?
return true unless activated?
return true if recipients.nil? || recipients.empty?
map_recipients
......
......@@ -7,5 +7,6 @@ class ProjectSnippet < Snippet
# Scopes
scope :fresh, -> { order("created_at DESC") }
participant :author, :notes
participant :author
participant :notes_with_associations
end
......@@ -27,6 +27,10 @@ class ProjectWiki
@project.path_with_namespace + ".wiki"
end
def web_url
Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home)
end
def url_to_repo
gitlab_shell.url_to_repo(path_with_namespace)
end
......@@ -142,6 +146,16 @@ class ProjectWiki
wiki
end
def hook_attrs
{
web_url: web_url,
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
path_with_namespace: path_with_namespace,
default_branch: default_branch
}
end
private
def init_repo(path_with_namespace)
......
......@@ -30,7 +30,8 @@ class Snippet < ActiveRecord::Base
scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) }
scope :fresh, -> { order("created_at DESC") }
participant :author, :notes
participant :author
participant :notes_with_associations
def self.reference_prefix
'$'
......@@ -100,6 +101,10 @@ class Snippet < ActiveRecord::Base
content.lines.count > 1000
end
def notes_with_associations
notes.includes(:author, :project)
end
class << self
# Searches for snippets with a matching title or file name.
#
......
......@@ -20,6 +20,11 @@ class User < ActiveRecord::Base
default_value_for :hide_no_password, false
default_value_for :theme_id, gitlab_config.default_theme
attr_encrypted :otp_secret,
key: Gitlab::Application.config.secret_key_base,
mode: :per_attribute_iv_and_salt,
algorithm: 'aes-256-cbc'
devise :two_factor_authenticatable,
otp_secret_encryption_key: Gitlab::Application.config.secret_key_base
alias_attribute :two_factor_enabled, :otp_required_for_login
......@@ -27,7 +32,7 @@ class User < ActiveRecord::Base
devise :two_factor_backupable, otp_number_of_backup_codes: 10
serialize :otp_backup_codes, JSON
devise :lockable, :async, :recoverable, :rememberable, :trackable,
devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable
attr_accessor :force_random_password
......@@ -772,6 +777,23 @@ class User < ActiveRecord::Base
notification_settings.find_or_initialize_by(source: source)
end
def assigned_open_merge_request_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_merge_request_count'], force: force) do
assigned_merge_requests.opened.count
end
end
def assigned_open_issues_count(force: false)
Rails.cache.fetch(['users', id, 'assigned_open_issues_count'], force: force) do
assigned_issues.opened.count
end
end
def update_cache_counts
assigned_open_merge_request_count(force: true)
assigned_open_issues_count(force: true)
end
private
def projects_union
......
module Auth
class ContainerRegistryAuthenticationService < BaseService
include Gitlab::CurrentSettings
AUDIENCE = 'container_registry'
def execute
return error('not found', 404) unless registry.enabled
if params[:offline_token]
return error('unauthorized', 401) unless current_user || project
else
unless current_user || project
return error('forbidden', 403) unless scope
end
......@@ -19,6 +19,7 @@ module Auth
token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer
token.audience = AUDIENCE
token.expire_time = token_expire_at
token[:access] = names.map do |name|
{ type: 'repository', name: name, actions: %w(*) }
end
......@@ -32,6 +33,7 @@ module Auth
token.issuer = registry.issuer
token.audience = params[:service]
token.subject = current_user.try(:username)
token.expire_time = ContainerRegistryAuthenticationService.token_expire_at
token[:access] = accesses.compact
token
end
......@@ -77,5 +79,9 @@ module Auth
def registry
Gitlab.config.registry
end
def self.token_expire_at
Time.now + current_application_settings.container_registry_token_expire_delay.minutes
end
end
end
......@@ -23,7 +23,7 @@ class GitTagPushService < BaseService
commits = []
message = nil
if !Gitlab::Git.blank_ref?(params[:newrev])
unless Gitlab::Git.blank_ref?(params[:newrev])
tag_name = Gitlab::Git.ref_name(params[:ref])
tag = project.repository.find_tag(tag_name)
if tag && tag.target == params[:newrev]
......
......@@ -56,14 +56,14 @@ module Projects
after_create_actions if @project.persisted?
@project.add_import_job if @project.import?
if @project.errors.empty?
@project.add_import_job if @project.import?
else
fail(error: @project.errors.full_messages.join(', '))
end
@project
rescue => e
message = "Unable to save project: #{e.message}"
Rails.logger.error(message)
@project.errors.add(:base, message) if @project
@project
fail(error: e.message)
end
protected
......@@ -103,5 +103,19 @@ module Projects
end
end
end
def fail(error:)
message = "Unable to save project. Error: #{error}"
message << "Project ID: #{@project.id}" if @project && @project.id
Rails.logger.error(message)
if @project && @project.import?
@project.errors.add(:base, message)
@project.mark_import_as_failed(message)
end
@project
end
end
end
......@@ -22,7 +22,7 @@ module Projects
end
def execute
raise LeaseTaken if !try_obtain_lease
raise LeaseTaken unless try_obtain_lease
GitlabShellOneShotWorker.perform_async(:gc, @project.path_with_namespace)
ensure
......
......@@ -39,7 +39,7 @@ module Projects
begin
gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
rescue Gitlab::Shell::Error => e
raise Error, e.message
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end
end
......
......@@ -6,9 +6,8 @@ module WikiPages
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
object_attributes: page.hook_attrs,
# DEPRECATED
repository: @project.hook_attrs.slice(:name, :url, :description, :homepage)
wiki: @project.wiki.hook_attrs,
object_attributes: page.hook_attrs
}
page_url = Gitlab::UrlBuilder.build(page)
......
......@@ -16,7 +16,7 @@
.light.small
= time_ago_with_tooltip(abuse_report.created_at)
%td
= markdown(abuse_report.message.squish!, pipeline: :single_line)
= markdown(abuse_report.message.squish!, pipeline: :single_line, author: reporter)
%td
- if user
= link_to 'Remove user & report', admin_abuse_report_path(abuse_report, remove_user: true),
......
......@@ -178,6 +178,14 @@
.col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control'
- if Gitlab.config.registry.enabled
%fieldset
%legend Container Registry
.form-group
= f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :container_registry_token_expire_delay, class: 'form-control'
%fieldset
%legend Metrics
%p
......
......@@ -4,6 +4,7 @@
%h3 Two-factor Authentication
.login-body
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
= f.hidden_field :remember_me, value: params[resource_name][:remember_me]
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor Authentication code', required: true, autofocus: true
%p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20
......
......@@ -2,4 +2,4 @@
.commit-row-title
= link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: '', title: truncate_sha(commit[:id])
&middot;
= markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line
= markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line, author: event.author
%div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(issue.description, pipeline: :atom, project: issue.project)
= markdown(issue.description, pipeline: :atom, project: issue.project, author: issue.author)
%div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(merge_request.description, pipeline: :atom, project: merge_request.project)
= markdown(merge_request.description, pipeline: :atom, project: merge_request.project, author: merge_request.author)
%div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(note.note, pipeline: :atom, project: note.project)
= markdown(note.note, pipeline: :atom, project: note.project, author: note.author)
......@@ -6,7 +6,7 @@
%i
at
= commit[:timestamp].to_time.to_s(:short)
%blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project)
%blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project, author: event.author)
- if event.commits_count > 15
%p
%i
......
......@@ -4,7 +4,7 @@
= event_action_name(event)
- if event.target
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], title: event.target_title
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
= event_preposition(event)
......
......@@ -15,7 +15,7 @@
%ul.well-list.event_commits
- few_commits = event.commits[0...2]
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project
= render "events/commit", commit: commit, project: project, event: event
- create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project)
- if event.commits_count > 1
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the first page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li.first
......
-# Non-link tag that stands for skipped pages...
-# available local variables
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li{class: "page"}
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the last page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li.last
......
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the next page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.last?
......
......@@ -3,7 +3,7 @@
-# page: a page object for "this" page
-# url: url to this page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
%li{class: "page#{' active' if page.current?}"}
......
-# The container tag
-# available local variables
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
-# paginator: the paginator that renders the pagination tags inside
......@@ -9,7 +9,7 @@
%div.gl-pagination
%ul.pagination.clearfix
- unless current_page.first?
= first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages
= first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages
= prev_page_tag
- each_page do |page|
- if page.left_outer? || page.right_outer? || page.inside_window?
......@@ -18,5 +18,5 @@
= gap_tag
= next_page_tag
- unless current_page.last?
= last_page_tag unless num_pages < 5
= last_page_tag unless total_pages < 5
......@@ -2,7 +2,7 @@
-# available local variables
-# url: url to the previous page
-# current_page: a page object for the currently displayed page
-# num_pages: total number of pages
-# total_pages: total number of pages
-# per_page: number of items to fetch per page
-# remote: data-remote
- if current_page.first?
......
......@@ -30,6 +30,9 @@
= javascript_include_tag "application"
- if page_specific_javascripts
= javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true}
= csrf_meta_tags
= include_gon
......
......@@ -6,11 +6,8 @@
.search.search-form{class: "#{'has-location-badge' if label.present?}"}
= form_tag search_path, method: :get, class: 'navbar-form' do |f|
.search-input-container
.search-location-badge
- if label.present?
%span.location-badge
%i.location-text
= label
- if label.present?
.location-badge= label
.search-input-wrap
.dropdown{ data: {url: search_autocomplete_path } }
= search_field_tag "search", nil, placeholder: 'Search', class: "search-input dropdown-menu-toggle", spellcheck: false, tabindex: "1", autocomplete: 'off', data: { toggle: 'dropdown' }
......
......@@ -30,13 +30,13 @@
= icon('exclamation-circle fw')
%span
Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
%span.count= number_with_delimiter(current_user.assigned_open_issues_count)
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= icon('tasks fw')
%span
Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
%span.count= number_with_delimiter(current_user.assigned_open_merge_request_count)
= nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
......
......@@ -33,18 +33,11 @@
%span
Activity
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
= link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do
= icon('files-o fw')
= icon('code 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')
%span
Commits
Code
- if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do
......@@ -52,15 +45,6 @@
= icon('ship fw')
%span
Pipelines
%span.badge.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count)
- 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? :container_registry
= nav_link(controller: %w(container_registry)) do
......@@ -132,4 +116,16 @@
= link_to new_namespace_project_issue_path(@project.namespace, @project), class: 'shortcuts-new-issue' do
Create a new issue
-# Shortcut to builds page
- if project_nav_tab? :builds
%li.hidden
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
Builds
-# Shortcut to commits page
- if project_nav_tab? :commits
%li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits
.fade-right
......@@ -2,4 +2,4 @@
%div
#{link_to @note.author_name, user_url(@note.author)} wrote:
%div
= markdown(@note.note, pipeline: :email)
= markdown(@note.note, pipeline: :email, author: @note.author)
......@@ -2,7 +2,7 @@
%div
#{link_to @issue.author_name, user_url(@issue.author)} wrote:
-if @issue.description
= markdown(@issue.description, pipeline: :email)
= markdown(@issue.description, pipeline: :email, author: @issue.author)
- if @issue.assignee_id.present?
%p
......
......@@ -9,4 +9,4 @@
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
-if @merge_request.description
= markdown(@merge_request.description, pipeline: :email)
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
$('.js-totalbranch-count').html("#{@repository.branch_count}")
- page_title "Builds"
= render "projects/pipelines/head"
.top-area
%ul.nav-links
......
......@@ -63,9 +63,9 @@
%span #{build.name}
- if can?(current_user, :update_pipeline, @project)
- if commit.retryable? && commit.builds.failed.any?
- if commit.retryable?
= 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?
- if commit.cancelable?
= 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")
.row-content-block.build-content.middle-block
.pull-right
- if can?(current_user, :update_pipeline, @project)
- if can?(current_user, :update_pipeline, ci_commit.project)
- if ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), class: 'btn btn-grouped btn-primary', method: :post
= link_to "Retry failed", retry_namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), class: 'btn btn-grouped btn-primary', method: :post
- if ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
= link_to "Cancel running", cancel_namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline.clearfix
- if defined?(pipeline_details) && pipeline_details
Pipeline
= link_to "##{ci_commit.id}", namespace_project_pipeline_path(@project.namespace, @project, ci_commit.id), class: "monospace"
= link_to "##{ci_commit.id}", namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), class: "monospace"
with
= pluralize ci_commit.statuses.count(:id), "build"
- if ci_commit.ref
for
= link_to ci_commit.ref, namespace_project_commits_path(@project.namespace, @project, ci_commit.ref), class: "monospace"
= link_to ci_commit.ref, namespace_project_commits_path(ci_commit.project.namespace, ci_commit.project, ci_commit.ref), class: "monospace"
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to ci_commit.short_sha, namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: "monospace"
= link_to ci_commit.short_sha, namespace_project_commit_path(ci_commit.project.namespace, ci_commit.project, ci_commit.sha), class: "monospace"
- if ci_commit.duration
in
= time_interval_in_words ci_commit.duration
......@@ -31,7 +31,7 @@
%li= error
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- if @project.builds_enabled? && !ci_commit.ci_yaml_file
- if ci_commit.project.builds_enabled? && !ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
......@@ -45,7 +45,7 @@
%th Tags
%th Duration
%th Finished at
- if @project.build_coverage_enabled?
- if ci_commit.project.build_coverage_enabled?
%th Coverage
%th
- ci_commit.statuses.stages.each do |stage|
......
......@@ -63,10 +63,10 @@
.commit-box.content-block
%h3.commit-title
= markdown escape_once(@commit.title), pipeline: :single_line
= markdown escape_once(@commit.title), pipeline: :single_line, author: @commit.author
- if @commit.description.present?
%pre.commit-description
= preserve(markdown(escape_once(@commit.description), pipeline: :single_line))
= preserve(markdown(escape_once(@commit.description), pipeline: :single_line, author: @commit.author))
:javascript
$(".commit-info.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}");
......@@ -24,7 +24,7 @@
- if commit.description?
.commit-row-description.js-toggle-content
%pre
= preserve(markdown(escape_once(commit.description), pipeline: :single_line))
= preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
.commit-row-info
by
......
%ul.nav-links
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do
Files
= nav_link(controller: [:commit, :commits]) do
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits
%span.badge
= number_with_delimiter(@repository.commit_count)
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
......@@ -16,9 +18,7 @@
= nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do
Branches
%span.badge.js-totalbranch-count= @repository.branch_count
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
%span.badge.js-totaltags-count= @repository.tag_count
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment