Commit 93f0a8c9 authored by David Barri's avatar David Barri

Merge remote-tracking branch 'gitlabhq/master' into git_commit_fix

Conflicts:
	doc/install/installation.md
parents 944d3823 3c7806c3
...@@ -4,7 +4,7 @@ env: ...@@ -4,7 +4,7 @@ env:
before_install: before_install:
- sudo apt-get install libicu-dev -y - sudo apt-get install libicu-dev -y
- sudo apt-get install libqt4-dev libqtwebkit-dev -y - sudo apt-get install libqt4-dev libqtwebkit-dev -y
- gem install charlock_holmes -v="0.6.8" - gem install charlock_holmes -v="0.6.9"
branches: branches:
only: only:
- 'master' - 'master'
......
...@@ -28,7 +28,7 @@ v 3.0.0 ...@@ -28,7 +28,7 @@ v 3.0.0
- Reject ssh keys that break gitolite - Reject ssh keys that break gitolite
- [API] list one project hook - [API] list one project hook
- [API] edit project hook - [API] edit project hook
- [API] add project snippets list - [API] list project snippets
- [API] allow to authorize using private token in HTTP header - [API] allow to authorize using private token in HTTP header
- [API] add user creation - [API] add user creation
......
## Contribute to GitLab ## Contribute to GitLab
If you want to contribute to GitLab, follow this process: If you want to contribute to GitLab, follow this process:
...@@ -7,24 +7,20 @@ If you want to contribute to GitLab, follow this process: ...@@ -7,24 +7,20 @@ If you want to contribute to GitLab, follow this process:
3. Code 3. Code
4. Create a pull request 4. Create a pull request
We will only accept pull requests if: We will only accept pull requests if:
* Your code has proper tests and all tests pass * Your code has proper tests and all tests pass
* Your code can be merged w/o problems * Your code can be merged w/o problems
* It won't break existing functionality * It won't break existing functionality
* It's quality code * It's quality code
* We like it :) * We like it :)
## [You may need a developer VM](https://github.com/gitlabhq/developer-vm) For examples of feedback on pull requests please look at the [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
## Running tests ## Installation
To run the specs for GitLab, you need to run seeds for test db.
cd gitlabhq Install the Gitlab development in a virtual machine with the [Gitlab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm). Installing it in a virtual machine makes it much easier to set up all the dependencies for integration testing.
rake db:seed_fu RAILS_ENV=test
Then you can run the test suite with rake: ## Running tests
rake gitlab:test
For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
...@@ -11,9 +11,9 @@ end ...@@ -11,9 +11,9 @@ end
gem "rails", "3.2.8" gem "rails", "3.2.8"
# Supported DBs # Supported DBs
gem "sqlite3", :group => :sqlite gem "sqlite3", group: :sqlite
gem "mysql2", :group => :mysql gem "mysql2", group: :mysql
gem "pg", :group => :postgres gem "pg", group: :postgres
# Auth # Auth
gem "devise", "~> 2.1.0" gem "devise", "~> 2.1.0"
...@@ -23,10 +23,11 @@ gem 'omniauth-twitter' ...@@ -23,10 +23,11 @@ gem 'omniauth-twitter'
gem 'omniauth-github' gem 'omniauth-github'
# GITLAB patched libs # GITLAB patched libs
gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837'
gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e" gem "omniauth-ldap", git: "https://github.com/gitlabhq/omniauth-ldap.git", ref: 'f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e'
gem 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.git" gem 'yaml_db', git: "https://github.com/gitlabhq/yaml_db.git", ref: '98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd'
gem 'grack', :git => "https://github.com/gitlabhq/grack.git" gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '212fd40bea61f3c6a167223768e7295dc32bbc10'
# Gitolite client (for work with gitolite-admin repo) # Gitolite client (for work with gitolite-admin repo)
gem "gitolite", '1.1.0' gem "gitolite", '1.1.0'
...@@ -35,7 +36,7 @@ gem "gitolite", '1.1.0' ...@@ -35,7 +36,7 @@ gem "gitolite", '1.1.0'
gem "pygments.rb", "0.3.1" gem "pygments.rb", "0.3.1"
# Language detection # Language detection
gem "github-linguist", "~> 2.3.4" , :require => "linguist" gem "github-linguist", "~> 2.3.4" , require: "linguist"
# API # API
gem "grape", "~> 0.2.1" gem "grape", "~> 0.2.1"
...@@ -83,9 +84,6 @@ gem 'resque_mailer' ...@@ -83,9 +84,6 @@ gem 'resque_mailer'
# HTTP requests # HTTP requests
gem "httparty" gem "httparty"
# Handle encodings
gem "charlock_holmes"
# Colored output to console # Colored output to console
gem "colored" gem "colored"
...@@ -114,8 +112,9 @@ group :assets do ...@@ -114,8 +112,9 @@ group :assets do
end end
group :development do group :development do
gem "annotate", git: "https://github.com/ctran/annotate_models.git"
gem "letter_opener" gem "letter_opener"
gem "annotate", :git => "https://github.com/ctran/annotate_models.git" gem 'quiet_assets', '1.0.1'
gem 'rack-mini-profiler' gem 'rack-mini-profiler'
end end
...@@ -137,13 +136,13 @@ group :development, :test do ...@@ -137,13 +136,13 @@ group :development, :test do
gem 'guard-spinach' gem 'guard-spinach'
# Notification # Notification
gem 'rb-fsevent', :require => darwin_only('rb-fsevent') gem 'rb-fsevent', require: darwin_only('rb-fsevent')
gem 'growl', :require => darwin_only('growl') gem 'growl', require: darwin_only('growl')
gem 'rb-inotify', :require => linux_only('rb-inotify') gem 'rb-inotify', require: linux_only('rb-inotify')
end end
group :test do group :test do
gem "simplecov", :require => false gem "simplecov", require: false
gem "shoulda-matchers" gem "shoulda-matchers"
gem 'email_spec' gem 'email_spec'
gem 'resque_spec' gem 'resque_spec'
......
...@@ -7,6 +7,7 @@ GIT ...@@ -7,6 +7,7 @@ GIT
GIT GIT
remote: https://github.com/gitlabhq/grack.git remote: https://github.com/gitlabhq/grack.git
revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8 revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
specs: specs:
grack (1.0.0) grack (1.0.0)
rack (~> 1.4.1) rack (~> 1.4.1)
...@@ -21,6 +22,14 @@ GIT ...@@ -21,6 +22,14 @@ GIT
mime-types (~> 1.15) mime-types (~> 1.15)
posix-spawn (~> 0.3.6) posix-spawn (~> 0.3.6)
GIT
remote: https://github.com/gitlabhq/grit_ext.git
revision: 212fd40bea61f3c6a167223768e7295dc32bbc10
ref: 212fd40bea61f3c6a167223768e7295dc32bbc10
specs:
grit_ext (0.6.0)
charlock_holmes (~> 0.6.9)
GIT GIT
remote: https://github.com/gitlabhq/omniauth-ldap.git remote: https://github.com/gitlabhq/omniauth-ldap.git
revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
...@@ -35,6 +44,7 @@ GIT ...@@ -35,6 +44,7 @@ GIT
GIT GIT
remote: https://github.com/gitlabhq/yaml_db.git remote: https://github.com/gitlabhq/yaml_db.git
revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
ref: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
specs: specs:
yaml_db (0.2.2) yaml_db (0.2.2)
...@@ -90,7 +100,7 @@ GEM ...@@ -90,7 +100,7 @@ GEM
carrierwave (0.6.2) carrierwave (0.6.2)
activemodel (>= 3.2.0) activemodel (>= 3.2.0)
activesupport (>= 3.2.0) activesupport (>= 3.2.0)
charlock_holmes (0.6.8) charlock_holmes (0.6.9)
childprocess (0.3.2) childprocess (0.3.2)
ffi (~> 1.0.6) ffi (~> 1.0.6)
chosen-rails (0.9.8.3) chosen-rails (0.9.8.3)
...@@ -260,6 +270,8 @@ GEM ...@@ -260,6 +270,8 @@ GEM
posix-spawn (~> 0.3.6) posix-spawn (~> 0.3.6)
yajl-ruby (~> 1.1.0) yajl-ruby (~> 1.1.0)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
quiet_assets (1.0.1)
railties (~> 3.1)
rack (1.4.1) rack (1.4.1)
rack-cache (1.2) rack-cache (1.2)
rack (>= 0.4) rack (>= 0.4)
...@@ -411,7 +423,6 @@ DEPENDENCIES ...@@ -411,7 +423,6 @@ DEPENDENCIES
capybara capybara
capybara-webkit capybara-webkit
carrierwave carrierwave
charlock_holmes
chosen-rails chosen-rails
coffee-rails (= 3.2.2) coffee-rails (= 3.2.2)
colored colored
...@@ -432,6 +443,7 @@ DEPENDENCIES ...@@ -432,6 +443,7 @@ DEPENDENCIES
grack! grack!
grape (~> 0.2.1) grape (~> 0.2.1)
grit! grit!
grit_ext!
growl growl
guard-rspec guard-rspec
guard-spinach guard-spinach
...@@ -454,6 +466,7 @@ DEPENDENCIES ...@@ -454,6 +466,7 @@ DEPENDENCIES
pg pg
pry pry
pygments.rb (= 0.3.1) pygments.rb (= 0.3.1)
quiet_assets (= 1.0.1)
rack-mini-profiler rack-mini-profiler
rails (= 3.2.8) rails (= 3.2.8)
rails-dev-tweaks rails-dev-tweaks
......
web: bundle exec rails s -p $PORT -e production
worker: bundle exec rake environment resque:work RAILS_ENV=production QUEUE=*
## GitLab Roadmap
### Common
* Help page for service tasks like repos import, backup etc
* Hide last push widget after following link
* Add comment events
* Dashboard/Project activity events filter
### Issues
* labels autocomplete via jquery autocomplete
* Import/Export issues
* Form: Assign to me link right to the selectbox
### Merge Request
* CI build status
* Save code fragments with MR comments
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
//= require modernizr //= require modernizr
//= require chosen-jquery //= require chosen-jquery
//= require raphael //= require raphael
//= require g.raphael-min
//= require g.bar-min
//= require branch-graph //= require branch-graph
//= require ace-src-noconflict/ace //= require ace-src-noconflict/ace
//= require_tree . //= require_tree .
# Creates the variables for setting up GFM auto-completion
###
Creates the variables for setting up GFM auto-completion
###
# Emoji # Emoji
window.autocompleteEmojiData = []; data = []
window.autocompleteEmojiTemplate = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>"; template = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>"
window.autocompleteEmoji = {data, template}
# Team Members # Team Members
window.autocompleteMembersUrl = ""; url = '';
window.autocompleteMembersParams = params = {private_token: '', page: 1}
private_token: "" window.autocompleteMembers = {data, url, params}
page: 1
window.autocompleteMembersData = [];
# Add GFM auto-completion to all input fields, that accept GFM input.
window.setupGfmAutoComplete = ->
$input = $('.js-gfm-input')
# Emoji
$input.atWho ':',
data: autocompleteEmoji.data,
tpl: autocompleteEmoji.template
### # Team Members
Add GFM auto-completion to all input fields, that accept GFM input. $input.atWho '@', (query, callback) ->
###
window.setupGfmAutoComplete = ->
###
Emoji
###
$('.gfm-input').atWho ':',
data: autocompleteEmojiData,
tpl: autocompleteEmojiTemplate
###
Team Members
###
$('.gfm-input').atWho '@', (query, callback) ->
(getMoreMembers = -> (getMoreMembers = ->
$.getJSON(autocompleteMembersUrl, autocompleteMembersParams) $.getJSON(autocompleteMembers.url, autocompleteMembers.params).success (members) ->
.success (members) -> # pick the data we need
# pick the data we need newMembersData = $.map members, (m) -> m.name
newMembersData = $.map members, (m) -> m.name
# add the new page of data to the rest
# add the new page of data to the rest $.merge autocompleteMembers.data, newMembersData
$.merge autocompleteMembersData, newMembersData
# show the pop-up with a copy of the current data
# show the pop-up with a copy of the current data callback autocompleteMembers.data[..]
callback autocompleteMembersData[..]
# are we past the last page?
# are we past the last page? if newMembersData.length is 0
if newMembersData.length == 0 # set static data and stop callbacks
# set static data and stop callbacks $input.atWho '@',
$('.gfm-input').atWho '@', data: autocompleteMembers.data
data: autocompleteMembersData callback: null
callback: null else
else # get next page
# get next page getMoreMembers()
getMoreMembers()
# so the next request gets the next page # so the next request gets the next page
autocompleteMembersParams.page += 1; autocompleteMembers.params.page += 1
).call(); ).call()
\ No newline at end of file
initGraphNav = ->
$('.graph svg').css 'position', 'relative'
$('body').bind 'keyup', (e) ->
if e.keyCode is 37 # left
$('.graph svg').animate left: '+=400'
else if e.keyCode is 39 # right
$('.graph svg').animate left: '-=400'
window.initGraphNav = initGraphNav
function switchToNewIssue(form){ function switchToNewIssue(){
$(".issues_content").hide("fade", { direction: "left" }, 150, function(){ $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
$(".issues_content").after(form);
$('select#issue_assignee_id').chosen(); $('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen(); $('select#issue_milestone_id').chosen();
$("#new_issue_dialog").show("fade", { direction: "right" }, 150); $("#new_issue_dialog").show("fade", { direction: "right" }, 150);
...@@ -10,9 +9,8 @@ function switchToNewIssue(form){ ...@@ -10,9 +9,8 @@ function switchToNewIssue(form){
}); });
} }
function switchToEditIssue(form){ function switchToEditIssue(){
$(".issues_content").hide("fade", { direction: "left" }, 150, function(){ $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
$(".issues_content").after(form);
$('select#issue_assignee_id').chosen(); $('select#issue_assignee_id').chosen();
$('select#issue_milestone_id').chosen(); $('select#issue_milestone_id').chosen();
$("#edit_issue_dialog").show("fade", { direction: "right" }, 150); $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
...@@ -33,8 +31,8 @@ function switchFromEditIssue(){ ...@@ -33,8 +31,8 @@ function switchFromEditIssue(){
function backToIssues(){ function backToIssues(){
$("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){ $("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){
$(".issues_content").show("fade", { direction: "left" }, 150, function() { $(".issues_content").show("fade", { direction: "left" }, 150, function() {
$("#edit_issue_dialog").remove(); $("#edit_issue_dialog").html("");
$("#new_issue_dialog").remove(); $("#new_issue_dialog").html("");
$('.add_new').show(); $('.add_new').show();
}); });
}); });
......
Loader =
html: (width) ->
$('<img>').attr src: '/assets/ajax-loader.gif', width: width
window.Loader = Loader
...@@ -7,29 +7,32 @@ window.slugify = (text) -> ...@@ -7,29 +7,32 @@ window.slugify = (text) ->
window.ajaxGet = (url) -> window.ajaxGet = (url) ->
$.ajax({type: "GET", url: url, dataType: "script"}) $.ajax({type: "GET", url: url, dataType: "script"})
# 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 field.val() is "" closest_submit.disable() if field.val() is ""
field.on "keyup", -> field.on "input", ->
if $(this).val() is "" if $(@).val() is ""
closest_submit.disable() closest_submit.disable()
else else
closest_submit.enable() closest_submit.enable()
$ -> $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
$(".one_click_select").live 'click', -> $(this).select() $(".one_click_select").on 'click', -> $(@).select()
# Initialize chosen selects # Initialize chosen selects
$('select.chosen').chosen() $('select.chosen').chosen()
# Initialize tooltips
$('.has_tooltip').tooltip()
# Disable form buttons while a form is submitting # Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', this) buttons = $('[type="submit"]', @)
switch e.type switch e.type
when 'ajax:beforeSend', 'submit' when 'ajax:beforeSend', 'submit'
...@@ -38,7 +41,7 @@ $ -> ...@@ -38,7 +41,7 @@ $ ->
buttons.enable() buttons.enable()
# Show/Hide the profile menu when hovering the account box # Show/Hide the profile menu when hovering the account box
$('.account-box').hover -> $(this).toggleClass('hover') $('.account-box').hover -> $(@).toggleClass('hover')
# Focus search field by pressing 's' key # Focus search field by pressing 's' key
$(document).keypress (e) -> $(document).keypress (e) ->
...@@ -52,22 +55,22 @@ $ -> ...@@ -52,22 +55,22 @@ $ ->
# Commit show suppressed diff # Commit show suppressed diff
$(".supp_diff_link").bind "click", -> $(".supp_diff_link").bind "click", ->
$(this).next('table').show() $(@).next('table').show()
$(this).remove() $(@).remove()
# Note markdown preview # Note markdown preview
$(document).on 'click', '#preview-link', (e) -> $(document).on 'click', '#preview-link', (e) ->
$('#preview-note').text('Loading...') $('#preview-note').text 'Loading...'
previewLinkText = if $(this).text() == 'Preview' then 'Edit' else 'Preview' previewLinkText = if $(@).text() is 'Preview' then 'Edit' else 'Preview'
$(this).text(previewLinkText) $(@).text previewLinkText
note = $('#note_note').val() note = $('#note_note').val()
if note.trim().length == 0 if note.trim().length is 0
$('#preview-note').text("Nothing to preview.") $('#preview-note').text 'Nothing to preview.'
else else
$.post $(this).attr('href'), {note: note}, (data) -> $.post $(@).attr('href'), {note: note}, (data) ->
$('#preview-note').html(data) $('#preview-note').html(data)
$('#preview-note, #note_note').toggle() $('#preview-note, #note_note').toggle()
...@@ -79,14 +82,14 @@ $ -> ...@@ -79,14 +82,14 @@ $ ->
$.fn.extend chosen: (options) -> $.fn.extend chosen: (options) ->
default_options = search_contains: "true" default_options = search_contains: "true"
$.extend default_options, options $.extend default_options, options
_chosen.apply this, [default_options] _chosen.apply @, [default_options]
# Disable an element and add the 'disabled' Bootstrap class # Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: -> $.fn.extend disable: ->
$(this).attr('disabled', 'disabled').addClass('disabled') $(@).attr('disabled', 'disabled').addClass('disabled')
# Enable an element and remove the 'disabled' Bootstrap class # Enable an element and remove the 'disabled' Bootstrap class
$.fn.extend enable: -> $.fn.extend enable: ->
$(this).removeAttr('disabled').removeClass('disabled') $(@).removeAttr('disabled').removeClass('disabled')
)(jQuery) )(jQuery)
...@@ -115,4 +115,15 @@ var MergeRequest = { ...@@ -115,4 +115,15 @@ var MergeRequest = {
$(".merge_in_progress").hide(); $(".merge_in_progress").hide();
$(".automerge_widget.already_cannot_be_merged").show(); $(".automerge_widget.already_cannot_be_merged").show();
} }
};
/*
* Filter merge requests
*/
function merge_requestsPage() {
$("#assignee_id").chosen();
$("#milestone_id").chosen();
$("#milestone_id, #assignee_id").on("change", function(){
$(this).closest("form").submit();
});
} }
...@@ -5,3 +5,10 @@ $ -> ...@@ -5,3 +5,10 @@ $ ->
$('.milestone-issue-filter li').toggleClass('active') $('.milestone-issue-filter li').toggleClass('active')
$('.milestone-issue-filter tr[data-closed]').toggleClass('hide') $('.milestone-issue-filter tr[data-closed]').toggleClass('hide')
false false
$('.milestone-merge-requests-filter tr[data-closed]').addClass('hide')
$('.milestone-merge-requests-filter ul.nav li a').click ->
$('.milestone-merge-requests-filter li').toggleClass('active')
$('.milestone-merge-requests-filter tr[data-closed]').toggleClass('hide')
false
...@@ -22,3 +22,10 @@ $ -> ...@@ -22,3 +22,10 @@ $ ->
# Ref switcher # Ref switcher
$('.project-refs-select').on 'change', -> $('.project-refs-select').on 'change', ->
$(@).parents('form').submit() $(@).parents('form').submit()
class @GraphNav
@init: ->
$('.graph svg').css 'position', 'relative'
$('body').bind 'keyup', (e) ->
$('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left
$('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right
$ ->
$('#snippets-table .snippet').live 'click', (e) ->
if e.target.nodeName isnt 'A' and e.target.nodeName isnt 'INPUT'
location.href = $(@).attr 'url'
e.stopPropagation()
false
...@@ -17,23 +17,21 @@ $ -> ...@@ -17,23 +17,21 @@ $ ->
"ajax:beforeSend": -> $('.tree_progress').addClass("loading") "ajax:beforeSend": -> $('.tree_progress').addClass("loading")
"ajax:complete": -> $('.tree_progress').removeClass("loading") "ajax:complete": -> $('.tree_progress').removeClass("loading")
# Maintain forward/back history while browsing the file tree # Maintain forward/back history while browsing the file tree
((window) ->
((window) -> History = window.History
History = window.History $ = window.jQuery
$ = window.jQuery document = window.document
document = window.document
# Check to see if History.js is enabled for our Browser
# Check to see if History.js is enabled for our Browser unless History.enabled
unless History.enabled return false
return false
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) ->
$ -> History.pushState(null, null, $(@).attr('href'))
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) -> return false
History.pushState(null, null, $(@).attr('href'))
return false History.Adapter.bind window, 'statechange', ->
state = History.getState()
History.Adapter.bind window, 'statechange', -> window.ajaxGet(state.url)
state = History.getState() )(window)
window.ajaxGet(state.url)
)(window)
...@@ -670,3 +670,16 @@ pre { ...@@ -670,3 +670,16 @@ pre {
padding:0; padding:0;
} }
} }
.milestone .progress {
margin-bottom: 0;
margin-top:4px;
}
.float-link {
float:left;
margin-right:15px;
.s16 {
margin-right:5px;
}
}
...@@ -26,8 +26,10 @@ ...@@ -26,8 +26,10 @@
.underlined { border-bottom: 1px solid #CCC; } .underlined { border-bottom: 1px solid #CCC; }
.no-borders { border:none; } .no-borders { border:none; }
.vlink { color: $link_color !important; } .vlink { color: $link_color !important; }
.underlined_link { text-decoration: underline; }
.borders { border: 1px solid #ccc; @include shade; } .borders { border: 1px solid #ccc; @include shade; }
.hint { font-style: italic; color: #999; } .hint { font-style: italic; color: #999; }
.light { color: #888 }
/** PILLS & TABS**/ /** PILLS & TABS**/
.nav-pills a:hover { background-color:#888; } .nav-pills a:hover { background-color:#888; }
...@@ -66,10 +68,10 @@ ...@@ -66,10 +68,10 @@
.alert-message.error { @extend .alert-error; } .alert-message.error { @extend .alert-error; }
/** AVATARS **/ /** AVATARS **/
img.avatar { float:left; margin-right:15px; width:40px; border:1px solid #ddd; padding:1px; } img.avatar { float:left; margin-right:12px; width:40px; border:1px solid #ddd; padding:1px; }
img.avatar.s16 { width:16px; height:16px; } img.avatar.s16 { width:16px; height:16px; margin-right:6px; }
img.avatar.s24 { width:24px; height:24px; } img.avatar.s24 { width:24px; height:24px; margin-right:8px; }
img.avatar.s32 { width:32px; height:32px; } img.avatar.s32 { width:32px; height:32px; margin-right:10px; }
img.lil_av { padding-left: 4px; padding-right:3px; } img.lil_av { padding-left: 4px; padding-right:3px; }
/** HELPERS **/ /** HELPERS **/
......
...@@ -157,10 +157,15 @@ ...@@ -157,10 +157,15 @@
font-size:12px !important; font-size:12px !important;
} }
table.highlighttable .linenodiv pre { table.highlighttable .linenodiv {
text-align: right; a {
padding-right: 4px; color: #666;
color:#666; }
pre {
text-align: right;
padding-right: 4px;
color:#666;
}
} }
} }
} }
......
...@@ -21,7 +21,7 @@ ul { ...@@ -21,7 +21,7 @@ ul {
.author { color: #999; } .author { color: #999; }
p { p {
padding-top:5px; padding-top: 1px;
margin:0; margin:0;
color:#222; color:#222;
img { img {
...@@ -31,3 +31,11 @@ ul { ...@@ -31,3 +31,11 @@ ul {
} }
} }
} }
ol, ul {
&.styled {
li {
padding:2px;
}
}
}
...@@ -34,6 +34,11 @@ table { ...@@ -34,6 +34,11 @@ table {
border-color:#f1f1f1; border-color:#f1f1f1;
line-height:28px; line-height:28px;
.s16 {
margin-top: 5px;
margin-right: 5px;
}
&:first-child { &:first-child {
border-left:1px solid #bbb; border-left:1px solid #bbb;
} }
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
@import 'font-awesome'; @import 'font-awesome';
/** GitLab colors **/ /** GitLab colors **/
$link_color:#3A89A3; $link_color: #3A89A3;
$blue_link: #2fa0bb; $blue_link: #2FA0BB;
$style_color: #474d57; $style_color: #474D57;
$hover: #D9EDF7; $hover: #D9EDF7;
$hover_border: #ADF;
/** GitLab Fonts **/ /** GitLab Fonts **/
@font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); } @font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); }
...@@ -19,9 +20,9 @@ $hover: #D9EDF7; ...@@ -19,9 +20,9 @@ $hover: #D9EDF7;
} }
@mixin solid_shade { @mixin solid_shade {
-moz-box-shadow: 0 0 0 3px #eee; -moz-box-shadow: 0 0 0 3px #f1f1f1;
-webkit-box-shadow: 0 0 0 3px #eee; -webkit-box-shadow: 0 0 0 3px #f1f1f1;
box-shadow: 0 0 0 3px #eee; box-shadow: 0 0 0 3px #f1f1f1;
} }
@mixin border-radius($radius) { @mixin border-radius($radius) {
...@@ -64,6 +65,14 @@ $hover: #D9EDF7; ...@@ -64,6 +65,14 @@ $hover: #D9EDF7;
background-image: -o-linear-gradient($from, $to); background-image: -o-linear-gradient($from, $to);
} }
@mixin bg-light-gray-gradient {
background:#f1f1f1;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
}
@mixin bg-gray-gradient { @mixin bg-gray-gradient {
background:#eee; background:#eee;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
......
...@@ -19,31 +19,66 @@ ...@@ -19,31 +19,66 @@
margin-right: 10px; margin-right: 10px;
.chzn-drop { .chzn-drop {
margin:7px 0;
min-width: 400px; min-width: 400px;
border: 2px solid $blue_link; .chzn-results {
@include border-radius(4px); max-height:300px;
}
.chzn-search input {
min-width:365px;
}
}
}
/** Fix for Search Dropdown Border **/
.chzn-container {
.chzn-search {
input:focus {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
.chzn-drop {
margin:7px 0;
min-width: 200px;
border: 1px solid #bbb;
border-radius:0;
.chzn-results { .chzn-results {
margin-top: 5px;
max-height:300px; max-height:300px;
.group-result { .group-result {
color: $blue_link; color: $style_color;
border-bottom: 1px solid #EEE;
padding: 8px;
} }
.active-result { .active-result {
border-radius: 0;
&.highlighted { &.highlighted {
background: $blue_link; background: $hover;
color: $style_color;
}
&.result-selected {
background: #EEE;
border-left: 4px solid #CCC;
} }
} }
} }
.chzn-search input { .chzn-search {
min-width:365px; @include bg-gray-gradient;
input {
min-width:165px;
border-color: #CCC;
}
} }
} }
.chzn-single { .chzn-single {
@include bg-gray-gradient; @include bg-light-gray-gradient;
div { div {
background:transparent; background:transparent;
...@@ -55,14 +90,3 @@ ...@@ -55,14 +90,3 @@
} }
} }
} }
/** Fix for Search Dropdown Border **/
.chzn-container {
.chzn-search {
input:focus {
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
}
...@@ -47,12 +47,15 @@ ...@@ -47,12 +47,15 @@
padding-left: 32px; padding-left: 32px;
} }
.author, .author a,
.committer { .committer a {
font-size:14px; font-size:14px;
line-height:22px; line-height:22px;
text-shadow:0 1px 1px #fff; text-shadow:0 1px 1px #fff;
color:#777; color:#777;
&:hover {
color: #999;
}
} }
.avatar { .avatar {
...@@ -227,6 +230,9 @@ ...@@ -227,6 +230,9 @@
.commit-author-name { .commit-author-name {
color: #777; color: #777;
&:hover {
color: #999;
}
} }
} }
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
.event-body { .event-body {
p { p {
color:#555; color:#555;
padding-top: 5px;
} }
.event-info { .event-info {
color:#666; color:#666;
...@@ -115,3 +116,29 @@ ...@@ -115,3 +116,29 @@
margin: -3px; margin: -3px;
} }
} }
/**
* Event filter
*
*/
.event_filter {
position: absolute;
width: 40px;
margin-left: -50px;
.filter_icon {
float: left;
border-left: 3px solid #4bc;
padding: 7px;
background: #f9f9f9;
margin-bottom: 10px;
img {
width:20px;
}
&.inactive {
border-left: 3px solid #EEE;
opacity: 0.5;
}
}
}
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
img.avatar { img.avatar {
width:32px; width:32px;
margin-top:4px; margin-top:1px;
} }
} }
} }
......
...@@ -71,7 +71,7 @@ li.merge_request { ...@@ -71,7 +71,7 @@ li.merge_request {
padding:7px 10px; padding:7px 10px;
img.avatar { img.avatar {
width: 32px; width: 32px;
margin-top: 4px; margin-top: 1px;
} }
p { p {
padding: 0px; padding: 0px;
...@@ -121,3 +121,20 @@ li.merge_request { ...@@ -121,3 +121,20 @@ li.merge_request {
.mr_direction_tip { .mr_direction_tip {
margin-top:40px margin-top:40px
} }
.merge_requests_form_box {
@extend .main_box;
.merge_requests_middle_box {
@extend .middle_box_content;
height:30px;
.merge_requests_assignee {
@extend .span6;
float:left;
}
.merge_requests_milestone {
@extend .span4;
float:left;
}
}
}
...@@ -57,10 +57,7 @@ ...@@ -57,10 +57,7 @@
padding-right: 8px; padding-right: 8px;
img.avatar { img.avatar {
border: 0 none; margin-top: 0;
float: none;
margin-right: 0;
padding: 0;
width: 16px; width: 16px;
} }
} }
...@@ -75,6 +72,15 @@ ...@@ -75,6 +72,15 @@
} }
} }
} }
.blame {
img.avatar {
border: 0 none;
float: none;
margin: 0;
padding: 0;
}
}
} }
.tree-btn-group { .tree-btn-group {
......
...@@ -37,9 +37,6 @@ ...@@ -37,9 +37,6 @@
background-image: -o-linear-gradient(#595D63 6.6%, #202227); background-image: -o-linear-gradient(#595D63 6.6%, #202227);
background-position:0 0; background-position:0 0;
color:#fff; color:#fff;
i {
@extend .icon-white;
}
} }
border: 1px solid #31363E; border: 1px solid #31363E;
......
...@@ -70,9 +70,6 @@ ...@@ -70,9 +70,6 @@
color:#ccc; color:#ccc;
&:hover { &:hover {
color:#fff; color:#fff;
i {
@extend .icon-white;
}
} }
border: none; border: none;
box-shadow:none; box-shadow:none;
......
...@@ -21,7 +21,7 @@ class CommitLoadContext < BaseContext ...@@ -21,7 +21,7 @@ class CommitLoadContext < BaseContext
result[:notes_count] = line_notes.count + project.commit_notes(commit).count result[:notes_count] = line_notes.count + project.commit_notes(commit).count
begin begin
result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff] result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff]
rescue Grit::Git::GitTimeout rescue Grit::Git::GitTimeout
result[:suppress_diff] = true result[:suppress_diff] = true
result[:status] = :huge_commit result[:status] = :huge_commit
......
# Build collection of Merge Requests
# based on filtering passed via params for @project
class MergeRequestsLoadContext < BaseContext class MergeRequestsLoadContext < BaseContext
def execute def execute
type = params[:f] type = params[:f]
...@@ -9,8 +11,21 @@ class MergeRequestsLoadContext < BaseContext ...@@ -9,8 +11,21 @@ class MergeRequestsLoadContext < BaseContext
when 'closed' then merge_requests.closed when 'closed' then merge_requests.closed
when 'assigned-to-me' then merge_requests.opened.assigned(current_user) when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
else merge_requests.opened else merge_requests.opened
end.page(params[:page]).per(20) end
merge_requests.includes(:author, :project).order("closed, created_at desc") merge_requests = merge_requests.page(params[:page]).per(20)
merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc")
# Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present?
merge_requests = merge_requests.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
end
# Filter by specific milestone_id (or lack thereof)?
if params[:milestone_id].present?
merge_requests = merge_requests.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
end
merge_requests
end end
end end
...@@ -13,6 +13,7 @@ class SearchContext ...@@ -13,6 +13,7 @@ class SearchContext
result[:projects] = Project.where(id: project_ids).search(query).limit(10) result[:projects] = Project.where(id: project_ids).search(query).limit(10)
result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10) result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10)
result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10) result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10)
result[:wiki_pages] = Wiki.where(project_id: project_ids).search(query).limit(10)
result result
end end
...@@ -20,7 +21,8 @@ class SearchContext ...@@ -20,7 +21,8 @@ class SearchContext
@result ||= { @result ||= {
projects: [], projects: [],
merge_requests: [], merge_requests: [],
issues: [] issues: [],
wiki_pages: []
} }
end end
end end
......
...@@ -9,19 +9,28 @@ class ApplicationController < ActionController::Base ...@@ -9,19 +9,28 @@ class ApplicationController < ActionController::Base
helper_method :abilities, :can? helper_method :abilities, :can?
rescue_from Gitlab::Gitolite::AccessDenied do |exception| rescue_from Gitlab::Gitolite::AccessDenied do |exception|
log_exception(exception)
render "errors/gitolite", layout: "errors", status: 500 render "errors/gitolite", layout: "errors", status: 500
end end
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
render "errors/encoding", layout: "errors", status: 500 render "errors/encoding", layout: "errors", status: 500
end end
rescue_from ActiveRecord::RecordNotFound do |exception| rescue_from ActiveRecord::RecordNotFound do |exception|
log_exception(exception)
render "errors/not_found", layout: "errors", status: 404 render "errors/not_found", layout: "errors", status: 404
end end
protected protected
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
end
def reject_blocked! def reject_blocked!
if current_user && current_user.blocked if current_user && current_user.blocked
sign_out current_user sign_out current_user
......
# Controller for viewing a file's blame # Controller for viewing a file's blame
class BlobController < ProjectResourceController class BlobController < ProjectResourceController
include ExtractsPath include ExtractsPath
include Gitlab::Encode
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
...@@ -12,16 +11,9 @@ class BlobController < ProjectResourceController ...@@ -12,16 +11,9 @@ class BlobController < ProjectResourceController
def show def show
if @tree.is_blob? if @tree.is_blob?
if @tree.text?
encoding = detect_encoding(@tree.data)
mime_type = encoding ? "text/plain; charset=#{encoding}" : "text/plain"
else
mime_type = @tree.mime_type
end
send_data( send_data(
@tree.data, @tree.data,
type: mime_type, type: @tree.mime_type,
disposition: 'inline', disposition: 'inline',
filename: @tree.name filename: @tree.name
) )
......
class DashboardController < ApplicationController class DashboardController < ApplicationController
respond_to :html respond_to :html
before_filter :event_filter, only: :index
def index def index
@groups = Group.where(id: current_user.projects.pluck(:group_id)) @groups = Group.where(id: current_user.projects.pluck(:group_id))
@projects = current_user.projects_with_events @projects = current_user.projects_with_events
@projects = @projects.page(params[:page]).per(30) @projects = @projects.page(params[:page]).per(30)
@events = Event.in_projects(current_user.project_ids).limit(20).offset(params[:offset] || 0) @events = Event.in_projects(current_user.project_ids)
@events = @event_filter.apply_filter(@events)
@events = @events.limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push @last_push = current_user.recent_push
respond_to do |format| respond_to do |format|
...@@ -34,4 +39,8 @@ class DashboardController < ApplicationController ...@@ -34,4 +39,8 @@ class DashboardController < ApplicationController
format.atom { render layout: false } format.atom { render layout: false }
end end
end end
def event_filter
@event_filter ||= EventFilter.new(params[:event_filter])
end
end end
...@@ -31,7 +31,8 @@ class MilestonesController < ProjectResourceController ...@@ -31,7 +31,8 @@ class MilestonesController < ProjectResourceController
def show def show
@issues = @milestone.issues @issues = @milestone.issues
@users = @milestone.participants @users = UserDecorator.decorate(@milestone.participants)
@merge_requests = @milestone.merge_requests
respond_to do |format| respond_to do |format|
format.html format.html
......
...@@ -22,7 +22,7 @@ class ProfileController < ApplicationController ...@@ -22,7 +22,7 @@ class ProfileController < ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it" flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path redirect_to new_user_session_path
else else
render action: "password" render 'account'
end end
end end
......
require Rails.root.join('lib', 'gitlab', 'graph_commit') require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
class ProjectsController < ProjectResourceController class ProjectsController < ProjectResourceController
skip_before_filter :project, only: [:new, :create] skip_before_filter :project, only: [:new, :create]
...@@ -79,7 +79,9 @@ class ProjectsController < ProjectResourceController ...@@ -79,7 +79,9 @@ class ProjectsController < ProjectResourceController
end end
def graph def graph
@days_json, @commits_json = Gitlab::GraphCommit.to_graph(project) graph = Gitlab::Graph::JsonBuilder.new(project)
@days_json, @commits_json = graph.days_json, graph.commits_json
end end
def destroy def destroy
......
class RefsController < ProjectResourceController class RefsController < ProjectResourceController
include Gitlab::Encode
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_read_project!
......
...@@ -16,9 +16,14 @@ class RepositoriesController < ProjectResourceController ...@@ -16,9 +16,14 @@ class RepositoriesController < ProjectResourceController
@tags = @project.tags @tags = @project.tags
end end
def stats
@stats = Gitlab::GitStats.new(@project.repo, @project.root_ref)
@graph = @stats.graph
end
def archive def archive
unless can?(current_user, :download_code, @project) unless can?(current_user, :download_code, @project)
render_404 and return render_404 and return
end end
......
...@@ -5,5 +5,6 @@ class SearchController < ApplicationController ...@@ -5,5 +5,6 @@ class SearchController < ApplicationController
@projects = result[:projects] @projects = result[:projects]
@merge_requests = result[:merge_requests] @merge_requests = result[:merge_requests]
@issues = result[:issues] @issues = result[:issues]
@wiki_pages = result[:wiki_pages]
end end
end end
...@@ -26,15 +26,14 @@ class TreeController < ProjectResourceController ...@@ -26,15 +26,14 @@ class TreeController < ProjectResourceController
end end
def update def update
file_editor = Gitlab::FileEditor.new(current_user, @project, @ref) edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
update_status = file_editor.update( updated_successfully = edit_file_action.commit!(
@path,
params[:content], params[:content],
params[:commit_message], params[:commit_message],
params[:last_commit] params[:last_commit]
) )
if update_status if updated_successfully
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited" redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
else else
flash[:notice] = "Your changes could not be commited, because the file has been changed" flash[:notice] = "Your changes could not be commited, because the file has been changed"
......
...@@ -47,21 +47,15 @@ class CommitDecorator < ApplicationDecorator ...@@ -47,21 +47,15 @@ class CommitDecorator < ApplicationDecorator
# Otherwise it will link to the author email as specified in the commit. # Otherwise it will link to the author email as specified in the commit.
# #
# options: # options:
# avatar: true will prepend avatar image # avatar: true will prepend the avatar image
def author_link(options) # size: size of the avatar image in px
text = if options[:avatar] def author_link(options = {})
avatar = h.image_tag h.gravatar_icon(author_email), class: "avatar", width: 16 person_link(options.merge source: :author)
"#{avatar} #{author_name}" end
else
author_name
end
team_member = @project.try(:team_member_by_name_or_email, author_name, author_email)
if team_member.nil? # Just like #author_link but for the committer.
h.mail_to author_email, text.html_safe, class: "commit-author-link" def committer_link(options = {})
else person_link(options.merge source: :committer)
h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-author-link"
end
end end
protected protected
...@@ -69,4 +63,30 @@ class CommitDecorator < ApplicationDecorator ...@@ -69,4 +63,30 @@ class CommitDecorator < ApplicationDecorator
def no_commit_message def no_commit_message
"--no commit message" "--no commit message"
end end
# Private: Returns a link to a person. If the person has a matching user and
# is a member of the current @project it will link to the team member page.
# Otherwise it will link to the person email as specified in the commit.
#
# options:
# source: one of :author or :committer
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def person_link(options = {})
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size]
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
end
team_member = @project.try(:team_member_by_name_or_email, source_name, source_email)
if team_member.nil?
h.mail_to source_email, text.html_safe, class: "commit-#{options[:source]}-link"
else
h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-#{options[:source]}-link"
end
end
end end
...@@ -8,14 +8,14 @@ class TreeDecorator < ApplicationDecorator ...@@ -8,14 +8,14 @@ class TreeDecorator < ApplicationDecorator
#parts = parts[0...-1] if is_blob? #parts = parts[0...-1] if is_blob?
yield(h.link_to("..", "#", remote: true)) if parts.count > max_links yield(h.link_to("..", "#")) if parts.count > max_links
parts.each do |part| parts.each do |part|
part_path = File.join(part_path, part) unless part_path.empty? part_path = File.join(part_path, part) unless part_path.empty?
part_path = part if part_path.empty? part_path = part if part_path.empty?
next unless parts.last(2).include?(part) if parts.count > max_links next unless parts.last(2).include?(part) if parts.count > max_links
yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: true)) yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path))))
end end
end end
end end
......
class UserDecorator < ApplicationDecorator
decorates :user
def avatar_image size = 16
h.image_tag h.gravatar_icon(self.email, size), class: "avatar #{"s#{size}"}", width: size
end
def tm_of(project)
project.team_member_by_id(self.id)
end
end
...@@ -36,7 +36,7 @@ module ApplicationHelper ...@@ -36,7 +36,7 @@ module ApplicationHelper
else else
gravatar_prefix = request.ssl? ? "https://secure" : "http://www" gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
user_email.strip! user_email.strip!
"#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon" "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm"
end end
end end
......
...@@ -65,4 +65,9 @@ module CommitsHelper ...@@ -65,4 +65,9 @@ module CommitsHelper
end end
end end
def commit_to_html commit
if commit.model
escape_javascript(render 'commits/commit', commit: commit)
end
end
end end
...@@ -33,4 +33,22 @@ module EventsHelper ...@@ -33,4 +33,22 @@ module EventsHelper
image_tag event_image_path image_tag event_image_path
end end
end end
def event_filter_link key, tooltip
key = key.to_s
filter = @event_filter.options key
inactive = if @event_filter.active? key
nil
else
'inactive'
end
content_tag :div, class: "filter_icon #{inactive}" do
link_to dashboard_path(event_filter: filter), class: 'has_tooltip', 'data-original-title' => tooltip do
image_tag "event_filter_#{key}.png"
end
end
end
end end
...@@ -10,5 +10,9 @@ module ProjectsHelper ...@@ -10,5 +10,9 @@ module ProjectsHelper
def link_to_project project def link_to_project project
link_to project.name, project link_to project.name, project
end end
def tm_path team_member
project_team_member_path(@project, team_member)
end
end end
...@@ -67,4 +67,29 @@ module TreeHelper ...@@ -67,4 +67,29 @@ module TreeHelper
can?(current_user, :push_code, @project) can?(current_user, :push_code, @project)
end end
end end
# Breadcrumb links for a Project and, if applicable, a tree path
def breadcrumbs
return unless @project && @ref
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
content_tag(:span, nil, class: 'arrow') +
link_to(@project.name, project_commits_path(@project, @ref))
end
if @path
parts = @path.split('/')
parts.each_with_index do |part, i|
crumbs += content_tag(:span, '/', class: 'divider')
crumbs += content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
end
end
end
crumbs.html_safe
end
end end
class Commit class Commit
include ActiveModel::Conversion include ActiveModel::Conversion
include Gitlab::Encode
include StaticModel include StaticModel
extend ActiveModel::Naming extend ActiveModel::Naming
# Safe amount of files with diffs in one commit to render
# Used to prevent 500 error on huge commits by suppressing diff
#
DIFF_SAFE_SIZE = 100
attr_accessor :commit, :head, :refs attr_accessor :commit, :head, :refs
delegate :message, :authored_date, :committed_date, :parents, :sha, delegate :message, :authored_date, :committed_date, :parents, :sha,
...@@ -107,7 +111,7 @@ class Commit ...@@ -107,7 +111,7 @@ class Commit
end end
def safe_message def safe_message
@safe_message ||= utf8 message @safe_message ||= message
end end
def created_at def created_at
...@@ -119,7 +123,7 @@ class Commit ...@@ -119,7 +123,7 @@ class Commit
end end
def author_name def author_name
utf8 author.name author.name
end end
# Was this commit committed by a different person than the original author? # Was this commit committed by a different person than the original author?
...@@ -128,7 +132,7 @@ class Commit ...@@ -128,7 +132,7 @@ class Commit
end end
def committer_name def committer_name
utf8 committer.name committer.name
end end
def committer_email def committer_email
......
...@@ -7,8 +7,6 @@ class Issue < ActiveRecord::Base ...@@ -7,8 +7,6 @@ class Issue < ActiveRecord::Base
acts_as_taggable_on :labels acts_as_taggable_on :labels
belongs_to :milestone
validates :description, length: { within: 0..2000 } validates :description, length: { within: 0..2000 }
def self.open_for(user) def self.open_for(user)
......
require Rails.root.join("app/models/commit") require Rails.root.join("app/models/commit")
require Rails.root.join("app/roles/static_model")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include IssueCommonality include IssueCommonality
include Votes include Votes
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes :author_id_of_changes
attr_accessor :should_remove_source_branch attr_accessor :should_remove_source_branch
...@@ -26,6 +27,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -26,6 +27,10 @@ class MergeRequest < ActiveRecord::Base
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
end end
def self.find_all_by_milestone(milestone)
where("milestone_id = :milestone_id", milestone_id: milestone)
end
def human_state def human_state
states = { states = {
CAN_BE_MERGED => "can_be_merged", CAN_BE_MERGED => "can_be_merged",
...@@ -60,7 +65,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -60,7 +65,7 @@ class MergeRequest < ActiveRecord::Base
end end
def check_if_can_be_merged def check_if_can_be_merged
self.state = if Gitlab::Merge.new(self, self.author).can_be_merged? self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
CAN_BE_MERGED CAN_BE_MERGED
else else
CANNOT_BE_MERGED CANNOT_BE_MERGED
...@@ -167,7 +172,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -167,7 +172,7 @@ class MergeRequest < ActiveRecord::Base
end end
def automerge!(current_user) def automerge!(current_user)
if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty? if Gitlab::Satellite::MergeAction.new(current_user, self).merge! && self.unmerged_commits.empty?
self.merge!(current_user.id) self.merge!(current_user.id)
true true
end end
...@@ -212,5 +217,6 @@ end ...@@ -212,5 +217,6 @@ end
# st_diffs :text(4294967295 # st_diffs :text(4294967295
# merged :boolean default(FALSE), not null # merged :boolean default(FALSE), not null
# state :integer default(1), not null # state :integer default(1), not null
# milestone_id :integer
# #
...@@ -3,6 +3,7 @@ class Milestone < ActiveRecord::Base ...@@ -3,6 +3,7 @@ class Milestone < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :issues has_many :issues
has_many :merge_requests
validates :title, presence: true validates :title, presence: true
validates :project, presence: true validates :project, presence: true
...@@ -15,8 +16,20 @@ class Milestone < ActiveRecord::Base ...@@ -15,8 +16,20 @@ class Milestone < ActiveRecord::Base
User.where(id: issues.pluck(:assignee_id)) User.where(id: issues.pluck(:assignee_id))
end end
def open_items_count
self.issues.opened.count + self.merge_requests.opened.count
end
def closed_items_count
self.issues.closed.count + self.merge_requests.closed.count
end
def total_items_count
self.issues.count + self.merge_requests.count
end
def percent_complete def percent_complete
((self.issues.closed.count * 100) / self.issues.count).abs ((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError rescue ZeroDivisionError
100 100
end end
......
...@@ -23,13 +23,13 @@ class Note < ActiveRecord::Base ...@@ -23,13 +23,13 @@ class Note < ActiveRecord::Base
mount_uploader :attachment, AttachmentUploader mount_uploader :attachment, AttachmentUploader
# Scopes # Scopes
scope :common, where(noteable_id: nil) scope :common, ->{ where(noteable_id: nil) }
scope :today, where("created_at >= :date", date: Date.today) scope :today, ->{ where("created_at >= :date", date: Date.today) }
scope :last_week, where("created_at >= :date", date: (Date.today - 7.days)) scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
scope :since, ->(day) { where("created_at >= :date", date: (day)) } scope :since, ->(day) { where("created_at >= :date", date: (day)) }
scope :fresh, order("created_at ASC, id ASC") scope :fresh, ->{ order("created_at ASC, id ASC") }
scope :inc_author_project, includes(:project, :author) scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, includes(:author) scope :inc_author, ->{ includes(:author) }
def self.create_status_change_note(noteable, author, status) def self.create_status_change_note(noteable, author, status)
create({ create({
......
...@@ -104,8 +104,10 @@ class Project < ActiveRecord::Base ...@@ -104,8 +104,10 @@ class Project < ActiveRecord::Base
end end
def repo_name def repo_name
if path == "gitolite-admin" denied_paths = %w(gitolite-admin groups projects dashboard)
errors.add(:path, " like 'gitolite-admin' is not allowed")
if denied_paths.include?(path)
errors.add(:path, "like #{path} is not allowed")
end end
end end
......
...@@ -8,7 +8,7 @@ class Tree ...@@ -8,7 +8,7 @@ class Tree
def initialize(raw_tree, project, ref = nil, path = nil) def initialize(raw_tree, project, ref = nil, path = nil)
@project, @ref, @path = project, ref, path @project, @ref, @path = project, ref, path
@tree = if path.present? @tree = if path.present?
raw_tree / path.dup.force_encoding('ascii-8bit') raw_tree / path
else else
raw_tree raw_tree
end end
......
...@@ -15,6 +15,12 @@ class Wiki < ActiveRecord::Base ...@@ -15,6 +15,12 @@ class Wiki < ActiveRecord::Base
slug slug
end end
class << self
def search(query)
where("title like :query OR content like :query", query: "%#{query}%")
end
end
protected protected
def self.regenerate_from wiki def self.regenerate_from wiki
......
...@@ -6,6 +6,7 @@ module IssueCommonality ...@@ -6,6 +6,7 @@ module IssueCommonality
belongs_to :project belongs_to :project
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
belongs_to :assignee, class_name: "User" belongs_to :assignee, class_name: "User"
belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy has_many :notes, as: :noteable, dependent: :destroy
validates :project, presence: true validates :project, presence: true
......
...@@ -41,7 +41,7 @@ module Repository ...@@ -41,7 +41,7 @@ module Repository
end end
def satellite def satellite
@satellite ||= Gitlab::Satellite.new(self) @satellite ||= Gitlab::Satellite::Satellite.new(self)
end end
def has_post_receive_file? def has_post_receive_file?
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
%b %b
Owner: Owner:
%td %td
= @admin_project.owner.name = @admin_project.owner_name || '(deleted)'
%tr %tr
%td %td
%b %b
......
%h3.page_title Resque %h3.page_title Resque
%br %br
.ui-box .ui-box
%iframe{src: resque_url, width: '100%', height: 600, style: "border: none"} %iframe{src: resque_path, width: '100%', height: 600, style: "border: none"}
...@@ -4,7 +4,4 @@ ...@@ -4,7 +4,4 @@
= nav_link(controller: :refs) do = nav_link(controller: :refs) do
= link_to 'Source', project_tree_path(@project, @ref) = link_to 'Source', project_tree_path(@project, @ref)
%li.right %li.right
.input-prepend.project_clone_holder = render "shared/clone_panel"
%button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH
%button{class: "btn small", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.web_protocol.upcase
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.file_title .file_title
%i.icon-file %i.icon-file
%span.file_name %span.file_name
= @tree.name.force_encoding('utf-8') = @tree.name
%small= number_to_human_size @tree.size %small= number_to_human_size @tree.size
%span.options= render "tree/blob_actions" %span.options= render "tree/blob_actions"
.file_content.blame .file_content.blame
...@@ -24,9 +24,7 @@ ...@@ -24,9 +24,7 @@
- commit = Commit.new(commit) - commit = Commit.new(commit)
- commit = CommitDecorator.decorate(commit) - commit = CommitDecorator.decorate(commit)
%tr %tr
%td.author %td.author= commit.author_link avatar: true, size: 16
= image_tag gravatar_icon(commit.author_email, 16)
= commit.author_name
%td.blame_commit %td.blame_commit
&nbsp; &nbsp;
%code= link_to commit.short_id, project_commit_path(@project, commit) %code= link_to commit.short_id, project_commit_path(@project, commit)
...@@ -34,4 +32,4 @@ ...@@ -34,4 +32,4 @@
%td.lines %td.lines
= preserve do = preserve do
%pre %pre
= Gitlab::Encode.utf8 lines.join("\n") = lines.join("\n")
...@@ -4,9 +4,8 @@ ...@@ -4,9 +4,8 @@
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right" %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
%p %p
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
%strong.commit-author-name= commit.author_name = commit.author_link avatar: true, size: 24
%span.dash &ndash; &nbsp;
= image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title"
%span.committed_ago %span.committed_ago
......
...@@ -18,16 +18,15 @@ ...@@ -18,16 +18,15 @@
.commit-info .commit-info
.row .row
.span5 .span5
= image_tag gravatar_icon(@commit.author_email, 40), class: "avatar"
.author .author
%strong= @commit.author_name %strong= @commit.author_link avatar: true, size: 40
authored authored
%time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")} %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.authored_date)} ago #{time_ago_in_words(@commit.authored_date)} ago
- if @commit.different_committer? - if @commit.different_committer?
.committer .committer
&rarr; &rarr;
%strong= @commit.committer_name %strong= @commit.committer_link
committed committed
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")} %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
#{time_ago_in_words(@commit.committed_date)} ago #{time_ago_in_words(@commit.committed_date)} ago
......
- if @suppress_diff - if @suppress_diff
.alert-message.block-message .alert-message.block-message
%p %p
%strong Warning! Large commit with more then 200 files changed. %strong Warning! Large commit with more then #{Commit::DIFF_SAFE_SIZE} files changed.
%p To prevent performance issue we rejected diff information. %p To prevent performance issue we rejected diff information.
%p %p
But if you still want to see diff But if you still want to see diff
= link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "dark" = link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "underlined_link"
%p.cgray %p.cgray
Showing #{pluralize(diffs.count, "changed file")} Showing #{pluralize(diffs.count, "changed file")}
...@@ -35,10 +35,10 @@ ...@@ -35,10 +35,10 @@
- if file.text? - if file.text?
= render "commits/text_file", diff: diff, index: i = render "commits/text_file", diff: diff, index: i
- elsif file.image? - elsif file.image?
- if diff.renamed_file || diff.new_file || diff.deleted_file - if diff.renamed_file || diff.new_file || diff.deleted_file
.diff_file_content_image .diff_file_content_image
%img{class: image_diff_class(diff), src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} %img{class: image_diff_class(diff), src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
- else - else
- old_file = (@commit.prev_commit.tree / diff.old_path) - old_file = (@commit.prev_commit.tree / diff.old_path)
.diff_file_content_image.img_compared .diff_file_content_image.img_compared
%img{class: "diff_image_removed", src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"} %img{class: "diff_image_removed", src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
......
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
Tags Tags
%span.badge= @project.tags.length %span.badge= @project.tags.length
= nav_link(controller: :repositories, action: :stats) do
= link_to stats_project_repository_path(@project) do
Stats
- if current_controller?(:commits) && current_user.private_token - if current_controller?(:commits) && current_user.private_token
%li.right %li.right
%span.rss-icon %span.rss-icon
......
...@@ -2,14 +2,7 @@ ...@@ -2,14 +2,7 @@
- if @path.present? - if @path.present?
%ul.breadcrumb %ul.breadcrumb
%li = breadcrumbs
%span.arrow
= link_to project_commits_path(@project) do
= @project.name
%span.divider
\/
%li
%a{href: "#"}= @path.split("/").join(" / ")
%div{id: dom_id(@project)} %div{id: dom_id(@project)}
#commits_list= render "commits" #commits_list= render "commits"
......
...@@ -3,10 +3,17 @@ ...@@ -3,10 +3,17 @@
.activities.span8 .activities.span8
= render "events/event_last_push", event: @last_push = render "events/event_last_push", event: @last_push
= render 'shared/no_ssh' = render 'shared/no_ssh'
.event_filter
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
- if @events.any? - if @events.any?
.content_list= render @events .content_list= render @events
- else - else
%h4.nothing_here_message Projects activity will be displayed here %p.nothing_here_message Projects activity will be displayed here
.loading.hide .loading.hide
.side .side
- if @groups.present? - if @groups.present?
......
...@@ -14,8 +14,9 @@ ...@@ -14,8 +14,9 @@
= f.submit "Sign in", :class => "primary btn wide" = f.submit "Sign in", :class => "primary btn wide"
.right .right
= render :partial => "devise/shared/links" = render :partial => "devise/shared/links"
- if devise_mapping.omniauthable? .clearfix
%hr/ - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present?
- resource_class.omniauth_providers.each do |provider| %div
%span - resource_class.omniauth_providers.each do |provider|
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) %span
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
...@@ -5,9 +5,10 @@ ...@@ -5,9 +5,10 @@
%hr %hr
%p.slead %p.slead
Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member. Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member.
%br %br
System Hooks can be used for logging or change information in LDAP server. %br
System Hooks can be used, e.g. for logging or changing information in a LDAP server.
%br %br
%h5 Hooks request example: %h5 Hooks request example:
= render "admin/hooks/data_ex" = render "admin/hooks/data_ex"
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= f.label :title do = f.label :title do
%strong= "Subject *" %strong= "Subject *"
.input .input
= f.text_field :title, maxlength: 255, class: "xxlarge gfm-input", autofocus: true = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true
.issue_middle_block .issue_middle_block
.issue_assignee .issue_assignee
= f.label :assignee_id do = f.label :assignee_id do
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
.clearfix .clearfix
= f.label :description, "Details" = f.label :description, "Details"
.input .input
= f.text_area :description, maxlength: 2000, class: "xxlarge gfm-input", rows: 14 = f.text_area :description, maxlength: 2000, class: "xxlarge js-gfm-input", rows: 14
%p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
......
:plain :plain
var edit_issue_dialog = $("<div id='edit_issue_dialog'></div>"); $("#edit_issue_dialog").html("#{escape_javascript(render('form'))}");
edit_issue_dialog.html("#{escape_javascript(render('form'))}"); switchToEditIssue();
switchToEditIssue(edit_issue_dialog);
...@@ -58,6 +58,8 @@ ...@@ -58,6 +58,8 @@
%ul#issues-table.unstyled.issues_table %ul#issues-table.unstyled.issues_table
= render "issues" = render "issues"
#new_issue_dialog
#edit_issue_dialog
:javascript :javascript
$(function(){ $(function(){
......
:plain :plain
var new_issue_dialog = $("<div id='new_issue_dialog'></div>"); $("#new_issue_dialog").html("#{escape_javascript(render('form'))}");
new_issue_dialog.html("#{escape_javascript(render('form'))}"); switchToNewIssue();
switchToNewIssue(new_issue_dialog);
...@@ -8,9 +8,7 @@ ...@@ -8,9 +8,7 @@
GITLAB GITLAB
%span.separator %span.separator
%h1.project_name= title %h1.project_name= title
.search = render "layouts/search"
= form_tag search_path, method: :get do |f|
= text_field_tag "search", nil, placeholder: "Search", class: "search-input"
.fbtn .fbtn
- if current_user.is_admin? - if current_user.is_admin?
= link_to admin_root_path, class: "btn small", title: "Admin area" do = link_to admin_root_path, class: "btn small", title: "Admin area" do
...@@ -29,11 +27,3 @@ ...@@ -29,11 +27,3 @@
= link_to 'Logout', destroy_user_session_path, class: "logout", method: :delete = link_to 'Logout', destroy_user_session_path, class: "logout", method: :delete
= render "layouts/init_auto_complete" = render "layouts/init_auto_complete"
:javascript
$(function(){
$("#search").autocomplete({
source: #{raw search_autocomplete_source},
select: function(event, ui) { location.href = ui.item.url }
});
});
:javascript :javascript
$(function() { $(function() {
autocompleteMembersUrl = "#{ "/api/v2/projects/#{@project.code}/members" if @project }"; autocompleteMembers.url = "#{ "/api/v2/projects/#{@project.code}/members" if @project }";
autocompleteMembersParams.private_token = "#{current_user.authentication_token}"; autocompleteMembers.params.private_token = "#{current_user.private_token}";
autocompleteEmojiData = #{raw emoji_autocomplete_source}; autocompleteEmoji.data = #{raw emoji_autocomplete_source};
// convert the list so that the items have the right format for completion // convert the list so that the items have the right format for completion
autocompleteEmojiData = $.map(autocompleteEmojiData, function(value) { autocompleteEmoji.data = $.map(autocompleteEmoji.data, function(value) {
return { return {
name: value, name: value,
insert: value+':', insert: value+':',
......
.search
= form_tag search_path, method: :get do |f|
= text_field_tag "search", nil, placeholder: "Search", class: "search-input"
:javascript
$(function(){
$("#search").autocomplete({
source: #{raw search_autocomplete_source},
select: function(event, ui) { location.href = ui.item.url }
});
});
...@@ -28,16 +28,22 @@ ...@@ -28,16 +28,22 @@
%h4.cdark 2. Fill info %h4.cdark 2. Fill info
.clearfix .clearfix
.main_box .merge_requests_form_box
.top_box_content .top_box_content
= f.label :title do = f.label :title do
%strong= "Title *" %strong= "Title *"
.input= f.text_field :title, class: "input-xxlarge pad gfm-input", maxlength: 255, rows: 5 .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5
.middle_box_content .merge_requests_middle_box
= f.label :assignee_id do .merge_requests_assignee
%i.icon-user = f.label :assignee_id do
Assign to %i.icon-user
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'}) Assign to
.input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
.merge_requests_milestone
= f.label :milestone_id do
%i.icon-time
Milestone
.input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
.control-group .control-group
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
%span.btn.small.disabled.grouped %span.btn.small.disabled.grouped
%i.icon-comment %i.icon-comment
= merge_request.mr_and_commit_notes.count = merge_request.mr_and_commit_notes.count
- if merge_request.milestone_id?
%span.btn.small.disabled.grouped
%i.icon-time
= merge_request.milestone.title
%span.btn.small.disabled.grouped %span.btn.small.disabled.grouped
= merge_request.source_branch = merge_request.source_branch
&rarr; &rarr;
......
:plain :plain
$(".mr_source_commit").html("#{escape_javascript(render 'commits/commit', commit: @commit)}"); $(".mr_source_commit").html("#{commit_to_html(@commit)}");
:plain :plain
$(".mr_target_commit").html("#{escape_javascript(render 'commits/commit', commit: @commit)}"); $(".mr_target_commit").html("#{commit_to_html(@commit)}");
...@@ -9,19 +9,26 @@ ...@@ -9,19 +9,26 @@
.ui-box .ui-box
.title .title
%ul.nav.nav-pills .left
%li{class: ("active" if (params[:f] == 'open' || !params[:f]))} %ul.nav.nav-pills
= link_to project_merge_requests_path(@project, f: 'open') do %li{class: ("active" if (params[:f] == 'open' || !params[:f]))}
Open = link_to project_merge_requests_path(@project, f: 'open', milestone_id: params[:milestone_id]) do
%li{class: ("active" if params[:f] == "closed")} Open
= link_to project_merge_requests_path(@project, f: "closed") do %li{class: ("active" if params[:f] == "closed")}
Closed = link_to project_merge_requests_path(@project, f: "closed", milestone_id: params[:milestone_id]) do
%li{class: ("active" if params[:f] == 'assigned-to-me')} Closed
= link_to project_merge_requests_path(@project, f: 'assigned-to-me') do %li{class: ("active" if params[:f] == 'assigned-to-me')}
To Me = link_to project_merge_requests_path(@project, f: 'assigned-to-me', milestone_id: params[:milestone_id]) do
%li{class: ("active" if params[:f] == 'all')} To Me
= link_to project_merge_requests_path(@project, f: 'all') do %li{class: ("active" if params[:f] == 'all')}
All = link_to project_merge_requests_path(@project, f: 'all', milestone_id: params[:milestone_id]) do
All
.right
= form_tag project_merge_requests_path(@project), id: "merge_requests_search_form", method: :get, class: :right do
= select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee")
= select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone")
= hidden_field_tag :f, params[:f]
.clearfix
%ul.unstyled %ul.unstyled
= render @merge_requests = render @merge_requests
...@@ -35,3 +42,7 @@ ...@@ -35,3 +42,7 @@
.span4.right .span4.right
%span.cgray.right #{@merge_requests.total_count} merge requests for this filter %span.cgray.right #{@merge_requests.total_count} merge requests for this filter
:javascript
$(function() {
merge_requestsPage();
})
...@@ -14,9 +14,13 @@ ...@@ -14,9 +14,13 @@
%strong.author= link_to_merge_request_author(@merge_request) %strong.author= link_to_merge_request_author(@merge_request)
- if @merge_request.assignee - if @merge_request.assignee
%cite.cgray and currently assigned to %cite.cgray , currently assigned to
= image_tag gravatar_icon(@merge_request.assignee_email), width: 16, class: "lil_av" = image_tag gravatar_icon(@merge_request.assignee_email), width: 16, class: "lil_av"
%strong.author= link_to_merge_request_assignee(@merge_request) %strong.author= link_to_merge_request_assignee(@merge_request)
- if @merge_request.milestone
- milestone = @merge_request.milestone
%cite.cgray and attached to milestone
%strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
- if @merge_request.closed - if @merge_request.closed
......
%li{class: "milestone", id: dom_id(milestone) } %li{class: "milestone", id: dom_id(milestone) }
.right .right
- if milestone.issues.any?
%span.btn.small.disabled.grouped= pluralize milestone.issues.count, 'issues'
- if milestone.issues.count > 0
= link_to 'Browse Issues', project_issues_path(milestone.project, milestone_id: milestone.id), class: "btn small grouped"
- if can? current_user, :admin_milestone, milestone.project - if can? current_user, :admin_milestone, milestone.project
= link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do
%i.icon-edit
Edit
%h4 %h4
= link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone), class: "row_title" = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
%small %small
= milestone.expires_at = milestone.expires_at
%br .row
.progress.progress-success.span3 .span4
.bar{style: "width: #{milestone.percent_complete}%;"} .progress.progress-info
.bar{style: "width: #{milestone.percent_complete}%;"}
.span6
&nbsp; = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do
= pluralize milestone.issues.count, 'Issue'
&nbsp;
= link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do
= pluralize milestone.merge_requests.count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
...@@ -31,10 +31,10 @@ ...@@ -31,10 +31,10 @@
%h5 %h5
Progress: Progress:
%small %small
#{@milestone.issues.closed.count} closed #{@milestone.closed_items_count} closed
&ndash; &ndash;
#{@milestone.issues.opened.count} open #{@milestone.open_items_count} open
.progress.progress-success .progress.progress-info
.bar{style: "width: #{@milestone.percent_complete}%;"} .bar{style: "width: #{@milestone.percent_complete}%;"}
...@@ -58,15 +58,28 @@ ...@@ -58,15 +58,28 @@
%span.badge.badge-info ##{issue.id} %span.badge.badge-info ##{issue.id}
&ndash; &ndash;
= link_to_gfm truncate(issue.title, length: 60), [@project, issue] = link_to_gfm truncate(issue.title, length: 60), [@project, issue]
%br
.span6 .span6
%table %table.milestone-merge-requests-filter
%thead %thead
%th Participants %th
- @users.each do |user| %ul.nav.nav-pills
%tr %li.active= link_to('Open Merge Requests', '#')
%li=link_to('All Merge Requests', '#')
- @merge_requests.each do |merge_request|
%tr{data: {closed: merge_request.closed}}
%td %td
= image_tag gravatar_icon(user.email, 24), width: "24" = link_to [@project, merge_request] do
&nbsp; %span.badge.badge-info ##{merge_request.id}
= user.name &ndash;
= link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
%hr
%h6 Participants:
%div
- @users.each do |user|
= link_to tm_path(user.tm_of(@project)), class: 'float-link' do
= user.avatar_image
= user.name
.clearfix
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= f.text_area :note, size: 255, class: 'note-text gfm-input' = f.text_area :note, size: 255, class: 'note-text js-gfm-input'
#preview-note.preview_note.hide #preview-note.preview_note.hide
.hint .hint
.right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= f.hidden_field :line_code = f.hidden_field :line_code
= f.text_area :note, size: 255, class: 'line-note-text gfm-input' = f.text_area :note, size: 255, class: 'line-note-text js-gfm-input'
.note_actions .note_actions
.buttons .buttons
= f.submit 'Add note', class: "btn save-btn submit_note submit_inline_note", id: "submit_note" = f.submit 'Add note', class: "btn save-btn submit_note submit_inline_note", id: "submit_note"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%td{style: "font-size: 1px; line-height: 1px;", width: "21"} %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%td{align: "left", style: "padding: 20px 0 0;"} %td{align: "left", style: "padding: 20px 0 0;"}
%h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "} %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
= "You got granted #{@users_project.project_access_human} access to project" = "You have been granted #{@users_project.project_access_human} access to project"
%td{style: "font-size: 1px; line-height: 1px;", width: "21"} %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
%tr %tr
%td{style: "font-size: 1px; line-height: 1px;", width: "21"} %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%h3.page_title %h3.page_title
Private token Private token
%span.cred.right %span.cred.right
keep it in secret! keep it secret!
.padded .padded
= form_for @user, url: profile_reset_private_token_path, method: :put do |f| = form_for @user, url: profile_reset_private_token_path, method: :put do |f|
.data .data
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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