Commit 93f0a8c9b3ca2aa4fdaccbea1547af3b0038a7dd

Authored by David Barri
2 parents 944d3823 3c7806c3

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

Conflicts:
	doc/install/installation.md
Showing 371 changed files with 102847 additions and 1549 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 371 files displayed.

.travis.yml
... ... @@ -4,7 +4,7 @@ env:
4 4 before_install:
5 5 - sudo apt-get install libicu-dev -y
6 6 - sudo apt-get install libqt4-dev libqtwebkit-dev -y
7   - - gem install charlock_holmes -v="0.6.8"
  7 + - gem install charlock_holmes -v="0.6.9"
8 8 branches:
9 9 only:
10 10 - 'master'
... ...
CHANGELOG
... ... @@ -28,7 +28,7 @@ v 3.0.0
28 28 - Reject ssh keys that break gitolite
29 29 - [API] list one project hook
30 30 - [API] edit project hook
31   - - [API] add project snippets list
  31 + - [API] list project snippets
32 32 - [API] allow to authorize using private token in HTTP header
33 33 - [API] add user creation
34 34  
... ...
CONTRIBUTING.md
1   -## Contribute to GitLab
  1 +## Contribute to GitLab
2 2  
3 3 If you want to contribute to GitLab, follow this process:
4 4  
... ... @@ -7,24 +7,20 @@ If you want to contribute to GitLab, follow this process:
7 7 3. Code
8 8 4. Create a pull request
9 9  
10   -We will only accept pull requests if:
  10 +We will only accept pull requests if:
11 11  
12 12 * Your code has proper tests and all tests pass
13   -* Your code can be merged w/o problems
  13 +* Your code can be merged w/o problems
14 14 * It won't break existing functionality
15 15 * It's quality code
16 16 * We like it :)
17 17  
18   -## [You may need a developer VM](https://github.com/gitlabhq/developer-vm)
  18 +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).
19 19  
20   -## Running tests
21   -
22   -To run the specs for GitLab, you need to run seeds for test db.
  20 +## Installation
23 21  
24   - cd gitlabhq
25   - rake db:seed_fu RAILS_ENV=test
  22 +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.
26 23  
27   -Then you can run the test suite with rake:
28   -
29   - rake gitlab:test
  24 +## Running tests
30 25  
  26 +For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
... ...
Gemfile
... ... @@ -11,9 +11,9 @@ end
11 11 gem "rails", "3.2.8"
12 12  
13 13 # Supported DBs
14   -gem "sqlite3", :group => :sqlite
15   -gem "mysql2", :group => :mysql
16   -gem "pg", :group => :postgres
  14 +gem "sqlite3", group: :sqlite
  15 +gem "mysql2", group: :mysql
  16 +gem "pg", group: :postgres
17 17  
18 18 # Auth
19 19 gem "devise", "~> 2.1.0"
... ... @@ -23,10 +23,11 @@ gem 'omniauth-twitter'
23 23 gem 'omniauth-github'
24 24  
25 25 # GITLAB patched libs
26   -gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
27   -gem "omniauth-ldap", :git => "https://github.com/gitlabhq/omniauth-ldap.git", :ref => "f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e"
28   -gem 'yaml_db', :git => "https://github.com/gitlabhq/yaml_db.git"
29   -gem 'grack', :git => "https://github.com/gitlabhq/grack.git"
  26 +gem "grit", git: "https://github.com/gitlabhq/grit.git", ref: '7f35cb98ff17d534a07e3ce6ec3d580f67402837'
  27 +gem "omniauth-ldap", git: "https://github.com/gitlabhq/omniauth-ldap.git", ref: 'f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e'
  28 +gem 'yaml_db', git: "https://github.com/gitlabhq/yaml_db.git", ref: '98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd'
  29 +gem 'grack', git: "https://github.com/gitlabhq/grack.git", ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
  30 +gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: '212fd40bea61f3c6a167223768e7295dc32bbc10'
30 31  
31 32 # Gitolite client (for work with gitolite-admin repo)
32 33 gem "gitolite", '1.1.0'
... ... @@ -35,7 +36,7 @@ gem "gitolite", '1.1.0'
35 36 gem "pygments.rb", "0.3.1"
36 37  
37 38 # Language detection
38   -gem "github-linguist", "~> 2.3.4" , :require => "linguist"
  39 +gem "github-linguist", "~> 2.3.4" , require: "linguist"
39 40  
40 41 # API
41 42 gem "grape", "~> 0.2.1"
... ... @@ -83,9 +84,6 @@ gem 'resque_mailer'
83 84 # HTTP requests
84 85 gem "httparty"
85 86  
86   -# Handle encodings
87   -gem "charlock_holmes"
88   -
89 87 # Colored output to console
90 88 gem "colored"
91 89  
... ... @@ -114,8 +112,9 @@ group :assets do
114 112 end
115 113  
116 114 group :development do
  115 + gem "annotate", git: "https://github.com/ctran/annotate_models.git"
117 116 gem "letter_opener"
118   - gem "annotate", :git => "https://github.com/ctran/annotate_models.git"
  117 + gem 'quiet_assets', '1.0.1'
119 118 gem 'rack-mini-profiler'
120 119 end
121 120  
... ... @@ -137,13 +136,13 @@ group :development, :test do
137 136 gem 'guard-spinach'
138 137  
139 138 # Notification
140   - gem 'rb-fsevent', :require => darwin_only('rb-fsevent')
141   - gem 'growl', :require => darwin_only('growl')
142   - gem 'rb-inotify', :require => linux_only('rb-inotify')
  139 + gem 'rb-fsevent', require: darwin_only('rb-fsevent')
  140 + gem 'growl', require: darwin_only('growl')
  141 + gem 'rb-inotify', require: linux_only('rb-inotify')
143 142 end
144 143  
145 144 group :test do
146   - gem "simplecov", :require => false
  145 + gem "simplecov", require: false
147 146 gem "shoulda-matchers"
148 147 gem 'email_spec'
149 148 gem 'resque_spec'
... ...
Gemfile.lock
... ... @@ -7,6 +7,7 @@ GIT
7 7 GIT
8 8 remote: https://github.com/gitlabhq/grack.git
9 9 revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
  10 + ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
10 11 specs:
11 12 grack (1.0.0)
12 13 rack (~> 1.4.1)
... ... @@ -22,6 +23,14 @@ GIT
22 23 posix-spawn (~> 0.3.6)
23 24  
24 25 GIT
  26 + remote: https://github.com/gitlabhq/grit_ext.git
  27 + revision: 212fd40bea61f3c6a167223768e7295dc32bbc10
  28 + ref: 212fd40bea61f3c6a167223768e7295dc32bbc10
  29 + specs:
  30 + grit_ext (0.6.0)
  31 + charlock_holmes (~> 0.6.9)
  32 +
  33 +GIT
25 34 remote: https://github.com/gitlabhq/omniauth-ldap.git
26 35 revision: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
27 36 ref: f038dd852d7bd473a557e385d5d7c2fd5dc1dc2e
... ... @@ -35,6 +44,7 @@ GIT
35 44 GIT
36 45 remote: https://github.com/gitlabhq/yaml_db.git
37 46 revision: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
  47 + ref: 98e9a5dca43e3fedd3268c76a73af40d1bdf1dfd
38 48 specs:
39 49 yaml_db (0.2.2)
40 50  
... ... @@ -90,7 +100,7 @@ GEM
90 100 carrierwave (0.6.2)
91 101 activemodel (>= 3.2.0)
92 102 activesupport (>= 3.2.0)
93   - charlock_holmes (0.6.8)
  103 + charlock_holmes (0.6.9)
94 104 childprocess (0.3.2)
95 105 ffi (~> 1.0.6)
96 106 chosen-rails (0.9.8.3)
... ... @@ -260,6 +270,8 @@ GEM
260 270 posix-spawn (~> 0.3.6)
261 271 yajl-ruby (~> 1.1.0)
262 272 pyu-ruby-sasl (0.0.3.3)
  273 + quiet_assets (1.0.1)
  274 + railties (~> 3.1)
263 275 rack (1.4.1)
264 276 rack-cache (1.2)
265 277 rack (>= 0.4)
... ... @@ -411,7 +423,6 @@ DEPENDENCIES
411 423 capybara
412 424 capybara-webkit
413 425 carrierwave
414   - charlock_holmes
415 426 chosen-rails
416 427 coffee-rails (= 3.2.2)
417 428 colored
... ... @@ -432,6 +443,7 @@ DEPENDENCIES
432 443 grack!
433 444 grape (~> 0.2.1)
434 445 grit!
  446 + grit_ext!
435 447 growl
436 448 guard-rspec
437 449 guard-spinach
... ... @@ -454,6 +466,7 @@ DEPENDENCIES
454 466 pg
455 467 pry
456 468 pygments.rb (= 0.3.1)
  469 + quiet_assets (= 1.0.1)
457 470 rack-mini-profiler
458 471 rails (= 3.2.8)
459 472 rails-dev-tweaks
... ...
Procfile.production
... ... @@ -1,2 +0,0 @@
1   -web: bundle exec rails s -p $PORT -e production
2   -worker: bundle exec rake environment resque:work RAILS_ENV=production QUEUE=*
ROADMAP.md 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +## GitLab Roadmap
  2 +
  3 +### Common
  4 +
  5 +* Help page for service tasks like repos import, backup etc
  6 +* Hide last push widget after following link
  7 +* Add comment events
  8 +* Dashboard/Project activity events filter
  9 +
  10 +### Issues
  11 +
  12 +* labels autocomplete via jquery autocomplete
  13 +* Import/Export issues
  14 +* Form: Assign to me link right to the selectbox
  15 +
  16 +### Merge Request
  17 +
  18 +* CI build status
  19 +* Save code fragments with MR comments
... ...
VERSION
1   -3.0.3
  1 +3.1.0pre
... ...
app/assets/images/event_filter_comments.png 0 → 100644

750 Bytes

app/assets/images/event_filter_merged.png 0 → 100644

463 Bytes

app/assets/images/event_filter_push.png 0 → 100644

632 Bytes

app/assets/images/event_filter_team.png 0 → 100644

1.31 KB

