Commit d92f4de1 authored by Yorick Peterse's avatar Yorick Peterse

Merged GitLab CE commit 587f85

parents 4b1f5f95 587f8501
......@@ -26,6 +26,7 @@ config/initializers/smtp_settings.rb
config/resque.yml
config/unicorn.rb
config/secrets.yml
config/sidekiq.yml
coverage/*
db/*.sqlite3
db/*.sqlite3-journal
......
......@@ -3,24 +3,60 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased)
- Merge when build succeeds (Zeger-Jan van de Weg)
v 8.4.0 (unreleased)
- Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse)
- Improved performance of finding issues for an entire group (Yorick Peterse)
- Added custom application performance measuring system powered by InfluxDB (Yorick Peterse)
- Bump fog to 1.36.0 (Stan Hu)
- Add housekeeping function to project settings page
- The default GitLab logo now acts as a loading indicator
- Fix caching issue where build status was not updating in project dashboard (Stan Hu)
- Accept 2xx status codes for successful Web hook triggers (Stan Hu)
- Fix missing date of month in network graph when commits span a month (Stan Hu)
- Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
- Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
- Implement new UI for group page
- Implement search inside emoji picker
- Add API support for looking up a user by username (Stan Hu)
- Add project permissions to all project API endpoints (Stan Hu)
- Link to milestone in "Milestone changed" system note
- Only allow group/project members to mention `@all`
- Expose Git's version in the admin area (Trey Davis)
- Add "Frequently used" category to emoji picker
- Add CAS support (tduehr)
- Add link to merge request on build detail page
- Fix: Problem with projects ending with .keys (Jose Corcuera)
- Revert back upvote and downvote button to the issue and MR pages
- Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg)
- Add system hook messages for project rename and transfer (Steve Norman)
- Fix version check image in Safari
v 8.3.3 (unreleased)
- Show 'All' tab by default in the builds page
- Add Open Graph and Twitter Card data to all pages
- Fix API project lookups when querying with a namespace with dots (Stan Hu)
- Enable forcing Two-Factor authentication sitewide, with optional grace period
- Import GitHub Pull Requests into GitLab
- Change single user API endpoint to return more detailed data (Michael Potthoff)
- Update version check images to use SVG
- Validate README format before displaying
- Enable Microsoft Azure OAuth2 support (Janis Meybohm)
- Properly set task-list class on single item task lists
- Add file finder feature in tree view (Kyungchul Shin)
- Ajax filter by message for commits page
- API: Add support for deleting a tag via the API (Robert Schilling)
- Allow subsequent validations in CI Linter
v 8.3.3
- Preserve CE behavior with JIRA integration by only calling API if URL is set
- Fix duplicated branch creation/deletion events when using Web UI (Stan Hu)
- Add configurable LDAP server query timeout
- Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
- Suppress e-mails on failed builds if allow_failure is set (Stan Hu)
- Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
- Better support for referencing and closing issues in Asana service (Mike Wyatt)
- Enable "Add key" button when user fills in a proper key (Stan Hu)
- Fix error in processing reply-by-email messages (Jason Lee)
- Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
- Use WOFF versions of SourceSansPro fonts
- Fix regression when builds were not generated for tags created through web/api interface
v 8.3.2
- Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
......@@ -31,7 +67,6 @@ v 8.3.1
- Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
- Fix LDAP identity and user retrieval when special characters are used
- Move Sidekiq-cron configuration to gitlab.yml
- Enable forcing Two-Factor authentication sitewide, with optional grace period
v 8.3.0
- Bump rack-attack to 4.3.1 for security fix (Stan Hu)
......@@ -96,6 +131,8 @@ v 8.3.0
- Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present
- Persist runners registration token in database
- Fix online editor should not remove newlines at the end of the file
- Expose Git's version in the admin area
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
......@@ -157,6 +194,8 @@ v 8.2.0
- Allow to define cache in `.gitlab-ci.yml`
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- Remove deprecated CI events from project settings page
- Use issue editor as cross reference comment author when issue is edited with a new mention.
- Add graphs of commits ahead and behind default branch (Jeff Stubler)
- Improve personal snippet access workflow (Douglas Alexandre)
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
......
......@@ -22,6 +22,7 @@ gem 'devise', '~> 3.5.3'
gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.2.2'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0'
......@@ -32,7 +33,7 @@ gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.4.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'gssapi', group: :kerberos
gem 'rack-oauth2', '~> 1.2.1'
......@@ -49,7 +50,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.20'
gem "gitlab_git", '~> 7.2.22'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -68,13 +69,6 @@ gem 'grape', '~> 0.13.0'
gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Format dates and times
# based on human-friendly examples
gem "stamp", '~> 0.6.0'
# Enumeration fields
gem 'enumerize', '~> 0.7.0'
# Pagination
gem "kaminari", "~> 0.16.3"
......@@ -88,7 +82,7 @@ gem "carrierwave", '~> 0.9.0'
gem 'dropzonejs-rails', '~> 0.7.1'
# for aws storage
gem "fog", "~> 1.25.0"
gem "fog", "~> 1.36.0"
gem "unf", '~> 0.1.4'
# Authorization
......
......@@ -221,21 +221,45 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
fog (1.25.0)
fog (1.36.0)
fog-aliyun (>= 0.1.0)
fog-atmos
fog-aws (>= 0.6.0)
fog-brightbox (~> 0.4)
fog-core (~> 1.25)
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)
opennebula
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-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
......@@ -244,21 +268,48 @@ GEM
builder
excon (~> 0.45)
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-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-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.4)
fog-radosgw (0.0.5)
fog-core (>= 1.21.0)
fog-json
fog-xml (>= 0.0.1)
fog-sakuracloud (1.5.0)
fog-riakcs (0.1.0)
fog-core
fog-json
fog-xml
fog-sakuracloud (1.7.5)
fog-core
fog-json
fog-softlayer (1.0.2)
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)
......@@ -270,6 +321,9 @@ GEM
fog-voxel (0.1.0)
fog-core
fog-xml
fog-xenserver (0.2.2)
fog-core
fog-xml
fog-xml (0.1.2)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
......@@ -382,7 +436,7 @@ GEM
influxdb (0.2.3)
cause
json
ipaddress (0.8.0)
ipaddress (0.8.2)
jquery-atwho-rails (1.3.2)
jquery-rails (4.0.5)
rails-dom-testing (~> 1.0)
......@@ -448,6 +502,10 @@ GEM
omniauth (1.2.2)
hashie (>= 1.2, < 4)
rack (~> 1.0)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1)
omniauth-bitbucket (0.0.2)
multi_json (~> 1.7)
omniauth (~> 1.1)
......@@ -493,10 +551,6 @@ GEM
activesupport
nokogiri (>= 1.4.4)
omniauth (~> 1.0)
opennebula (4.14.2)
json
nokogiri
rbvmomi
org-ruby (0.9.12)
rubypants (~> 0.2)
orm_adapter (0.5.0)
......@@ -572,10 +626,6 @@ GEM
ffi (>= 0.5.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rbvmomi (1.8.2)
builder
nokogiri (>= 1.4.1)
trollop
rdoc (3.12.2)
json (~> 1.4)
recaptcha (1.0.2)
......@@ -735,7 +785,6 @@ GEM
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
stamp (0.6.0)
state_machines (0.4.0)
state_machines-activemodel (0.3.0)
activemodel (~> 4.1)
......@@ -775,7 +824,6 @@ GEM
multi_json (~> 1.7)
twitter-stream (~> 0.1)
tins (1.6.0)
trollop (2.1.2)
turbolinks (2.5.3)
coffee-rails
twitter-stream (0.1.16)
......@@ -824,6 +872,7 @@ GEM
builder
expression_parser
rinku
xml-simple (1.1.5)
xpath (2.0.0)
nokogiri (~> 1.3)
......@@ -880,7 +929,7 @@ DEPENDENCIES
ffaker (~> 2.0.0)
flay
flog
fog (~> 1.25.0)
fog (~> 1.36.0)
font-awesome-rails (~> 4.2)
foreman
fuubar (~> 2.0.0)
......@@ -890,7 +939,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 0.0.4)
gitlab_emoji (~> 0.2.0)
gitlab_git (~> 7.2.20)
gitlab_git (~> 7.2.22)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0)
......@@ -925,6 +974,7 @@ DEPENDENCIES
oauth2 (~> 1.0.0)
octokit (~> 3.7.0)
omniauth (~> 1.2.2)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0)
......@@ -935,7 +985,7 @@ DEPENDENCIES
omniauth-saml (~> 1.4.0)
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
omniauth_crowd
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
pg (~> 0.18.2)
......@@ -982,7 +1032,6 @@ DEPENDENCIES
spring-commands-spinach (~> 1.0.0)
spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 2.12.3)
stamp (~> 0.6.0)
state_machines-activerecord (~> 0.3.0)
task_list (~> 1.0.2)
teaspoon (~> 1.0.0)
......@@ -1003,4 +1052,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.10.6
1.11.2
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
......
......@@ -41,6 +41,7 @@
#= require shortcuts_network
#= require jquery.nicescroll.min
#= require_tree .
#= require fuzzaldrin-plus.min
window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
......@@ -66,7 +66,7 @@ class @BranchGraph
r.rect(40, 0, 30, @barHeight).attr fill: "#444"
for day, mm in @days
if cuday isnt day[0]
if cuday isnt day[0] || cumonth isnt day[1]
# Dates
r.text(55, @offsetY + @unitTime * mm, day[0])
.attr(
......
class @CommitsList
@data =
ref: null
limit: 0
offset: 0
@disable = false
@showProgress: ->
$('.loading').show()
@hideProgress: ->
$('.loading').hide()
@timer = null
@init: (ref, limit) ->
$("body").on "click", ".day-commits-table li.commit", (event) ->
......@@ -18,38 +8,32 @@ class @CommitsList
e.stopPropagation()
return false
@data.ref = ref
@data.limit = limit
@data.offset = limit
Pager.init limit, false
@content = $("#commits-list")
@searchField = $("#commits-search")
@initSearch()
this.initLoadMore()
this.showProgress()
@initSearch: ->
@timer = null
@searchField.keyup =>
clearTimeout(@timer)
@timer = setTimeout(@filterResults, 500)
@filterResults: =>
form = $(".commits-search-form")
search = @searchField.val()
commitsUrl = form.attr("action") + '?' + form.serialize()
@content.fadeTo('fast', 0.5)
@getOld: ->
this.showProgress()
$.ajax
type: "GET"
url: location.href
data: @data
complete: this.hideProgress
success: (data) ->
CommitsList.append(data.count, data.html)
url: form.attr("action")
data: form.serialize()
complete: =>
@content.fadeTo('fast', 1.0)
success: (data) =>
@content.html(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: commitsUrl}, document.title, commitsUrl
dataType: "json"
@append: (count, html) ->
$("#commits-list").append(html)
if count > 0
@data.offset += count
else
@disable = true
@initLoadMore: ->
$(document).unbind('scroll')
$(document).endlessScroll
bottomPixels: 400
fireDelay: 1000
fireOnce: true
ceaseFire: =>
@disable
callback: =>
this.getOld()
......@@ -87,7 +87,9 @@ class Dispatcher
new GroupAvatar()
when 'projects:tree:show'
new TreeView()
shortcut_handler = new ShortcutsNavigation()
shortcut_handler = new ShortcutsTree()
when 'projects:find_file:show'
shortcut_handler = true
when 'projects:blob:show'
new LineHighlighter()
shortcut_handler = new ShortcutsNavigation()
......
......@@ -66,7 +66,7 @@ class @DropzoneInput
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
$(child).val $(child).val() + response.link.markdown + "\n"
return
error: (temp, errorMessage) ->
......@@ -99,11 +99,6 @@ class @DropzoneInput
child = $(dropzone[0]).children("textarea")
formatLink = (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
handlePaste = (event) ->
pasteEvent = event.originalEvent
if pasteEvent.clipboardData and pasteEvent.clipboardData.items
......@@ -162,7 +157,7 @@ class @DropzoneInput
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
insertToTextArea(filename, response.responseJSON.link.markdown)
error: (response) ->
showError(response.responseJSON.message)
......@@ -202,8 +197,3 @@ class @DropzoneInput
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
formatLink: (link) ->
text = "[#{link.alt}](#{link.url})"
text = "!#{text}" if link.is_image
text
......@@ -68,7 +68,7 @@ GitLab.GfmAutoComplete =
title: sanitize(m.title)
search: "#{m.iid} #{m.title}"
input.one 'focus', =>
if @dataSource
$.getJSON(@dataSource).done (data) ->
# load members
input.atwho 'load', '@', data.members
......
......@@ -15,13 +15,6 @@
$(this).html totalIssues + 1
else
$(this).html totalIssues - 1
$("body").on "click", ".issues-other-filters .dropdown-menu a", ->
$('.issues-list').block(
message: null,
overlayCSS:
backgroundColor: '#DDD'
opacity: .4
)
reload: ->
Issues.initSelects()
......
NProgress.configure(showSpinner: false)
defaultClass = 'tanuki-shape'
pieces = [
'path#tanuki-right-cheek',
'path#tanuki-right-eye, path#tanuki-right-ear',
'path#tanuki-nose',
'path#tanuki-left-eye, path#tanuki-left-ear',
'path#tanuki-left-cheek',
]
pieceIndex = 0
firstPiece = pieces[0]
currentTimer = null
delay = 150
clearHighlights = ->
$(".#{defaultClass}.highlight").attr('class', defaultClass)
start = ->
clearHighlights()
pieceIndex = 0
pieces.reverse() unless pieces[0] == firstPiece
clearInterval(currentTimer) if currentTimer
currentTimer = setInterval(work, delay)
stop = ->
clearInterval(currentTimer)
clearHighlights()
work = ->
clearHighlights()
$(pieces[pieceIndex]).attr('class', "#{defaultClass} highlight")
# If we hit the last piece, reset the index and then reverse the array to
# get a nice back-and-forth sweeping look
if pieceIndex == pieces.length - 1
pieceIndex = 0
pieces.reverse()
else
pieceIndex++
$(document).on('page:fetch', start)
$(document).on('page:change', stop)
class @ProjectFindFile
constructor: (@element, @options)->
@filePaths = {}
@inputElement = @element.find(".file-finder-input")
# init event
@initEvent()
# focus text input box
@inputElement.focus()
# load file list
@load(@options.url)
# init event
initEvent: ->
@inputElement.off "keyup"
@inputElement.on "keyup", (event) =>
target = $(event.target)
value = target.val()
oldValue = target.data("oldValue") ? ""
if value != oldValue
target.data("oldValue", value)
@findFile()
@element.find("tr.tree-item").eq(0).addClass("selected").focus()
@element.find(".tree-content-holder .tree-table").on "click", (event) ->
if (event.target.nodeName != "A")
path = @element.find(".tree-item-file-name a", this).attr("href")
location.href = path if path
# find file
findFile: ->
searchText = @inputElement.val()
result = if searchText.length > 0 then fuzzaldrinPlus.filter(@filePaths, searchText) else @filePaths
@renderList result, searchText
# files pathes load
load: (url) ->
$.ajax
url: url
method: "get"
dataType: "json"
success: (data) =>
@element.find(".loading").hide()
@filePaths = data
@findFile()
@element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus()
# render result
renderList: (filePaths, searchText) ->
@element.find(".tree-table > tbody").empty()
for filePath, i in filePaths
break if i == 20
if searchText
matches = fuzzaldrinPlus.match(filePath, searchText)
blobItemUrl = "#{@options.blobUrlTemplate}/#{filePath}"
html = @makeHtml filePath, matches, blobItemUrl
@element.find(".tree-table > tbody").append(html)
# highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
highlighter = (element, text, matches) ->
lastIndex = 0
highlightText = ""
matchedChars = []
for matchIndex in matches
unmatched = text.substring(lastIndex, matchIndex)
if unmatched
element.append(matchedChars.join("").bold()) if matchedChars.length
matchedChars = []
element.append(document.createTextNode(unmatched))
matchedChars.push(text[matchIndex])
lastIndex = matchIndex + 1
element.append(matchedChars.join("").bold()) if matchedChars.length
element.append(document.createTextNode(text.substring(lastIndex)))
# make tbody row html
makeHtml: (filePath, matches, blobItemUrl) ->
$tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>")
if matches
$tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl))
else
$tr.find("a").attr("href", blobItemUrl).text(filePath)
return $tr
selectRow: (type) ->
rows = @element.find(".files-slider tr.tree-item")
selectedRow = @element.find(".files-slider tr.tree-item.selected")
if rows && rows.length > 0
if selectedRow && selectedRow.length > 0
if type == "UP"
next = selectedRow.prev()
else if type == "DOWN"
next = selectedRow.next()
if next.length > 0
selectedRow.removeClass "selected"
selectedRow = next
else
selectedRow = rows.eq(0)
selectedRow.addClass("selected").focus()
selectRowUp: =>
@selectRow "UP"
selectRowDown: =>
@selectRow "DOWN"
goToTree: =>
location.href = @options.treeUrl
goToBlob: =>
path = @element.find(".tree-item.selected .tree-item-file-name a").attr("href")
location.href = path if path
#= require shortcuts_navigation
class @ShortcutsFindFile extends ShortcutsNavigation
constructor: (@projectFindFile) ->
super()
_oldStopCallback = Mousetrap.stopCallback
# override to fire shortcuts action when focus in textbox
Mousetrap.stopCallback = (event, element, combo) =>
if element == @projectFindFile.inputElement[0] and (combo == 'up' or combo == 'down' or combo == 'esc' or combo == 'enter')
# when press up/down key in textbox, cusor prevent to move to home/end
event.preventDefault()
return false
return _oldStopCallback(event, element, combo)
Mousetrap.bind('up', @projectFindFile.selectRowUp)
Mousetrap.bind('down', @projectFindFile.selectRowDown)
Mousetrap.bind('esc', @projectFindFile.goToTree)
Mousetrap.bind('enter', @projectFindFile.goToBlob)
class @ShortcutsTree extends ShortcutsNavigation
constructor: ->
super()
Mousetrap.bind('t', -> ShortcutsTree.findAndFollowLink('.shortcuts-find-file'))
# Zen Mode (full screen) textarea
#
#= provides zen_mode:enter
#= provides zen_mode:leave
#
#= require jquery.scrollTo
#= require dropzone
#= require mousetrap
#= require mousetrap/pause
#
# ### Events
#
# `zen_mode:enter`
#
# Fired when the "Edit in fullscreen" link is clicked.
#
# **Synchronicity** Sync
# **Bubbles** Yes
# **Cancelable** No
# **Target** a.js-zen-enter
#
# `zen_mode:leave`
#
# Fired when the "Leave Fullscreen" link is clicked.
#
# **Synchronicity** Sync
# **Bubbles** Yes
# **Cancelable** No
# **Target** a.js-zen-leave
#
class @ZenMode
constructor: ->
@active_zen_area = null
@active_checkbox = null
@scroll_position = 0
@active_backdrop = null
@active_textarea = null
$(window).scroll =>
if not @active_checkbox
@scroll_position = window.pageYOffset
$(document).on 'click', '.js-zen-enter', (e) ->
e.preventDefault()
$(e.currentTarget).trigger('zen_mode:enter')
$('body').on 'click', '.zen-enter-link', (e) =>
$(document).on 'click', '.js-zen-leave', (e) ->
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true).change()
$(e.currentTarget).trigger('zen_mode:leave')
$(document).on 'zen_mode:enter', (e) =>
@enter(e.target.parentNode)
$(document).on 'zen_mode:leave', (e) =>
@exit()
$('body').on 'click', '.zen-leave-link', (e) =>
$(document).on 'keydown', (e) ->
if e.keyCode == 27 # Esc
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false).change()
$(document).trigger('zen_mode:leave')
$('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
enter: (backdrop) ->
Mousetrap.pause()
@updateActiveZenArea(checkbox)
else
@exitZenMode()
$(document).on 'keydown', (e) =>
if e.keyCode is 27 # Esc
@exitZenMode()
e.preventDefault()
@active_backdrop = $(backdrop)
@active_backdrop.addClass('fullscreen')
@active_textarea = @active_backdrop.find('textarea')
updateActiveZenArea: (checkbox) =>
@active_checkbox = $(checkbox)
@active_checkbox.prop('checked', true)
@active_zen_area = @active_checkbox.parent().find('textarea')
# Prevent a user-resized textarea from persisting to fullscreen
@active_zen_area.removeAttr('style')
@active_zen_area.focus()
@active_textarea.removeAttr('style')
@active_textarea.focus()
exitZenMode: =>
if @active_zen_area isnt null
exit: ->
if @active_textarea
Mousetrap.unpause()
@active_checkbox.prop('checked', false)
@active_zen_area = null
@active_checkbox = null
@restoreScroll(@scroll_position)
# Enable dropzone when leaving ZEN mode
@active_textarea.closest('.zen-backdrop').removeClass('fullscreen')
@scrollTo(@active_textarea)
@active_textarea = null
@active_backdrop = null
Dropzone.forElement('.div-dropzone').enable()
restoreScroll: (y) ->
window.scrollTo(window.pageXOffset, y)
scrollTo: (zen_area) ->
$.scrollTo(zen_area, 0, offset: -150)
......@@ -72,6 +72,15 @@
> p:last-child {
margin-bottom: 0;
}
.block-controls {
float: right;
.control {
float: left;
margin-left: 10px;
}
}
}
.cover-block {
......
......@@ -3,23 +3,23 @@
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 300;
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf');
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf.woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 400;
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf');
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf.woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 600;
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf');
src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf.woff');
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf');
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf.woff');
}
......@@ -105,7 +105,7 @@
.tanuki-shape {
transition: all 0.8s;
&:hover {
&:hover, &.highlight {
fill: rgb(255, 255, 255);
transition: all 0.1s;
}
......
......@@ -54,17 +54,17 @@
h3 {
margin: 24px 0 12px 0;
font-size: 1.25em;
font-size: 1.1em;
}
h4 {
margin: 24px 0 12px 0;
font-size: 1.1em;
font-size: 0.98em;
}
h5 {
margin: 24px 0 12px 0;
font-size: 1em;
font-size: 0.95em;
}
h6 {
......
.zennable {
.zen-toggle-comment {
display: none;
}
.zen-enter-link {
a.js-zen-enter {
color: $gl-gray;
position: absolute;
top: 0px;
......@@ -11,7 +7,7 @@
line-height: 40px;
}
.zen-leave-link {
a.js-zen-leave {
display: none;
color: $gl-text-color;
position: absolute;
......@@ -25,19 +21,8 @@
}
}
// Hide the Enter link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
// Show the Leave link when we're in Zen mode
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
top: 0;
}
input:checked ~ .zen-backdrop {
.zen-backdrop {
&.fullscreen {
background-color: white;
position: fixed;
top: 0;
......@@ -61,26 +46,16 @@
max-width: 900px;
margin: 0 auto;
}
}
// Make the color of the placeholder text in the Zenned-out textarea darker,
// so it becomes visible
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #A8A8A8;
a.js-zen-enter {
display: none;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #A8A8A8;
opacity: 1;
a.js-zen-leave {
display: block;
position: absolute;
top: 0;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #A8A8A8;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #A8A8A8;
}
}
......@@ -28,10 +28,6 @@
}
}
.commits-feed-holder {
float: right;
}
li.commit {
list-style: none;
......@@ -122,3 +118,59 @@ li.commit {
color: $gl-gray;
}
}
.divergence-graph {
padding: 12px 12px 0 0;
float: right;
.graph-side {
position: relative;
width: 80px;
height: 22px;
padding: 5px 0 13px;
float: left;
.bar {
position: absolute;
height: 4px;
background-color: #ccc;
}
.bar-behind {
right: 0;
border-radius: 3px 0 0 3px;
}
.bar-ahead {
left: 0;
border-radius: 0 3px 3px 0;
}
.count {
padding-top: 6px;
padding-bottom: 0px;
font-size: 12px;
color: #333;
display: block;
}
.count-behind {
padding-right: 4px;
text-align: right;
}
.count-ahead {
padding-left: 4px;
text-align: left;
}
}
.graph-separator {
position: relative;
width: 1px;
height: 18px;
margin: 5px 0 0;
float: left;
background-color: #ccc;
}
}
......@@ -138,6 +138,7 @@
*/
.event-last-push {
overflow: auto;
width: 100%;
.event-last-push-text {
@include str-truncated(100%);
padding: 5px 0;
......
......@@ -94,9 +94,17 @@
}
.cross-project-reference {
font-weight: bold;
color: $gl-link-color;
span {
white-space: nowrap;
width: 85%;
overflow: hidden;
position: relative;
display: inline-block;
text-overflow: ellipsis;
}
button {
float: right;
}
......
......@@ -26,6 +26,13 @@
}
.project-home-panel {
.cover-controls {
.project-settings-dropdown {
margin-left: 10px;
}
}
.project-identicon-holder {
margin-bottom: 16px;
......@@ -292,10 +299,9 @@
border: 1px solid #c6cacf !important;
background-color: #e4e7ed !important;
text-transform: uppercase;
text-transform: none;
color: #313236 !important;
font-size: 13px;
font-weight: 600;
font-size: 15px;
}
.dropdown-menu {
......@@ -408,10 +414,15 @@ ul.nav.nav-projects-tabs {
}
}
.last-push-widget {
margin-top: -1px;
}
.top-area {
border-bottom: 1px solid #EEE;
margin: 0 -16px;
padding: 0 $gl-padding;
height: 42px;
ul.left-top-menu {
display: inline-block;
......@@ -518,6 +529,7 @@ pre.light-well {
.projects-search-form {
margin: -$gl-padding;
padding: $gl-padding;
padding-bottom: 0;
margin-bottom: 0px;
input {
......
.tree-holder {
.file-finder {
width: 50%;
.file-finder-input {
width: 95%;
display: inline-block;
}
}
.tree-table {
margin-bottom: 0;
......
......@@ -9,12 +9,10 @@ class AbuseReportsController < ApplicationController
@abuse_report.reporter = current_user
if @abuse_report.save
if current_application_settings.admin_notification_email.present?
AbuseReportMailer.notify(@abuse_report.id).deliver_later
end
@abuse_report.notify
message = "Thank you for your report. A GitLab administrator will look into it shortly."
redirect_to root_path, notice: message
redirect_to @abuse_report.user, notice: message
else
render :new
end
......@@ -23,6 +21,9 @@ class AbuseReportsController < ApplicationController
private
def report_params
params.require(:abuse_report).permit(:user_id, :message)
params.require(:abuse_report).permit(%i(
message
user_id
))
end
end
......@@ -72,8 +72,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_enabled,
:metrics_host,
:metrics_port,
:metrics_username,
:metrics_password,
:metrics_pool_size,
:metrics_timeout,
:metrics_method_call_threshold,
......
......@@ -5,12 +5,12 @@ class Admin::BuildsController < Admin::ApplicationController
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
when 'all'
@builds
when 'running'
@builds.running_or_pending.reverse_order
when 'finished'
@builds.finished
else
@builds.running_or_pending.reverse_order
@builds
end
@builds = @builds.page(params[:page]).per(30)
end
......
......@@ -292,7 +292,7 @@ class ApplicationController < ActionController::Base
end
def set_filters_params
params[:sort] ||= 'created_desc'
params[:sort] ||= 'id_desc'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
......
......@@ -6,11 +6,13 @@ module Ci
end
def create
if params[:content].blank?
@content = params[:content]
if @content.blank?
@status = false
@error = "Please provide content of .gitlab-ci.yml"
else
@config_processor = Ci::GitlabCiYamlProcessor.new params[:content]
@config_processor = Ci::GitlabCiYamlProcessor.new(@content)
@stages = @config_processor.stages
@builds = @config_processor.builds
@status = true
......
......@@ -9,6 +9,11 @@ class Projects::BranchesController < Projects::ApplicationController
@sort = params[:sort] || 'name'
@branches = @repository.branches_sorted_by(@sort)
@branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE)
@max_commits = @branches.reduce(0) do |memo, branch|
diverging_commit_counts = repository.diverging_commit_counts(branch)
[memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max
end
end
def recent
......
......@@ -12,12 +12,12 @@ class Projects::BuildsController < Projects::ApplicationController
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
when 'all'
@builds
when 'running'
@builds.running_or_pending.reverse_order
when 'finished'
@builds.finished
else
@builds.running_or_pending.reverse_order
@builds
end
@builds = @builds.page(params[:page]).per(30)
end
......
......@@ -8,10 +8,16 @@ class Projects::CommitsController < Projects::ApplicationController
before_action :authorize_download_code!
def show
@repo = @project.repository
@limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i
search = params[:search]
@commits =
if search.present?
@repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact
else
@repository.commits(@ref, @path, @limit, @offset)
end
@commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
......
# 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
......@@ -159,7 +159,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def merge_check
@merge_request.check_if_can_be_merged if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
......
......@@ -20,6 +20,8 @@ class Projects::RefsController < Projects::ApplicationController
namespace_project_network_path(@project.namespace, @project, @id, @options)
when "graphs"
namespace_project_graph_path(@project.namespace, @project, @id)
when "find_file"
namespace_project_find_file_path(@project.namespace, @project, @id)
when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id)
else
......
......@@ -8,7 +8,7 @@ class ProjectsController < ApplicationController
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
# Authorize
before_action :authorize_admin_project!, only: [:edit, :update]
before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping]
before_action :event_filter, only: [:show, :activity]
layout :determine_layout
......@@ -177,6 +177,15 @@ class ProjectsController < ApplicationController
end
end
def housekeeping
::Projects::HousekeepingService.new(@project).execute
respond_to do |format|
flash[:notice] = "Housekeeping successfully started."
format.html { redirect_to project_path(@project) }
end
end
def toggle_star
current_user.toggle_star(@project)
@project.reload
......
......@@ -80,9 +80,9 @@ class IssuableFinder
if project?
@projects = project
elsif current_user && params[:authorized_only].presence && !current_user_related?
@projects = current_user.authorized_projects
@projects = current_user.authorized_projects.reorder(nil)
else
@projects = ProjectsFinder.new.execute(current_user)
@projects = ProjectsFinder.new.execute(current_user).reorder(nil)
end
end
......
......@@ -205,8 +205,8 @@ module ApplicationHelper
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago js-timeago-pending",
datetime: time.to_time.getutc.iso8601,
title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'),
datetime: time.getutc.iso8601,
title: time.in_time_zone.to_s(:medium),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
unless skip_js
......
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook).freeze
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze
def ldap_enabled?
......
......@@ -80,7 +80,7 @@ module IssuesHelper
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.strftime("%Y-%m-%dT%H:%M:%SZ")
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
......@@ -103,9 +103,12 @@ module IssuesHelper
content_tag :div, "",
class: "icon emoji-icon emoji-#{unicode}",
"data-emoji" => name,
"data-aliases" => aliases.join(" "),
"data-unicode-name" => unicode
title: name,
data: {
aliases: aliases.join(' '),
emoji: name,
unicode_name: unicode
}
end
def emoji_author_list(notes, current_user)
......
......@@ -27,35 +27,20 @@ module PageLayoutHelper
#
# Returns an HTML-safe String.
def page_description(description = nil)
@page_description ||= page_description_default
if description.present?
@page_description = description.squish
else
elsif @page_description.present?
sanitize(@page_description, tags: []).truncate_words(30)
end
end
# Default value for page_description when one hasn't been defined manually by
# a view
def page_description_default
if @project
@project.description || brand_title
else
brand_title
end
end
def page_image
default = image_url('gitlab_logo.png')
if @project
@project.avatar_url || default
elsif @user
avatar_icon(@user)
else
default
end
subject = @project || @user || @group
image = subject.avatar_url if subject.present?
image || default
end
# Define or get attributes to be used as Twitter card metadata
......
......@@ -19,7 +19,7 @@ module SortingHelper
end
def sort_title_recently_updated
'Recently updated'
'Last updated'
end
def sort_title_oldest_created
......@@ -27,7 +27,7 @@ module SortingHelper
end
def sort_title_recently_created
'Recently created'
'Last created'
end
def sort_title_milestone_soon
......@@ -63,11 +63,11 @@ module SortingHelper
end
def sort_value_oldest_created
'created_asc'
'id_asc'
end
def sort_value_recently_created
'created_desc'
'id_desc'
end
def sort_value_milestone_soon
......
......@@ -2,6 +2,8 @@ class AbuseReportMailer < BaseMailer
include Gitlab::CurrentSettings
def notify(abuse_report_id)
return unless deliverable?
@abuse_report = AbuseReport.find(abuse_report_id)
mail(
......@@ -9,4 +11,10 @@ class AbuseReportMailer < BaseMailer
subject: "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse"
)
end
private
def deliverable?
current_application_settings.admin_notification_email.present?
end
end
......@@ -48,7 +48,7 @@ module Emails
yield
SentNotification.record(@note, recipient_id, reply_key)
SentNotification.record_note(@note, recipient_id, reply_key)
end
end
end
......@@ -18,4 +18,10 @@ class AbuseReport < ActiveRecord::Base
validates :user, presence: true
validates :message, presence: true
validates :user_id, uniqueness: true
def notify
return unless self.persisted?
AbuseReportMailer.notify(self.id).deliver_later
end
end
......@@ -28,9 +28,20 @@
# admin_notification_email :string(255)
# shared_runners_enabled :boolean default(TRUE), not null
# max_artifacts_size :integer default(100), not null
# runners_registration_token :string(255)
# require_two_factor_authentication :boolean default(TRUE)
# runners_registration_token :string
# require_two_factor_authentication :boolean default(FALSE)
# two_factor_grace_period :integer default(48)
# metrics_enabled :boolean default(FALSE)
# metrics_host :string default("localhost")
# metrics_username :string
# metrics_password :string
# metrics_pool_size :integer default(16)
# metrics_timeout :integer default(10)
# metrics_method_call_threshold :integer default(10)
# recaptcha_enabled :boolean default(FALSE)
# recaptcha_site_key :string
# recaptcha_private_key :string
# metrics_port :integer default(8089)
#
class ApplicationSetting < ActiveRecord::Base
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
module Ci
......@@ -54,6 +55,8 @@ module Ci
# To prevent db load megabytes of data from trace
default_scope -> { select(Ci::Build.columns_without_lazy) }
before_destroy { project }
class << self
def columns_without_lazy
(column_names - LAZY_ATTRIBUTES).map do |column_name|
......@@ -145,10 +148,6 @@ module Ci
end
end
def project
commit.project
end
def project_id
gl_project_id
end
......@@ -207,7 +206,7 @@ module Ci
def trace
trace = raw_trace
if project && trace.present?
if project && trace.present? && project.runners_token.present?
trace.gsub(project.runners_token, 'xxxxxx')
else
trace
......
......@@ -4,9 +4,10 @@
#
# id :integer not null, primary key
# runner_id :integer not null
# project_id :integer not null
# project_id :integer
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
module Ci
......
......@@ -4,10 +4,11 @@
#
# id :integer not null, primary key
# token :string(255)
# project_id :integer not null
# project_id :integer
# deleted_at :datetime
# created_at :datetime
# updated_at :datetime
# gl_project_id :integer
#
module Ci
......
......@@ -3,12 +3,13 @@
# Table name: ci_variables
#
# id :integer not null, primary key
# project_id :integer not null
# project_id :integer
# key :string(255)
# value :text
# encrypted_value :text
# encrypted_value_salt :string(255)
# encrypted_value_iv :string(255)
# gl_project_id :integer
#
module Ci
......
# == Schema Information
#
# project_id integer
# status string
# finished_at datetime
# trace text
# created_at datetime
# updated_at datetime
# started_at datetime
# runner_id integer
# coverage float
# commit_id integer
# commands text
# job_id integer
# name string
# deploy boolean default: false
# options text
# allow_failure boolean default: false, null: false
# stage string
# trigger_request_id integer
# stage_idx integer
# tag boolean
# ref string
# user_id integer
# type string
# target_url string
# description string
# Table name: ci_builds
#
# id :integer not null, primary key
# project_id :integer
# status :string(255)
# finished_at :datetime
# trace :text
# created_at :datetime
# updated_at :datetime
# started_at :datetime
# runner_id :integer
# coverage :float
# commit_id :integer
# commands :text
# job_id :integer
# name :string(255)
# deploy :boolean default(FALSE)
# options :text
# allow_failure :boolean default(FALSE), not null
# stage :string(255)
# trigger_request_id :integer
# stage_idx :integer
# tag :boolean
# ref :string(255)
# user_id :integer
# type :string(255)
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
class CommitStatus < ActiveRecord::Base
......
......@@ -51,8 +51,11 @@ module Mentionable
else
self.class.mentionable_attrs.each do |attr, options|
text = send(attr)
options[:cache_key] = [self, attr] if options.delete(:cache) && self.persisted?
ext.analyze(text, options)
context = options.dup
context[:cache_key] = [self, attr] if context.delete(:cache) && self.persisted?
ext.analyze(text, context)
end
end
......
......@@ -11,6 +11,7 @@ module Sortable
default_scope { order_id_desc }
scope :order_id_desc, -> { reorder(id: :desc) }
scope :order_id_asc, -> { reorder(id: :asc) }
scope :order_created_desc, -> { reorder(created_at: :desc) }
scope :order_created_asc, -> { reorder(created_at: :asc) }
scope :order_updated_desc, -> { reorder(updated_at: :desc) }
......@@ -28,6 +29,8 @@ module Sortable
when 'updated_desc' then order_updated_desc
when 'created_asc' then order_created_asc
when 'created_desc' then order_created_desc
when 'id_desc' then order_id_desc
when 'id_asc' then order_id_asc
else
all
end
......
......@@ -29,6 +29,7 @@
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# gl_project_id :integer
#
class GenericCommitStatus < CommitStatus
......
......@@ -121,9 +121,9 @@ class GlobalMilestone
def expires_at
if due_date
if due_date.past?
"expired at #{due_date.stamp("Aug 21, 2011")}"
"expired on #{due_date.to_s(:medium)}"
else
"expires at #{due_date.stamp("Aug 21, 2011")}"
"expires on #{due_date.to_s(:medium)}"
end
end
end
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class ProjectHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class ServiceHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class SystemHook < WebHook
......
......@@ -15,6 +15,7 @@
# tag_push_events :boolean default(FALSE)
# note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null
#
class WebHook < ActiveRecord::Base
......@@ -60,7 +61,7 @@ class WebHook < ActiveRecord::Base
basic_auth: auth)
end
[response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)]
[(response.code >= 200 && response.code < 300), ActionView::Base.full_sanitizer.sanitize(response.to_s)]
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}")
[false, e.to_s]
......
......@@ -34,7 +34,9 @@ class Issue < ActiveRecord::Base
belongs_to :project
validates :project, presence: true
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_group,
->(group) { where(project_id: group.projects.select(:id).reorder(nil)) }
scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) }
......
......@@ -21,8 +21,8 @@
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text (serialized to hash)
# merge_when_build_succeeds :boolean default(false), not null
# merge_params :text
# merge_when_build_succeeds :boolean default(FALSE), not null
# merge_user_id :integer
#
......@@ -133,7 +133,7 @@ class MergeRequest < ActiveRecord::Base
validate :validate_branches
validate :validate_fork
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) }
scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) }
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
......@@ -232,6 +232,8 @@ class MergeRequest < ActiveRecord::Base
end
def check_if_can_be_merged
return unless unchecked?
can_be_merged =
project.repository.can_be_merged?(source_sha, target_branch)
......@@ -255,7 +257,11 @@ class MergeRequest < ActiveRecord::Base
end
def mergeable?
open? && !work_in_progress? && can_be_merged?
return false unless open? && !work_in_progress?
check_if_can_be_merged
can_be_merged?
end
def gitlab_merge_status
......@@ -501,6 +507,10 @@ class MergeRequest < ActiveRecord::Base
!source_branch_exists? || !target_branch_exists?
end
def broken?
self.commits.blank? || branch_missing? || cannot_be_merged?
end
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end
......@@ -578,8 +588,4 @@ class MergeRequest < ActiveRecord::Base
def ci_commit
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
end
def broken?
self.commits.blank? || branch_missing? || cannot_be_merged?
end
end
......@@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base
include InternalId
include Sortable
include Referable
include StripAttribute
belongs_to :project
......@@ -61,6 +62,27 @@ class Milestone < ActiveRecord::Base
end
end
def self.reference_pattern
nil
end
def self.link_reference_pattern
super("milestones", /(?<milestone>\d+)/)
end
def to_reference(from_project = nil)
escaped_title = self.title.gsub("]", "\\]")
h = Gitlab::Application.routes.url_helpers
url = h.namespace_project_milestone_url(self.project.namespace, self.project, self)
"[#{escaped_title}](#{url})"
end
def reference_link_text(from_project = nil)
self.title
end
def expired?
if due_date
due_date.past?
......@@ -90,9 +112,9 @@ class Milestone < ActiveRecord::Base
def expires_at
if due_date
if due_date.past?
"expired at #{due_date.stamp("Aug 21, 2011")}"
"expired on #{due_date.to_s(:medium)}"
else
"expires at #{due_date.stamp("Aug 21, 2011")}"
"expires on #{due_date.to_s(:medium)}"
end
end
end
......
......@@ -11,7 +11,6 @@
# type :string(255)
# description :string(255) default(""), not null
# avatar :string(255)
# public :boolean default(FALSE)
#
class Namespace < ActiveRecord::Base
......
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.
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