Commit ee8836a9 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge commit '27cf081e' into merge_ce_27cf081e

Conflicts:
	VERSION
	app/models/user.rb
	app/services/merge_requests/build_service.rb
	config/gitlab.yml.example
	db/schema.rb
	doc/integration/README.md
	features/project/merge_requests.feature
	features/steps/project/merge_requests.rb
parents 3d469188 27cf081e
...@@ -17,8 +17,8 @@ env: ...@@ -17,8 +17,8 @@ env:
- TASK=jasmine:ci DB=postgresql - TASK=jasmine:ci DB=postgresql
before_install: before_install:
- sudo apt-get install libicu-dev -y - sudo apt-get install libicu-dev -y
install: install:
- "bundle install --deployment --without production" - "travis_retry bundle install --deployment --without production --retry 5"
branches: branches:
only: only:
- 'master' - 'master'
......
v 7.2.0
- Explore page
- Add project stars (Ciro Santilli)
- Log Sidekiq arguments
- Fix cpu usage issue in Firefox
- Better labels: colors, ability to rename and remove
- Improve the way merge request collects diffs
- Improve compare page for large diffs
- Expose the full commit message via API
- Fix 500 error on repository rename
- Fix bug when MR download patch return invalid diff
- Test gitlab-shell integration
- Repository import timeout increased from 2 to 4 minutes allowing larger repos to be imported
- API for labels (Robert Schilling)
v 7.1.1
- Fix cpu usage issue in Firefox
- Fix redirect loop when changing password by new user
- Fix 500 error on new merge request page
v 7.1.0 v 7.1.0
- Remove observers - Remove observers
- Improve MR discussions - Improve MR discussions
...@@ -8,7 +28,7 @@ v 7.1.0 ...@@ -8,7 +28,7 @@ v 7.1.0
- Dont show reply button if user is not signed in - Dont show reply button if user is not signed in
- Expose more information for issues with webhook - Expose more information for issues with webhook
- Add a mention of the merge request into the default merge request commit message - Add a mention of the merge request into the default merge request commit message
- Imrpove code highlight, introduce support for more languages like Go, Clojure, Erlang etc - Improve code highlight, introduce support for more languages like Go, Clojure, Erlang etc
- Fix concurrency issue in repository download - Fix concurrency issue in repository download
- Dont allow repository name start with ? - Dont allow repository name start with ?
- Improve email threading (Pierre de La Morinerie) - Improve email threading (Pierre de La Morinerie)
......
...@@ -80,16 +80,20 @@ gem "six" ...@@ -80,16 +80,20 @@ gem "six"
gem "seed-fu" gem "seed-fu"
# Markdown to HTML # Markdown to HTML
gem "redcarpet", "~> 2.2.2"
gem "github-markup" gem "github-markup"
gem "org-ruby" # For rendering .org files
# Required markup gems by github-markdown
gem 'redcarpet', '~> 2.2.2'
gem 'RedCloth'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.1'
gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4'
# Diffs # Diffs
gem 'diffy', '~> 3.0.3' gem 'diffy', '~> 3.0.3'
# Asciidoc to HTML
gem "asciidoctor"
# Application server # Application server
group :unicorn do group :unicorn do
gem "unicorn", '~> 4.6.3' gem "unicorn", '~> 4.6.3'
...@@ -175,6 +179,7 @@ gem "gitlab_emoji", "~> 0.0.1.1" ...@@ -175,6 +179,7 @@ gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0' gem "gon", '~> 5.0.0'
gem 'nprogress-rails' gem 'nprogress-rails'
gem 'request_store' gem 'request_store'
gem "virtus"
group :development do group :development do
gem "annotate", "~> 2.6.0.beta2" gem "annotate", "~> 2.6.0.beta2"
......
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
RedCloth (4.2.9)
ace-rails-ap (2.0.1) ace-rails-ap (2.0.1)
actionmailer (4.1.1) actionmailer (4.1.1)
actionpack (= 4.1.1) actionpack (= 4.1.1)
...@@ -86,6 +87,7 @@ GEM ...@@ -86,6 +87,7 @@ GEM
thor thor
crack (0.4.1) crack (0.4.1)
safe_yaml (~> 0.9.0) safe_yaml (~> 0.9.0)
creole (0.3.8)
d3_rails (3.1.10) d3_rails (3.1.10)
railties (>= 3.1.0) railties (>= 3.1.0)
daemons (1.1.9) daemons (1.1.9)
...@@ -121,6 +123,7 @@ GEM ...@@ -121,6 +123,7 @@ GEM
eventmachine (1.0.3) eventmachine (1.0.3)
excon (0.32.1) excon (0.32.1)
execjs (2.0.2) execjs (2.0.2)
expression_parser (0.9.0)
factory_girl (4.3.0) factory_girl (4.3.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
factory_girl_rails (4.3.0) factory_girl_rails (4.3.0)
...@@ -176,12 +179,12 @@ GEM ...@@ -176,12 +179,12 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.0.1.1) gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1) emoji (~> 1.0.1)
gitlab_git (6.0.1) gitlab_git (6.2.1)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-grit (~> 2.6) gitlab-grit (~> 2.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.19.0) rugged (~> 0.21.0)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.0.4) gitlab_omniauth-ldap (1.0.4)
net-ldap (~> 0.3.1) net-ldap (~> 0.3.1)
...@@ -241,7 +244,7 @@ GEM ...@@ -241,7 +244,7 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpauth (0.2.0) httpauth (0.2.0)
i18n (0.6.9) i18n (0.6.11)
ice_nine (0.10.0) ice_nine (0.10.0)
jasmine (2.0.2) jasmine (2.0.2)
jasmine-core (~> 2.0.0) jasmine-core (~> 2.0.0)
...@@ -320,7 +323,7 @@ GEM ...@@ -320,7 +323,7 @@ GEM
omniauth-twitter (1.0.1) omniauth-twitter (1.0.1)
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
org-ruby (0.9.6) org-ruby (0.9.1)
rubypants (>= 0.2.0) rubypants (>= 0.2.0)
orm_adapter (0.5.0) orm_adapter (0.5.0)
pg (0.15.1) pg (0.15.1)
...@@ -331,7 +334,7 @@ GEM ...@@ -331,7 +334,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
websocket-driver (>= 0.2.0) websocket-driver (>= 0.2.0)
polyglot (0.3.4) polyglot (0.3.4)
posix-spawn (0.3.8) posix-spawn (0.3.9)
pry (0.9.12.4) pry (0.9.12.4)
coderay (~> 1.0) coderay (~> 1.0)
method_source (~> 0.8) method_source (~> 0.8)
...@@ -413,6 +416,7 @@ GEM ...@@ -413,6 +416,7 @@ GEM
require_all (1.3.2) require_all (1.3.2)
rest-client (1.6.7) rest-client (1.6.7)
mime-types (>= 1.16) mime-types (>= 1.16)
rinku (1.7.3)
rouge (1.3.3) rouge (1.3.3)
rspec (2.14.1) rspec (2.14.1)
rspec-core (~> 2.14.0) rspec-core (~> 2.14.0)
...@@ -432,7 +436,7 @@ GEM ...@@ -432,7 +436,7 @@ GEM
ruby-progressbar (1.2.0) ruby-progressbar (1.2.0)
rubyntlm (0.1.1) rubyntlm (0.1.1)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.19.0) rugged (0.21.0)
safe_yaml (0.9.7) safe_yaml (0.9.7)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -536,7 +540,7 @@ GEM ...@@ -536,7 +540,7 @@ GEM
eventmachine (>= 0.12.8) eventmachine (>= 0.12.8)
http_parser.rb (~> 0.5.1) http_parser.rb (~> 0.5.1)
simple_oauth (~> 0.1.4) simple_oauth (~> 0.1.4)
tzinfo (1.2.1) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uglifier (2.3.2) uglifier (2.3.2)
execjs (>= 0.3.0) execjs (>= 0.3.0)
...@@ -563,6 +567,10 @@ GEM ...@@ -563,6 +567,10 @@ GEM
addressable (>= 2.2.7) addressable (>= 2.2.7)
crack (>= 0.3.2) crack (>= 0.3.2)
websocket-driver (0.3.3) websocket-driver (0.3.3)
wikicloth (0.8.1)
builder
expression_parser
rinku
xpath (2.0.0) xpath (2.0.0)
nokogiri (~> 1.3) nokogiri (~> 1.3)
...@@ -570,10 +578,11 @@ PLATFORMS ...@@ -570,10 +578,11 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
RedCloth
ace-rails-ap ace-rails-ap
acts-as-taggable-on acts-as-taggable-on
annotate (~> 2.6.0.beta2) annotate (~> 2.6.0.beta2)
asciidoctor asciidoctor (= 0.1.4)
awesome_print awesome_print
better_errors better_errors
binding_of_caller binding_of_caller
...@@ -583,6 +592,7 @@ DEPENDENCIES ...@@ -583,6 +592,7 @@ DEPENDENCIES
coffee-rails coffee-rails
colored colored
coveralls coveralls
creole (~> 0.3.6)
d3_rails (~> 3.1.4) d3_rails (~> 3.1.4)
database_cleaner database_cleaner
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
...@@ -633,7 +643,7 @@ DEPENDENCIES ...@@ -633,7 +643,7 @@ DEPENDENCIES
omniauth-github omniauth-github
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-twitter omniauth-twitter
org-ruby org-ruby (= 0.9.1)
pg pg
poltergeist (~> 1.5.1) poltergeist (~> 1.5.1)
pry pry
...@@ -647,6 +657,7 @@ DEPENDENCIES ...@@ -647,6 +657,7 @@ DEPENDENCIES
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rb-fsevent rb-fsevent
rb-inotify rb-inotify
rdoc (~> 3.6)
redcarpet (~> 2.2.2) redcarpet (~> 2.2.2)
redis-rails redis-rails
request_store request_store
...@@ -682,4 +693,6 @@ DEPENDENCIES ...@@ -682,4 +693,6 @@ DEPENDENCIES
unicorn (~> 4.6.3) unicorn (~> 4.6.3)
unicorn-worker-killer unicorn-worker-killer
version_sorter version_sorter
virtus
webmock webmock
wikicloth (= 0.8.1)
...@@ -85,7 +85,7 @@ or by directly calling the script: ...@@ -85,7 +85,7 @@ or by directly calling the script:
sudo /etc/init.d/gitlab start sudo /etc/init.d/gitlab start
Please login with `root` / `5iveL!fe`. Please login with `root` / `5iveL!fe`
## Install a development environment ## Install a development environment
......
7.1.0-ee 7.2.0-ee.pre
...@@ -53,15 +53,40 @@ window.split = (val) -> ...@@ -53,15 +53,40 @@ window.split = (val) ->
window.extractLast = (term) -> window.extractLast = (term) ->
return split( term ).pop() return split( term ).pop()
window.rstrip = (val) ->
return val.replace(/\s+$/, '')
# Disable button if text field is empty # Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) -> window.disableButtonIfEmptyField = (field_selector, button_selector) ->
field = $(field_selector) field = $(field_selector)
closest_submit = field.closest("form").find(button_selector) closest_submit = field.closest('form').find(button_selector)
closest_submit.disable() if rstrip(field.val()) is ""
field.on 'input', ->
if rstrip($(@).val()) is ""
closest_submit.disable()
else
closest_submit.enable()
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
empty = false
form.find('input').filter(form_selector).each ->
empty = true if rstrip($(this).val()) is ""
if empty
closest_submit.disable()
else
closest_submit.enable()
closest_submit.disable() if field.val() is "" form.keyup ->
empty = false
form.find('input').filter(form_selector).each ->
empty = true if rstrip($(this).val()) is ""
field.on "input", -> if empty
if $(@).val() is ""
closest_submit.disable() closest_submit.disable()
else else
closest_submit.enable() closest_submit.enable()
......
class Diff
UNFOLD_COUNT = 20
constructor: ->
$(document).on('click', '.js-unfold', (event) =>
target = $(event.target)
unfoldBottom = target.hasClass('js-unfold-bottom')
unfold = true
[old_line, line_number] = @lineNumbers(target.parent())
offset = line_number - old_line
if unfoldBottom
line_number += 1
since = line_number
to = line_number + UNFOLD_COUNT
else
[prev_old_line, prev_new_line] = @lineNumbers(target.parent().prev())
line_number -= 1
to = line_number
if line_number - UNFOLD_COUNT > prev_new_line + 1
since = line_number - UNFOLD_COUNT
else
since = prev_new_line + 1
unfold = false
link = target.parents('.diff-file').attr('data-blob-diff-path')
params =
since: since
to: to
bottom: unfoldBottom
offset: offset
unfold: unfold
$.get(link, params, (response) =>
target.parent().replaceWith(response)
)
)
lineNumbers: (line) ->
return ([0, 0]) unless line.children().length
lines = line.children().slice(0, 2)
line_numbers = ($(l).attr('data-linenumber') for l in lines)
(parseInt(line_number) for line_number in line_numbers)
@Diff = Diff
...@@ -23,13 +23,21 @@ class Dispatcher ...@@ -23,13 +23,21 @@ class Dispatcher
new Issue() new Issue()
when 'projects:milestones:show' when 'projects:milestones:show'
new Milestone() new Milestone()
when 'projects:issues:new', 'projects:merge_requests:new' when 'projects:issues:new'
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
when 'projects:merge_requests:new'
GitLab.GfmAutoComplete.setup()
new Diff()
when 'projects:merge_requests:show'
new Diff()
when "projects:merge_requests:diffs"
new Diff()
when 'dashboard:show' when 'dashboard:show'
new Dashboard() new Dashboard()
new Activities() new Activities()
when 'projects:commit:show' when 'projects:commit:show'
new Commit() new Commit()
new Diff()
when 'groups:show', 'projects:show' when 'groups:show', 'projects:show'
new Activities() new Activities()
when 'projects:new', 'projects:edit' when 'projects:new', 'projects:edit'
...@@ -42,6 +50,8 @@ class Dispatcher ...@@ -42,6 +50,8 @@ class Dispatcher
new TreeView() new TreeView()
when 'projects:blob:show' when 'projects:blob:show'
new BlobView() new BlobView()
when 'projects:labels:new'
new Labels()
switch path.first() switch path.first()
when 'admin' then new Admin() when 'admin' then new Admin()
......
class Labels
constructor: ->
form = $('.label-form')
@setupLabelForm(form)
@cleanBinding()
@addBinding()
@updateColorPreview
addBinding: ->
$(document).on 'click', '.suggest-colors a', @setSuggestedColor
$(document).on 'input', 'input#label_color', @updateColorPreview
cleanBinding: ->
$(document).off 'click', '.suggest-colors a'
$(document).off 'input', 'input#label_color'
# Initializes the form to disable the save button if no color or title is entered
setupLabelForm: (form) ->
disableButtonIfAnyEmptyField form, '.form-control', form.find('.js-save-button')
# Updates the the preview color with the hex-color input
updateColorPreview: =>
previewColor = $('input#label_color').val()
$('div.label-color-preview').css('background-color', previewColor)
# Updates the preview color with a click on a suggested color
setSuggestedColor: (e) =>
color = $(e.currentTarget).data('color')
$('input#label_color').val(color)
@updateColorPreview()
# Notify the form, that color has changed
$('.label-form').trigger('keyup')
e.preventDefault()
@Labels = Labels
...@@ -20,6 +20,9 @@ $(document).ready -> ...@@ -20,6 +20,9 @@ $(document).ready ->
$(".div-dropzone-hover").append iconPicture $(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner $(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner $(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
dropzone = $(".div-dropzone").dropzone( dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload url: project_image_path_upload
...@@ -66,13 +69,17 @@ $(document).ready -> ...@@ -66,13 +69,17 @@ $(document).ready ->
return return
sending: -> sending: ->
$(".div-dropzone-spinner").css "opacity", 0.7 $(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return return
complete: -> complete: ->
$(".dz-preview").remove() $(".dz-preview").remove()
$(".markdown-area").trigger "input" $(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css "opacity", 0 $(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return return
) )
...@@ -163,10 +170,14 @@ $(document).ready -> ...@@ -163,10 +170,14 @@ $(document).ready ->
val + url + "\n" val + url + "\n"
showSpinner = (e) -> showSpinner = (e) ->
$(".div-dropzone-spinner").css "opacity", 0.7 $(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = -> closeSpinner = ->
$(".div-dropzone-spinner").css "opacity", 0 $(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) -> showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length checkIfMsgExists = $(".error-alert").children().length
......
...@@ -321,7 +321,9 @@ class Notes ...@@ -321,7 +321,9 @@ class Notes
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
form = note.find(".note-edit-form") form = note.find(".note-edit-form")
form.show() form.show()
form.find("textarea").focus() textarea = form.find("textarea")
textarea.focus()
disableButtonIfEmptyField textarea, form.find(".js-comment-button")
### ###
Called in response to clicking the edit note link Called in response to clicking the edit note link
......
@Pager = @Pager =
limit: 0 init: (@limit = 0, preload, @disable = false) ->
offset: 0 @loading = $(".loading")
disable: false
init: (limit, preload) ->
@limit = limit
if preload if preload
@offset = 0 @offset = 0
@getOld() @getOld()
else else
@offset = limit @offset = @limit
@initLoadMore() @initLoadMore()
getOld: -> getOld: ->
$(".loading").show() @loading.show()
$.ajax $.ajax
type: "GET" type: "GET"
url: location.href url: location.href
data: "limit=" + @limit + "&offset=" + @offset data: "limit=" + @limit + "&offset=" + @offset
complete: -> complete: =>
$(".loading").hide() @loading.hide()
success: (data) -> success: (data) ->
Pager.append(data.count, data.html) Pager.append(data.count, data.html)
dataType: "json" dataType: "json"
...@@ -39,6 +36,7 @@ ...@@ -39,6 +36,7 @@
ceaseFire: -> ceaseFire: ->
Pager.disable Pager.disable
callback: (i) -> callback: (i) =>
$(".loading").show() unless @loading.is(':visible')
Pager.getOld() @loading.show()
Pager.getOld()
...@@ -54,3 +54,8 @@ $ -> ...@@ -54,3 +54,8 @@ $ ->
$.cookie('hide_no_ssh_message', 'false', { path: path }) $.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide() $(@).parents('.no-ssh-key-message').hide()
e.preventDefault() e.preventDefault()
$('.project-side .star').on 'ajax:success', (e, data, status, xhr) ->
$(@).toggleClass('on').find('.count').html(data.star_count)
.on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert')
...@@ -59,4 +59,4 @@ ...@@ -59,4 +59,4 @@
/** /**
* Styles for responsive sidebar * Styles for responsive sidebar
*/ */
@import "semantic-ui/modules/sidebar" @import "semantic-ui/modules/sidebar";
...@@ -4,3 +4,9 @@ ...@@ -4,3 +4,9 @@
.js-details-container .content.hide { display: block; } .js-details-container .content.hide { display: block; }
.js-details-container.open .content { display: block; } .js-details-container.open .content { display: block; }
.js-details-container.open .content.hide { display: none; } .js-details-container.open .content.hide { display: none; }
// Toggle between two states.
.js-toggler-container .turn-on { display: block; }
.js-toggler-container .turn-off { display: none; }
.js-toggler-container.on .turn-on { display: none; }
.js-toggler-container.on .turn-off { display: block; }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
background-image: none; background-image: none;
border: 1px solid transparent; border: $btn-border;
white-space: nowrap; white-space: nowrap;
padding: 6px 12px; padding: 6px 12px;
font-size: 13px; font-size: 13px;
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
user-select: none; user-select: none;
color: #444444; color: #444444;
background-color: #fff; background-color: #fff;
border-color: #ccc;
text-shadow: none; text-shadow: none;
&.hover, &.hover,
......
...@@ -350,3 +350,18 @@ table { ...@@ -350,3 +350,18 @@ table {
.footer-links a { .footer-links a {
margin-right: 15px; margin-right: 15px;
} }
.search_box {
position: relative;
padding: 30px;
text-align: center;
background-color: #F9F9F9;
border: 1px solid #DDDDDD;
border-radius: 0px;
}
.search_glyph {
color: #555;
font-size: 42px;
}
...@@ -10,6 +10,8 @@ $hover: #D9EDF7; ...@@ -10,6 +10,8 @@ $hover: #D9EDF7;
$link_color: #446e9b; $link_color: #446e9b;
$link_hover_color: #2FA0BB; $link_hover_color: #2FA0BB;
$btn-border: 1px solid #ccc;
/* /*
* Success colors (green) * Success colors (green)
*/ */
......
...@@ -244,6 +244,7 @@ li.commit { ...@@ -244,6 +244,7 @@ li.commit {
font-family: inherit; font-family: inherit;
padding-left: $left; padding-left: $left;
position: relative; position: relative;
resize: vertical;
z-index: 2; z-index: 2;
} }
} }
...@@ -40,14 +40,17 @@ ...@@ -40,14 +40,17 @@
font-size: 12px; font-size: 12px;
.old { .old {
span.idiff { span.idiff {
background-color: #FAA; background-color: #F99;
} }
} }
.new { .new {
span.idiff { span.idiff {
background-color: #AFA; background-color: #8F8;
} }
} }
.unfold {
cursor: pointer;
}
.file-mode-changed { .file-mode-changed {
padding: 10px; padding: 10px;
......
.explore-title {
text-align: center;
h3 {
font-weight: normal;
font-size: 30px;
}
}
...@@ -63,26 +63,10 @@ ...@@ -63,26 +63,10 @@
@media (min-width: 800px) { .issues_bulk_update .select2-container { min-width: 120px; } } @media (min-width: 800px) { .issues_bulk_update .select2-container { min-width: 120px; } }
@media (min-width: 1200px) { .issues_bulk_update .select2-container { min-width: 160px; } } @media (min-width: 1200px) { .issues_bulk_update .select2-container { min-width: 160px; } }
.issues-holder { .issues_bulk_update {
.issues_filters { .select2-container .select2-choice {
} color: #444 !important;
font-weight: 500;
.issues_bulk_update {
margin: 0;
form {
float:left;
}
.update_selected_issues {
margin-left: 4px;
}
.select2-container .select2-choice {
height: 32px;
line-height: 28px;
color: #444 !important;
font-weight: 500;
}
} }
} }
...@@ -110,7 +94,7 @@ ...@@ -110,7 +94,7 @@
} }
} }
.issue-show-labels .label { .issue-show-labels .color-label {
padding: 6px 10px; padding: 6px 10px;
} }
......
.suggest-colors {
margin-top: 5px;
a {
@include border-radius(4px);
width: 30px;
height: 30px;
display: inline-block;
margin-right: 10px;
}
}
.manage-labels-list {
.label {
padding: 9px;
font-size: 14px;
}
}
.color-label {
padding: 3px 4px;
}
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
max-width: 100%; max-width: 100%;
margin-bottom: 20px; margin-bottom: 20px;
} }
&.default-brand-image {
margin: 0 80px;
}
} }
.login-logo{ .login-logo{
......
/** /**
* MR -> show: Automerge widget * MR -> show: Automerge widget
* *
*/ */
.automerge_widget { .automerge_widget {
...@@ -48,10 +48,10 @@ ...@@ -48,10 +48,10 @@
.label-branch { .label-branch {
@include border-radius(4px); @include border-radius(4px);
padding: 2px 4px; padding: 3px 4px;
border: none; border: none;
background: #555; background: $hover;
color: #fff; color: #333;
font-family: $monospace_font; font-family: $monospace_font;
font-weight: normal; font-weight: normal;
overflow: hidden; overflow: hidden;
......
...@@ -190,28 +190,39 @@ ul.nav.nav-projects-tabs { ...@@ -190,28 +190,39 @@ ul.nav.nav-projects-tabs {
.project-side { .project-side {
.btn-block { .btn-block {
background-image: none; background-image: none;
.btn,
&.btn, .btn, &.btn {
&.btn-group ul.dropdown-menu { text-align: left;
padding: 10px 15px;
background-color: #F1f1f1; background-color: #F1f1f1;
border-color: #EEE; border-color: #EEE;
&:hover { &:hover {
background-color: #eee; background-color: #eee;
border-color: #DDD; border-color: #DDD;
} }
} }
&.btn-group-justified {
.btn { .count {
width: 100%; float: right;
} font-weight: 500;
.dropdown-toggle { text-shadow: 0 1px #FFF;
width: 26px;
}
} }
ul {
&.btn-group-justified {
.btn {
width: 100%;
}
.dropdown-toggle {
width: 30px;
padding: 10px;
}
ul {
width: 100%; width: 100%;
}
} }
} }
.project-fork-icon { .project-fork-icon {
float: left; float: left;
font-size: 26px; font-size: 26px;
......
...@@ -39,12 +39,13 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -39,12 +39,13 @@ class Admin::UsersController < Admin::ApplicationController
def create def create
opts = { opts = {
force_random_password: true, force_random_password: true,
password_expires_at: Time.now password_expires_at: nil
} }
@user = User.new(user_params.merge(opts)) @user = User.new(user_params.merge(opts))
@user.created_by_id = current_user.id @user.created_by_id = current_user.id
@user.generate_password @user.generate_password
@user.generate_reset_token
@user.skip_confirmation! @user.skip_confirmation!
respond_to do |format| respond_to do |format|
......
...@@ -46,11 +46,11 @@ class DashboardController < ApplicationController ...@@ -46,11 +46,11 @@ class DashboardController < ApplicationController
@projects = @projects.where(namespace_id: Group.find_by(name: params[:group])) if params[:group].present? @projects = @projects.where(namespace_id: Group.find_by(name: params[:group])) if params[:group].present?
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.includes(:namespace) @projects = @projects.includes(:namespace)
@projects = @projects.tagged_with(params[:label]) if params[:label].present? @projects = @projects.tagged_with(params[:tag]) if params[:tag].present?
@projects = @projects.sort(@sort = params[:sort]) @projects = @projects.sort(@sort = params[:sort])
@projects = @projects.page(params[:page]).per(30) @projects = @projects.page(params[:page]).per(30)
@labels = current_user.authorized_projects.tags_on(:labels) @tags = current_user.authorized_projects.tags_on(:tags)
@groups = current_user.authorized_groups @groups = current_user.authorized_groups
end end
......
class Public::ProjectsController < ApplicationController class Explore::GroupsController < ApplicationController
skip_before_filter :authenticate_user!, skip_before_filter :authenticate_user!,
:reject_blocked, :set_current_user_for_observers, :reject_blocked, :set_current_user_for_observers,
:add_abilities :add_abilities
layout 'public' layout "explore"
def index def index
@projects = Project.publicish(current_user) @groups = GroupsFinder.new.execute(current_user)
@projects = @projects.search(params[:search]) if params[:search].present? @groups = @groups.search(params[:search]) if params[:search].present?
@projects = @projects.sort(@sort = params[:sort]) @groups = @groups.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(20) @groups = @groups.page(params[:page]).per(20)
end end
end end
class Explore::ProjectsController < ApplicationController
skip_before_filter :authenticate_user!,
:reject_blocked,
:add_abilities
layout 'explore'
def index
@projects = ProjectsFinder.new.execute(current_user)
@projects = @projects.search(params[:search]) if params[:search].present?
@projects = @projects.sort(@sort = params[:sort])
@projects = @projects.includes(:namespace).page(params[:page]).per(20)
end
def trending
@trending_projects = TrendingProjectsFinder.new.execute(current_user)
@trending_projects = @trending_projects.page(params[:page]).per(10)
end
def starred
@starred_projects = ProjectsFinder.new.execute(current_user)
@starred_projects = @starred_projects.order('star_count DESC')
@starred_projects = @starred_projects.page(params[:page]).per(10)
end
end
...@@ -11,6 +11,11 @@ class Profiles::PasswordsController < ApplicationController ...@@ -11,6 +11,11 @@ class Profiles::PasswordsController < ApplicationController
end end
def create def create
unless @user.valid_password?(user_params[:current_password])
redirect_to new_profile_password_path, alert: 'You must provide a valid current password'
return
end
new_password = user_params[:password] new_password = user_params[:password]
new_password_confirmation = user_params[:password_confirmation] new_password_confirmation = user_params[:password_confirmation]
......
...@@ -25,6 +25,21 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -25,6 +25,21 @@ class Projects::BlobController < Projects::ApplicationController
end end
end end
def diff
@form = UnfoldForm.new(params)
@lines = @blob.data.lines[@form.since - 1..@form.to - 1]
if @form.bottom?
@match_line = ''
else
lines_length = @lines.length - 1
line = [@form.since, lines_length].join(',')
@match_line = "@@ -#{line}+#{line} @@"
end
render layout: false
end
private private
def blob def blob
......
...@@ -8,14 +8,21 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -8,14 +8,21 @@ class Projects::CompareController < Projects::ApplicationController
end end
def show def show
compare = Gitlab::Git::Compare.new(@repository.raw_repository, params[:from], params[:to], MergeRequestDiff::COMMITS_SAFE_SIZE) base_ref = params[:from]
head_ref = params[:to]
@commits = compare.commits compare_result = CompareService.new.execute(
@commit = compare.commit current_user,
@diffs = compare.diffs @project,
@refs_are_same = compare.same head_ref,
@line_notes = [] @project,
@diff_timeout = compare.timeout base_ref
)
@commits = compare_result.commits
@diffs = compare_result.diffs
@commit = @commits.last
@line_notes = []
end end
def create def create
......
...@@ -24,7 +24,17 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -24,7 +24,17 @@ class Projects::HooksController < Projects::ApplicationController
end end
def test def test
TestHookService.new.execute(hook, current_user) if !@project.empty_repo?
status = TestHookService.new.execute(hook, current_user)
if status
flash[:notice] = 'Hook successfully executed.'
else
flash[:alert] = 'Hook execution failed. '\
'Ensure hook URL is correct and service is up.'
end
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
end
redirect_to :back redirect_to :back
end end
......
...@@ -152,7 +152,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -152,7 +152,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :state_event :milestone_id, :state_event, label_ids: []
) )
end end
end end
class Projects::LabelsController < Projects::ApplicationController class Projects::LabelsController < Projects::ApplicationController
before_filter :module_enabled before_filter :module_enabled
before_filter :label, only: [:edit, :update, :destroy]
before_filter :authorize_labels! before_filter :authorize_labels!
before_filter :authorize_admin_labels!, except: [:index]
respond_to :js, :html respond_to :js, :html
def index def index
@labels = @project.issues_labels @labels = @project.labels.order_by_name.page(params[:page]).per(20)
end
def new
@label = @project.labels.new
end
def create
@label = @project.labels.create(label_params)
if @label.valid?
redirect_to project_labels_path(@project)
else
render 'new'
end
end
def edit
end
def update
if @label.update_attributes(label_params)
redirect_to project_labels_path(@project)
else
render 'edit'
end
end end
def generate def generate
...@@ -21,6 +47,15 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -21,6 +47,15 @@ class Projects::LabelsController < Projects::ApplicationController
end end
end end
def destroy
@label.destroy
respond_to do |format|
format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' }
format.js { render nothing: true }
end
end
protected protected
def module_enabled def module_enabled
...@@ -28,4 +63,16 @@ class Projects::LabelsController < Projects::ApplicationController ...@@ -28,4 +63,16 @@ class Projects::LabelsController < Projects::ApplicationController
return render_404 return render_404
end end
end end
def label_params
params.require(:label).permit(:title, :color)
end
def label
@label = @project.labels.find(params[:id])
end
def authorize_admin_labels!
return render_404 unless can?(current_user, :admin_label, @project)
end
end end
...@@ -70,7 +70,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -70,7 +70,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@target_project = merge_request.target_project @target_project = merge_request.target_project
@source_project = merge_request.source_project @source_project = merge_request.source_project
@commits = @merge_request.compare_commits @commits = @merge_request.compare_commits
@commit = @merge_request.compare_base_commit @commit = @merge_request.compare_commits.last
@diffs = @merge_request.compare_diffs @diffs = @merge_request.compare_diffs
@note_counts = Note.where(commit_id: @commits.map(&:id)). @note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count group(:commit_id).count
...@@ -242,7 +242,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -242,7 +242,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params.require(:merge_request).permit( params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch, :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id, :target_project_id, :target_branch, :milestone_id,
:state_event, :description, :label_list :state_event, :description, label_ids: []
) )
end end
end end
...@@ -14,11 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -14,11 +14,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
render_404 and return render_404 and return
end end
storage_path = Gitlab.config.gitlab.repository_downloads_path file_path = ArchiveRepositoryService.new.execute(@project, params[:ref], params[:format])
@repository.clean_old_archives
file_path = @repository.archive_repo(params[:ref], storage_path, params[:format].downcase)
if file_path if file_path
# Send file to user # Send file to user
......
...@@ -12,12 +12,10 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -12,12 +12,10 @@ class Projects::WikisController < Projects::ApplicationController
def show def show
@page = @project_wiki.find_page(params[:id], params[:version_id]) @page = @project_wiki.find_page(params[:id], params[:version_id])
gollum_wiki = @project_wiki.wiki
file = gollum_wiki.file(params[:id], gollum_wiki.ref, true)
if @page if @page
render 'show' render 'show'
elsif file elsif file = @project_wiki.find_file(params[:id], params[:version_id])
if file.on_disk? if file.on_disk?
send_file file.on_disk_path, disposition: 'inline' send_file file.on_disk_path, disposition: 'inline'
else else
......
...@@ -60,6 +60,8 @@ class ProjectsController < ApplicationController ...@@ -60,6 +60,8 @@ class ProjectsController < ApplicationController
@events = event_filter.apply_filter(@events) @events = event_filter.apply_filter(@events)
@events = @events.limit(limit).offset(params[:offset] || 0) @events = @events.limit(limit).offset(params[:offset] || 0)
@show_star = !(current_user && current_user.starred?(@project))
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.empty_repo? if @project.empty_repo?
...@@ -167,6 +169,12 @@ class ProjectsController < ApplicationController ...@@ -167,6 +169,12 @@ class ProjectsController < ApplicationController
end end
end end
def toggle_star
current_user.toggle_star(@project)
@project.reload
render json: { star_count: @project.star_count }
end
private private
def upload_path def upload_path
...@@ -188,7 +196,7 @@ class ProjectsController < ApplicationController ...@@ -188,7 +196,7 @@ class ProjectsController < ApplicationController
def project_params def project_params
params.require(:project).permit( params.require(:project).permit(
:name, :path, :description, :issues_tracker, :label_list, :name, :path, :description, :issues_tracker, :tag_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :merge_requests_template :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :merge_requests_template
) )
......
class SessionsController < Devise::SessionsController class SessionsController < Devise::SessionsController
def new def new
redirect_url = if request.referer.present? redirect_path = if request.referer.present? && (params['redirect_to_referer'] == 'yes')
referer_uri = URI(request.referer) referer_uri = URI(request.referer)
if referer_uri.host == Gitlab.config.gitlab.host if referer_uri.host == Gitlab.config.gitlab.host
referer_uri.path referer_uri.path
...@@ -12,7 +12,11 @@ class SessionsController < Devise::SessionsController ...@@ -12,7 +12,11 @@ class SessionsController < Devise::SessionsController
request.fullpath request.fullpath
end end
store_location_for(:redirect, redirect_url) # Prevent a 'you are already signed in' message directly after signing:
# we should never redirect to '/users/sign_in' after signing in successfully.
unless redirect_path == '/users/sign_in'
store_location_for(:redirect, redirect_path)
end
super super
end end
......
...@@ -125,7 +125,13 @@ class BaseFinder ...@@ -125,7 +125,13 @@ class BaseFinder
def by_label(items) def by_label(items)
if params[:label_name].present? if params[:label_name].present?
items = items.tagged_with(params[:label_name]) label_names = params[:label_name].split(",")
item_ids = LabelLink.joins(:label).
where('labels.title in (?)', label_names).
where(target_type: klass.name).pluck(:target_id)
items = items.where(id: item_ids)
end end
items items
......
class TrendingProjectsFinder
def execute(current_user, start_date = nil)
start_date ||= Date.today - 1.month
projects = projects_for(current_user)
# Determine trending projects based on comments count
# for period of time - ex. month
projects.joins(:notes).where('notes.created_at > ?', start_date).
select("projects.*, count(notes.id) as ncount").
group("projects.id").order("ncount DESC")
end
private
def projects_for(current_user)
ProjectsFinder.new.execute(current_user)
end
end
...@@ -221,7 +221,18 @@ module ApplicationHelper ...@@ -221,7 +221,18 @@ module ApplicationHelper
end end
def render_markup(file_name, file_content) def render_markup(file_name, file_content)
GitHub::Markup.render(file_name, file_content).html_safe GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
rescue RuntimeError
simple_format(file_content)
end
def markup?(filename)
Gitlab::MarkdownHelper.markup?(filename)
end
def gitlab_markdown?(filename)
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
end end
def spinner(text = nil, visible = false) def spinner(text = nil, visible = false)
......
...@@ -232,4 +232,16 @@ module CommitsHelper ...@@ -232,4 +232,16 @@ module CommitsHelper
def diff_file_mode_changed?(diff) def diff_file_mode_changed?(diff)
diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
end end
def unfold_bottom_class(bottom)
(bottom) ? 'js-unfold-bottom' : ''
end
def view_file_btn(commit_sha, diff, project)
link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
class: 'btn btn-small view-file js-view-file' do
raw('View file @') + content_tag(:span, commit_sha[0..6],
class: 'commit-short-id')
end
end
end end
...@@ -179,7 +179,11 @@ module GitlabMarkdownHelper ...@@ -179,7 +179,11 @@ module GitlabMarkdownHelper
if @commit if @commit
@commit.id @commit.id
elsif @repository && !@repository.empty? elsif @repository && !@repository.empty?
@repository.head_commit.sha if @ref
@repository.commit(@ref).try(:sha)
else
@repository.head_commit.sha
end
end end
end end
......
module LabelsHelper module LabelsHelper
def issue_label_names def project_label_names
@project.issues_labels.map(&:name) @project.labels.pluck(:title)
end end
def labels_autocomplete_source def render_colored_label(label)
labels = @project.issues_labels label_color = label.color || Label::DEFAULT_COLOR
labels = labels.map{ |l| { label: l.name, value: l.name } } text_color = text_color_for_bg(label_color)
labels.to_json
content_tag :span, class: 'label color-label', style: "background:#{label_color};color:#{text_color}" do
label.name
end
end
def suggested_colors
[
'#d9534f',
'#f0ad4e',
'#428bca',
'#5cb85c',
'#34495e',
'#7f8c8d',
'#8e44ad',
'#FFECDB'
]
end end
def label_css_class(name) def text_color_for_bg(bg_color)
klass = Gitlab::IssuesLabels r, g, b = bg_color.slice(1,7).scan(/.{2}/).map(&:hex)
case name.downcase if (r + g + b) > 500
when *klass.warning_labels "#333"
'label-warning'
when *klass.neutral_labels
'label-primary'
when *klass.positive_labels
'label-success'
when *klass.important_labels
'label-danger'
else else
'label-info' "#FFF"
end end
end end
end end
...@@ -122,6 +122,40 @@ module ProjectsHelper ...@@ -122,6 +122,40 @@ module ProjectsHelper
options_for_select(values, current_tracker) options_for_select(values, current_tracker)
end end
def link_to_toggle_star(title, starred, signed_in)
cls = 'btn btn-block'
cls += ' disabled' unless signed_in
toggle_html = content_tag('span', class: 'toggle') do
toggle_text = if starred
'Unstar'
else
'Star'
end
content_tag('i', ' ', class: 'icon-star') + toggle_text
end
count_html = content_tag('span', class: 'count') do
@project.star_count.to_s
end
link_opts = {
title: title,
class: cls,
method: :post,
remote: true,
data: {type: 'json'}
}
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
link_to toggle_star_project_path(@project), link_opts do
toggle_html + count_html
end
end
end
private private
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
......
...@@ -21,6 +21,16 @@ module TreeHelper ...@@ -21,6 +21,16 @@ module TreeHelper
tree.html_safe tree.html_safe
end end
def render_readme(readme)
if gitlab_markdown?(readme.name)
preserve(markdown(readme.data))
elsif markup?(readme.name)
render_markup(readme.name, readme.data)
else
simple_format(readme.data)
end
end
# Return an image icon depending on the file type # Return an image icon depending on the file type
# #
# type - String type of the tree item; either 'folder' or 'file' # type - String type of the tree item; either 'folder' or 'file'
...@@ -38,24 +48,6 @@ module TreeHelper ...@@ -38,24 +48,6 @@ module TreeHelper
"file_#{hexdigest(content.name)}" "file_#{hexdigest(content.name)}"
end end
# Public: Determines if a given filename is compatible with GitHub::Markup.
#
# filename - Filename string to check
#
# Returns boolean
def markup?(filename)
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole
.mediawiki .rst .adoc .asciidoc .pod))
end
def gitlab_markdown?(filename)
filename.downcase.end_with?(*%w(.mdown .md .markdown))
end
def plain_text_readme? filename
filename =~ /^README(.txt)?$/i
end
# Simple shortcut to File.join # Simple shortcut to File.join
def tree_join(*args) def tree_join(*args)
File.join(*args) File.join(*args)
......
...@@ -49,9 +49,10 @@ module Emails ...@@ -49,9 +49,10 @@ module Emails
@updated_by = User.find updated_by_user_id @updated_by = User.find updated_by_user_id
@target_url = project_merge_request_url(@project, @merge_request) @target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}") set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id), mail_answer_thread(@merge_request,
to: recipient(recipient_id), from: sender(updated_by_user_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid}) #{@mr_status}")) to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid}) #{@mr_status}"))
end end
end end
......
module Emails module Emails
module Profile module Profile
def new_user_email(user_id, password) def new_user_email(user_id, password, token = nil)
@user = User.find(user_id) @user = User.find(user_id)
@password = password @password = password
@target_url = user_url(@user) @target_url = user_url(@user)
@token = token
mail(to: @user.email, subject: subject("Account was created for you")) mail(to: @user.email, subject: subject("Account was created for you"))
end end
......
...@@ -142,6 +142,7 @@ class Ability ...@@ -142,6 +142,7 @@ class Ability
:write_wiki, :write_wiki,
:modify_issue, :modify_issue,
:admin_issue, :admin_issue,
:admin_label,
:push_code :push_code
] ]
end end
......
...@@ -13,6 +13,8 @@ module Issuable ...@@ -13,6 +13,8 @@ module Issuable
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
belongs_to :milestone belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
...@@ -131,4 +133,16 @@ module Issuable ...@@ -131,4 +133,16 @@ module Issuable
object_attributes: self.attributes object_attributes: self.attributes
} }
end end
def label_names
labels.order('title ASC').pluck(:title)
end
def add_labels_by_names(label_names)
label_names.each do |label_name|
label = project.labels.create_with(
color: Label::DEFAULT_COLOR).find_or_create_by(title: label_name.strip)
self.labels << label
end
end
end end
...@@ -70,6 +70,12 @@ class Event < ActiveRecord::Base ...@@ -70,6 +70,12 @@ class Event < ActiveRecord::Base
author_id: user.id author_id: user.id
) )
end end
def reset_event_cache_for(target)
Event.where(target_id: target.id, target_type: target.class.to_s).
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
end end
def proper? def proper?
......
...@@ -84,4 +84,20 @@ class Group < Namespace ...@@ -84,4 +84,20 @@ class Group < Namespace
def public_profile? def public_profile?
projects.public_only.any? projects.public_only.any?
end end
class << self
def search(query)
where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")
end
def sort(method)
case method.to_s
when "newest" then reorder("namespaces.created_at DESC")
when "oldest" then reorder("namespaces.created_at ASC")
when "recently_updated" then reorder("namespaces.updated_at DESC")
when "last_updated" then reorder("namespaces.updated_at ASC")
else reorder("namespaces.path, namespaces.name ASC")
end
end
end
end end
...@@ -32,9 +32,6 @@ class Issue < ActiveRecord::Base ...@@ -32,9 +32,6 @@ class Issue < ActiveRecord::Base
scope :of_group, ->(group) { where(project_id: group.project_ids) } scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
acts_as_taggable_on :labels
scope :cared, ->(user) { where(assignee_id: user) } scope :cared, ->(user) { where(assignee_id: user) }
scope :open_for, ->(user) { opened.assigned_to(user) } scope :open_for, ->(user) { opened.assigned_to(user) }
...@@ -67,8 +64,6 @@ class Issue < ActiveRecord::Base ...@@ -67,8 +64,6 @@ class Issue < ActiveRecord::Base
# Thus it will automatically generate a new fragment # Thus it will automatically generate a new fragment
# when the event is updated because the key changes. # when the event is updated because the key changes.
def reset_events_cache def reset_events_cache
Event.where(target_id: self.id, target_type: 'Issue'). Event.reset_event_cache_for(self)
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end end
end end
class Label < ActiveRecord::Base
DEFAULT_COLOR = '#428bca'
belongs_to :project
has_many :label_links, dependent: :destroy
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
validates :color,
format: { with: /\A\#[0-9A-Fa-f]{6}+\Z/ },
allow_blank: false
validates :project, presence: true
# Don't allow '?', '&', and ',' for label titles
validates :title,
presence: true,
format: { with: /\A[^&\?,&]*\z/ },
uniqueness: { scope: :project_id }
scope :order_by_name, -> { reorder("labels.title ASC") }
alias_attribute :name, :title
def open_issues_count
issues.opened.count
end
end
class LabelLink < ActiveRecord::Base
belongs_to :target, polymorphic: true
belongs_to :label
validates :target, presence: true
validates :label, presence: true
end
...@@ -44,12 +44,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -44,12 +44,9 @@ class MergeRequest < ActiveRecord::Base
# Temporary fields to store compare vars # Temporary fields to store compare vars
# when creating new merge request # when creating new merge request
attr_accessor :can_be_created, :compare_failed, :compare_base_commit, attr_accessor :can_be_created, :compare_failed,
:compare_commits, :compare_diffs :compare_commits, :compare_diffs
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :labels
state_machine :state, initial: :opened do state_machine :state, initial: :opened do
event :close do event :close do
transition [:reopened, :opened] => :closed transition [:reopened, :opened] => :closed
...@@ -287,9 +284,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -287,9 +284,7 @@ class MergeRequest < ActiveRecord::Base
# Thus it will automatically generate a new fragment # Thus it will automatically generate a new fragment
# when the event is updated because the key changes. # when the event is updated because the key changes.
def reset_events_cache def reset_events_cache
Event.where(target_id: self.id, target_type: 'MergeRequest'). Event.reset_event_cache_for(self)
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end end
def merge_commit_message def merge_commit_message
......
...@@ -83,11 +83,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -83,11 +83,7 @@ class MergeRequestDiff < ActiveRecord::Base
# Collect array of Git::Commit objects # Collect array of Git::Commit objects
# between target and source branches # between target and source branches
def unmerged_commits def unmerged_commits
commits = if merge_request.for_fork? commits = compare_result.commits
compare_action.commits
else
repository.commits_between(target_branch, source_branch)
end
if commits.present? if commits.present?
commits = Commit.decorate(commits). commits = Commit.decorate(commits).
...@@ -147,12 +143,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -147,12 +143,7 @@ class MergeRequestDiff < ActiveRecord::Base
# Collect array of Git::Diff objects # Collect array of Git::Diff objects
# between target and source branches # between target and source branches
def unmerged_diffs def unmerged_diffs
diffs = if merge_request.for_fork? diffs = compare_result.diffs
compare_action.diffs
else
Gitlab::Git::Diff.between(repository, source_branch, target_branch)
end
diffs ||= [] diffs ||= []
diffs diffs
rescue Gitlab::Git::Diff::TimeoutError => ex rescue Gitlab::Git::Diff::TimeoutError => ex
...@@ -166,13 +157,13 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -166,13 +157,13 @@ class MergeRequestDiff < ActiveRecord::Base
private private
def compare_action def compare_result
Gitlab::Satellite::CompareAction.new( @compare_result ||= CompareService.new.execute(
merge_request.author, merge_request.author,
merge_request.source_project,
merge_request.source_branch,
merge_request.target_project, merge_request.target_project,
merge_request.target_branch, merge_request.target_branch,
merge_request.source_project,
merge_request.source_branch
) )
end end
end end
...@@ -327,9 +327,7 @@ class Note < ActiveRecord::Base ...@@ -327,9 +327,7 @@ class Note < ActiveRecord::Base
# Thus it will automatically generate a new fragment # Thus it will automatically generate a new fragment
# when the event is updated because the key changes. # when the event is updated because the key changes.
def reset_events_cache def reset_events_cache
Event.where(target_id: self.id, target_type: 'Note'). Event.reset_event_cache_for(self)
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end end
def set_references def set_references
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
# visibility_level :integer default(0), not null # visibility_level :integer default(0), not null
# archived :boolean default(FALSE), not null # archived :boolean default(FALSE), not null
# import_status :string(255) # import_status :string(255)
# star_count :integer
# #
class Project < ActiveRecord::Base class Project < ActiveRecord::Base
...@@ -40,8 +41,7 @@ class Project < ActiveRecord::Base ...@@ -40,8 +41,7 @@ class Project < ActiveRecord::Base
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
ActsAsTaggableOn.strict_case_match = true ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :tags
acts_as_taggable_on :labels, :issues_default_labels
attr_accessor :new_default_branch attr_accessor :new_default_branch
...@@ -74,6 +74,7 @@ class Project < ActiveRecord::Base ...@@ -74,6 +74,7 @@ class Project < ActiveRecord::Base
# Merge requests from source project should be kept when source project was removed # Merge requests from source project should be kept when source project was removed
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy
has_many :labels, dependent: :destroy
has_many :services, dependent: :destroy has_many :services, dependent: :destroy
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
has_many :milestones, dependent: :destroy has_many :milestones, dependent: :destroy
...@@ -85,6 +86,8 @@ class Project < ActiveRecord::Base ...@@ -85,6 +86,8 @@ class Project < ActiveRecord::Base
has_many :users, through: :users_projects has_many :users, through: :users_projects
has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys_projects, dependent: :destroy
has_many :deploy_keys, through: :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects
has_many :users_star_projects, dependent: :destroy
has_many :starrers, through: :users_star_projects, source: :user
has_many :project_group_links, dependent: :destroy has_many :project_group_links, dependent: :destroy
has_many :invited_groups, through: :project_group_links, source: :group has_many :invited_groups, through: :project_group_links, source: :group
...@@ -114,6 +117,7 @@ class Project < ActiveRecord::Base ...@@ -114,6 +117,7 @@ class Project < ActiveRecord::Base
validates :import_url, validates :import_url,
format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" }, format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
if: :import? if: :import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
# Scopes # Scopes
...@@ -285,13 +289,6 @@ class Project < ActiveRecord::Base ...@@ -285,13 +289,6 @@ class Project < ActiveRecord::Base
self.id self.id
end end
# Tags are shared by issues and merge requests
def issues_labels
@issues_labels ||= (issues_default_labels +
merge_requests.tags_on(:labels) +
issues.tags_on(:labels)).uniq.sort_by(&:name)
end
def issue_exists?(issue_id) def issue_exists?(issue_id)
if used_default_issues_tracker? if used_default_issues_tracker?
self.issues.where(iid: issue_id).first.present? self.issues.where(iid: issue_id).first.present?
...@@ -508,6 +505,7 @@ class Project < ActiveRecord::Base ...@@ -508,6 +505,7 @@ class Project < ActiveRecord::Base
end end
def rename_repo def rename_repo
path_was = previous_changes['path'].first
old_path_with_namespace = File.join(namespace_dir, path_was) old_path_with_namespace = File.join(namespace_dir, path_was)
new_path_with_namespace = File.join(namespace_dir, path) new_path_with_namespace = File.join(namespace_dir, path)
...@@ -586,4 +584,12 @@ class Project < ActiveRecord::Base ...@@ -586,4 +584,12 @@ class Project < ActiveRecord::Base
def update_repository_size def update_repository_size
update_attribute(:repository_size, repository.size) update_attribute(:repository_size, repository.size)
end end
def forks_count
ForkedProjectLink.where(forked_from_project_id: self.id).count
end
def find_label(name)
labels.find_by(name: name)
end
end end
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
# #
class HipchatService < Service class HipchatService < Service
MAX_COMMITS = 3
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
def title def title
...@@ -57,15 +59,24 @@ class HipchatService < Service ...@@ -57,15 +59,24 @@ class HipchatService < Service
message = "" message = ""
message << "#{push[:user_name]} " message << "#{push[:user_name]} "
if before =~ /000000/ if before =~ /000000/
message << "pushed new branch <a href=\"#{project.web_url}/commits/#{ref}\">#{ref}</a> to <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a>\n" message << "pushed new branch <a href=\""\
"#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
" to <a href=\"#{project.web_url}\">"\
"#{project.name_with_namespace.gsub!(/\s/, "")}</a>\n"
elsif after =~ /000000/ elsif after =~ /000000/
message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n" message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n"
else else
message << "pushed to branch <a href=\"#{project.web_url}/commits/#{ref}\">#{ref}</a> " message << "pushed to branch <a href=\""\
"#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> "
message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> "
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
for commit in push[:commits] do
message << "<br /> - #{commit[:message]} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)" push[:commits].take(MAX_COMMITS).each do |commit|
message << "<br /> - #{commit[:message].lines.first} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)"
end
if push[:commits].count > MAX_COMMITS
message << "<br />... #{push[:commits].count - MAX_COMMITS} more commits"
end end
end end
......
...@@ -72,6 +72,15 @@ class ProjectWiki ...@@ -72,6 +72,15 @@ class ProjectWiki
end end
end end
def find_file(name, version = nil, try_on_disk = true)
version = wiki.ref if version.nil? # Gollum::Wiki#file ?
if wiki_file = wiki.file(name, version, try_on_disk)
wiki_file
else
nil
end
end
def create_page(title, content, format = :markdown, message = nil) def create_page(title, content, format = :markdown, message = nil)
commit = commit_details(:created, message, title) commit = commit_details(:created, message, title)
......
...@@ -10,8 +10,11 @@ class Repository ...@@ -10,8 +10,11 @@ class Repository
nil nil
end end
# Return absolute path to repository
def path_to_repo def path_to_repo
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git") @path_to_repo ||= File.expand_path(
File.join(Gitlab.config.gitlab_shell.repos_path, path_with_namespace + ".git")
)
end end
def exists? def exists?
...@@ -134,7 +137,7 @@ class Repository ...@@ -134,7 +137,7 @@ class Repository
def graph_log def graph_log
Rails.cache.fetch(cache_key(:graph_log)) do Rails.cache.fetch(cache_key(:graph_log)) do
stats = Gitlab::Git::GitStats.new(raw, root_ref) stats = Gitlab::Git::GitStats.new(raw, root_ref, Gitlab.config.git.timeout)
stats.parsed_log stats.parsed_log
end end
end end
...@@ -263,4 +266,20 @@ class Repository ...@@ -263,4 +266,20 @@ class Repository
contributor contributor
end end
end end
def blob_for_diff(commit, diff)
file = blob_at(commit.id, diff.new_path)
unless file
file = prev_blob_for_diff(commit, diff)
end
file
end
def prev_blob_for_diff(commit, diff)
if commit.parent_id
blob_at(commit.parent_id, diff.old_path)
end
end
end end
class Tree class Tree
include Gitlab::MarkdownHelper
attr_accessor :entries, :readme, :contribution_guide attr_accessor :entries, :readme, :contribution_guide
def initialize(repository, sha, path = '/') def initialize(repository, sha, path = '/')
...@@ -6,7 +8,23 @@ class Tree ...@@ -6,7 +8,23 @@ class Tree
git_repo = repository.raw_repository git_repo = repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, sha, path) @entries = Gitlab::Git::Tree.where(git_repo, sha, path)
if readme_tree = @entries.find(&:readme?) available_readmes = @entries.select(&:readme?)
if available_readmes.count > 0
# If there is more than 1 readme in tree, find readme which is supported
# by markup renderer.
if available_readmes.length > 1
supported_readmes = available_readmes.select do |readme|
gitlab_markdown?(readme.name) || markup?(readme.name)
end
# Take the first supported readme, or the first available readme, if we
# don't support any of them
readme_tree = supported_readmes.first || available_readmes.first
else
readme_tree = available_readmes.first
end
readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name) readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
@readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path) @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path)
end end
......
...@@ -91,6 +91,8 @@ class User < ActiveRecord::Base ...@@ -91,6 +91,8 @@ class User < ActiveRecord::Base
has_many :personal_projects, through: :namespace, source: :projects has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects has_many :projects, through: :users_projects
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy
has_many :starred_projects, through: :users_star_projects, source: :project
has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet" has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy has_many :users_projects, dependent: :destroy
...@@ -241,6 +243,15 @@ class User < ActiveRecord::Base ...@@ -241,6 +243,15 @@ class User < ActiveRecord::Base
end end
end end
def generate_reset_token
@reset_token, enc = Devise.token_generator.generate(self.class, :reset_password_token)
self.reset_password_token = enc
self.reset_password_sent_at = Time.now.utc
@reset_token
end
def namespace_uniq def namespace_uniq
namespace_name = self.username namespace_name = self.username
if Namespace.find_by(path: namespace_name) if Namespace.find_by(path: namespace_name)
...@@ -492,7 +503,7 @@ class User < ActiveRecord::Base ...@@ -492,7 +503,7 @@ class User < ActiveRecord::Base
def post_create_hook def post_create_hook
log_info("User \"#{self.name}\" (#{self.email}) was created") log_info("User \"#{self.name}\" (#{self.email}) was created")
notification_service.new_user(self) notification_service.new_user(self, @reset_token)
system_hook_service.execute_hooks_for(self, :create) system_hook_service.execute_hooks_for(self, :create)
end end
...@@ -516,4 +527,18 @@ class User < ActiveRecord::Base ...@@ -516,4 +527,18 @@ class User < ActiveRecord::Base
def admin_unsubscribe! def admin_unsubscribe!
update_column :admin_email_unsubscribed_at, Time.now update_column :admin_email_unsubscribed_at, Time.now
end end
def starred?(project)
starred_projects.exists?(project)
end
def toggle_star(project)
user_star_project = users_star_projects.
where(project: project, user: self).take
if user_star_project
user_star_project.destroy
else
UsersStarProject.create!(project: project, user: self)
end
end
end end
# == Schema Information
#
# Table name: users_star_projects
#
# id :integer not null, primary key
# starrer_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
#
class UsersStarProject < ActiveRecord::Base
belongs_to :project, counter_cache: :star_count
belongs_to :user
validates :user, presence: true
validates :user_id, uniqueness: { scope: [:project_id] }
validates :project, presence: true
end
class ArchiveRepositoryService
def execute(project, ref, format)
storage_path = Gitlab.config.gitlab.repository_downloads_path
unless File.directory?(storage_path)
FileUtils.mkdir_p(storage_path)
end
format ||= 'tar.gz'
repository = project.repository
repository.clean_old_archives
repository.archive_repo(ref, storage_path, format.downcase)
end
end
# Compare 2 branches for one repo or between repositories
# and return Gitlab::CompareResult object that responds to commits and diffs
class CompareService
def execute(current_user, source_project, source_branch, target_project, target_branch)
# Try to compare branches to get commits list and diffs
#
# Note: Use satellite only when need to compare between to repos
# because satellites are slower then operations on bare repo
if target_project == source_project
Gitlab::CompareResult.new(
Gitlab::Git::Compare.new(
target_project.repository.raw_repository,
target_branch,
source_branch,
)
)
else
Gitlab::Satellite::CompareAction.new(
current_user,
target_project,
target_branch,
source_project,
source_branch
).result
end
end
end
...@@ -37,7 +37,7 @@ module Files ...@@ -37,7 +37,7 @@ module Files
if created_successfully if created_successfully
success success
else else
error("Your changes could not be committed, because the file has been changed") error("Your changes could not be committed. Maybe the file was changed by another process or there was nothing to commit?")
end end
end end
end end
......
module Issues module Issues
class CreateService < Issues::BaseService class CreateService < Issues::BaseService
def execute def execute
issue = project.issues.new(params) label_params = params[:label_ids]
issue = project.issues.new(params.except(:label_ids))
issue.author = current_user issue.author = current_user
if issue.save if issue.save
issue.update_attributes(label_ids: label_params)
notification_service.new_issue(issue, current_user) notification_service.new_issue(issue, current_user)
event_service.open_issue(issue, current_user) event_service.open_issue(issue, current_user)
issue.create_cross_references!(issue.project, current_user) issue.create_cross_references!(issue.project, current_user)
......
...@@ -22,27 +22,25 @@ module MergeRequests ...@@ -22,27 +22,25 @@ module MergeRequests
# Set MR description based on project template # Set MR description based on project template
merge_request.description = merge_request.target_project.merge_requests_template merge_request.description = merge_request.target_project.merge_requests_template
# Try to compare branches to get commits list and diffs compare_result = CompareService.new.execute(
compare_action = Gitlab::Satellite::CompareAction.new(
current_user, current_user,
merge_request.source_project,
merge_request.source_branch,
merge_request.target_project, merge_request.target_project,
merge_request.target_branch, merge_request.target_branch,
merge_request.source_project,
merge_request.source_branch
) )
commits = compare_action.commits commits = compare_result.commits
# At this point we decide if merge request can be created # At this point we decide if merge request can be created
# If we have at least one commit to merge -> creation allowed # If we have at least one commit to merge -> creation allowed
if commits.present? if commits.present?
merge_request.compare_commits = Commit.decorate(commits) merge_request.compare_commits = Commit.decorate(commits)
merge_request.compare_base_commit = Commit.new(commits.first)
merge_request.can_be_created = true merge_request.can_be_created = true
merge_request.compare_failed = false merge_request.compare_failed = false
# Try to collect diff for merge request. # Try to collect diff for merge request.
diffs = compare_action.diffs diffs = compare_result.diffs
if diffs.present? if diffs.present?
merge_request.compare_diffs = diffs merge_request.compare_diffs = diffs
......
module MergeRequests module MergeRequests
class CreateService < MergeRequests::BaseService class CreateService < MergeRequests::BaseService
def execute def execute
merge_request = MergeRequest.new(params) label_params = params[:label_ids]
merge_request = MergeRequest.new(params.except(:label_ids))
merge_request.source_project = project merge_request.source_project = project
merge_request.target_project ||= project merge_request.target_project ||= project
merge_request.author = current_user merge_request.author = current_user
if merge_request.save if merge_request.save
merge_request.update_attributes(label_ids: label_params)
event_service.open_mr(merge_request, current_user) event_service.open_mr(merge_request, current_user)
notification_service.new_merge_request(merge_request, current_user) notification_service.new_merge_request(merge_request, current_user)
merge_request.create_cross_references!(merge_request.project, current_user) merge_request.create_cross_references!(merge_request.project, current_user)
......
...@@ -105,9 +105,9 @@ class NotificationService ...@@ -105,9 +105,9 @@ class NotificationService
end end
# Notify new user with email after creation # Notify new user with email after creation
def new_user(user) def new_user(user, token = nil)
# Don't email omniauth created users # Don't email omniauth created users
mailer.new_user_email(user.id, user.password) unless user.extern_uid? mailer.new_user_email(user.id, user.password, token) unless user.extern_uid?
end end
# Notify users on new note in system # Notify users on new note in system
......
...@@ -57,7 +57,6 @@ module Projects ...@@ -57,7 +57,6 @@ module Projects
:add_repository, :add_repository,
@project.path_with_namespace @project.path_with_namespace
) )
end end
if @project.wiki_enabled? if @project.wiki_enabled?
......
...@@ -12,7 +12,13 @@ module Search ...@@ -12,7 +12,13 @@ module Search
return result unless query.present? return result unless query.present?
if params[:search_code].present? if params[:search_code].present?
blobs = project.repository.search_files(query, params[:repository_ref]) unless project.empty_repo? if !@project.empty_repo?
blobs = project.repository.search_files(query,
params[:repository_ref])
else
blobs = Array.new
end
blobs = Kaminari.paginate_array(blobs).page(params[:page]).per(20) blobs = Kaminari.paginate_array(blobs).page(params[:page]).per(20)
result[:blobs] = blobs result[:blobs] = blobs
result[:total_results] = blobs.total_count result[:total_results] = blobs.total_count
......
...@@ -2,5 +2,8 @@ class TestHookService ...@@ -2,5 +2,8 @@ class TestHookService
def execute(hook, current_user) def execute(hook, current_user)
data = GitPushService.new.sample_data(hook.project, current_user) data = GitPushService.new.sample_data(hook.project, current_user)
hook.execute(data) hook.execute(data)
true
rescue SocketError
false
end end
end end
...@@ -31,9 +31,9 @@ ...@@ -31,9 +31,9 @@
= f.label :password, class: 'control-label' = f.label :password, class: 'control-label'
.col-sm-10 .col-sm-10
%strong %strong
A temporary password will be generated and sent to user. Reset link will be generated and sent to the user.
%br %br
User will be forced to change it after first sign in User will be forced to set the password on first sign in.
- else - else
%fieldset %fieldset
%legend Password %legend Password
......
...@@ -44,12 +44,12 @@ ...@@ -44,12 +44,12 @@
- if @labels.present? - if @tags.present?
%fieldset %fieldset
%legend Labels %legend Tags
%ul.nav.nav-pills.nav-stacked.nav-small %ul.nav.nav-pills.nav-stacked.nav-small
- @labels.each do |label| - @tags.each do |tag|
%li{ class: (label.name == params[:label]) ? 'active' : 'light' } %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
= link_to projects_dashboard_filter_path(scope: params[:scope], label: label.name) do = link_to projects_dashboard_filter_path(scope: params[:scope], tag: tag.name) do
%i.icon-tag %i.icon-tag
= label.name = tag.name
...@@ -46,5 +46,5 @@ ...@@ -46,5 +46,5 @@
%br %br
Public projects are an easy way to allow everyone to have read-only access. Public projects are an easy way to allow everyone to have read-only access.
.link_holder .link_holder
= link_to public_projects_path, class: "btn btn-new" do = link_to explore_projects_path, class: "btn btn-new" do
Browse public projects » Browse public projects »
...@@ -54,10 +54,10 @@ ...@@ -54,10 +54,10 @@
%span.label %span.label
%i.icon-archive %i.icon-archive
Archived Archived
- project.labels.each do |label| - project.tags.each do |tag|
%span.label.label-info %span.label.label-info
%i.icon-tag %i.icon-tag
= label.name = tag.name
- if project.description.present? - if project.description.present?
%p= truncate project.description, length: 100 %p= truncate project.description, length: 100
.last-activity .last-activity
......
.clearfix
.pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search"
.form-group
= submit_tag 'Search', class: "btn btn-primary wide"
.pull-right
.dropdown.inline
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
= @sort.humanize
- else
Name
%b.caret
%ul.dropdown-menu
%li
= link_to explore_groups_path(sort: nil) do
Name
= link_to explore_groups_path(sort: 'newest') do
Newest
= link_to explore_groups_path(sort: 'oldest') do
Oldest
= link_to explore_groups_path(sort: 'recently_updated') do
Recently updated
= link_to explore_groups_path(sort: 'last_updated') do
Last updated
%hr
%ul.bordered-list
- @groups.each do |group|
%li
.clearfix
%h4
= link_to group_path(id: group.path) do
%i.icon-group
= group.name
.clearfix
%p
= truncate group.description, length: 150
.clearfix
%p.light
#{pluralize(group.members.size, 'member')}, #{pluralize(group.projects.count, 'project')}
- unless @groups.present?
.nothing-here-block No public groups
= paginate @groups, theme: "gitlab"
%li
%h4.project-title
.project-access-icon
= visibility_level_icon(project.visibility_level)
= link_to project.name_with_namespace, project
- if current_page?(starred_explore_projects_path)
%strong.pull-right
= pluralize project.star_count, 'star'
- if project.description.present?
%p.project-description.str-truncated
= project.description
.repo-info
- unless project.empty_repo?
= link_to pluralize(project.repository.round_commit_count, 'commit'), project_commits_path(project, project.default_branch)
&middot;
= link_to pluralize(project.repository.branch_names.count, 'branch'), project_branches_path(project)
&middot;
= link_to pluralize(project.repository.tag_names.count, 'tag'), project_tags_path(project)
- else
%i.icon-warning-sign
Empty repository
%h3.page-title
Projects (#{@projects.total_count})
.light
You can browse public projects in read-only mode until signed in.
%hr
.clearfix .clearfix
.pull-left .pull-left
= form_tag public_projects_path, method: :get, class: 'form-inline form-tiny' do |f| = form_tag explore_projects_path, method: :get, class: 'form-inline form-tiny' do |f|
.form-group .form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search" = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "projects_search"
.form-group .form-group
...@@ -22,46 +17,21 @@ ...@@ -22,46 +17,21 @@
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu
%li %li
= link_to public_projects_path(sort: nil) do = link_to explore_projects_path(sort: nil) do
Name Name
= link_to public_projects_path(sort: 'newest') do = link_to explore_projects_path(sort: 'newest') do
Newest Newest
= link_to public_projects_path(sort: 'oldest') do = link_to explore_projects_path(sort: 'oldest') do
Oldest Oldest
= link_to public_projects_path(sort: 'recently_updated') do = link_to explore_projects_path(sort: 'recently_updated') do
Recently updated Recently updated
= link_to public_projects_path(sort: 'last_updated') do = link_to explore_projects_path(sort: 'last_updated') do
Last updated Last updated
%hr %hr
.public-projects .public-projects
%ul.bordered-list.top-list %ul.bordered-list.top-list
- @projects.each do |project| = render @projects
%li
%h4
= link_to project_path(project) do
= project.name_with_namespace
- if project.internal?
%small.access-icon
= internal_icon
Internal
.pull-right.hidden-sm.hidden-xs
%pre.public-clone git clone #{project.http_url_to_repo}
- if project.description.present?
%p
= project.description
.repo-info
- unless project.empty_repo?
= link_to pluralize(project.repository.round_commit_count, 'commit'), project_commits_path(project, project.default_branch)
&middot;
= link_to pluralize(project.repository.branch_names.count, 'branch'), project_branches_path(project)
&middot;
= link_to pluralize(project.repository.tag_names.count, 'tag'), project_tags_path(project)
- else
%i.icon-warning-sign
Empty repository
- unless @projects.present? - unless @projects.present?
.nothing-here-block No public projects .nothing-here-block No public projects
......
.explore-trending-block
%p.lead
%i.icon-comments-alt
See most starred projects
%hr
.public-projects
%ul.bordered-list
= render @starred_projects
= paginate @starred_projects, theme: 'gitlab'
.explore-trending-block
%p.lead
%i.icon-comments-alt
See most discussed projects for last month
%hr
.public-projects
%ul.bordered-list
= render @trending_projects
.center
= link_to 'Show all projects', explore_projects_path, class: 'btn btn-primary'
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
'data-original-title' => 'Help' do 'data-original-title' => 'Help' do
%i.icon-question-sign %i.icon-question-sign
%li %li
= link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do = link_to explore_root_path, title: "Explore", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.icon-globe %i.icon-globe
%li %li
= link_to user_snippets_path(current_user), title: "My snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'My snippets' do = link_to user_snippets_path(current_user), title: "My snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'My snippets' do
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.container .container
%div.app_logo %div.app_logo
%span.separator %span.separator
= link_to public_root_path, class: "home" do = link_to explore_root_path, class: "home" do
%h1 GITLAB %h1 GITLAB
%span.separator %span.separator
%h1.title= title %h1.title= title
...@@ -13,10 +13,10 @@ ...@@ -13,10 +13,10 @@
%i.icon-reorder %i.icon-reorder
.pull-right.hidden-xs .pull-right.hidden-xs
= link_to "Sign in", new_session_path(:user), class: 'btn btn-sign-in btn-new' = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new'
.navbar-collapse.collapse .navbar-collapse.collapse
%ul.nav.navbar-nav %ul.nav.navbar-nav
%li.visible-xs %li.visible-xs
= link_to "Sign in", new_session_path(:user) = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
.brand_text .brand_text
= brand_text = brand_text
- else - else
.brand-image.hidden-sm.hidden-xs .brand-image.default-brand-image.hidden-sm.hidden-xs
= image_tag 'brand_logo.png' = image_tag 'brand_logo.png'
.brand_text.hidden-xs .brand_text.hidden-xs
%h2 Open source software to collaborate on code %h2 Open source software to collaborate on code
...@@ -33,6 +33,6 @@ ...@@ -33,6 +33,6 @@
%hr %hr
.container .container
.footer-links .footer-links
= link_to "Explore public projects", public_projects_path = link_to "Explore", explore_root_path
= link_to "Documentation", "http://doc.gitlab.com/" = link_to "Documentation", "http://doc.gitlab.com/"
= link_to "About GitLab", "https://about.gitlab.com/" = link_to "About GitLab", "https://about.gitlab.com/"
- page_title = 'Explore'
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: page_title
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- if current_user
= render "layouts/head_panel", title: page_title
- else
= render "layouts/public_head_panel", title: page_title
.container.navless-container
.content
.explore-title
%h3
Explore GitLab
%p.lead
Discover projects and groups. Share your projects with others
%ul.nav.nav-tabs
= nav_link(path: 'projects#trending') do
= link_to 'Trending Projects', explore_root_path
= nav_link(path: 'projects#starred') do
= link_to 'Most Starred Projects', starred_explore_projects_path
= nav_link(path: 'projects#index') do
= link_to 'All Projects', explore_projects_path
= nav_link(controller: :groups) do
= link_to 'All Groups', explore_groups_path
= yield
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Public Projects"
%body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- if current_user
= render "layouts/head_panel", title: "Public Projects"
- else
= render "layouts/public_head_panel", title: "Public Projects"
.container.navless-container
.content= yield
...@@ -11,11 +11,4 @@ ...@@ -11,11 +11,4 @@
- if @user.created_by_id - if @user.created_by_id
%p %p
password.................................. = link_to "Click here to set your password", edit_password_url(@user, :reset_password_token => @token)
%code= @password
%p
You will be forced to change this password immediately after login.
%p
= link_to "Click here to login", root_url
...@@ -4,10 +4,5 @@ The Administrator created an account for you. Now you are a member of the compan ...@@ -4,10 +4,5 @@ The Administrator created an account for you. Now you are a member of the compan
login.................. <%= @user.email %> login.................. <%= @user.email %>
<% if @user.created_by_id %> <% if @user.created_by_id %>
password............... <%= @password %> <%= link_to "Click here to set your password", edit_password_url(@user, :reset_password_token => @token) %>
You will be forced to change this password immediately after login.
<% end %> <% end %>
Click here to login: <%= url_for(root_url) %>
...@@ -25,6 +25,4 @@ ...@@ -25,6 +25,4 @@
%br %br
- if @compare.timeout - if @compare.timeout
%h5 To prevent performance issues changes are hidden %h5 Huge diff. To prevent performance issues changes are hidden
- elsif @compare.commits_over_limit?
%h5 Changes are not shown due to large amount of commits
...@@ -23,5 +23,3 @@ Changes: ...@@ -23,5 +23,3 @@ Changes:
\ \
- if @compare.timeout - if @compare.timeout
Huge diff. To prevent performance issues it was hidden Huge diff. To prevent performance issues it was hidden
- elsif @compare.commits_over_limit?
Changes are not shown due to large amount of commits
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
- @user.errors.full_messages.each do |msg| - @user.errors.full_messages.each do |msg|
%li= msg %li= msg
.form-group
= f.label :current_password, class: 'control-label'
.col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.form-group .form-group
= f.label :password, class: 'control-label' = f.label :password, class: 'control-label'
.col-sm-10= f.password_field :password, required: true, class: 'form-control' .col-sm-10= f.password_field :password, required: true, class: 'form-control'
......
...@@ -27,9 +27,9 @@ ...@@ -27,9 +27,9 @@
= link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do
= readme.name = readme.name
- unless empty_repo .col-md-5
.col-md-5 .project-home-links
.project-home-links - unless empty_repo
= link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), project_commits_path(@project, @ref || @repository.root_ref) = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
= link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), project_branches_path(@project) = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), project_branches_path(@project)
= link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), project_tags_path(@project) = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), project_tags_path(@project)
......
- if @lines.present?
- if @form.unfold? && @form.since != 1 && !@form.bottom?
%tr.line_holder{ id: @form.since }
= render "projects/commits/diffs/match_line", {line: @match_line,
line_old: @form.since, line_new: @form.since, bottom: false}
- @lines.each_with_index do |line, index|
- line_new = index + @form.since
- line_old = line_new - @form.offset
%tr.line_holder
%td.old_line.diff-line-num{data: {linenumber: line_old}}
= link_to raw(line_old), "#"
%td.new_line= link_to raw(line_new) , "#"
%td.line_content.noteable_line= line
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to }
= render "projects/commits/diffs/match_line", {line: @match_line,
line_old: @form.to, line_new: @form.to, bottom: true}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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