app/assets/javascripts/application.js
... ... @@ -17,6 +17,8 @@
17 17 //= require modernizr
18 18 //= require chosen-jquery
19 19 //= require raphael
  20 +//= require g.raphael-min
  21 +//= require g.bar-min
20 22 //= require branch-graph
21 23 //= require ace-src-noconflict/ace
22 24 //= require_tree .
... ...
app/assets/javascripts/gfm_auto_complete.js.coffee
  1 +# Creates the variables for setting up GFM auto-completion
1 2  
2   -###
3   - Creates the variables for setting up GFM auto-completion
4   -###
5 3 # Emoji
6   -window.autocompleteEmojiData = [];
7   -window.autocompleteEmojiTemplate = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>";
  4 +data = []
  5 +template = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>"
  6 +window.autocompleteEmoji = {data, template}
8 7  
9 8 # Team Members
10   -window.autocompleteMembersUrl = "";
11   -window.autocompleteMembersParams =
12   - private_token: ""
13   - page: 1
14   -window.autocompleteMembersData = [];
  9 +url = '';
  10 +params = {private_token: '', page: 1}
  11 +window.autocompleteMembers = {data, url, params}
15 12  
  13 +# Add GFM auto-completion to all input fields, that accept GFM input.
  14 +window.setupGfmAutoComplete = ->
  15 + $input = $('.js-gfm-input')
16 16  
  17 + # Emoji
  18 + $input.atWho ':',
  19 + data: autocompleteEmoji.data,
  20 + tpl: autocompleteEmoji.template
17 21  
18   -###
19   - Add GFM auto-completion to all input fields, that accept GFM input.
20   -###
21   -window.setupGfmAutoComplete = ->
22   - ###
23   - Emoji
24   - ###
25   - $('.gfm-input').atWho ':',
26   - data: autocompleteEmojiData,
27   - tpl: autocompleteEmojiTemplate
28   -
29   - ###
30   - Team Members
31   - ###
32   - $('.gfm-input').atWho '@', (query, callback) ->
  22 + # Team Members
  23 + $input.atWho '@', (query, callback) ->
33 24 (getMoreMembers = ->
34   - $.getJSON(autocompleteMembersUrl, autocompleteMembersParams)
35   - .success (members) ->
36   - # pick the data we need
37   - newMembersData = $.map members, (m) -> m.name
38   -
39   - # add the new page of data to the rest
40   - $.merge autocompleteMembersData, newMembersData
41   -
42   - # show the pop-up with a copy of the current data
43   - callback autocompleteMembersData[..]
44   -
45   - # are we past the last page?
46   - if newMembersData.length == 0
47   - # set static data and stop callbacks
48   - $('.gfm-input').atWho '@',
49   - data: autocompleteMembersData
50   - callback: null
51   - else
52   - # get next page
53   - getMoreMembers()
  25 + $.getJSON(autocompleteMembers.url, autocompleteMembers.params).success (members) ->
  26 + # pick the data we need
  27 + newMembersData = $.map members, (m) -> m.name
  28 +
  29 + # add the new page of data to the rest
  30 + $.merge autocompleteMembers.data, newMembersData
  31 +
  32 + # show the pop-up with a copy of the current data
  33 + callback autocompleteMembers.data[..]
  34 +
  35 + # are we past the last page?
  36 + if newMembersData.length is 0
  37 + # set static data and stop callbacks
  38 + $input.atWho '@',
  39 + data: autocompleteMembers.data
  40 + callback: null
  41 + else
  42 + # get next page
  43 + getMoreMembers()
54 44  
55 45 # so the next request gets the next page
56   - autocompleteMembersParams.page += 1;
57   - ).call();
58 46 \ No newline at end of file
  47 + autocompleteMembers.params.page += 1
  48 + ).call()
... ...
app/assets/javascripts/graph.js.coffee
... ... @@ -1,10 +0,0 @@
1   -initGraphNav = ->
2   - $('.graph svg').css 'position', 'relative'
3   -
4   - $('body').bind 'keyup', (e) ->
5   - if e.keyCode is 37 # left
6   - $('.graph svg').animate left: '+=400'
7   - else if e.keyCode is 39 # right
8   - $('.graph svg').animate left: '-=400'
9   -
10   -window.initGraphNav = initGraphNav
app/assets/javascripts/issues.js
1   -function switchToNewIssue(form){
  1 +function switchToNewIssue(){
2 2 $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
3   - $(".issues_content").after(form);
4 3 $('select#issue_assignee_id').chosen();
5 4 $('select#issue_milestone_id').chosen();
6 5 $("#new_issue_dialog").show("fade", { direction: "right" }, 150);
... ... @@ -10,9 +9,8 @@ function switchToNewIssue(form){
10 9 });
11 10 }
12 11  
13   -function switchToEditIssue(form){
  12 +function switchToEditIssue(){
14 13 $(".issues_content").hide("fade", { direction: "left" }, 150, function(){
15   - $(".issues_content").after(form);
16 14 $('select#issue_assignee_id').chosen();
17 15 $('select#issue_milestone_id').chosen();
18 16 $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);
... ... @@ -33,8 +31,8 @@ function switchFromEditIssue(){
33 31 function backToIssues(){
34 32 $("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){
35 33 $(".issues_content").show("fade", { direction: "left" }, 150, function() {
36   - $("#edit_issue_dialog").remove();
37   - $("#new_issue_dialog").remove();
  34 + $("#edit_issue_dialog").html("");
  35 + $("#new_issue_dialog").html("");
38 36 $('.add_new').show();
39 37 });
40 38 });
... ...
app/assets/javascripts/loader.js.coffee
... ... @@ -1,5 +0,0 @@
1   -Loader =
2   - html: (width) ->
3   - $('<img>').attr src: '/assets/ajax-loader.gif', width: width
4   -
5   -window.Loader = Loader
app/assets/javascripts/main.js.coffee
... ... @@ -7,29 +7,32 @@ window.slugify = (text) -&gt;
7 7 window.ajaxGet = (url) ->
8 8 $.ajax({type: "GET", url: url, dataType: "script"})
9 9  
10   - # Disable button if text field is empty
  10 +# Disable button if text field is empty
11 11 window.disableButtonIfEmptyField = (field_selector, button_selector) ->
12 12 field = $(field_selector)
13 13 closest_submit = field.closest("form").find(button_selector)
14 14  
15 15 closest_submit.disable() if field.val() is ""
16 16  
17   - field.on "keyup", ->
18   - if $(this).val() is ""
  17 + field.on "input", ->
  18 + if $(@).val() is ""
19 19 closest_submit.disable()
20 20 else
21 21 closest_submit.enable()
22 22  
23 23 $ ->
24 24 # Click a .one_click_select field, select the contents
25   - $(".one_click_select").live 'click', -> $(this).select()
  25 + $(".one_click_select").on 'click', -> $(@).select()
26 26  
27 27 # Initialize chosen selects
28 28 $('select.chosen').chosen()
29 29  
  30 + # Initialize tooltips
  31 + $('.has_tooltip').tooltip()
  32 +
30 33 # Disable form buttons while a form is submitting
31 34 $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
32   - buttons = $('[type="submit"]', this)
  35 + buttons = $('[type="submit"]', @)
33 36  
34 37 switch e.type
35 38 when 'ajax:beforeSend', 'submit'
... ... @@ -38,7 +41,7 @@ $ -&gt;
38 41 buttons.enable()
39 42  
40 43 # Show/Hide the profile menu when hovering the account box
41   - $('.account-box').hover -> $(this).toggleClass('hover')
  44 + $('.account-box').hover -> $(@).toggleClass('hover')
42 45  
43 46 # Focus search field by pressing 's' key
44 47 $(document).keypress (e) ->
... ... @@ -52,22 +55,22 @@ $ -&gt;
52 55  
53 56 # Commit show suppressed diff
54 57 $(".supp_diff_link").bind "click", ->
55   - $(this).next('table').show()
56   - $(this).remove()
  58 + $(@).next('table').show()
  59 + $(@).remove()
57 60  
58 61 # Note markdown preview
59 62 $(document).on 'click', '#preview-link', (e) ->
60   - $('#preview-note').text('Loading...')
  63 + $('#preview-note').text 'Loading...'
61 64  
62   - previewLinkText = if $(this).text() == 'Preview' then 'Edit' else 'Preview'
63   - $(this).text(previewLinkText)
  65 + previewLinkText = if $(@).text() is 'Preview' then 'Edit' else 'Preview'
  66 + $(@).text previewLinkText
64 67  
65 68 note = $('#note_note').val()
66 69  
67   - if note.trim().length == 0
68   - $('#preview-note').text("Nothing to preview.")
  70 + if note.trim().length is 0
  71 + $('#preview-note').text 'Nothing to preview.'
69 72 else
70   - $.post $(this).attr('href'), {note: note}, (data) ->
  73 + $.post $(@).attr('href'), {note: note}, (data) ->
71 74 $('#preview-note').html(data)
72 75  
73 76 $('#preview-note, #note_note').toggle()
... ... @@ -79,14 +82,14 @@ $ -&gt;
79 82 $.fn.extend chosen: (options) ->
80 83 default_options = search_contains: "true"
81 84 $.extend default_options, options
82   - _chosen.apply this, [default_options]
  85 + _chosen.apply @, [default_options]
83 86  
84 87 # Disable an element and add the 'disabled' Bootstrap class
85 88 $.fn.extend disable: ->
86   - $(this).attr('disabled', 'disabled').addClass('disabled')
  89 + $(@).attr('disabled', 'disabled').addClass('disabled')
87 90  
88 91 # Enable an element and remove the 'disabled' Bootstrap class
89 92 $.fn.extend enable: ->
90   - $(this).removeAttr('disabled').removeClass('disabled')
  93 + $(@).removeAttr('disabled').removeClass('disabled')
91 94  
92 95 )(jQuery)
... ...
app/assets/javascripts/merge_requests.js
... ... @@ -115,4 +115,15 @@ var MergeRequest = {
115 115 $(".merge_in_progress").hide();
116 116 $(".automerge_widget.already_cannot_be_merged").show();
117 117 }
  118 +};
  119 +
  120 +/*
  121 + * Filter merge requests
  122 + */
  123 +function merge_requestsPage() {
  124 + $("#assignee_id").chosen();
  125 + $("#milestone_id").chosen();
  126 + $("#milestone_id, #assignee_id").on("change", function(){
  127 + $(this).closest("form").submit();
  128 + });
118 129 }
... ...
app/assets/javascripts/milestones.js.coffee
... ... @@ -5,3 +5,10 @@ $ -&gt;
5 5 $('.milestone-issue-filter li').toggleClass('active')
6 6 $('.milestone-issue-filter tr[data-closed]').toggleClass('hide')
7 7 false
  8 +
  9 + $('.milestone-merge-requests-filter tr[data-closed]').addClass('hide')
  10 +
  11 + $('.milestone-merge-requests-filter ul.nav li a').click ->
  12 + $('.milestone-merge-requests-filter li').toggleClass('active')
  13 + $('.milestone-merge-requests-filter tr[data-closed]').toggleClass('hide')
  14 + false
... ...
app/assets/javascripts/projects.js.coffee
... ... @@ -22,3 +22,10 @@ $ -&gt;
22 22 # Ref switcher
23 23 $('.project-refs-select').on 'change', ->
24 24 $(@).parents('form').submit()
  25 +
  26 +class @GraphNav
  27 + @init: ->
  28 + $('.graph svg').css 'position', 'relative'
  29 + $('body').bind 'keyup', (e) ->
  30 + $('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left
  31 + $('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right
... ...
app/assets/javascripts/snippets.js.coffee
... ... @@ -1,6 +0,0 @@
1   -$ ->
2   - $('#snippets-table .snippet').live 'click', (e) ->
3   - if e.target.nodeName isnt 'A' and e.target.nodeName isnt 'INPUT'
4   - location.href = $(@).attr 'url'
5   - e.stopPropagation()
6   - false
app/assets/javascripts/tree.js.coffee
... ... @@ -17,23 +17,21 @@ $ -&gt;
17 17 "ajax:beforeSend": -> $('.tree_progress').addClass("loading")
18 18 "ajax:complete": -> $('.tree_progress').removeClass("loading")
19 19  
20   -# Maintain forward/back history while browsing the file tree
21   -
22   -((window) ->
23   - History = window.History
24   - $ = window.jQuery
25   - document = window.document
26   -
27   - # Check to see if History.js is enabled for our Browser
28   - unless History.enabled
29   - return false
30   -
31   - $ ->
32   - $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) ->
33   - History.pushState(null, null, $(@).attr('href'))
34   - return false
35   -
36   - History.Adapter.bind window, 'statechange', ->
37   - state = History.getState()
38   - window.ajaxGet(state.url)
39   -)(window)
  20 + # Maintain forward/back history while browsing the file tree
  21 + ((window) ->
  22 + History = window.History
  23 + $ = window.jQuery
  24 + document = window.document
  25 +
  26 + # Check to see if History.js is enabled for our Browser
  27 + unless History.enabled
  28 + return false
  29 +
  30 + $('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live 'click', (e) ->
  31 + History.pushState(null, null, $(@).attr('href'))
  32 + return false
  33 +
  34 + History.Adapter.bind window, 'statechange', ->
  35 + state = History.getState()
  36 + window.ajaxGet(state.url)
  37 + )(window)
... ...
app/assets/stylesheets/common.scss
... ... @@ -670,3 +670,16 @@ pre {
670 670 padding:0;
671 671 }
672 672 }
  673 +
  674 +.milestone .progress {
  675 + margin-bottom: 0;
  676 + margin-top:4px;
  677 +}
  678 +
  679 +.float-link {
  680 + float:left;
  681 + margin-right:15px;
  682 + .s16 {
  683 + margin-right:5px;
  684 + }
  685 +}
... ...
app/assets/stylesheets/gitlab_bootstrap/common.scss
... ... @@ -26,8 +26,10 @@
26 26 .underlined { border-bottom: 1px solid #CCC; }
27 27 .no-borders { border:none; }
28 28 .vlink { color: $link_color !important; }
  29 +.underlined_link { text-decoration: underline; }
29 30 .borders { border: 1px solid #ccc; @include shade; }
30 31 .hint { font-style: italic; color: #999; }
  32 +.light { color: #888 }
31 33  
32 34 /** PILLS & TABS**/
33 35 .nav-pills a:hover { background-color:#888; }
... ... @@ -66,10 +68,10 @@
66 68 .alert-message.error { @extend .alert-error; }
67 69  
68 70 /** AVATARS **/
69   -img.avatar { float:left; margin-right:15px; width:40px; border:1px solid #ddd; padding:1px; }
70   -img.avatar.s16 { width:16px; height:16px; }
71   -img.avatar.s24 { width:24px; height:24px; }
72   -img.avatar.s32 { width:32px; height:32px; }
  71 +img.avatar { float:left; margin-right:12px; width:40px; border:1px solid #ddd; padding:1px; }
  72 +img.avatar.s16 { width:16px; height:16px; margin-right:6px; }
  73 +img.avatar.s24 { width:24px; height:24px; margin-right:8px; }
  74 +img.avatar.s32 { width:32px; height:32px; margin-right:10px; }
73 75 img.lil_av { padding-left: 4px; padding-right:3px; }
74 76  
75 77 /** HELPERS **/
... ...
app/assets/stylesheets/gitlab_bootstrap/files.scss
... ... @@ -157,10 +157,15 @@
157 157 font-size:12px !important;
158 158 }
159 159  
160   - table.highlighttable .linenodiv pre {
161   - text-align: right;
162   - padding-right: 4px;
163   - color:#666;
  160 + table.highlighttable .linenodiv {
  161 + a {
  162 + color: #666;
  163 + }
  164 + pre {
  165 + text-align: right;
  166 + padding-right: 4px;
  167 + color:#666;
  168 + }
164 169 }
165 170 }
166 171 }
... ...
app/assets/stylesheets/gitlab_bootstrap/lists.scss
... ... @@ -21,7 +21,7 @@ ul {
21 21 .author { color: #999; }
22 22  
23 23 p {
24   - padding-top:5px;
  24 + padding-top: 1px;
25 25 margin:0;
26 26 color:#222;
27 27 img {
... ... @@ -31,3 +31,11 @@ ul {
31 31 }
32 32 }
33 33 }
  34 +
  35 +ol, ul {
  36 + &.styled {
  37 + li {
  38 + padding:2px;
  39 + }
  40 + }
  41 +}
... ...
app/assets/stylesheets/gitlab_bootstrap/tables.scss
... ... @@ -34,6 +34,11 @@ table {
34 34 border-color:#f1f1f1;
35 35 line-height:28px;
36 36  
  37 + .s16 {
  38 + margin-top: 5px;
  39 + margin-right: 5px;
  40 + }
  41 +
37 42 &:first-child {
38 43 border-left:1px solid #bbb;
39 44 }
... ...
app/assets/stylesheets/main.scss
... ... @@ -3,10 +3,11 @@
3 3 @import 'font-awesome';
4 4  
5 5 /** GitLab colors **/
6   -$link_color:#3A89A3;
7   -$blue_link: #2fa0bb;
8   -$style_color: #474d57;
  6 +$link_color: #3A89A3;
  7 +$blue_link: #2FA0BB;
  8 +$style_color: #474D57;
9 9 $hover: #D9EDF7;
  10 +$hover_border: #ADF;
10 11  
11 12 /** GitLab Fonts **/
12 13 @font-face { font-family: Korolev; src: font-url('korolev-medium-compressed.otf'); }
... ... @@ -19,9 +20,9 @@ $hover: #D9EDF7;
19 20 }
20 21  
21 22 @mixin solid_shade {
22   - -moz-box-shadow: 0 0 0 3px #eee;
23   - -webkit-box-shadow: 0 0 0 3px #eee;
24   - box-shadow: 0 0 0 3px #eee;
  23 + -moz-box-shadow: 0 0 0 3px #f1f1f1;
  24 + -webkit-box-shadow: 0 0 0 3px #f1f1f1;
  25 + box-shadow: 0 0 0 3px #f1f1f1;
25 26 }
26 27  
27 28 @mixin border-radius($radius) {
... ... @@ -64,6 +65,14 @@ $hover: #D9EDF7;
64 65 background-image: -o-linear-gradient($from, $to);
65 66 }
66 67  
  68 +@mixin bg-light-gray-gradient {
  69 + background:#f1f1f1;
  70 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
  71 + background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  72 + background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  73 + background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  74 +}
  75 +
67 76 @mixin bg-gray-gradient {
68 77 background:#eee;
69 78 background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
... ...
app/assets/stylesheets/ref_select.scss
... ... @@ -19,31 +19,66 @@
19 19 margin-right: 10px;
20 20  
21 21 .chzn-drop {
22   - margin:7px 0;
23 22 min-width: 400px;
24   - border: 2px solid $blue_link;
25   - @include border-radius(4px);
  23 + .chzn-results {
  24 + max-height:300px;
  25 + }
  26 + .chzn-search input {
  27 + min-width:365px;
  28 + }
  29 + }
  30 +}
  31 +
  32 +/** Fix for Search Dropdown Border **/
  33 +.chzn-container {
  34 + .chzn-search {
  35 + input:focus {
  36 + -webkit-box-shadow: none;
  37 + -moz-box-shadow: none;
  38 + box-shadow: none;
  39 + }
  40 + }
  41 +
  42 + .chzn-drop {
  43 + margin:7px 0;
  44 + min-width: 200px;
  45 + border: 1px solid #bbb;
  46 + border-radius:0;
26 47  
27 48 .chzn-results {
  49 + margin-top: 5px;
28 50 max-height:300px;
29 51  
30 52 .group-result {
31   - color: $blue_link;
  53 + color: $style_color;
  54 + border-bottom: 1px solid #EEE;
  55 + padding: 8px;
32 56 }
33 57 .active-result {
  58 + border-radius: 0;
  59 +
34 60 &.highlighted {
35   - background: $blue_link;
  61 + background: $hover;
  62 + color: $style_color;
  63 + }
  64 + &.result-selected {
  65 + background: #EEE;
  66 + border-left: 4px solid #CCC;
36 67 }
37 68 }
38 69 }
39 70  
40   - .chzn-search input {
41   - min-width:365px;
  71 + .chzn-search {
  72 + @include bg-gray-gradient;
  73 + input {
  74 + min-width:165px;
  75 + border-color: #CCC;
  76 + }
42 77 }
43 78 }
44 79  
45 80 .chzn-single {
46   - @include bg-gray-gradient;
  81 + @include bg-light-gray-gradient;
47 82  
48 83 div {
49 84 background:transparent;
... ... @@ -55,14 +90,3 @@
55 90 }
56 91 }
57 92 }
58   -
59   -/** Fix for Search Dropdown Border **/
60   -.chzn-container {
61   - .chzn-search {
62   - input:focus {
63   - -webkit-box-shadow: none;
64   - -moz-box-shadow: none;
65   - box-shadow: none;
66   - }
67   - }
68   -}
... ...
app/assets/stylesheets/sections/commits.scss
... ... @@ -47,12 +47,15 @@
47 47 padding-left: 32px;
48 48 }
49 49  
50   - .author,
51   - .committer {
  50 + .author a,
  51 + .committer a {
52 52 font-size:14px;
53 53 line-height:22px;
54 54 text-shadow:0 1px 1px #fff;
55 55 color:#777;
  56 + &:hover {
  57 + color: #999;
  58 + }
56 59 }
57 60  
58 61 .avatar {
... ... @@ -227,6 +230,9 @@
227 230  
228 231 .commit-author-name {
229 232 color: #777;
  233 + &:hover {
  234 + color: #999;
  235 + }
230 236 }
231 237 }
232 238  
... ...
app/assets/stylesheets/sections/events.scss
... ... @@ -43,6 +43,7 @@
43 43 .event-body {
44 44 p {
45 45 color:#555;
  46 + padding-top: 5px;
46 47 }
47 48 .event-info {
48 49 color:#666;
... ... @@ -115,3 +116,29 @@
115 116 margin: -3px;
116 117 }
117 118 }
  119 +
  120 +/**
  121 + * Event filter
  122 + *
  123 + */
  124 +.event_filter {
  125 + position: absolute;
  126 + width: 40px;
  127 + margin-left: -50px;
  128 +
  129 + .filter_icon {
  130 + float: left;
  131 + border-left: 3px solid #4bc;
  132 + padding: 7px;
  133 + background: #f9f9f9;
  134 + margin-bottom: 10px;
  135 + img {
  136 + width:20px;
  137 + }
  138 +
  139 + &.inactive {
  140 + border-left: 3px solid #EEE;
  141 + opacity: 0.5;
  142 + }
  143 + }
  144 +}
... ...
app/assets/stylesheets/sections/issues.scss
... ... @@ -44,7 +44,7 @@
44 44  
45 45 img.avatar {
46 46 width:32px;
47   - margin-top:4px;
  47 + margin-top:1px;
48 48 }
49 49 }
50 50 }
... ...
app/assets/stylesheets/sections/merge_requests.scss
... ... @@ -71,7 +71,7 @@ li.merge_request {
71 71 padding:7px 10px;
72 72 img.avatar {
73 73 width: 32px;
74   - margin-top: 4px;
  74 + margin-top: 1px;
75 75 }
76 76 p {
77 77 padding: 0px;
... ... @@ -121,3 +121,20 @@ li.merge_request {
121 121 .mr_direction_tip {
122 122 margin-top:40px
123 123 }
  124 +
  125 +.merge_requests_form_box {
  126 + @extend .main_box;
  127 + .merge_requests_middle_box {
  128 + @extend .middle_box_content;
  129 + height:30px;
  130 + .merge_requests_assignee {
  131 + @extend .span6;
  132 + float:left;
  133 + }
  134 + .merge_requests_milestone {
  135 + @extend .span4;
  136 + float:left;
  137 + }
  138 + }
  139 +}
  140 +
... ...
app/assets/stylesheets/sections/tree.scss
... ... @@ -57,10 +57,7 @@
57 57 padding-right: 8px;
58 58  
59 59 img.avatar {
60   - border: 0 none;
61   - float: none;
62   - margin-right: 0;
63   - padding: 0;
  60 + margin-top: 0;
64 61 width: 16px;
65 62 }
66 63 }
... ... @@ -75,6 +72,15 @@
75 72 }
76 73 }
77 74 }
  75 +
  76 + .blame {
  77 + img.avatar {
  78 + border: 0 none;
  79 + float: none;
  80 + margin: 0;
  81 + padding: 0;
  82 + }
  83 + }
78 84 }
79 85  
80 86 .tree-btn-group {
... ...
app/assets/stylesheets/themes/ui_mars.scss
... ... @@ -37,9 +37,6 @@
37 37 background-image: -o-linear-gradient(#595D63 6.6%, #202227);
38 38 background-position:0 0;
39 39 color:#fff;
40   - i {
41   - @extend .icon-white;
42   - }
43 40 }
44 41  
45 42 border: 1px solid #31363E;
... ...
app/assets/stylesheets/themes/ui_modern.scss
... ... @@ -70,9 +70,6 @@
70 70 color:#ccc;
71 71 &:hover {
72 72 color:#fff;
73   - i {
74   - @extend .icon-white;
75   - }
76 73 }
77 74 border: none;
78 75 box-shadow:none;
... ...
app/contexts/commit_load_context.rb
... ... @@ -21,7 +21,7 @@ class CommitLoadContext &lt; BaseContext
21 21 result[:notes_count] = line_notes.count + project.commit_notes(commit).count
22 22  
23 23 begin
24   - result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff]
  24 + result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff]
25 25 rescue Grit::Git::GitTimeout
26 26 result[:suppress_diff] = true
27 27 result[:status] = :huge_commit
... ...
app/contexts/merge_requests_load_context.rb
  1 +# Build collection of Merge Requests
  2 +# based on filtering passed via params for @project
1 3 class MergeRequestsLoadContext < BaseContext
2 4 def execute
3 5 type = params[:f]
... ... @@ -9,8 +11,21 @@ class MergeRequestsLoadContext &lt; BaseContext
9 11 when 'closed' then merge_requests.closed
10 12 when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
11 13 else merge_requests.opened
12   - end.page(params[:page]).per(20)
  14 + end
13 15  
14   - merge_requests.includes(:author, :project).order("closed, created_at desc")
  16 + merge_requests = merge_requests.page(params[:page]).per(20)
  17 + merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc")
  18 +
  19 + # Filter by specific assignee_id (or lack thereof)?
  20 + if params[:assignee_id].present?
  21 + merge_requests = merge_requests.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id]))
  22 + end
  23 +
  24 + # Filter by specific milestone_id (or lack thereof)?
  25 + if params[:milestone_id].present?
  26 + merge_requests = merge_requests.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
  27 + end
  28 +
  29 + merge_requests
15 30 end
16 31 end
... ...
app/contexts/search_context.rb
... ... @@ -13,6 +13,7 @@ class SearchContext
13 13 result[:projects] = Project.where(id: project_ids).search(query).limit(10)
14 14 result[:merge_requests] = MergeRequest.where(project_id: project_ids).search(query).limit(10)
15 15 result[:issues] = Issue.where(project_id: project_ids).search(query).limit(10)
  16 + result[:wiki_pages] = Wiki.where(project_id: project_ids).search(query).limit(10)
16 17 result
17 18 end
18 19  
... ... @@ -20,7 +21,8 @@ class SearchContext
20 21 @result ||= {
21 22 projects: [],
22 23 merge_requests: [],
23   - issues: []
  24 + issues: [],
  25 + wiki_pages: []
24 26 }
25 27 end
26 28 end
... ...
app/controllers/application_controller.rb
... ... @@ -9,19 +9,28 @@ class ApplicationController &lt; ActionController::Base
9 9 helper_method :abilities, :can?
10 10  
11 11 rescue_from Gitlab::Gitolite::AccessDenied do |exception|
  12 + log_exception(exception)
12 13 render "errors/gitolite", layout: "errors", status: 500
13 14 end
14 15  
15 16 rescue_from Encoding::CompatibilityError do |exception|
  17 + log_exception(exception)
16 18 render "errors/encoding", layout: "errors", status: 500
17 19 end
18 20  
19 21 rescue_from ActiveRecord::RecordNotFound do |exception|
  22 + log_exception(exception)
20 23 render "errors/not_found", layout: "errors", status: 404
21 24 end
22 25  
23 26 protected
24 27  
  28 + def log_exception(exception)
  29 + application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
  30 + application_trace.map!{ |t| " #{t}\n" }
  31 + logger.error "\n#{exception.class.name} (#{exception.message}):\n#{application_trace.join}"
  32 + end
  33 +
25 34 def reject_blocked!
26 35 if current_user && current_user.blocked
27 36 sign_out current_user
... ...
app/controllers/blob_controller.rb
1 1 # Controller for viewing a file's blame
2 2 class BlobController < ProjectResourceController
3 3 include ExtractsPath
4   - include Gitlab::Encode
5 4  
6 5 # Authorize
7 6 before_filter :authorize_read_project!
... ... @@ -12,16 +11,9 @@ class BlobController &lt; ProjectResourceController
12 11  
13 12 def show
14 13 if @tree.is_blob?
15   - if @tree.text?
16   - encoding = detect_encoding(@tree.data)
17   - mime_type = encoding ? "text/plain; charset=#{encoding}" : "text/plain"
18   - else
19   - mime_type = @tree.mime_type
20   - end
21   -
22 14 send_data(
23 15 @tree.data,
24   - type: mime_type,
  16 + type: @tree.mime_type,
25 17 disposition: 'inline',
26 18 filename: @tree.name
27 19 )
... ...
app/controllers/dashboard_controller.rb
1 1 class DashboardController < ApplicationController
2 2 respond_to :html
3 3  
  4 + before_filter :event_filter, only: :index
  5 +
4 6 def index
5 7 @groups = Group.where(id: current_user.projects.pluck(:group_id))
6 8 @projects = current_user.projects_with_events
7 9 @projects = @projects.page(params[:page]).per(30)
8 10  
9   - @events = Event.in_projects(current_user.project_ids).limit(20).offset(params[:offset] || 0)
  11 + @events = Event.in_projects(current_user.project_ids)
  12 + @events = @event_filter.apply_filter(@events)
  13 + @events = @events.limit(20).offset(params[:offset] || 0)
  14 +
10 15 @last_push = current_user.recent_push
11 16  
12 17 respond_to do |format|
... ... @@ -34,4 +39,8 @@ class DashboardController &lt; ApplicationController
34 39 format.atom { render layout: false }
35 40 end
36 41 end
  42 +
  43 + def event_filter
  44 + @event_filter ||= EventFilter.new(params[:event_filter])
  45 + end
37 46 end
... ...
app/controllers/milestones_controller.rb
... ... @@ -31,7 +31,8 @@ class MilestonesController &lt; ProjectResourceController
31 31  
32 32 def show
33 33 @issues = @milestone.issues
34   - @users = @milestone.participants
  34 + @users = UserDecorator.decorate(@milestone.participants)
  35 + @merge_requests = @milestone.merge_requests
35 36  
36 37 respond_to do |format|
37 38 format.html
... ...
app/controllers/profile_controller.rb
... ... @@ -22,7 +22,7 @@ class ProfileController &lt; ApplicationController
22 22 flash[:notice] = "Password was successfully updated. Please login with it"
23 23 redirect_to new_user_session_path
24 24 else
25   - render action: "password"
  25 + render 'account'
26 26 end
27 27 end
28 28  
... ...
app/controllers/projects_controller.rb
1   -require Rails.root.join('lib', 'gitlab', 'graph_commit')
  1 +require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
2 2  
3 3 class ProjectsController < ProjectResourceController
4 4 skip_before_filter :project, only: [:new, :create]
... ... @@ -79,7 +79,9 @@ class ProjectsController &lt; ProjectResourceController
79 79 end
80 80  
81 81 def graph
82   - @days_json, @commits_json = Gitlab::GraphCommit.to_graph(project)
  82 + graph = Gitlab::Graph::JsonBuilder.new(project)
  83 +
  84 + @days_json, @commits_json = graph.days_json, graph.commits_json
83 85 end
84 86  
85 87 def destroy
... ...
app/controllers/refs_controller.rb
1 1 class RefsController < ProjectResourceController
2   - include Gitlab::Encode
3 2  
4 3 # Authorize
5 4 before_filter :authorize_read_project!
... ...
app/controllers/repositories_controller.rb
... ... @@ -16,9 +16,14 @@ class RepositoriesController &lt; ProjectResourceController
16 16 @tags = @project.tags
17 17 end
18 18  
  19 + def stats
  20 + @stats = Gitlab::GitStats.new(@project.repo, @project.root_ref)
  21 + @graph = @stats.graph
  22 + end
  23 +
19 24 def archive
20 25 unless can?(current_user, :download_code, @project)
21   - render_404 and return
  26 + render_404 and return
22 27 end
23 28  
24 29  
... ...
app/controllers/search_controller.rb
... ... @@ -5,5 +5,6 @@ class SearchController &lt; ApplicationController
5 5 @projects = result[:projects]
6 6 @merge_requests = result[:merge_requests]
7 7 @issues = result[:issues]
  8 + @wiki_pages = result[:wiki_pages]
8 9 end
9 10 end
... ...
app/controllers/tree_controller.rb
... ... @@ -26,15 +26,14 @@ class TreeController &lt; ProjectResourceController
26 26 end
27 27  
28 28 def update
29   - file_editor = Gitlab::FileEditor.new(current_user, @project, @ref)
30   - update_status = file_editor.update(
31   - @path,
  29 + edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
  30 + updated_successfully = edit_file_action.commit!(
32 31 params[:content],
33 32 params[:commit_message],
34 33 params[:last_commit]
35 34 )
36 35  
37   - if update_status
  36 + if updated_successfully
38 37 redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
39 38 else
40 39 flash[:notice] = "Your changes could not be commited, because the file has been changed"
... ...
app/decorators/commit_decorator.rb
... ... @@ -47,21 +47,15 @@ class CommitDecorator &lt; ApplicationDecorator
47 47 # Otherwise it will link to the author email as specified in the commit.
48 48 #
49 49 # options:
50   - # avatar: true will prepend avatar image
51   - def author_link(options)
52   - text = if options[:avatar]
53   - avatar = h.image_tag h.gravatar_icon(author_email), class: "avatar", width: 16
54   - "#{avatar} #{author_name}"
55   - else
56   - author_name
57   - end
58   - team_member = @project.try(:team_member_by_name_or_email, author_name, author_email)
  50 + # avatar: true will prepend the avatar image
  51 + # size: size of the avatar image in px
  52 + def author_link(options = {})
  53 + person_link(options.merge source: :author)
  54 + end
59 55  
60   - if team_member.nil?
61   - h.mail_to author_email, text.html_safe, class: "commit-author-link"
62   - else
63   - h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-author-link"
64   - end
  56 + # Just like #author_link but for the committer.
  57 + def committer_link(options = {})
  58 + person_link(options.merge source: :committer)
65 59 end
66 60  
67 61 protected
... ... @@ -69,4 +63,30 @@ class CommitDecorator &lt; ApplicationDecorator
69 63 def no_commit_message
70 64 "--no commit message"
71 65 end
  66 +
  67 + # Private: Returns a link to a person. If the person has a matching user and
  68 + # is a member of the current @project it will link to the team member page.
  69 + # Otherwise it will link to the person email as specified in the commit.
  70 + #
  71 + # options:
  72 + # source: one of :author or :committer
  73 + # avatar: true will prepend the avatar image
  74 + # size: size of the avatar image in px
  75 + def person_link(options = {})
  76 + source_name = send "#{options[:source]}_name".to_sym
  77 + source_email = send "#{options[:source]}_email".to_sym
  78 + text = if options[:avatar]
  79 + avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size]
  80 + %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
  81 + else
  82 + source_name
  83 + end
  84 + team_member = @project.try(:team_member_by_name_or_email, source_name, source_email)
  85 +
  86 + if team_member.nil?
  87 + h.mail_to source_email, text.html_safe, class: "commit-#{options[:source]}-link"
  88 + else
  89 + h.link_to text, h.project_team_member_path(@project, team_member), class: "commit-#{options[:source]}-link"
  90 + end
  91 + end
72 92 end
... ...
app/decorators/tree_decorator.rb
... ... @@ -8,14 +8,14 @@ class TreeDecorator &lt; ApplicationDecorator
8 8  
9 9 #parts = parts[0...-1] if is_blob?
10 10  
11   - yield(h.link_to("..", "#", remote: true)) if parts.count > max_links
  11 + yield(h.link_to("..", "#")) if parts.count > max_links
12 12  
13 13 parts.each do |part|
14 14 part_path = File.join(part_path, part) unless part_path.empty?
15 15 part_path = part if part_path.empty?
16 16  
17 17 next unless parts.last(2).include?(part) if parts.count > max_links
18   - yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path)), remote: true))
  18 + yield(h.link_to(h.truncate(part, length: 40), h.project_tree_path(project, h.tree_join(ref, part_path))))
19 19 end
20 20 end
21 21 end
... ...
app/decorators/user_decorator.rb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +class UserDecorator < ApplicationDecorator
  2 + decorates :user
  3 +
  4 + def avatar_image size = 16
  5 + h.image_tag h.gravatar_icon(self.email, size), class: "avatar #{"s#{size}"}", width: size
  6 + end
  7 +
  8 + def tm_of(project)
  9 + project.team_member_by_id(self.id)
  10 + end
  11 +end
... ...
app/helpers/application_helper.rb
... ... @@ -36,7 +36,7 @@ module ApplicationHelper
36 36 else
37 37 gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
38 38 user_email.strip!
39   - "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=identicon"
  39 + "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm"
40 40 end
41 41 end
42 42  
... ...
app/helpers/commits_helper.rb
... ... @@ -65,4 +65,9 @@ module CommitsHelper
65 65 end
66 66 end
67 67  
  68 + def commit_to_html commit
  69 + if commit.model
  70 + escape_javascript(render 'commits/commit', commit: commit)
  71 + end
  72 + end
68 73 end
... ...
app/helpers/events_helper.rb
... ... @@ -33,4 +33,22 @@ module EventsHelper
33 33 image_tag event_image_path
34 34 end
35 35 end
  36 +
  37 + def event_filter_link key, tooltip
  38 + key = key.to_s
  39 +
  40 + filter = @event_filter.options key
  41 +
  42 + inactive = if @event_filter.active? key
  43 + nil
  44 + else
  45 + 'inactive'
  46 + end
  47 +
  48 + content_tag :div, class: "filter_icon #{inactive}" do
  49 + link_to dashboard_path(event_filter: filter), class: 'has_tooltip', 'data-original-title' => tooltip do
  50 + image_tag "event_filter_#{key}.png"
  51 + end
  52 + end
  53 + end
36 54 end
... ...
app/helpers/projects_helper.rb
... ... @@ -10,5 +10,9 @@ module ProjectsHelper
10 10 def link_to_project project
11 11 link_to project.name, project
12 12 end
  13 +
  14 + def tm_path team_member
  15 + project_team_member_path(@project, team_member)
  16 + end
13 17 end
14 18  
... ...
app/helpers/tree_helper.rb
... ... @@ -67,4 +67,29 @@ module TreeHelper
67 67 can?(current_user, :push_code, @project)
68 68 end
69 69 end
  70 +
  71 + # Breadcrumb links for a Project and, if applicable, a tree path
  72 + def breadcrumbs
  73 + return unless @project && @ref
  74 +
  75 + # Add the root project link and the arrow icon
  76 + crumbs = content_tag(:li) do
  77 + content_tag(:span, nil, class: 'arrow') +
  78 + link_to(@project.name, project_commits_path(@project, @ref))
  79 + end
  80 +
  81 + if @path
  82 + parts = @path.split('/')
  83 +
  84 + parts.each_with_index do |part, i|
  85 + crumbs += content_tag(:span, '/', class: 'divider')
  86 + crumbs += content_tag(:li) do
  87 + # The text is just the individual part, but the link needs all the parts before it
  88 + link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
  89 + end
  90 + end
  91 + end
  92 +
  93 + crumbs.html_safe
  94 + end
70 95 end
... ...
app/models/commit.rb
1 1 class Commit
2 2 include ActiveModel::Conversion
3   - include Gitlab::Encode
4 3 include StaticModel
5 4 extend ActiveModel::Naming
6 5  
  6 + # Safe amount of files with diffs in one commit to render
  7 + # Used to prevent 500 error on huge commits by suppressing diff
  8 + #
  9 + DIFF_SAFE_SIZE = 100
  10 +
7 11 attr_accessor :commit, :head, :refs
8 12  
9 13 delegate :message, :authored_date, :committed_date, :parents, :sha,
... ... @@ -107,7 +111,7 @@ class Commit
107 111 end
108 112  
109 113 def safe_message
110   - @safe_message ||= utf8 message
  114 + @safe_message ||= message
111 115 end
112 116  
113 117 def created_at
... ... @@ -119,7 +123,7 @@ class Commit
119 123 end
120 124  
121 125 def author_name
122   - utf8 author.name
  126 + author.name
123 127 end
124 128  
125 129 # Was this commit committed by a different person than the original author?
... ... @@ -128,7 +132,7 @@ class Commit
128 132 end
129 133  
130 134 def committer_name
131   - utf8 committer.name
  135 + committer.name
132 136 end
133 137  
134 138 def committer_email
... ...
app/models/issue.rb
... ... @@ -7,8 +7,6 @@ class Issue &lt; ActiveRecord::Base
7 7  
8 8 acts_as_taggable_on :labels
9 9  
10   - belongs_to :milestone
11   -
12 10 validates :description, length: { within: 0..2000 }
13 11  
14 12 def self.open_for(user)
... ...
app/models/merge_request.rb
1 1 require Rails.root.join("app/models/commit")
  2 +require Rails.root.join("app/roles/static_model")
2 3  
3 4 class MergeRequest < ActiveRecord::Base
4 5 include IssueCommonality
5 6 include Votes
6 7  
7   - attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch,
  8 + attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
8 9 :author_id_of_changes
9 10  
10 11 attr_accessor :should_remove_source_branch
... ... @@ -26,6 +27,10 @@ class MergeRequest &lt; ActiveRecord::Base
26 27 where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
27 28 end
28 29  
  30 + def self.find_all_by_milestone(milestone)
  31 + where("milestone_id = :milestone_id", milestone_id: milestone)
  32 + end
  33 +
29 34 def human_state
30 35 states = {
31 36 CAN_BE_MERGED => "can_be_merged",
... ... @@ -60,7 +65,7 @@ class MergeRequest &lt; ActiveRecord::Base
60 65 end
61 66  
62 67 def check_if_can_be_merged
63   - self.state = if Gitlab::Merge.new(self, self.author).can_be_merged?
  68 + self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
64 69 CAN_BE_MERGED
65 70 else
66 71 CANNOT_BE_MERGED
... ... @@ -167,7 +172,7 @@ class MergeRequest &lt; ActiveRecord::Base
167 172 end
168 173  
169 174 def automerge!(current_user)
170   - if Gitlab::Merge.new(self, current_user).merge! && self.unmerged_commits.empty?
  175 + if Gitlab::Satellite::MergeAction.new(current_user, self).merge! && self.unmerged_commits.empty?
171 176 self.merge!(current_user.id)
172 177 true
173 178 end
... ... @@ -212,5 +217,6 @@ end
212 217 # st_diffs :text(4294967295
213 218 # merged :boolean default(FALSE), not null
214 219 # state :integer default(1), not null
  220 +# milestone_id :integer
215 221 #
216 222  
... ...
app/models/milestone.rb
... ... @@ -3,6 +3,7 @@ class Milestone &lt; ActiveRecord::Base
3 3  
4 4 belongs_to :project
5 5 has_many :issues
  6 + has_many :merge_requests
6 7  
7 8 validates :title, presence: true
8 9 validates :project, presence: true
... ... @@ -15,8 +16,20 @@ class Milestone &lt; ActiveRecord::Base
15 16 User.where(id: issues.pluck(:assignee_id))
16 17 end
17 18  
  19 + def open_items_count
  20 + self.issues.opened.count + self.merge_requests.opened.count
  21 + end
  22 +
  23 + def closed_items_count
  24 + self.issues.closed.count + self.merge_requests.closed.count
  25 + end
  26 +
  27 + def total_items_count
  28 + self.issues.count + self.merge_requests.count
  29 + end
  30 +
18 31 def percent_complete
19   - ((self.issues.closed.count * 100) / self.issues.count).abs
  32 + ((closed_items_count * 100) / total_items_count).abs
20 33 rescue ZeroDivisionError
21 34 100
22 35 end
... ...
app/models/note.rb
... ... @@ -23,13 +23,13 @@ class Note &lt; ActiveRecord::Base
23 23 mount_uploader :attachment, AttachmentUploader
24 24  
25 25 # Scopes
26   - scope :common, where(noteable_id: nil)
27   - scope :today, where("created_at >= :date", date: Date.today)
28   - scope :last_week, where("created_at >= :date", date: (Date.today - 7.days))
  26 + scope :common, ->{ where(noteable_id: nil) }
  27 + scope :today, ->{ where("created_at >= :date", date: Date.today) }
  28 + scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
29 29 scope :since, ->(day) { where("created_at >= :date", date: (day)) }
30   - scope :fresh, order("created_at ASC, id ASC")
31   - scope :inc_author_project, includes(:project, :author)
32   - scope :inc_author, includes(:author)
  30 + scope :fresh, ->{ order("created_at ASC, id ASC") }
  31 + scope :inc_author_project, ->{ includes(:project, :author) }
  32 + scope :inc_author, ->{ includes(:author) }
33 33  
34 34 def self.create_status_change_note(noteable, author, status)
35 35 create({
... ...
app/models/project.rb
... ... @@ -104,8 +104,10 @@ class Project &lt; ActiveRecord::Base
104 104 end
105 105  
106 106 def repo_name
107   - if path == "gitolite-admin"
108   - errors.add(:path, " like 'gitolite-admin' is not allowed")
  107 + denied_paths = %w(gitolite-admin groups projects dashboard)
  108 +
  109 + if denied_paths.include?(path)
  110 + errors.add(:path, "like #{path} is not allowed")
109 111 end
110 112 end
111 113  
... ...
app/models/tree.rb
... ... @@ -8,7 +8,7 @@ class Tree
8 8 def initialize(raw_tree, project, ref = nil, path = nil)
9 9 @project, @ref, @path = project, ref, path
10 10 @tree = if path.present?
11   - raw_tree / path.dup.force_encoding('ascii-8bit')
  11 + raw_tree / path
12 12 else
13 13 raw_tree
14 14 end
... ...
app/models/wiki.rb
... ... @@ -15,6 +15,12 @@ class Wiki &lt; ActiveRecord::Base
15 15 slug
16 16 end
17 17  
  18 + class << self
  19 + def search(query)
  20 + where("title like :query OR content like :query", query: "%#{query}%")
  21 + end
  22 + end
  23 +
18 24 protected
19 25  
20 26 def self.regenerate_from wiki
... ...
app/roles/issue_commonality.rb
... ... @@ -6,6 +6,7 @@ module IssueCommonality
6 6 belongs_to :project
7 7 belongs_to :author, class_name: "User"
8 8 belongs_to :assignee, class_name: "User"
  9 + belongs_to :milestone
9 10 has_many :notes, as: :noteable, dependent: :destroy
10 11  
11 12 validates :project, presence: true
... ...
app/roles/repository.rb
... ... @@ -41,7 +41,7 @@ module Repository
41 41 end
42 42  
43 43 def satellite
44   - @satellite ||= Gitlab::Satellite.new(self)
  44 + @satellite ||= Gitlab::Satellite::Satellite.new(self)
45 45 end
46 46  
47 47 def has_post_receive_file?
... ...
app/views/admin/projects/show.html.haml
... ... @@ -43,7 +43,7 @@
43 43 %b
44 44 Owner:
45 45 %td
46   - = @admin_project.owner.name
  46 + = @admin_project.owner_name || '(deleted)'
47 47 %tr
48 48 %td
49 49 %b
... ...
app/views/admin/resque/show.html.haml
1 1 %h3.page_title Resque
2 2 %br
3 3 .ui-box
4   - %iframe{src: resque_url, width: '100%', height: 600, style: "border: none"}
  4 + %iframe{src: resque_path, width: '100%', height: 600, style: "border: none"}
... ...
app/views/blame/_head.html.haml
... ... @@ -4,7 +4,4 @@
4 4 = nav_link(controller: :refs) do
5 5 = link_to 'Source', project_tree_path(@project, @ref)
6 6 %li.right
7   - .input-prepend.project_clone_holder
8   - %button{class: "btn small active", :"data-clone" => @project.ssh_url_to_repo} SSH
9   - %button{class: "btn small", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.web_protocol.upcase
10   - = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5"
  7 + = render "shared/clone_panel"
... ...
app/views/blame/show.html.haml
... ... @@ -15,7 +15,7 @@
15 15 .file_title
16 16 %i.icon-file
17 17 %span.file_name
18   - = @tree.name.force_encoding('utf-8')
  18 + = @tree.name
19 19 %small= number_to_human_size @tree.size
20 20 %span.options= render "tree/blob_actions"
21 21 .file_content.blame
... ... @@ -24,9 +24,7 @@
24 24 - commit = Commit.new(commit)
25 25 - commit = CommitDecorator.decorate(commit)
26 26 %tr
27   - %td.author
28   - = image_tag gravatar_icon(commit.author_email, 16)
29   - = commit.author_name
  27 + %td.author= commit.author_link avatar: true, size: 16
30 28 %td.blame_commit
31 29 &nbsp;
32 30 %code= link_to commit.short_id, project_commit_path(@project, commit)
... ... @@ -34,4 +32,4 @@
34 32 %td.lines
35 33 = preserve do
36 34 %pre
37   - = Gitlab::Encode.utf8 lines.join("\n")
  35 + = lines.join("\n")
... ...
app/views/commit/huge_commit.html.haml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 += render "commits/commit_box"
  2 +.alert-message.block-message.error
  3 + %h4 Commit diffs are too big to be displayed
... ...
app/views/commits/_commit.html.haml
... ... @@ -4,9 +4,8 @@
4 4 %strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
5 5 %p
6 6 = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
7   - %strong.commit-author-name= commit.author_name
8   - %span.dash &ndash;
9   - = image_tag gravatar_icon(commit.author_email), class: "avatar", width: 16
  7 + = commit.author_link avatar: true, size: 24
  8 + &nbsp;
10 9 = link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title"
11 10  
12 11 %span.committed_ago
... ...
app/views/commits/_commit_box.html.haml
... ... @@ -18,16 +18,15 @@
18 18 .commit-info
19 19 .row
20 20 .span5
21   - = image_tag gravatar_icon(@commit.author_email, 40), class: "avatar"
22 21 .author
23   - %strong= @commit.author_name
  22 + %strong= @commit.author_link avatar: true, size: 40
24 23 authored
25 24 %time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
26 25 #{time_ago_in_words(@commit.authored_date)} ago
27 26 - if @commit.different_committer?
28 27 .committer
29 28 &rarr;
30   - %strong= @commit.committer_name
  29 + %strong= @commit.committer_link
31 30 committed
32 31 %time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
33 32 #{time_ago_in_words(@commit.committed_date)} ago
... ...
app/views/commits/_diffs.html.haml
1 1 - if @suppress_diff
2 2 .alert-message.block-message
3 3 %p
4   - %strong Warning! Large commit with more then 200 files changed.
  4 + %strong Warning! Large commit with more then #{Commit::DIFF_SAFE_SIZE} files changed.
5 5 %p To prevent performance issue we rejected diff information.
6 6 %p
7 7 But if you still want to see diff
8   - = link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "dark"
  8 + = link_to "click this link", project_commit_path(@project, @commit, force_show_diff: true), class: "underlined_link"
9 9  
10 10 %p.cgray
11 11 Showing #{pluralize(diffs.count, "changed file")}
... ... @@ -35,10 +35,10 @@
35 35 - if file.text?
36 36 = render "commits/text_file", diff: diff, index: i
37 37 - elsif file.image?
38   - - if diff.renamed_file || diff.new_file || diff.deleted_file
  38 + - if diff.renamed_file || diff.new_file || diff.deleted_file
39 39 .diff_file_content_image
40 40 %img{class: image_diff_class(diff), src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
41   - - else
  41 + - else
42 42 - old_file = (@commit.prev_commit.tree / diff.old_path)
43 43 .diff_file_content_image.img_compared
44 44 %img{class: "diff_image_removed", src: "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
... ...
app/views/commits/_head.html.haml
... ... @@ -16,6 +16,11 @@
16 16 Tags
17 17 %span.badge= @project.tags.length
18 18  
  19 + = nav_link(controller: :repositories, action: :stats) do
  20 + = link_to stats_project_repository_path(@project) do
  21 + Stats
  22 +
  23 +
19 24 - if current_controller?(:commits) && current_user.private_token
20 25 %li.right
21 26 %span.rss-icon
... ...
app/views/commits/huge_commit.html.haml
... ... @@ -1,3 +0,0 @@
1   -= render "commits/commit_box"
2   -.alert-message.block-message.error
3   - %h4 Commit diffs are too big to be displayed
app/views/commits/show.html.haml
... ... @@ -2,14 +2,7 @@
2 2  
3 3 - if @path.present?
4 4 %ul.breadcrumb
5   - %li
6   - %span.arrow
7   - = link_to project_commits_path(@project) do
8   - = @project.name
9   - %span.divider
10   - \/
11   - %li
12   - %a{href: "#"}= @path.split("/").join(" / ")
  5 + = breadcrumbs
13 6  
14 7 %div{id: dom_id(@project)}
15 8 #commits_list= render "commits"
... ...
app/views/dashboard/index.html.haml
... ... @@ -3,10 +3,17 @@
3 3 .activities.span8
4 4 = render "events/event_last_push", event: @last_push
5 5 = render 'shared/no_ssh'
  6 +
  7 + .event_filter
  8 + = event_filter_link EventFilter.push, 'Push events'
  9 + = event_filter_link EventFilter.merged, 'Merge events'
  10 + = event_filter_link EventFilter.comments, 'Comments'
  11 + = event_filter_link EventFilter.team, 'Team'
  12 +
6 13 - if @events.any?
7 14 .content_list= render @events
8 15 - else
9   - %h4.nothing_here_message Projects activity will be displayed here
  16 + %p.nothing_here_message Projects activity will be displayed here
10 17 .loading.hide
11 18 .side
12 19 - if @groups.present?
... ...
app/views/devise/sessions/new.html.haml
... ... @@ -14,8 +14,9 @@
14 14 = f.submit "Sign in", :class => "primary btn wide"
15 15 .right
16 16 = render :partial => "devise/shared/links"
17   - - if devise_mapping.omniauthable?
18   - %hr/
19   - - resource_class.omniauth_providers.each do |provider|
20   - %span
21   - = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
  17 + .clearfix
  18 + - if devise_mapping.omniauthable? && resource_class.omniauth_providers.present?
  19 + %div
  20 + - resource_class.omniauth_providers.each do |provider|
  21 + %span
  22 + = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
... ...
app/views/help/system_hooks.html.haml
... ... @@ -5,9 +5,10 @@
5 5 %hr
6 6  
7 7 %p.slead
8   - Your GitLab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
  8 + Your GitLab instance can perform HTTP POST requests on the following events: create_project, delete_project, create_user, delete_user, change_team_member.
9 9 %br
10   - System Hooks can be used for logging or change information in LDAP server.
  10 + %br
  11 + System Hooks can be used, e.g. for logging or changing information in a LDAP server.
11 12 %br
12 13 %h5 Hooks request example:
13 14 = render "admin/hooks/data_ex"
... ...
app/views/issues/_form.html.haml
... ... @@ -12,7 +12,7 @@
12 12 = f.label :title do
13 13 %strong= "Subject *"
14 14 .input
15   - = f.text_field :title, maxlength: 255, class: "xxlarge gfm-input", autofocus: true
  15 + = f.text_field :title, maxlength: 255, class: "xxlarge js-gfm-input", autofocus: true
16 16 .issue_middle_block
17 17 .issue_assignee
18 18 = f.label :assignee_id do
... ... @@ -37,7 +37,7 @@
37 37 .clearfix
38 38 = f.label :description, "Details"
39 39 .input
40   - = f.text_area :description, maxlength: 2000, class: "xxlarge gfm-input", rows: 14
  40 + = f.text_area :description, maxlength: 2000, class: "xxlarge js-gfm-input", rows: 14
41 41 %p.hint Issues are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
42 42  
43 43  
... ...
app/views/issues/edit.js.haml
1 1 :plain
2   - var edit_issue_dialog = $("<div id='edit_issue_dialog'></div>");
3   - edit_issue_dialog.html("#{escape_javascript(render('form'))}");
4   - switchToEditIssue(edit_issue_dialog);
  2 + $("#edit_issue_dialog").html("#{escape_javascript(render('form'))}");
  3 + switchToEditIssue();
5 4  
... ...
app/views/issues/index.html.haml
... ... @@ -58,6 +58,8 @@
58 58 %ul#issues-table.unstyled.issues_table
59 59 = render "issues"
60 60  
  61 +#new_issue_dialog
  62 +#edit_issue_dialog
61 63  
62 64 :javascript
63 65 $(function(){
... ...
app/views/issues/new.js.haml
1 1 :plain
2   - var new_issue_dialog = $("<div id='new_issue_dialog'></div>");
3   - new_issue_dialog.html("#{escape_javascript(render('form'))}");
4   - switchToNewIssue(new_issue_dialog);
  2 + $("#new_issue_dialog").html("#{escape_javascript(render('form'))}");
  3 + switchToNewIssue();
... ...
app/views/layouts/_head_panel.html.haml
... ... @@ -8,9 +8,7 @@
8 8 GITLAB
9 9 %span.separator
10 10 %h1.project_name= title
11   - .search
12   - = form_tag search_path, method: :get do |f|
13   - = text_field_tag "search", nil, placeholder: "Search", class: "search-input"
  11 + = render "layouts/search"
14 12 .fbtn
15 13 - if current_user.is_admin?
16 14 = link_to admin_root_path, class: "btn small", title: "Admin area" do
... ... @@ -29,11 +27,3 @@
29 27 = link_to 'Logout', destroy_user_session_path, class: "logout", method: :delete
30 28  
31 29 = render "layouts/init_auto_complete"
32   -
33   -:javascript
34   - $(function(){
35   - $("#search").autocomplete({
36   - source: #{raw search_autocomplete_source},
37   - select: function(event, ui) { location.href = ui.item.url }
38   - });
39   - });
... ...
app/views/layouts/_init_auto_complete.html.haml
1 1 :javascript
2 2 $(function() {
3   - autocompleteMembersUrl = "#{ "/api/v2/projects/#{@project.code}/members" if @project }";
4   - autocompleteMembersParams.private_token = "#{current_user.authentication_token}";
  3 + autocompleteMembers.url = "#{ "/api/v2/projects/#{@project.code}/members" if @project }";
  4 + autocompleteMembers.params.private_token = "#{current_user.private_token}";
5 5  
6   - autocompleteEmojiData = #{raw emoji_autocomplete_source};
  6 + autocompleteEmoji.data = #{raw emoji_autocomplete_source};
7 7 // convert the list so that the items have the right format for completion
8   - autocompleteEmojiData = $.map(autocompleteEmojiData, function(value) {
  8 + autocompleteEmoji.data = $.map(autocompleteEmoji.data, function(value) {
9 9 return {
10 10 name: value,
11 11 insert: value+':',
... ...
app/views/layouts/_search.html.haml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +.search
  2 + = form_tag search_path, method: :get do |f|
  3 + = text_field_tag "search", nil, placeholder: "Search", class: "search-input"
  4 +
  5 +:javascript
  6 + $(function(){
  7 + $("#search").autocomplete({
  8 + source: #{raw search_autocomplete_source},
  9 + select: function(event, ui) { location.href = ui.item.url }
  10 + });
  11 + });
... ...
app/views/merge_requests/_form.html.haml
... ... @@ -28,16 +28,22 @@
28 28 %h4.cdark 2. Fill info
29 29  
30 30 .clearfix
31   - .main_box
  31 + .merge_requests_form_box
32 32 .top_box_content
33   - = f.label :title do
  33 + = f.label :title do
34 34 %strong= "Title *"
35   - .input= f.text_field :title, class: "input-xxlarge pad gfm-input", maxlength: 255, rows: 5
36   - .middle_box_content
37   - = f.label :assignee_id do
38   - %i.icon-user
39   - Assign to
40   - .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
  35 + .input= f.text_field :title, class: "input-xxlarge pad js-gfm-input", maxlength: 255, rows: 5
  36 + .merge_requests_middle_box
  37 + .merge_requests_assignee
  38 + = f.label :assignee_id do
  39 + %i.icon-user
  40 + Assign to
  41 + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { include_blank: "Select user" }, {class: 'chosen span3'})
  42 + .merge_requests_milestone
  43 + = f.label :milestone_id do
  44 + %i.icon-time
  45 + Milestone
  46 + .input= f.select(:milestone_id, @project.milestones.active.all.collect {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'})
41 47  
42 48 .control-group
43 49  
... ...
app/views/merge_requests/_merge_request.html.haml
... ... @@ -10,6 +10,10 @@
10 10 %span.btn.small.disabled.grouped
11 11 %i.icon-comment
12 12 = merge_request.mr_and_commit_notes.count
  13 + - if merge_request.milestone_id?
  14 + %span.btn.small.disabled.grouped
  15 + %i.icon-time
  16 + = merge_request.milestone.title
13 17 %span.btn.small.disabled.grouped
14 18 = merge_request.source_branch
15 19 &rarr;
... ...
app/views/merge_requests/branch_from.js.haml
1 1 :plain
2   - $(".mr_source_commit").html("#{escape_javascript(render 'commits/commit', commit: @commit)}");
  2 + $(".mr_source_commit").html("#{commit_to_html(@commit)}");
... ...
app/views/merge_requests/branch_to.js.haml
1 1 :plain
2   - $(".mr_target_commit").html("#{escape_javascript(render 'commits/commit', commit: @commit)}");
3   -
  2 + $(".mr_target_commit").html("#{commit_to_html(@commit)}");
... ...
app/views/merge_requests/index.html.haml
... ... @@ -9,19 +9,26 @@
9 9  
10 10 .ui-box
11 11 .title
12   - %ul.nav.nav-pills
13   - %li{class: ("active" if (params[:f] == 'open' || !params[:f]))}
14   - = link_to project_merge_requests_path(@project, f: 'open') do
15   - Open
16   - %li{class: ("active" if params[:f] == "closed")}
17   - = link_to project_merge_requests_path(@project, f: "closed") do
18   - Closed
19   - %li{class: ("active" if params[:f] == 'assigned-to-me')}
20   - = link_to project_merge_requests_path(@project, f: 'assigned-to-me') do
21   - To Me
22   - %li{class: ("active" if params[:f] == 'all')}
23   - = link_to project_merge_requests_path(@project, f: 'all') do
24   - All
  12 + .left
  13 + %ul.nav.nav-pills
  14 + %li{class: ("active" if (params[:f] == 'open' || !params[:f]))}
  15 + = link_to project_merge_requests_path(@project, f: 'open', milestone_id: params[:milestone_id]) do
  16 + Open
  17 + %li{class: ("active" if params[:f] == "closed")}
  18 + = link_to project_merge_requests_path(@project, f: "closed", milestone_id: params[:milestone_id]) do
  19 + Closed
  20 + %li{class: ("active" if params[:f] == 'assigned-to-me')}
  21 + = link_to project_merge_requests_path(@project, f: 'assigned-to-me', milestone_id: params[:milestone_id]) do
  22 + To Me
  23 + %li{class: ("active" if params[:f] == 'all')}
  24 + = link_to project_merge_requests_path(@project, f: 'all', milestone_id: params[:milestone_id]) do
  25 + All
  26 + .right
  27 + = form_tag project_merge_requests_path(@project), id: "merge_requests_search_form", method: :get, class: :right do
  28 + = select_tag(:assignee_id, options_from_collection_for_select([unassigned_filter] + @project.users.all, "id", "name", params[:assignee_id]), prompt: "Assignee")
  29 + = select_tag(:milestone_id, options_from_collection_for_select([unassigned_filter] + @project.milestones.order("id desc").all, "id", "title", params[:milestone_id]), prompt: "Milestone")
  30 + = hidden_field_tag :f, params[:f]
  31 + .clearfix
25 32  
26 33 %ul.unstyled
27 34 = render @merge_requests
... ... @@ -35,3 +42,7 @@
35 42 .span4.right
36 43 %span.cgray.right #{@merge_requests.total_count} merge requests for this filter
37 44  
  45 +:javascript
  46 + $(function() {
  47 + merge_requestsPage();
  48 + })
... ...
app/views/merge_requests/show/_mr_box.html.haml
... ... @@ -14,9 +14,13 @@
14 14 %strong.author= link_to_merge_request_author(@merge_request)
15 15  
16 16 - if @merge_request.assignee
17   - %cite.cgray and currently assigned to
  17 + %cite.cgray , currently assigned to
18 18 = image_tag gravatar_icon(@merge_request.assignee_email), width: 16, class: "lil_av"
19 19 %strong.author= link_to_merge_request_assignee(@merge_request)
  20 + - if @merge_request.milestone
  21 + - milestone = @merge_request.milestone
  22 + %cite.cgray and attached to milestone
  23 + %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
20 24  
21 25  
22 26 - if @merge_request.closed
... ...
app/views/milestones/_milestone.html.haml
1 1 %li{class: "milestone", id: dom_id(milestone) }
2 2 .right
3   - - if milestone.issues.any?
4   - %span.btn.small.disabled.grouped= pluralize milestone.issues.count, 'issues'
5   - - if milestone.issues.count > 0
6   - = link_to 'Browse Issues', project_issues_path(milestone.project, milestone_id: milestone.id), class: "btn small grouped"
7 3 - if can? current_user, :admin_milestone, milestone.project
8   - = link_to 'Edit', edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped"
  4 + = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn small edit-milestone-link grouped" do
  5 + %i.icon-edit
  6 + Edit
9 7 %h4
10   - = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone), class: "row_title"
  8 + = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
11 9 %small
12 10 = milestone.expires_at
13   - %br
14   - .progress.progress-success.span3
15   - .bar{style: "width: #{milestone.percent_complete}%;"}
16   -
17   -
18   - &nbsp;
  11 + .row
  12 + .span4
  13 + .progress.progress-info
  14 + .bar{style: "width: #{milestone.percent_complete}%;"}
  15 + .span6
  16 + = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do
  17 + = pluralize milestone.issues.count, 'Issue'
  18 + &nbsp;
  19 + = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do
  20 + = pluralize milestone.merge_requests.count, 'Merge Request'
  21 + &nbsp;
  22 + %span.light #{milestone.percent_complete}% complete
... ...
app/views/milestones/show.html.haml
... ... @@ -31,10 +31,10 @@
31 31 %h5
32 32 Progress:
33 33 %small
34   - #{@milestone.issues.closed.count} closed
  34 + #{@milestone.closed_items_count} closed
35 35 &ndash;
36   - #{@milestone.issues.opened.count} open
37   - .progress.progress-success
  36 + #{@milestone.open_items_count} open
  37 + .progress.progress-info
38 38 .bar{style: "width: #{@milestone.percent_complete}%;"}
39 39  
40 40  
... ... @@ -58,15 +58,28 @@
58 58 %span.badge.badge-info ##{issue.id}
59 59 &ndash;
60 60 = link_to_gfm truncate(issue.title, length: 60), [@project, issue]
61   - %br
62 61  
63 62 .span6
64   - %table
  63 + %table.milestone-merge-requests-filter
65 64 %thead
66   - %th Participants
67   - - @users.each do |user|
68   - %tr
  65 + %th
  66 + %ul.nav.nav-pills
  67 + %li.active= link_to('Open Merge Requests', '#')
  68 + %li=link_to('All Merge Requests', '#')
  69 + - @merge_requests.each do |merge_request|
  70 + %tr{data: {closed: merge_request.closed}}
69 71 %td
70   - = image_tag gravatar_icon(user.email, 24), width: "24"
71   - &nbsp;
72   - = user.name
  72 + = link_to [@project, merge_request] do
  73 + %span.badge.badge-info ##{merge_request.id}
  74 + &ndash;
  75 + = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
  76 +
  77 +%hr
  78 +%h6 Participants:
  79 +%div
  80 + - @users.each do |user|
  81 + = link_to tm_path(user.tm_of(@project)), class: 'float-link' do
  82 + = user.avatar_image
  83 + = user.name
  84 +
  85 +.clearfix
... ...
app/views/notes/_common_form.html.haml
... ... @@ -8,7 +8,7 @@
8 8  
9 9 = f.hidden_field :noteable_id
10 10 = f.hidden_field :noteable_type
11   - = f.text_area :note, size: 255, class: 'note-text gfm-input'
  11 + = f.text_area :note, size: 255, class: 'note-text js-gfm-input'
12 12 #preview-note.preview_note.hide
13 13 .hint
14 14 .right Comments are parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}.
... ...
app/views/notes/_per_line_form.html.haml
... ... @@ -13,7 +13,7 @@
13 13 = f.hidden_field :noteable_id
14 14 = f.hidden_field :noteable_type
15 15 = f.hidden_field :line_code
16   - = f.text_area :note, size: 255, class: 'line-note-text gfm-input'
  16 + = f.text_area :note, size: 255, class: 'line-note-text js-gfm-input'
17 17 .note_actions
18 18 .buttons
19 19 = f.submit 'Add note', class: "btn save-btn submit_note submit_inline_note", id: "submit_note"
... ...
app/views/notify/project_access_granted_email.html.haml
... ... @@ -4,7 +4,7 @@
4 4 %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
5 5 %td{align: "left", style: "padding: 20px 0 0;"}
6 6 %h2{style: "color:#646464; font-weight: bold; margin: 0; padding: 0; line-height: 26px; font-size: 18px; font-family: Helvetica, Arial, sans-serif; "}
7   - = "You got granted #{@users_project.project_access_human} access to project"
  7 + = "You have been granted #{@users_project.project_access_human} access to project"
8 8 %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
9 9 %tr
10 10 %td{style: "font-size: 1px; line-height: 1px;", width: "21"}
... ...