Commit db2c15369c365340aeaf4e431e8838714b40396b

Authored by Riyad Preukschas
2 parents b47173da 68c43d59

Merge branch 'master' into discussions

Conflicts:
	app/assets/stylesheets/main.scss
	app/models/project.rb
	app/views/notes/_common_form.html.haml
	app/views/notes/_per_line_form.html.haml
	lib/gitlab/markdown.rb
	spec/models/note_spec.rb
Showing 280 changed files with 4590 additions and 2727 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 280 files displayed.

1 env: 1 env:
  2 + - DB=postgresql
2 - DB=mysql 3 - DB=mysql
3 before_install: 4 before_install:
4 - sudo apt-get install libicu-dev -y 5 - sudo apt-get install libicu-dev -y
@@ -18,8 +19,7 @@ services: @@ -18,8 +19,7 @@ services:
18 before_script: 19 before_script:
19 - "cp config/database.yml.$DB config/database.yml" 20 - "cp config/database.yml.$DB config/database.yml"
20 - "cp config/gitlab.yml.example config/gitlab.yml" 21 - "cp config/gitlab.yml.example config/gitlab.yml"
21 - - "bundle exec rake db:create RAILS_ENV=test"  
22 - - "bundle exec rake db:migrate RAILS_ENV=test" 22 + - "bundle exec rake db:setup RAILS_ENV=test"
23 - "bundle exec rake db:seed_fu RAILS_ENV=test" 23 - "bundle exec rake db:seed_fu RAILS_ENV=test"
24 - "sh -e /etc/init.d/xvfb start" 24 - "sh -e /etc/init.d/xvfb start"
25 script: "bundle exec rake travis --trace" 25 script: "bundle exec rake travis --trace"
1 v 4.0.0 1 v 4.0.0
  2 + - Reorganized settings
  3 + - Fixed commits compare
  4 + - Refactored scss
  5 + - Improve status checks
  6 + - Validates presence of User#name
  7 + - Fixed postgres support
  8 + - Removed sqlite support
  9 + - Modified post-receive hook
  10 + - Milestones can be closed now
  11 + - Show comment events on dashboard
  12 + - Quick add team members via group#people page
2 - [API] expose created date for hooks and SSH keys 13 - [API] expose created date for hooks and SSH keys
3 - [API] list, create issue notes 14 - [API] list, create issue notes
4 - [API] list, create snippet notes 15 - [API] list, create snippet notes
CONTRIBUTING.md
1 -## Contribute to GitLab 1 +# Contact & support
2 2
3 -If you want to contribute to GitLab, follow this process: 3 +If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).
  4 +Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues.
4 5
5 -1. Fork the project  
6 -2. Create a feature branch  
7 -3. Code  
8 -4. Create a pull request  
9 6
10 -We will only accept pull requests if:  
11 7
12 -* Your code has proper tests and all tests pass  
13 -* Your code can be merged w/o problems  
14 -* It won't break existing functionality  
15 -* It's quality code  
16 -* We like it :) 8 +# Contribute to GitLab
17 9
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). 10 +## Recipes
19 11
20 -## Installation 12 +We collect user submitted installation scripts and config file templates for platforms we don't support officially.
  13 +We believe there is merit in allowing a certain amount of diversity.
  14 +You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc.
21 15
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. 16 +Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/)
23 17
24 -## Running tests  
25 18
26 -For more information on running the tests please read the [development tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md) 19 +## Feature suggestions
  20 +
  21 +Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own.
  22 +
  23 +
  24 +## Code
  25 +
  26 +Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab.
@@ -32,7 +32,7 @@ gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref: @@ -32,7 +32,7 @@ gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref:
32 gem "gitolite", '1.1.0' 32 gem "gitolite", '1.1.0'
33 33
34 # Syntax highlighter 34 # Syntax highlighter
35 -gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", ref: '4db80c599067e2d5f23c5c243bf85b8ca0368ad4' 35 +gem "pygments.rb", git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
36 36
37 # Language detection 37 # Language detection
38 gem "github-linguist", "~> 2.3.4" , require: "linguist" 38 gem "github-linguist", "~> 2.3.4" , require: "linguist"
@@ -100,7 +100,7 @@ group :assets do @@ -100,7 +100,7 @@ group :assets do
100 gem "therubyracer" 100 gem "therubyracer"
101 101
102 gem 'chosen-rails', "0.9.8" 102 gem 'chosen-rails', "0.9.8"
103 - gem 'jquery-atwho-rails', "0.1.6" 103 + gem 'jquery-atwho-rails', "0.1.7"
104 gem "jquery-rails", "2.1.3" 104 gem "jquery-rails", "2.1.3"
105 gem "jquery-ui-rails", "2.0.2" 105 gem "jquery-ui-rails", "2.0.2"
106 gem "modernizr", "2.6.2" 106 gem "modernizr", "2.6.2"
@@ -124,7 +124,7 @@ group :development, :test do @@ -124,7 +124,7 @@ group :development, :test do
124 gem "capybara" 124 gem "capybara"
125 gem "pry" 125 gem "pry"
126 gem "awesome_print" 126 gem "awesome_print"
127 - gem "database_cleaner" 127 + gem "database_cleaner", ref: "f89c34300e114be99532f14c115b2799a3380ac6", git: "https://github.com/bmabey/database_cleaner.git"
128 gem "launchy" 128 gem "launchy"
129 gem 'factory_girl_rails' 129 gem 'factory_girl_rails'
130 130
1 GIT 1 GIT
  2 + remote: https://github.com/bmabey/database_cleaner.git
  3 + revision: f89c34300e114be99532f14c115b2799a3380ac6
  4 + ref: f89c34300e114be99532f14c115b2799a3380ac6
  5 + specs:
  6 + database_cleaner (0.9.1)
  7 +
  8 +GIT
2 remote: https://github.com/ctran/annotate_models.git 9 remote: https://github.com/ctran/annotate_models.git
3 revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e 10 revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
4 specs: 11 specs:
@@ -45,8 +52,8 @@ GIT @@ -45,8 +52,8 @@ GIT
45 52
46 GIT 53 GIT
47 remote: https://github.com/gitlabhq/pygments.rb.git 54 remote: https://github.com/gitlabhq/pygments.rb.git
48 - revision: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4  
49 - ref: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4 55 + revision: db1da0343adf86b49bdc3add04d02d2e80438d38
  56 + branch: master
50 specs: 57 specs:
51 pygments.rb (0.3.2) 58 pygments.rb (0.3.2)
52 posix-spawn (~> 0.3.6) 59 posix-spawn (~> 0.3.6)
@@ -140,7 +147,6 @@ GEM @@ -140,7 +147,6 @@ GEM
140 colorize (0.5.8) 147 colorize (0.5.8)
141 crack (0.3.1) 148 crack (0.3.1)
142 daemons (1.1.9) 149 daemons (1.1.9)
143 - database_cleaner (0.9.1)  
144 devise (2.1.2) 150 devise (2.1.2)
145 bcrypt-ruby (~> 3.0) 151 bcrypt-ruby (~> 3.0)
146 orm_adapter (~> 0.1) 152 orm_adapter (~> 0.1)
@@ -227,7 +233,7 @@ GEM @@ -227,7 +233,7 @@ GEM
227 httpauth (0.2.0) 233 httpauth (0.2.0)
228 i18n (0.6.1) 234 i18n (0.6.1)
229 journey (1.0.4) 235 journey (1.0.4)
230 - jquery-atwho-rails (0.1.6) 236 + jquery-atwho-rails (0.1.7)
231 jquery-rails (2.1.3) 237 jquery-rails (2.1.3)
232 railties (>= 3.1.0, < 5.0) 238 railties (>= 3.1.0, < 5.0)
233 thor (~> 0.14) 239 thor (~> 0.14)
@@ -458,7 +464,7 @@ DEPENDENCIES @@ -458,7 +464,7 @@ DEPENDENCIES
458 chosen-rails (= 0.9.8) 464 chosen-rails (= 0.9.8)
459 coffee-rails (~> 3.2.2) 465 coffee-rails (~> 3.2.2)
460 colored 466 colored
461 - database_cleaner 467 + database_cleaner!
462 devise (~> 2.1.0) 468 devise (~> 2.1.0)
463 draper (~> 0.18.0) 469 draper (~> 0.18.0)
464 email_spec 470 email_spec
@@ -481,7 +487,7 @@ DEPENDENCIES @@ -481,7 +487,7 @@ DEPENDENCIES
481 guard-spinach 487 guard-spinach
482 haml-rails (~> 0.3.5) 488 haml-rails (~> 0.3.5)
483 httparty 489 httparty
484 - jquery-atwho-rails (= 0.1.6) 490 + jquery-atwho-rails (= 0.1.7)
485 jquery-rails (= 2.1.3) 491 jquery-rails (= 2.1.3)
486 jquery-ui-rails (= 2.0.2) 492 jquery-ui-rails (= 2.0.2)
487 kaminari (~> 0.14.1) 493 kaminari (~> 0.14.1)
1 -# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://secure.travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://secure.travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) 1 +# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://travis-ci.org/gitlabhq/grit) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
2 2
3 GitLab is a free project and repository management application 3 GitLab is a free project and repository management application
4 4
1 -4.0.0pre 1 +4.0.0rc1
app/assets/fonts/korolev-medium-compressed.otf
No preview for this file type
app/assets/images/ajax_loader_gray.gif 0 → 100644

8.17 KB

app/assets/images/logo.png

2.93 KB

app/assets/images/logo_basic.png

3.2 KB

app/assets/images/logo_text.png

4.49 KB

app/assets/images/logo_text_tr.png

3.21 KB

app/assets/images/logo_white.png

1.48 KB | W: | H:

1.88 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
app/assets/images/service-disabled-gitlab-ci.png

2.13 KB

app/assets/images/service-gitlab-ci.png

2.34 KB

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

1.17 KB

app/assets/javascripts/gfm_auto_complete.js.coffee
1 # Creates the variables for setting up GFM auto-completion 1 # Creates the variables for setting up GFM auto-completion
2 2
3 window.GitLab ?= {} 3 window.GitLab ?= {}
4 -GitLab.GfmAutoComplete ?= {}  
5 -  
6 -# Emoji  
7 -data = []  
8 -template = "<li data-value='${insert}'>${name} <img alt='${name}' height='20' src='${image}' width='20' /></li>"  
9 -GitLab.GfmAutoComplete.Emoji = {data, template}  
10 -  
11 -# Team Members  
12 -data = []  
13 -url = '';  
14 -params = {private_token: '', page: 1}  
15 -GitLab.GfmAutoComplete.Members = {data, url, params}  
16 -  
17 -# Add GFM auto-completion to all input fields, that accept GFM input.  
18 -GitLab.GfmAutoComplete.setup = ->  
19 - input = $('.js-gfm-input')  
20 - 4 +GitLab.GfmAutoComplete =
21 # Emoji 5 # Emoji
22 - input.atWho ':',  
23 - data: GitLab.GfmAutoComplete.Emoji.data,  
24 - tpl: GitLab.GfmAutoComplete.Emoji.template 6 + Emoji:
  7 + data: []
  8 + template: '<li data-value="${insert}">${name} <img alt="${name}" height="20" src="${image}" width="20" /></li>'
25 9
26 # Team Members 10 # Team Members
27 - input.atWho '@', (query, callback) ->  
28 - (getMoreMembers = ->  
29 - $.getJSON(GitLab.GfmAutoComplete.Members.url, GitLab.GfmAutoComplete.Members.params)  
30 - .success (members) ->  
31 - # pick the data we need  
32 - newMembersData = $.map(members, (m) -> m.name )  
33 -  
34 - # add the new page of data to the rest  
35 - $.merge(GitLab.GfmAutoComplete.Members.data, newMembersData)  
36 -  
37 - # show the pop-up with a copy of the current data  
38 - callback(GitLab.GfmAutoComplete.Members.data[..])  
39 -  
40 - # are we past the last page?  
41 - if newMembersData.length is 0  
42 - # set static data and stop callbacks  
43 - input.atWho '@',  
44 - data: GitLab.GfmAutoComplete.Members.data  
45 - callback: null  
46 - else  
47 - # get next page  
48 - getMoreMembers() 11 + Members:
  12 + data: []
  13 + url: ''
  14 + params:
  15 + private_token: ''
  16 + template: '<li data-value="${username}">${username} <small>${name}</small></li>'
  17 +
  18 + # Add GFM auto-completion to all input fields, that accept GFM input.
  19 + setup: ->
  20 + input = $('.js-gfm-input')
  21 +
  22 + # Emoji
  23 + input.atWho ':',
  24 + data: @Emoji.data
  25 + tpl: @Emoji.template
  26 +
  27 + # Team Members
  28 + input.atWho '@',
  29 + tpl: @Members.template
  30 + callback: (query, callback) =>
  31 + request_params = $.extend({}, @Members.params, query: query)
  32 + $.getJSON(@Members.url, request_params).done (members) =>
  33 + new_members_data = $.map(members, (m) ->
  34 + username: m.username,
  35 + name: m.name
  36 + )
  37 + callback(new_members_data)
49 38
50 - # so the next request gets the next page  
51 - GitLab.GfmAutoComplete.Members.params.page += 1  
52 - ).call()  
app/assets/javascripts/issues.js
1 -function switchToNewIssue(){  
2 - $(".issues_content").hide("fade", { direction: "left" }, 150, function(){  
3 - $('select#issue_assignee_id').chosen();  
4 - $('select#issue_milestone_id').chosen();  
5 - $("#new_issue_dialog").show("fade", { direction: "right" }, 150);  
6 - $('.top-tabs .add_new').hide();  
7 - disableButtonIfEmptyField("#issue_title", ".save-btn");  
8 - GitLab.GfmAutoComplete.setup();  
9 - });  
10 -}  
11 -  
12 -function switchToEditIssue(){  
13 - $(".issues_content").hide("fade", { direction: "left" }, 150, function(){  
14 - $('select#issue_assignee_id').chosen();  
15 - $('select#issue_milestone_id').chosen();  
16 - $("#edit_issue_dialog").show("fade", { direction: "right" }, 150);  
17 - $('.add_new').hide();  
18 - disableButtonIfEmptyField("#issue_title", ".save-btn");  
19 - GitLab.GfmAutoComplete.setup();  
20 - });  
21 -}  
22 -  
23 -function switchFromNewIssue(){  
24 - backToIssues();  
25 -}  
26 -  
27 -function switchFromEditIssue(){  
28 - backToIssues();  
29 -}  
30 -  
31 -function backToIssues(){  
32 - $("#edit_issue_dialog, #new_issue_dialog").hide("fade", { direction: "right" }, 150, function(){  
33 - $(".issues_content").show("fade", { direction: "left" }, 150, function() {  
34 - $("#edit_issue_dialog").html("");  
35 - $("#new_issue_dialog").html("");  
36 - $('.add_new').show();  
37 - });  
38 - });  
39 -}  
40 -  
41 function initIssuesSearch() { 1 function initIssuesSearch() {
42 var href = $('#issue_search_form').attr('action'); 2 var href = $('#issue_search_form').attr('action');
43 var last_terms = ''; 3 var last_terms = '';
@@ -76,23 +36,15 @@ function issuesPage(){ @@ -76,23 +36,15 @@ function issuesPage(){
76 $(this).closest("form").submit(); 36 $(this).closest("form").submit();
77 }); 37 });
78 38
79 - $("#new_issue_link").click(function(){  
80 - updateNewIssueURL();  
81 - });  
82 -  
83 - $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){ 39 + $('body').on('ajax:success', '.close_issue, .reopen_issue', function(){
84 var t = $(this), 40 var t = $(this),
85 totalIssues, 41 totalIssues,
86 - reopen = t.hasClass('reopen_issue'),  
87 - newIssue = false;  
88 - if( this.id == 'new_issue' ){  
89 - newIssue = true;  
90 - }  
91 - $('.issue_counter, #new_issue').each(function(){ 42 + reopen = t.hasClass('reopen_issue');
  43 + $('.issue_counter').each(function(){
92 var issue = $(this); 44 var issue = $(this);
93 totalIssues = parseInt( $(this).html(), 10 ); 45 totalIssues = parseInt( $(this).html(), 10 );
94 46
95 - if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){ 47 + if( reopen && issue.closest('.main_menu').length ){
96 $(this).html( totalIssues+1 ); 48 $(this).html( totalIssues+1 );
97 }else { 49 }else {
98 $(this).html( totalIssues-1 ); 50 $(this).html( totalIssues-1 );
@@ -126,20 +78,3 @@ function issuesCheckChanged() { @@ -126,20 +78,3 @@ function issuesCheckChanged() {
126 $('.issues_filters').show(); 78 $('.issues_filters').show();
127 } 79 }
128 } 80 }
129 -  
130 -function updateNewIssueURL(){  
131 - var new_issue_link = $("#new_issue_link");  
132 - var milestone_id = $("#milestone_id").val();  
133 - var assignee_id = $("#assignee_id").val();  
134 - var new_href = "";  
135 - if(milestone_id){  
136 - new_href = "issue[milestone_id]=" + milestone_id + "&";  
137 - }  
138 - if(assignee_id){  
139 - new_href = new_href + "issue[assignee_id]=" + assignee_id;  
140 - }  
141 - if(new_href.length){  
142 - new_href = new_issue_link.attr("href") + "?" + new_href;  
143 - new_issue_link.attr("href", new_href);  
144 - }  
145 -};  
app/assets/javascripts/main.js.coffee
@@ -7,6 +7,18 @@ window.slugify = (text) -&gt; @@ -7,6 +7,18 @@ window.slugify = (text) -&gt;
7 window.ajaxGet = (url) -> 7 window.ajaxGet = (url) ->
8 $.ajax({type: "GET", url: url, dataType: "script"}) 8 $.ajax({type: "GET", url: url, dataType: "script"})
9 9
  10 +window.errorMessage = (message) ->
  11 + ehtml = $("<p>")
  12 + ehtml.addClass("error_message")
  13 + ehtml.html(message)
  14 + ehtml
  15 +
  16 +window.split = (val) ->
  17 + return val.split( /,\s*/ )
  18 +
  19 +window.extractLast = (term) ->
  20 + return split( term ).pop()
  21 +
10 # Disable button if text field is empty 22 # Disable button if text field is empty
11 window.disableButtonIfEmptyField = (field_selector, button_selector) -> 23 window.disableButtonIfEmptyField = (field_selector, button_selector) ->
12 field = $(field_selector) 24 field = $(field_selector)
app/assets/javascripts/merge_requests.js
@@ -26,6 +26,12 @@ var MergeRequest = { @@ -26,6 +26,12 @@ var MergeRequest = {
26 self.showState(data.state); 26 self.showState(data.state);
27 }, "json"); 27 }, "json");
28 } 28 }
  29 +
  30 + if(self.opts.ci_enable){
  31 + $.get(self.opts.url_to_ci_check, function(data){
  32 + self.showCiState(data.status);
  33 + }, "json");
  34 + }
29 }, 35 },
30 36
31 initTabs: 37 initTabs:
@@ -79,6 +85,11 @@ var MergeRequest = { @@ -79,6 +85,11 @@ var MergeRequest = {
79 $(".automerge_widget." + state).show(); 85 $(".automerge_widget." + state).show();
80 }, 86 },
81 87
  88 + showCiState:
  89 + function(state){
  90 + $(".ci_widget").hide();
  91 + $(".ci_widget.ci-" + state).show();
  92 + },
82 93
83 loadDiff: 94 loadDiff:
84 function() { 95 function() {
app/assets/javascripts/projects.js.coffee
@@ -18,10 +18,3 @@ $ -&gt; @@ -18,10 +18,3 @@ $ -&gt;
18 # Ref switcher 18 # Ref switcher
19 $('.project-refs-select').on 'change', -> 19 $('.project-refs-select').on 'change', ->
20 $(@).parents('form').submit() 20 $(@).parents('form').submit()
21 -  
22 -class @GraphNav  
23 - @init: ->  
24 - $('.graph svg').css 'position', 'relative'  
25 - $('body').bind 'keyup', (e) ->  
26 - $('.graph svg').animate(left: '+=400') if e.keyCode is 37 # left  
27 - $('.graph svg').animate(left: '-=400') if e.keyCode is 39 # right  
app/assets/stylesheets/application.css
@@ -1,10 +0,0 @@ @@ -1,10 +0,0 @@
1 -/*  
2 - * This is a manifest file that'll automatically include all the stylesheets available in this directory  
3 - * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at  
4 - * the top of the compiled file, but it's generally better to create a new file per style scope.  
5 - *= require jquery.ui.gitlab  
6 - *= require jquery.atwho  
7 - *= require chosen  
8 - *= require_self  
9 - *= require main  
10 -*/  
app/assets/stylesheets/application.scss 0 → 100644
@@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
  1 +/*
  2 + * This is a manifest file that'll automatically include all the stylesheets available in this directory
  3 + * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
  4 + * the top of the compiled file, but it's generally better to create a new file per style scope.
  5 + *= require jquery.ui.gitlab
  6 + *= require jquery.atwho
  7 + *= require chosen
  8 + *= require_self
  9 +*/
  10 +
  11 +/**
  12 + * GitLab bootstrap:
  13 + */
  14 +@import "gitlab_bootstrap.scss";
  15 +
  16 +@import "common.scss";
  17 +@import "ref_select.scss";
  18 +
  19 +@import "sections/header.scss";
  20 +@import "sections/nav.scss";
  21 +@import "sections/commits.scss";
  22 +@import "sections/issues.scss";
  23 +@import "sections/projects.scss";
  24 +@import "sections/snippets.scss";
  25 +@import "sections/votes.scss";
  26 +@import "sections/merge_requests.scss";
  27 +@import "sections/graph.scss";
  28 +@import "sections/events.scss";
  29 +@import "sections/themes.scss";
  30 +@import "sections/tree.scss";
  31 +@import "sections/notes.scss";
  32 +@import "sections/profile.scss";
  33 +@import "sections/login.scss";
  34 +@import "sections/editor.scss";
  35 +
  36 +@import "highlight/white.scss";
  37 +@import "highlight/dark.scss";
  38 +
  39 +/**
  40 + * UI themes:
  41 + */
  42 +@import "themes/ui_basic.scss";
  43 +@import "themes/ui_mars.scss";
  44 +@import "themes/ui_modern.scss";
  45 +@import "themes/ui_gray.scss";
  46 +@import "themes/ui_color.scss";
  47 +
  48 +/**
  49 + * Styles for JS behaviors.
  50 + */
  51 +@import "behaviors.scss";
  52 +
app/assets/stylesheets/common.scss
@@ -13,20 +13,12 @@ body { @@ -13,20 +13,12 @@ body {
13 margin: 0 0; 13 margin: 0 0;
14 } 14 }
15 15
16 -.container .sidebar {  
17 - width: 200px;  
18 - height: 100%;  
19 - min-height: 450px;  
20 - float: right;  
21 -}  
22 -  
23 -  
24 .visible_link, 16 .visible_link,
25 .author_link { 17 .author_link {
26 color: $link_color; 18 color: $link_color;
27 } 19 }
28 20
29 -.help li { color:#111 } 21 +.help li { color:$style_color; }
30 22
31 .back_link { 23 .back_link {
32 text-decoration: underline; 24 text-decoration: underline;
@@ -65,6 +57,9 @@ table a code { @@ -65,6 +57,9 @@ table a code {
65 background: url(ajax_loader.gif) no-repeat center center; 57 background: url(ajax_loader.gif) no-repeat center center;
66 width: 40px; 58 width: 40px;
67 height: 40px; 59 height: 40px;
  60 + &.loading-gray {
  61 + background: url(ajax_loader_gray.gif) no-repeat center center;
  62 + }
68 } 63 }
69 64
70 /** FLASH message **/ 65 /** FLASH message **/
@@ -96,28 +91,17 @@ table a code { @@ -96,28 +91,17 @@ table a code {
96 margin-right:50px 91 margin-right:50px
97 } 92 }
98 93
99 -.handle:hover {  
100 - cursor: move;  
101 -}  
102 -  
103 span.update-author { 94 span.update-author {
104 display: block; 95 display: block;
105 -}  
106 -span.update-author {  
107 color: #999; 96 color: #999;
108 font-weight: normal; 97 font-weight: normal;
109 font-style: italic; 98 font-style: italic;
110 -}  
111 -span.update-author strong {  
112 - font-weight: bold;  
113 - font-style: normal; 99 + strong {
  100 + font-weight: bold;
  101 + font-style: normal;
  102 + }
114 } 103 }
115 104
116 -/** UPDATE ITEM **/  
117 -span.update-author {  
118 - display: block;  
119 -}  
120 -/** END UPDATE ITEM **/  
121 .dashboard-loader { 105 .dashboard-loader {
122 float: left; 106 float: left;
123 margin: 10px; 107 margin: 10px;
@@ -264,21 +248,6 @@ input.git_clone_url { @@ -264,21 +248,6 @@ input.git_clone_url {
264 } 248 }
265 249
266 250
267 -/** bordered list **/  
268 -ul.bordered-list {  
269 - margin: 5px 0px;  
270 - padding: 0px;  
271 - li {  
272 - padding: 5px 0;  
273 - border-bottom: 1px solid #EEE;  
274 - overflow: hidden;  
275 - display: block;  
276 - margin: 0px;  
277 - }  
278 -}  
279 -  
280 -ul.bordered-list li:last-child { border:none }  
281 -  
282 .line_holder { 251 .line_holder {
283 &:hover { 252 &:hover {
284 td { 253 td {
@@ -316,98 +285,6 @@ p.time { @@ -316,98 +285,6 @@ p.time {
316 } 285 }
317 286
318 287
319 -.ico {  
320 - background: url("images.png") no-repeat -85px -77px;  
321 - width: 19px;  
322 - height: 16px;  
323 - float: left;  
324 - position: relative;  
325 - margin-right: 10px;  
326 - top: 8px;  
327 -  
328 - &.project {  
329 - background-position: -37px -77px;  
330 - }  
331 -  
332 - &.activities {  
333 - background-position:-162px -22px;  
334 - }  
335 - &.projects {  
336 - background-position:-209px -21px;  
337 - }  
338 -}  
339 -  
340 -.leftbar {  
341 - h5, .title {  
342 - padding: 5px 10px;  
343 - }  
344 -  
345 - h4 {  
346 - font-size: 14px;  
347 - padding: 2px 10px;  
348 - color: #666;  
349 - border-bottom: 1px solid #f1f1f1;  
350 - }  
351 - a:last-child h4 { border: none; }  
352 -  
353 - a:hover {  
354 - h4 {  
355 - color: #111;  
356 - background: $hover;  
357 - border-color: #CCC;  
358 - .ico.project {  
359 - background-position:-209px -21px;  
360 - }  
361 - }  
362 - }  
363 - .bottom {  
364 - padding: 10px;  
365 - }  
366 -}  
367 -  
368 -.votes {  
369 - font-size: 13px;  
370 - line-height: 15px;  
371 - .progress {  
372 - height: 4px;  
373 - margin: 0;  
374 - .bar {  
375 - float: left;  
376 - height: 100%;  
377 - }  
378 - .bar-success {  
379 - @include linear-gradient(#62C462, #51A351);  
380 - background-color: #468847;  
381 - }  
382 - .bar-danger {  
383 - @include linear-gradient(#EE5F5B, #BD362F);  
384 - background-color: #B94A48;  
385 - }  
386 - }  
387 - .upvotes {  
388 - display: inline-block;  
389 - color: #468847;  
390 - }  
391 - .downvotes {  
392 - display: inline-block;  
393 - color: #B94A48;  
394 - }  
395 -}  
396 -.votes-block {  
397 - margin: 14px 6px 6px 0;  
398 - .downvotes {  
399 - float: right;  
400 - }  
401 -}  
402 -.votes-inline {  
403 - display: inline-block;  
404 - margin: 0 8px;  
405 - .progress {  
406 - display: inline-block;  
407 - padding: 0 0 2px;  
408 - width: 45px;  
409 - }  
410 -}  
411 288
412 /* Fix for readme code (stopped it from being yellow) */ 289 /* Fix for readme code (stopped it from being yellow) */
413 .readme { 290 .readme {
@@ -420,7 +297,6 @@ p.time { @@ -420,7 +297,6 @@ p.time {
420 } 297 }
421 } 298 }
422 299
423 -  
424 .highlight_word { 300 .highlight_word {
425 background: #EEDC94; 301 background: #EEDC94;
426 } 302 }
@@ -428,23 +304,16 @@ p.time { @@ -428,23 +304,16 @@ p.time {
428 .status_info { 304 .status_info {
429 font-size: 14px; 305 font-size: 14px;
430 padding: 5px 15px; 306 padding: 5px 15px;
431 - line-height: 24px;  
432 - width: 60px; 307 + line-height: 26px;
433 text-align: center; 308 text-align: center;
434 - float: left;  
435 - margin-right: 20px; 309 + float: right;
  310 + position: relative;
  311 + top: -5px;
  312 + @include border-radius(4px);
436 313
437 - &.success {  
438 - background: #5BB75B;  
439 - color: white;  
440 - text-shadow: 0 1px #111;  
441 - border-color: #9A9;  
442 - }  
443 &.error { 314 &.error {
444 background: #DA4E49; 315 background: #DA4E49;
445 - border-color: #BD362F;  
446 - color: white;  
447 - text-shadow: 0 1px #111; 316 + color: #FFF;
448 } 317 }
449 } 318 }
450 319
@@ -463,16 +332,6 @@ p.time { @@ -463,16 +332,6 @@ p.time {
463 height: 150px; 332 height: 150px;
464 } 333 }
465 334
466 -.gitlab_pagination {  
467 - span a { color: $link_color; }  
468 - .prev, .next, .current, .page a {  
469 - padding: 10px;  
470 - }  
471 - .current {  
472 - border-bottom: 2px solid $style_color;  
473 - }  
474 -}  
475 -  
476 // Fixes alignment on notes. 335 // Fixes alignment on notes.
477 .new_note { 336 .new_note {
478 label { 337 label {
@@ -647,9 +506,14 @@ pre { @@ -647,9 +506,14 @@ pre {
647 } 506 }
648 } 507 }
649 508
650 -.milestone .progress {  
651 - margin-bottom: 0;  
652 - margin-top: 4px; 509 +.milestone {
  510 + &.milestone-closed {
  511 + background: #eee;
  512 + }
  513 + .progress {
  514 + margin-bottom: 0;
  515 + margin-top: 4px;
  516 + }
653 } 517 }
654 518
655 .float-link { 519 .float-link {
app/assets/stylesheets/fonts.scss
@@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
1 -@font-face{  
2 - font-family: Korolev;  
3 - src: font-url('korolev-medium-compressed.otf');  
4 -}  
5 -  
6 -/** Typo **/  
7 -$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;  
8 \ No newline at end of file 0 \ No newline at end of file
app/assets/stylesheets/gitlab_bootstrap.scss 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +/** Override bootstrap variables **/
  2 +$baseFontSize: 13px !default;
  3 +$baseLineHeight: 18px !default;
  4 +
  5 +// BOOTSTRAP
  6 +@import "bootstrap";
  7 +@import "bootstrap/responsive-utilities";
  8 +@import "bootstrap/responsive-1200px-min";
  9 +
  10 +@import "font-awesome";
  11 +
  12 +/**
  13 + * GitLab bootstrap.
  14 + * Overrides some styles of twitter bootstrap.
  15 + * Also give some common classes for GitLab app
  16 + */
  17 +@import "gitlab_bootstrap/variables.scss";
  18 +@import "gitlab_bootstrap/fonts.scss";
  19 +@import "gitlab_bootstrap/mixins.scss";
  20 +@import "gitlab_bootstrap/common.scss";
  21 +@import "gitlab_bootstrap/typography.scss";
  22 +@import "gitlab_bootstrap/buttons.scss";
  23 +@import "gitlab_bootstrap/blocks.scss";
  24 +@import "gitlab_bootstrap/files.scss";
  25 +@import "gitlab_bootstrap/tables.scss";
  26 +@import "gitlab_bootstrap/lists.scss";
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
@@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
31 .middle_box_content, 31 .middle_box_content,
32 .bottom_box_content { 32 .bottom_box_content {
33 padding: 15px; 33 padding: 15px;
  34 + word-wrap: break-word;
34 35
35 pre { 36 pre {
36 background: none !important; 37 background: none !important;
@@ -40,6 +41,15 @@ @@ -40,6 +41,15 @@
40 } 41 }
41 } 42 }
42 43
  44 + .top_box_content {
  45 + .box-title {
  46 + color: $style_color;
  47 + font-size: 18px;
  48 + font-weight: normal;
  49 + line-height: 28px;
  50 + }
  51 + }
  52 +
43 .middle_box_content { 53 .middle_box_content {
44 @include border-radius(0); 54 @include border-radius(0);
45 border: none; 55 border: none;
@@ -64,7 +74,7 @@ @@ -64,7 +74,7 @@
64 74
65 border: 1px solid #eaeaea; 75 border: 1px solid #eaeaea;
66 @include border-radius(4px); 76 @include border-radius(4px);
67 - 77 +
68 border-color: #CCC; 78 border-color: #CCC;
69 @include solid-shade; 79 @include solid-shade;
70 80
@@ -83,6 +93,10 @@ @@ -83,6 +93,10 @@
83 border-top: 1px solid #eaeaea; 93 border-top: 1px solid #eaeaea;
84 border-bottom: 1px solid #bbb; 94 border-bottom: 1px solid #bbb;
85 95
  96 + > a {
  97 + text-shadow: 0 1px 1px #fff;
  98 + }
  99 +
86 &.small { 100 &.small {
87 line-height: 28px; 101 line-height: 28px;
88 font-size: 14px; 102 font-size: 14px;
@@ -138,19 +152,6 @@ @@ -138,19 +152,6 @@
138 } 152 }
139 } 153 }
140 154
141 - li, .wll {  
142 - padding: 10px;  
143 - &:first-child {  
144 - @include border-radius(4px 4px 0 0);  
145 - border-top: none;  
146 - }  
147 -  
148 - &:last-child {  
149 - @include border-radius(0 0 4px 4px);  
150 - border: none;  
151 - }  
152 - }  
153 -  
154 .ui-box-body { 155 .ui-box-body {
155 padding: 10px; 156 padding: 10px;
156 } 157 }
app/assets/stylesheets/gitlab_bootstrap/common.scss
@@ -10,11 +10,6 @@ @@ -10,11 +10,6 @@
10 /** COMMON CLASSES **/ 10 /** COMMON CLASSES **/
11 .left { float:left } 11 .left { float:left }
12 .right { float:right!important } 12 .right { float:right!important }
13 -.width-50p { width:50% }  
14 -.width-49p { width:49% }  
15 -.width-30p { width:30% }  
16 -.width-65p { width:65% }  
17 -.width-100p { width:100% }  
18 .append-bottom-10 { margin-bottom:10px } 13 .append-bottom-10 { margin-bottom:10px }
19 .append-bottom-20 { margin-bottom:20px } 14 .append-bottom-20 { margin-bottom:20px }
20 .prepend-top-10 { margin-top:10px } 15 .prepend-top-10 { margin-top:10px }
@@ -30,6 +25,7 @@ @@ -30,6 +25,7 @@
30 .borders { border: 1px solid #ccc; @include shade; } 25 .borders { border: 1px solid #ccc; @include shade; }
31 .hint { font-style: italic; color: #999; } 26 .hint { font-style: italic; color: #999; }
32 .light { color: #888 } 27 .light { color: #888 }
  28 +.tiny { font-weight: normal }
33 29
34 /** PILLS & TABS**/ 30 /** PILLS & TABS**/
35 .nav-pills a:hover { background-color: #888; } 31 .nav-pills a:hover { background-color: #888; }
@@ -99,18 +95,21 @@ input[type=&#39;search&#39;].search-text-input { @@ -99,18 +95,21 @@ input[type=&#39;search&#39;].search-text-input {
99 border: 1px solid #ccc; 95 border: 1px solid #ccc;
100 } 96 }
101 97
102 -fieldset legend { font-size: 17px; }  
103 -  
104 -ul.nav.nav-projects-tabs {  
105 - @extend .nav-tabs; 98 +input[type='text'].danger {
  99 + background: #F2DEDE!important;
  100 + border-color: #D66;
  101 + text-shadow: 0 1px 1px #fff
  102 +}
106 103
107 - padding-left: 8px; 104 +fieldset legend { font-size: 17px; }
108 105
109 - li {  
110 - a {  
111 - padding: 4px 20px;  
112 - margin-top: 2px;  
113 - border-color: #DDD;  
114 - } 106 +/** PAGINATION **/
  107 +.gitlab_pagination {
  108 + span a { color: $link_color; }
  109 + .prev, .next, .current, .page a {
  110 + padding: 10px;
  111 + }
  112 + .current {
  113 + border-bottom: 2px solid $style_color;
115 } 114 }
116 } 115 }
app/assets/stylesheets/gitlab_bootstrap/files.scss
@@ -43,11 +43,15 @@ @@ -43,11 +43,15 @@
43 padding: 0 4px; 43 padding: 0 4px;
44 } 44 }
45 padding: 20px; 45 padding: 20px;
46 - h1, h2 {  
47 - line-height: 46px;  
48 - }  
49 - h3, h4 {  
50 - line-height: 40px; 46 +
  47 + h1 { font-size: 26px; line-height: 46px; }
  48 + h2 { font-size: 22px; line-height: 42px; }
  49 + h3 { font-size: 20px; line-height: 40px; }
  50 + h4 { font-size: 18px; line-height: 32px; }
  51 + h5 { font-size: 16px; line-height: 26px; }
  52 +
  53 + .white .highlight pre {
  54 + background: #f5f5f5;
51 } 55 }
52 } 56 }
53 57
app/assets/stylesheets/gitlab_bootstrap/fonts.scss 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +@font-face{
  2 + font-family: Korolev;
  3 + src: font-url('korolev-medium-compressed.otf');
  4 +}
  5 +
  6 +/** Typo **/
  7 +$monospace: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
0 \ No newline at end of file 8 \ No newline at end of file
app/assets/stylesheets/gitlab_bootstrap/lists.scss
1 -/** LISTS **/  
2 -  
3 -ul {  
4 - /**  
5 - * List li block element #1  
6 - *  
7 - */  
8 - .wll { 1 +/**
  2 + * Well styled list
  3 + *
  4 + */
  5 +.well-list {
  6 + margin: 0;
  7 + list-style: none;
  8 + li {
9 background-color: #FFF; 9 background-color: #FFF;
10 - padding: 10px 5px; 10 + padding: 10px;
11 min-height: 20px; 11 min-height: 20px;
12 border-bottom: 1px solid #eee; 12 border-bottom: 1px solid #eee;
13 border-bottom: 1px solid rgba(0, 0, 0, 0.05); 13 border-bottom: 1px solid rgba(0, 0, 0, 0.05);
14 14
  15 + &.disabled {
  16 + color: #888;
  17 + }
  18 +
15 &.smoke { background-color: #f5f5f5; } 19 &.smoke { background-color: #f5f5f5; }
  20 +
16 &:hover { 21 &:hover {
17 background: $hover; 22 background: $hover;
18 border-bottom: 1px solid #ADF; 23 border-bottom: 1px solid #ADF;
19 } 24 }
20 - &:last-child { border:none } 25 +
  26 + &:first-child {
  27 + @include border-radius(4px 4px 0 0);
  28 + border-top: none;
  29 + }
  30 +
  31 + &:last-child {
  32 + @include border-radius(0 0 4px 4px);
  33 + border: none;
  34 + }
  35 +
21 .author { color: #999; } 36 .author { color: #999; }
22 37
23 p { 38 p {
@@ -29,6 +44,11 @@ ul { @@ -29,6 +44,11 @@ ul {
29 top: 3px; 44 top: 3px;
30 } 45 }
31 } 46 }
  47 +
  48 + .well-title {
  49 + font-size: 14px;
  50 + line-height: 18px;
  51 + }
32 } 52 }
33 } 53 }
34 54
@@ -39,3 +59,17 @@ ol, ul { @@ -39,3 +59,17 @@ ol, ul {
39 } 59 }
40 } 60 }
41 } 61 }
  62 +
  63 +/** light list with border-bottom between li **/
  64 +ul.bordered-list {
  65 + margin: 5px 0px;
  66 + padding: 0px;
  67 + li {
  68 + padding: 5px 0;
  69 + border-bottom: 1px solid #EEE;
  70 + overflow: hidden;
  71 + display: block;
  72 + margin: 0px;
  73 + &:last-child { border:none }
  74 + }
  75 +}
app/assets/stylesheets/gitlab_bootstrap/mixins.scss 0 → 100644
@@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
  1 +/**
  2 + * Generic mixins
  3 + */
  4 + @mixin box-shadow($shadow) {
  5 + -webkit-box-shadow: $shadow;
  6 + -moz-box-shadow: $shadow;
  7 + -ms-box-shadow: $shadow;
  8 + -o-box-shadow: $shadow;
  9 + box-shadow: $shadow;
  10 +}
  11 +
  12 +@mixin border-radius($radius) {
  13 + -webkit-border-radius: $radius;
  14 + -moz-border-radius: $radius;
  15 + -ms-border-radius: $radius;
  16 + -o-border-radius: $radius;
  17 + border-radius: $radius;
  18 +}
  19 +
  20 +@mixin linear-gradient($from, $to) {
  21 + background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));
  22 + background-image: -webkit-linear-gradient($from, $to);
  23 + background-image: -moz-linear-gradient($from, $to);
  24 + background-image: -o-linear-gradient($from, $to);
  25 +}
  26 +
  27 +/**
  28 + * Prefilled mixins
  29 + * Mixins with fixed values
  30 + */
  31 +@mixin bg-light-gray-gradient {
  32 + background: #f1f1f1;
  33 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));
  34 + background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  35 + background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  36 + background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);
  37 +}
  38 +
  39 +@mixin bg-gray-gradient {
  40 + background: #eee;
  41 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
  42 + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
  43 + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
  44 + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
  45 +}
  46 +
  47 +@mixin bg-dark-gray-gradient {
  48 + background: #eee;
  49 + background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);
  50 + background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);
  51 + background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);
  52 +}
  53 +
  54 +@mixin shade {
  55 + @include box-shadow(0 0 3px #ddd);
  56 +}
  57 +
  58 +@mixin solid-shade {
  59 + @include box-shadow(0 0 0 3px #f1f1f1);
  60 +}
  61 +
  62 +@mixin header-font {
  63 + color: $style_color;
  64 + text-shadow: 0 1px 1px #FFF;
  65 + font-family: 'Korolev', sans-serif;
  66 + font-size: 28px;
  67 + line-height: 48px;
  68 + font-weight: normal;
  69 +}
app/assets/stylesheets/gitlab_bootstrap/variables.scss 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +/** Colors **/
  2 +$primary_color: #2FA0BB;
  3 +$link_color: #3A89A3;
  4 +$style_color: #474D57;
  5 +$hover: #D9EDF7;
app/assets/stylesheets/highlight/dark.scss
1 .black .highlight { 1 .black .highlight {
  2 + background-color: #333;
2 pre { 3 pre {
3 - background-color: #333;  
4 color: #eee; 4 color: #eee;
  5 + background: inherit;
5 } 6 }
6 7
7 .hll { display: block; background-color: darken($hover, 65%) } 8 .hll { display: block; background-color: darken($hover, 65%) }
app/assets/stylesheets/jquery.ui.gitlab.css
1 -/*  
2 - * jQuery UI CSS Framework 1.8.7  
3 - *  
4 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
5 - * Dual licensed under the MIT or GPL Version 2 licenses.  
6 - * http://jquery.org/license  
7 - *  
8 - * http://docs.jquery.com/UI/Theming/API  
9 - */  
10 -  
11 -/* Layout helpers  
12 -----------------------------------*/  
13 -.ui-helper-hidden { display: none; }  
14 -.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }  
15 -.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }  
16 -.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }  
17 -.ui-helper-clearfix { display: inline-block; }  
18 -/* required comment for clearfix to work in Opera \*/  
19 -* html .ui-helper-clearfix { height:1%; }  
20 -.ui-helper-clearfix { display:block; }  
21 -/* end clearfix */  
22 -.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }  
23 -  
24 -  
25 /* Interaction Cues 1 /* Interaction Cues
26 ----------------------------------*/ 2 ----------------------------------*/
27 .ui-state-disabled { cursor: default !important; } 3 .ui-state-disabled { cursor: default !important; }
@@ -141,26 +117,6 @@ @@ -141,26 +117,6 @@
141 .ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); } 117 .ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); }
142 .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } 118 .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #000000; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }
143 /* 119 /*
144 - * jQuery UI Resizable 1.8.7  
145 - *  
146 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
147 - * Dual licensed under the MIT or GPL Version 2 licenses.  
148 - * http://jquery.org/license  
149 - *  
150 - * http://docs.jquery.com/UI/Resizable#theming  
151 - */  
152 -.ui-resizable { position: relative;}  
153 -.ui-resizable-handle { position: absolute; font-size: 0.1px; z-index: 999; display: block;}  
154 -.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }  
155 -.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }  
156 -.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }  
157 -.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }  
158 -.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }  
159 -.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }  
160 -.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }  
161 -.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }  
162 -.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}  
163 -/*  
164 * jQuery UI Selectable 1.8.7 120 * jQuery UI Selectable 1.8.7
165 * 121 *
166 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) 122 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
@@ -240,34 +196,7 @@ @@ -240,34 +196,7 @@
240 cursor: pointer; 196 cursor: pointer;
241 font-weight: bold; 197 font-weight: bold;
242 } 198 }
243 -/*  
244 - * jQuery UI Slider 1.8.7  
245 - *  
246 - * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)  
247 - * Dual licensed under the MIT or GPL Version 2 licenses.  
248 - * http://jquery.org/license  
249 - *  
250 - * http://docs.jquery.com/UI/Slider#theming  
251 - */  
252 -.ui-slider { position: relative; text-align: left; background: #d7d7d7; z-index: 1; }  
253 -.ui-slider { -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; box-shadow: 0 1px 2px rgba(0,0,0,0.5) inset; }  
254 -.ui-slider .ui-slider-handle { background: url(slider_handles.png) 0px -23px no-repeat; position: absolute; z-index: 2; width: 23px; height: 23px; cursor: default; border: none; outline: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; }  
255 -.ui-slider .ui-state-hover, .ui-slider .ui-state-active { background-position: 0 0; }  
256 -.ui-slider .ui-slider-range { background: #a3cae0; position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }  
257 -.ui-slider .ui-slider-range { -moz-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; -webkit-box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; box-shadow: 0 1px 2px rgba(17,35,45,0.6) inset; }  
258 199
259 -  
260 -.ui-slider-horizontal { height: 5px; }  
261 -.ui-slider-horizontal .ui-slider-handle { top: -8px; margin-left: -13px; }  
262 -.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }  
263 -.ui-slider-horizontal .ui-slider-range-min { left: 0; }  
264 -.ui-slider-horizontal .ui-slider-range-max { right: 0; }  
265 -  
266 -.ui-slider-vertical { width: 5px; height: 100px; }  
267 -.ui-slider-vertical .ui-slider-handle { left: -8px; margin-left: 0; margin-bottom: -13px; }  
268 -.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }  
269 -.ui-slider-vertical .ui-slider-range-min { bottom: 0; }  
270 -.ui-slider-vertical .ui-slider-range-max { top: 0; }  
271 /* 200 /*
272 * jQuery UI Datepicker 1.8.7 201 * jQuery UI Datepicker 1.8.7
273 * 202 *
@@ -326,45 +255,3 @@ @@ -326,45 +255,3 @@
326 .ui-datepicker table .ui-state-highlight { border-color: #ADE; } 255 .ui-datepicker table .ui-state-highlight { border-color: #ADE; }
327 .ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; } 256 .ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; }
328 .ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; } 257 .ui-datepicker-calendar .ui-state-active { background: #D9EDF7; border-color: #ADE; color: #3A89A3; font-weight: bold; text-shadow: 0 1px 1px #fff; }
329 -  
330 -/* with multiple calendars */  
331 -.ui-datepicker.ui-datepicker-multi { width:auto; }  
332 -.ui-datepicker-multi .ui-datepicker-group { float:left; }  
333 -.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }  
334 -.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }  
335 -.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }  
336 -.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }  
337 -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }  
338 -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }  
339 -.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }  
340 -.ui-datepicker-row-break { clear:both; width:100%; }  
341 -  
342 -  
343 -/* Extra Input Field Styling */  
344 -.ui-form textarea, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]) {  
345 - padding: 3px;  
346 - -webkit-border-radius: 2px;  
347 - -moz-border-radius: 2px;  
348 - border-radius: 2px;  
349 - border: 1px solid #cecece;  
350 - outline: none;  
351 - -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);  
352 - -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);  
353 - box-shadow: 0 1px 3px rgba(0,0,0,0.1) inset, 0 1px 0 rgba(255,255,255,0.2);  
354 - -webkit-transition: all 250ms ease-in-out;  
355 - -moz-transition: all 250ms ease-in-out;  
356 - -o-transition: all 250ms ease-in-out;  
357 - transition: all 250ms ease-in-out;  
358 -}  
359 -.ui-form textarea:hover, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):hover {  
360 - border: 1px solid #bdbdbd;  
361 - -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);  
362 - -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);  
363 - box-shadow: 0 1px 3px rgba(0,0,0,0.2) inset, 0 1px 0 rgba(255,255,255,0.2);  
364 -}  
365 -.ui-form textarea:focus, .ui-form input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):focus {  
366 - border: 1px solid #95bdd4;  
367 - -webkit-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);  
368 - -moz-box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);  
369 - box-shadow: 0 2px 3px rgba(161,202,226,0.5) inset, 0 1px 0 rgba(255,255,255,0.2);  
370 -}  
app/assets/stylesheets/main.scss
@@ -1,144 +0,0 @@ @@ -1,144 +0,0 @@
1 -/** Override bootstrap variables **/  
2 -$baseFontSize: 13px !default;  
3 -$baseLineHeight: 18px !default;  
4 -  
5 -// BOOTSTRAP  
6 -@import "bootstrap";  
7 -@import "bootstrap/responsive-utilities";  
8 -@import "bootstrap/responsive-1200px-min";  
9 -  
10 -// FONT AWESOME  
11 -@import "font-awesome";  
12 -  
13 -/**  
14 - * Variables  
15 - * Contains colors  
16 - */  
17 -@import "variables.scss";  
18 -  
19 -/**  
20 - * Custom fonts  
21 - * Contains @font-face font Korolev and default $monotype  
22 - */  
23 -@import "fonts.scss";  
24 -  
25 -/**  
26 - * General mixins.  
27 - * Contains rounded borders, gradients and shades  
28 - */  
29 -@import "mixins.scss";  
30 -  
31 -/**  
32 - * Header of application.  
33 - * Contain application logo, search panel, profile icon  
34 - */  
35 -@import "sections/header.scss";  
36 -  
37 -/**  
38 - * Navigation menu of application.  
39 - * Panel with links to pages depends on project, profile or admin area  
40 - */  
41 -@import "sections/nav.scss";  
42 -  
43 -/**  
44 - * This file represent some UI that can be changed  
45 - * during web app restyle or theme select.  
46 - *  
47 - * Next items should be placed there  
48 - * - link, button colors  
49 - * - header restyles  
50 - * - main menu restyles  
51 - *  
52 - */  
53 -@import "themes/ui_basic.scss";  
54 -  
55 -/**  
56 - * UI themes:  
57 - */  
58 -@import "themes/ui_mars.scss";  
59 -@import "themes/ui_modern.scss";  
60 -@import "themes/ui_gray.scss";  
61 -@import "themes/ui_color.scss";  
62 -  
63 -/**  
64 - * GitLab bootstrap.  
65 - * Overrides some styles of twitter bootstrap.  
66 - * Also give some common classes for GitLab app  
67 - */  
68 -@import "gitlab_bootstrap/common.scss";  
69 -@import "gitlab_bootstrap/typography.scss";  
70 -@import "gitlab_bootstrap/buttons.scss";  
71 -@import "gitlab_bootstrap/blocks.scss";  
72 -@import "gitlab_bootstrap/files.scss";  
73 -@import "gitlab_bootstrap/tables.scss";  
74 -@import "gitlab_bootstrap/lists.scss";  
75 -  
76 -  
77 -/**  
78 - * Most of application styles placed here.  
79 - * This file represent common UI that should not be changed between themes  
80 - * or project restyling like form width or user avatar class or commit title  
81 - *  
82 - * TODO: clean it  
83 - */  
84 -@import "common.scss";  
85 -  
86 -/**  
87 - * Styles necessary to support JS behaviours.  
88 - */  
89 -@import "behaviors.scss";  
90 -  
91 -/**  
92 - * Styles related to specific part of app  
93 - */  
94 -@import "sections/commits.scss";  
95 -@import "sections/issues.scss";  
96 -@import "sections/projects.scss";  
97 -@import "sections/merge_requests.scss";  
98 -@import "sections/graph.scss";  
99 -@import "sections/events.scss";  
100 -@import "sections/themes.scss";  
101 -  
102 -/**  
103 - * This scss file redefine chozen selectbox styles for  
104 - * project Branch/Tag select element  
105 - */  
106 -@import "ref_select.scss";  
107 -  
108 -/**  
109 - * Code (files list) styles. Browsing project files there  
110 - */  
111 -@import "sections/tree.scss";  
112 -  
113 -/**  
114 - * This file represent notes(comments) styles  
115 - */  
116 -@import "sections/notes.scss";  
117 -  
118 -/**  
119 - * This file represent profile styles  
120 - */  
121 -@import "sections/profile.scss";  
122 -  
123 -/**  
124 - * Devise styles  
125 - */  
126 -@import "sections/login.scss";  
127 -  
128 -/**  
129 - * CODE HIGHTLIGHT BASE  
130 - *  
131 - */  
132 -@import "highlight/white.scss";  
133 -  
134 -/**  
135 - * CODE HIGHTLIGHT DARK schema  
136 - *  
137 - */  
138 -@import "highlight/dark.scss";  
139 -  
140 -/**  
141 - * File Editor styles  
142 - *  
143 - */  
144 -@import "sections/editor.scss";  
app/assets/stylesheets/mixins.scss
@@ -1,60 +0,0 @@ @@ -1,60 +0,0 @@
1 -/**  
2 - * Generic mixins  
3 - */  
4 - @mixin box-shadow($shadow) {  
5 - -webkit-box-shadow: $shadow;  
6 - -moz-box-shadow: $shadow;  
7 - -ms-box-shadow: $shadow;  
8 - -o-box-shadow: $shadow;  
9 - box-shadow: $shadow;  
10 -}  
11 -  
12 -@mixin border-radius($radius) {  
13 - -webkit-border-radius: $radius;  
14 - -moz-border-radius: $radius;  
15 - -ms-border-radius: $radius;  
16 - -o-border-radius: $radius;  
17 - border-radius: $radius;  
18 -}  
19 -  
20 -@mixin linear-gradient($from, $to) {  
21 - background-image: -webkit-gradient(linear, 0 0, 0 100%, from($from), to($to));  
22 - background-image: -webkit-linear-gradient($from, $to);  
23 - background-image: -moz-linear-gradient($from, $to);  
24 - background-image: -o-linear-gradient($from, $to);  
25 -}  
26 -  
27 -/**  
28 - * Prefilled mixins  
29 - * Mixins with fixed values  
30 - */  
31 -@mixin bg-light-gray-gradient {  
32 - background: #f1f1f1;  
33 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #f5f5f5), to(#e1e1e1));  
34 - background-image: -webkit-linear-gradient(#f5f5f5 6.6%, #e1e1e1);  
35 - background-image: -moz-linear-gradient(#f5f5f5 6.6%, #e1e1e1);  
36 - background-image: -o-linear-gradient(#f5f5f5 6.6%, #e1e1e1);  
37 -}  
38 -  
39 -@mixin bg-gray-gradient {  
40 - background: #eee;  
41 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));  
42 - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);  
43 - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);  
44 - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);  
45 -}  
46 -  
47 -@mixin bg-dark-gray-gradient {  
48 - background: #eee;  
49 - background-image: -webkit-linear-gradient(#e9e9e9, #d7d7d7);  
50 - background-image: -moz-linear-gradient(#e9e9e9, #d7d7d7);  
51 - background-image: -o-linear-gradient(#e9e9e9, #d7d7d7);  
52 -}  
53 -  
54 -@mixin shade {  
55 - @include box-shadow(0 0 3px #ddd);  
56 -}  
57 -  
58 -@mixin solid-shade {  
59 - @include box-shadow(0 0 0 3px #f1f1f1);  
60 -}  
61 \ No newline at end of file 0 \ No newline at end of file
app/assets/stylesheets/sections/commits.scss
@@ -232,8 +232,6 @@ @@ -232,8 +232,6 @@
232 232
233 /** COMMIT ROW **/ 233 /** COMMIT ROW **/
234 .commit { 234 .commit {
235 - @extend .wll;  
236 -  
237 .browse_code_link_holder { 235 .browse_code_link_holder {
238 @extend .span2; 236 @extend .span2;
239 float: right; 237 float: right;
@@ -305,3 +303,17 @@ @@ -305,3 +303,17 @@
305 color: #fff; 303 color: #fff;
306 font-family: $monospace; 304 font-family: $monospace;
307 } 305 }
  306 +
  307 +
  308 +.commits-compare-switch{
  309 + background: url("switch_icon.png") no-repeat center center;
  310 + width: 16px;
  311 + height: 18px;
  312 + text-indent: -9999px;
  313 + float: left;
  314 + margin-right: 9px;
  315 + border: 1px solid #DDD;
  316 + @include border-radius(4px);
  317 + padding: 4px;
  318 + background-color: #EEE;
  319 +}
app/assets/stylesheets/sections/events.scss
@@ -31,7 +31,6 @@ @@ -31,7 +31,6 @@
31 * 31 *
32 */ 32 */
33 .event-item { 33 .event-item {
34 - min-height: 40px;  
35 border-bottom: 1px solid #eee; 34 border-bottom: 1px solid #eee;
36 .event-title { 35 .event-title {
37 color: #333; 36 color: #333;
@@ -50,14 +49,18 @@ @@ -50,14 +49,18 @@
50 } 49 }
51 } 50 }
52 .avatar { 51 .avatar {
53 - width: 32px; 52 + position: relative;
  53 + top: -3px;
54 } 54 }
55 .event_icon { 55 .event_icon {
  56 + position: relative;
56 float: right; 57 float: right;
57 border: 1px solid #EEE; 58 border: 1px solid #EEE;
58 padding: 5px; 59 padding: 5px;
59 @include border-radius(5px); 60 @include border-radius(5px);
60 background: #F9F9F9; 61 background: #F9F9F9;
  62 + margin-left: 10px;
  63 + top: -6px;
61 img { 64 img {
62 width: 20px; 65 width: 20px;
63 } 66 }
@@ -71,9 +74,8 @@ @@ -71,9 +74,8 @@
71 } 74 }
72 } 75 }
73 76
74 - padding: 15px 5px; 77 + padding: 16px 5px;
75 &:last-child { border:none } 78 &:last-child { border:none }
76 - .wll:hover { background:none }  
77 79
78 .event_commits { 80 .event_commits {
79 margin-top: 5px; 81 margin-top: 5px;
app/assets/stylesheets/sections/header.scss
@@ -44,14 +44,9 @@ header { @@ -44,14 +44,9 @@ header {
44 background: url('logo_dark.png') no-repeat 0px 2px; 44 background: url('logo_dark.png') no-repeat 0px 2px;
45 float: left; 45 float: left;
46 margin-left: 2px; 46 margin-left: 2px;
47 - font-size: 30px;  
48 - line-height: 48px;  
49 - font-weight: normal;  
50 - color: $style_color;  
51 - text-shadow: 0 1px 1px #FFF;  
52 padding-left: 45px; 47 padding-left: 45px;
53 height: 40px; 48 height: 40px;
54 - font-family: 'Korolev', sans-serif; 49 + @include header-font;
55 } 50 }
56 } 51 }
57 } 52 }
@@ -66,12 +61,7 @@ header { @@ -66,12 +61,7 @@ header {
66 float: left; 61 float: left;
67 margin: 0; 62 margin: 0;
68 margin-right: 30px; 63 margin-right: 30px;
69 - font-size: 30px;  
70 - line-height: 48px;  
71 - font-weight: normal;  
72 - color: $style_color;  
73 - text-shadow: 0 1px 1px #FFF;  
74 - font-family: 'Korolev', sans-serif; 64 + @include header-font;
75 } 65 }
76 66
77 /** 67 /**
@@ -172,7 +162,7 @@ header { @@ -172,7 +162,7 @@ header {
172 display: none; 162 display: none;
173 z-index: 100000; 163 z-index: 100000;
174 @include border-radius(4px); 164 @include border-radius(4px);
175 - width: 100px; 165 + width: 130px;
176 position: absolute; 166 position: absolute;
177 right: 5px; 167 right: 5px;
178 top: 38px; 168 top: 38px;
@@ -181,7 +171,7 @@ header { @@ -181,7 +171,7 @@ header {
181 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); 171 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
182 a { 172 a {
183 color: #fff; 173 color: #fff;
184 - padding: 7px 10px; 174 + padding: 12px 15px;
185 display: block; 175 display: block;
186 text-shadow: none; 176 text-shadow: none;
187 border-bottom: 1px solid #666; 177 border-bottom: 1px solid #666;
@@ -204,8 +194,8 @@ header { @@ -204,8 +194,8 @@ header {
204 } 194 }
205 &:last-child { 195 &:last-child {
206 @include border-radius(0 0 5px 5px); 196 @include border-radius(0 0 5px 5px);
207 - border-bottom: 0;  
208 - } 197 + border-bottom: 0;
  198 + }
209 } 199 }
210 200
211 201
app/assets/stylesheets/sections/issues.scss
@@ -121,12 +121,3 @@ input.check_all_issues { @@ -121,12 +121,3 @@ input.check_all_issues {
121 #update_status { 121 #update_status {
122 width: 100px; 122 width: 100px;
123 } 123 }
124 -  
125 -  
126 -/**  
127 - * Milestones list  
128 - *  
129 - */  
130 -.milestone {  
131 - @extend .wll;  
132 -}  
app/assets/stylesheets/sections/merge_requests.scss
@@ -136,9 +136,3 @@ li.merge_request { @@ -136,9 +136,3 @@ li.merge_request {
136 } 136 }
137 } 137 }
138 } 138 }
139 -  
140 -.status-badge {  
141 - height: 32px;  
142 - width: 100%;  
143 - @include border-radius(5px);  
144 -}  
app/assets/stylesheets/sections/nav.scss
@@ -3,15 +3,13 @@ @@ -3,15 +3,13 @@
3 * 3 *
4 */ 4 */
5 ul.main_menu { 5 ul.main_menu {
6 - @include border-radius(4px);  
7 margin: auto; 6 margin: auto;
8 margin: 30px 0; 7 margin: 30px 0;
9 - border: 1px solid #BBB; 8 + margin-top: 10px;
  9 + border-bottom: 1px solid #DDD;
10 height: 37px; 10 height: 37px;
11 - @include bg-gray-gradient;  
12 position: relative; 11 position: relative;
13 overflow: hidden; 12 overflow: hidden;
14 - @include shade;  
15 .count { 13 .count {
16 position: relative; 14 position: relative;
17 top: -1px; 15 top: -1px;
@@ -24,9 +22,6 @@ ul.main_menu { @@ -24,9 +22,6 @@ ul.main_menu {
24 line-height: 14px; 22 line-height: 14px;
25 text-align: center; 23 text-align: center;
26 color: #777; 24 color: #777;
27 - background: #f2f2f2;  
28 - border-top: 1px solid #CCC;  
29 - @include border-radius(8px);  
30 } 25 }
31 .label { 26 .label {
32 background: $hover; 27 background: $hover;
@@ -38,23 +33,10 @@ ul.main_menu { @@ -38,23 +33,10 @@ ul.main_menu {
38 margin: 0; 33 margin: 0;
39 display: table-cell; 34 display: table-cell;
40 width: 1%; 35 width: 1%;
41 - border-right: 1px solid #DDD;  
42 - border-left: 1px solid #EEE;  
43 - border-bottom: 2px solid #CFCFCF;  
44 -  
45 - &:first-child{  
46 - @include border-radius(5px 0 0 5px);  
47 - border-left: 0;  
48 - }  
49 -  
50 &.active { 36 &.active {
51 - background-color: #D5D5D5;  
52 - border-right: 1px solid #BBB;  
53 - border-left: 1px solid #BBB;  
54 - @include border-radius(0 0 1px 1px);  
55 - &:first-child{  
56 - border-bottom: none;  
57 - border-left: none; 37 + border-bottom: 2px solid #474D57;
  38 + a {
  39 + color: $style_color;
58 } 40 }
59 } 41 }
60 42
@@ -73,10 +55,10 @@ ul.main_menu { @@ -73,10 +55,10 @@ ul.main_menu {
73 a { 55 a {
74 display: block; 56 display: block;
75 text-align: center; 57 text-align: center;
76 - font-weight: bold; 58 + font-weight: normal;
77 height: 35px; 59 height: 35px;
78 line-height: 36px; 60 line-height: 36px;
79 - color: $style_color; 61 + color: #777;
80 text-shadow: 0 1px 1px white; 62 text-shadow: 0 1px 1px white;
81 padding: 0 10px; 63 padding: 0 10px;
82 } 64 }
app/assets/stylesheets/sections/projects.scss
@@ -4,12 +4,11 @@ @@ -4,12 +4,11 @@
4 } 4 }
5 5
6 .side { 6 .side {
7 - @extend .span4;  
8 @extend .right; 7 @extend .right;
9 8
10 .groups_box, 9 .groups_box,
11 .projects_box { 10 .projects_box {
12 - h5 { 11 + > h5 {
13 color: $style_color; 12 color: $style_color;
14 font-size: 16px; 13 font-size: 16px;
15 text-shadow: 0 1px 1px #fff; 14 text-shadow: 0 1px 1px #fff;
@@ -17,37 +16,22 @@ @@ -17,37 +16,22 @@
17 line-height: 32px; 16 line-height: 32px;
18 font-size: 14px; 17 font-size: 14px;
19 } 18 }
20 - ul {  
21 - li {  
22 - padding: 0;  
23 - a {  
24 - display: block;  
25 - .group_name {  
26 - font-size: 14px;  
27 - line-height: 18px;  
28 - }  
29 - .project_name {  
30 - color: #4fa2bd;  
31 - font-size: 14px;  
32 - line-height: 18px;  
33 - }  
34 - .arrow {  
35 - float: right;  
36 - padding: 10px;  
37 - margin: 0;  
38 - }  
39 - .last_activity {  
40 - padding-top: 5px;  
41 - display: block;  
42 - span, strong {  
43 - font-size: 12px;  
44 - color: #666;  
45 - }  
46 - } 19 + .nav-projects-tabs li { padding: 0; }
  20 + .well-list {
  21 + .arrow {
  22 + float: right;
  23 + padding: 10px;
  24 + margin: 0;
  25 + }
  26 + .last_activity {
  27 + padding-top: 5px;
  28 + display: block;
  29 + span, strong {
  30 + font-size: 12px;
  31 + color: #666;
47 } 32 }
48 } 33 }
49 } 34 }
50 - @extend .leftbar;  
51 @extend .ui-box; 35 @extend .ui-box;
52 } 36 }
53 } 37 }
@@ -117,3 +101,25 @@ @@ -117,3 +101,25 @@
117 } 101 }
118 102
119 } 103 }
  104 +
  105 +ul.nav.nav-projects-tabs {
  106 + @extend .nav-tabs;
  107 +
  108 + padding-left: 8px;
  109 +
  110 + li {
  111 + a {
  112 + padding: 4px 20px;
  113 + margin-top: 2px;
  114 + border-color: #DDD;
  115 + background-color: #EEE;
  116 + text-shadow: 0 1px 1px white;
  117 + color: #555;
  118 + }
  119 + &.active {
  120 + a {
  121 + font-weight: bold;
  122 + }
  123 + }
  124 + }
  125 +}
app/assets/stylesheets/sections/snippets.scss 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +.snippet.file_holder {
  2 + .file_title {
  3 + .snippet-file-name {
  4 + position: relative;
  5 + top: -4px;
  6 + left: -4px;
  7 + }
  8 + }
  9 +}
app/assets/stylesheets/sections/votes.scss 0 → 100644
@@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
  1 +.votes {
  2 + font-size: 13px;
  3 + line-height: 15px;
  4 + .progress {
  5 + height: 4px;
  6 + margin: 0;
  7 + .bar {
  8 + float: left;
  9 + height: 100%;
  10 + }
  11 + .bar-success {
  12 + @include linear-gradient(#62C462, #51A351);
  13 + background-color: #468847;
  14 + }
  15 + .bar-danger {
  16 + @include linear-gradient(#EE5F5B, #BD362F);
  17 + background-color: #B94A48;
  18 + }
  19 + }
  20 + .upvotes {
  21 + display: inline-block;
  22 + color: #468847;
  23 + }
  24 + .downvotes {
  25 + display: inline-block;
  26 + color: #B94A48;
  27 + }
  28 +}
  29 +.votes-block {
  30 + margin: 14px 6px 6px 0;
  31 + .downvotes {
  32 + float: right;
  33 + }
  34 +}
  35 +.votes-inline {
  36 + display: inline-block;
  37 + margin: 0 8px;
  38 + .progress {
  39 + display: inline-block;
  40 + padding: 0 0 2px;
  41 + width: 45px;
  42 + }
  43 +}
app/assets/stylesheets/themes/ui_basic.scss
@@ -4,18 +4,6 @@ @@ -4,18 +4,6 @@
4 * 4 *
5 */ 5 */
6 .ui_basic { 6 .ui_basic {
7 - /*  
8 - * Common styles  
9 - *  
10 - */  
11 - a {  
12 - color: $link_color;  
13 - &:hover {  
14 - text-decoration: none;  
15 - color: $primary_color;  
16 - }  
17 - }  
18 -  
19 .app_logo { 7 .app_logo {
20 .separator { 8 .separator {
21 margin-left: 0; 9 margin-left: 0;
app/assets/stylesheets/variables.scss
@@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
1 -/** Colors **/  
2 -$primary_color: #2FA0BB;  
3 -$link_color: #3A89A3;  
4 -$style_color: #474D57;  
5 -$hover: #D9EDF7;  
6 \ No newline at end of file 0 \ No newline at end of file
app/contexts/project_update_context.rb
@@ -2,7 +2,9 @@ class ProjectUpdateContext &lt; BaseContext @@ -2,7 +2,9 @@ class ProjectUpdateContext &lt; BaseContext
2 def execute(role = :default) 2 def execute(role = :default)
3 namespace_id = params[:project].delete(:namespace_id) 3 namespace_id = params[:project].delete(:namespace_id)
4 4
5 - if namespace_id.present? 5 + allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
  6 +
  7 + if allowed_transfer && namespace_id.present?
6 if namespace_id == Namespace.global_id 8 if namespace_id == Namespace.global_id
7 if project.namespace.present? 9 if project.namespace.present?
8 # Transfer to global namespace from anyone 10 # Transfer to global namespace from anyone
app/controllers/admin/groups_controller.rb
@@ -2,7 +2,7 @@ class Admin::GroupsController &lt; AdminController @@ -2,7 +2,7 @@ class Admin::GroupsController &lt; AdminController
2 before_filter :group, only: [:edit, :show, :update, :destroy, :project_update] 2 before_filter :group, only: [:edit, :show, :update, :destroy, :project_update]
3 3
4 def index 4 def index
5 - @groups = Group.scoped 5 + @groups = Group.order('name ASC')
6 @groups = @groups.search(params[:name]) if params[:name].present? 6 @groups = @groups.search(params[:name]) if params[:name].present?
7 @groups = @groups.page(params[:page]).per(20) 7 @groups = @groups.page(params[:page]).per(20)
8 end 8 end
@@ -11,6 +11,7 @@ class Admin::GroupsController &lt; AdminController @@ -11,6 +11,7 @@ class Admin::GroupsController &lt; AdminController
11 @projects = Project.scoped 11 @projects = Project.scoped
12 @projects = @projects.not_in_group(@group) if @group.projects.present? 12 @projects = @projects.not_in_group(@group) if @group.projects.present?
13 @projects = @projects.all 13 @projects = @projects.all
  14 + @projects.reject!(&:empty_repo?)
14 end 15 end
15 16
16 def new 17 def new
app/controllers/admin/projects_controller.rb
@@ -4,12 +4,13 @@ class Admin::ProjectsController &lt; AdminController @@ -4,12 +4,13 @@ class Admin::ProjectsController &lt; AdminController
4 def index 4 def index
5 @projects = Project.scoped 5 @projects = Project.scoped
6 @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present? 6 @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
  7 + @projects = @projects.where(namespace_id: nil) if params[:namespace_id] == Namespace.global_id
7 @projects = @projects.search(params[:name]) if params[:name].present? 8 @projects = @projects.search(params[:name]) if params[:name].present?
8 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) 9 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
9 end 10 end
10 11
11 def show 12 def show
12 - @users = User.scoped 13 + @users = User.active
13 @users = @users.not_in_project(@project) if @project.users.present? 14 @users = @users.not_in_project(@project) if @project.users.present?
14 @users = @users.all 15 @users = @users.all
15 end 16 end
app/controllers/admin/users_controller.rb
@@ -3,7 +3,7 @@ class Admin::UsersController &lt; AdminController @@ -3,7 +3,7 @@ class Admin::UsersController &lt; AdminController
3 @admin_users = User.scoped 3 @admin_users = User.scoped
4 @admin_users = @admin_users.filter(params[:filter]) 4 @admin_users = @admin_users.filter(params[:filter])
5 @admin_users = @admin_users.search(params[:name]) if params[:name].present? 5 @admin_users = @admin_users.search(params[:name]) if params[:name].present?
6 - @admin_users = @admin_users.order("updated_at DESC").page(params[:page]) 6 + @admin_users = @admin_users.order("name ASC").page(params[:page])
7 end 7 end
8 8
9 def show 9 def show
@@ -30,7 +30,7 @@ class Admin::UsersController &lt; AdminController @@ -30,7 +30,7 @@ class Admin::UsersController &lt; AdminController
30 30
31 31
32 def new 32 def new
33 - @admin_user = User.new({ projects_limit: Gitlab.config.default_projects_limit }, as: :admin) 33 + @admin_user = User.new({ projects_limit: Gitlab.config.gitlab.default_projects_limit }, as: :admin)
34 end 34 end
35 35
36 def edit 36 def edit
app/controllers/application_controller.rb
@@ -112,6 +112,10 @@ class ApplicationController &lt; ActionController::Base @@ -112,6 +112,10 @@ class ApplicationController &lt; ActionController::Base
112 render file: Rails.root.join("public", "404"), layout: false, status: "404" 112 render file: Rails.root.join("public", "404"), layout: false, status: "404"
113 end 113 end
114 114
  115 + def render_403
  116 + render file: Rails.root.join("public", "403"), layout: false, status: "403"
  117 + end
  118 +
115 def require_non_empty_project 119 def require_non_empty_project
116 redirect_to @project if @project.empty_repo? 120 redirect_to @project if @project.empty_repo?
117 end 121 end
app/controllers/dashboard_controller.rb
@@ -7,6 +7,8 @@ class DashboardController &lt; ApplicationController @@ -7,6 +7,8 @@ class DashboardController &lt; ApplicationController
7 def index 7 def index
8 @groups = current_user.authorized_groups 8 @groups = current_user.authorized_groups
9 9
  10 + @has_authorized_projects = @projects.count > 0
  11 +
10 @projects = case params[:scope] 12 @projects = case params[:scope]
11 when 'personal' then 13 when 'personal' then
12 @projects.personal(current_user) 14 @projects.personal(current_user)
app/controllers/groups_controller.rb
@@ -21,7 +21,7 @@ class GroupsController &lt; ApplicationController @@ -21,7 +21,7 @@ class GroupsController &lt; ApplicationController
21 21
22 # Get authored or assigned open merge requests 22 # Get authored or assigned open merge requests
23 def merge_requests 23 def merge_requests
24 - @merge_requests = current_user.cared_merge_requests 24 + @merge_requests = current_user.cared_merge_requests.opened
25 @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20) 25 @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20)
26 end 26 end
27 27
@@ -49,6 +49,7 @@ class GroupsController &lt; ApplicationController @@ -49,6 +49,7 @@ class GroupsController &lt; ApplicationController
49 def people 49 def people
50 @project = group.projects.find(params[:project_id]) if params[:project_id] 50 @project = group.projects.find(params[:project_id]) if params[:project_id]
51 @users = @project ? @project.users : group.users 51 @users = @project ? @project.users : group.users
  52 + @users.sort_by!(&:name)
52 53
53 if @project 54 if @project
54 @team_member = @project.users_projects.new 55 @team_member = @project.users_projects.new
app/controllers/issues_controller.rb
1 class IssuesController < ProjectResourceController 1 class IssuesController < ProjectResourceController
2 before_filter :module_enabled 2 before_filter :module_enabled
3 - before_filter :issue, only: [:edit, :update, :destroy, :show] 3 + before_filter :issue, only: [:edit, :update, :show]
4 4
5 # Allow read any issue 5 # Allow read any issue
6 before_filter :authorize_read_issue! 6 before_filter :authorize_read_issue!
@@ -11,9 +11,6 @@ class IssuesController &lt; ProjectResourceController @@ -11,9 +11,6 @@ class IssuesController &lt; ProjectResourceController
11 # Allow modify issue 11 # Allow modify issue
12 before_filter :authorize_modify_issue!, only: [:edit, :update] 12 before_filter :authorize_modify_issue!, only: [:edit, :update]
13 13
14 - # Allow destroy issue  
15 - before_filter :authorize_admin_issue!, only: [:destroy]  
16 -  
17 respond_to :js, :html 14 respond_to :js, :html
18 15
19 def index 16 def index
@@ -79,15 +76,6 @@ class IssuesController &lt; ProjectResourceController @@ -79,15 +76,6 @@ class IssuesController &lt; ProjectResourceController
79 end 76 end
80 end 77 end
81 78
82 - def destroy  
83 - @issue.destroy  
84 -  
85 - respond_to do |format|  
86 - format.html { redirect_to project_issues_path }  
87 - format.js { render nothing: true }  
88 - end  
89 - end  
90 -  
91 def sort 79 def sort
92 return render_404 unless can?(current_user, :admin_issue, @project) 80 return render_404 unless can?(current_user, :admin_issue, @project)
93 81
app/controllers/merge_requests_controller.rb
1 class MergeRequestsController < ProjectResourceController 1 class MergeRequestsController < ProjectResourceController
2 before_filter :module_enabled 2 before_filter :module_enabled
3 - before_filter :merge_request, only: [:edit, :update, :destroy, :show, :commits, :diffs, :automerge, :automerge_check] 3 + before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status]
4 before_filter :validates_merge_request, only: [:show, :diffs] 4 before_filter :validates_merge_request, only: [:show, :diffs]
5 before_filter :define_show_vars, only: [:show, :diffs] 5 before_filter :define_show_vars, only: [:show, :diffs]
6 6
@@ -13,9 +13,6 @@ class MergeRequestsController &lt; ProjectResourceController @@ -13,9 +13,6 @@ class MergeRequestsController &lt; ProjectResourceController
13 # Allow modify merge_request 13 # Allow modify merge_request
14 before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] 14 before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
15 15
16 - # Allow destroy merge_request  
17 - before_filter :authorize_admin_merge_request!, only: [:destroy]  
18 -  
19 def index 16 def index
20 @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute 17 @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
21 end 18 end
@@ -90,14 +87,6 @@ class MergeRequestsController &lt; ProjectResourceController @@ -90,14 +87,6 @@ class MergeRequestsController &lt; ProjectResourceController
90 end 87 end
91 end 88 end
92 89
93 - def destroy  
94 - @merge_request.destroy  
95 -  
96 - respond_to do |format|  
97 - format.html { redirect_to project_merge_requests_url(@project) }  
98 - end  
99 - end  
100 -  
101 def branch_from 90 def branch_from
102 @commit = project.commit(params[:ref]) 91 @commit = project.commit(params[:ref])
103 @commit = CommitDecorator.decorate(@commit) 92 @commit = CommitDecorator.decorate(@commit)
@@ -108,6 +97,13 @@ class MergeRequestsController &lt; ProjectResourceController @@ -108,6 +97,13 @@ class MergeRequestsController &lt; ProjectResourceController
108 @commit = CommitDecorator.decorate(@commit) 97 @commit = CommitDecorator.decorate(@commit)
109 end 98 end
110 99
  100 + def ci_status
  101 + status = project.gitlab_ci_service.commit_status(merge_request.last_commit.sha)
  102 + response = { status: status }
  103 +
  104 + render json: response
  105 + end
  106 +
111 protected 107 protected
112 108
113 def merge_request 109 def merge_request
app/controllers/milestones_controller.rb
@@ -12,11 +12,12 @@ class MilestonesController &lt; ProjectResourceController @@ -12,11 +12,12 @@ class MilestonesController &lt; ProjectResourceController
12 12
13 def index 13 def index
14 @milestones = case params[:f] 14 @milestones = case params[:f]
15 - when 'all'; @project.milestones  
16 - else @project.milestones.active 15 + when 'all'; @project.milestones.order("closed, due_date DESC")
  16 + when 'closed'; @project.milestones.closed.order("due_date DESC")
  17 + else @project.milestones.active.order("due_date ASC")
17 end 18 end
18 19
19 - @milestones = @milestones.includes(:project).order("due_date") 20 + @milestones = @milestones.includes(:project)
20 @milestones = @milestones.page(params[:page]).per(20) 21 @milestones = @milestones.page(params[:page]).per(20)
21 end 22 end
22 23
@@ -42,6 +43,7 @@ class MilestonesController &lt; ProjectResourceController @@ -42,6 +43,7 @@ class MilestonesController &lt; ProjectResourceController
42 43
43 def create 44 def create
44 @milestone = @project.milestones.new(params[:milestone]) 45 @milestone = @project.milestones.new(params[:milestone])
  46 + @milestone.author_id_of_changes = current_user.id
45 47
46 if @milestone.save 48 if @milestone.save
47 redirect_to project_milestone_path(@project, @milestone) 49 redirect_to project_milestone_path(@project, @milestone)
@@ -51,7 +53,7 @@ class MilestonesController &lt; ProjectResourceController @@ -51,7 +53,7 @@ class MilestonesController &lt; ProjectResourceController
51 end 53 end
52 54
53 def update 55 def update
54 - @milestone.update_attributes(params[:milestone]) 56 + @milestone.update_attributes(params[:milestone].merge(author_id_of_changes: current_user.id))
55 57
56 respond_to do |format| 58 respond_to do |format|
57 format.js 59 format.js
app/controllers/omniauth_callbacks_controller.rb
1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController 1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController
2 - Gitlab.config.omniauth_providers.each do |provider| 2 + Gitlab.config.omniauth.providers.each do |provider|
3 define_method provider['name'] do 3 define_method provider['name'] do
4 handle_omniauth 4 handle_omniauth
5 end 5 end
app/controllers/projects_controller.rb
@@ -46,6 +46,10 @@ class ProjectsController &lt; ProjectResourceController @@ -46,6 +46,10 @@ class ProjectsController &lt; ProjectResourceController
46 format.js 46 format.js
47 end 47 end
48 end 48 end
  49 +
  50 + rescue Project::TransferError => ex
  51 + @error = ex
  52 + render :update_failed
49 end 53 end
50 54
51 def show 55 def show
@@ -54,12 +58,12 @@ class ProjectsController &lt; ProjectResourceController @@ -54,12 +58,12 @@ class ProjectsController &lt; ProjectResourceController
54 58
55 respond_to do |format| 59 respond_to do |format|
56 format.html do 60 format.html do
57 - unless @project.empty_repo?  
58 - @last_push = current_user.recent_push(@project.id)  
59 - render :show  
60 - else  
61 - render "projects/empty"  
62 - end 61 + unless @project.empty_repo?
  62 + @last_push = current_user.recent_push(@project.id)
  63 + render :show
  64 + else
  65 + render "projects/empty"
  66 + end
63 end 67 end
64 format.js 68 format.js
65 end 69 end
@@ -86,12 +90,18 @@ class ProjectsController &lt; ProjectResourceController @@ -86,12 +90,18 @@ class ProjectsController &lt; ProjectResourceController
86 end 90 end
87 91
88 def graph 92 def graph
89 - graph = Gitlab::Graph::JsonBuilder.new(project)  
90 -  
91 - @days_json, @commits_json = graph.days_json, graph.commits_json 93 + respond_to do |format|
  94 + format.html
  95 + format.json do
  96 + graph = Gitlab::Graph::JsonBuilder.new(project)
  97 + render :json => graph.to_json
  98 + end
  99 + end
92 end 100 end
93 101
94 def destroy 102 def destroy
  103 + return access_denied! unless can?(current_user, :remove_project, project)
  104 +
95 # Disable the UsersProject update_repository call, otherwise it will be 105 # Disable the UsersProject update_repository call, otherwise it will be
96 # called once for every person removed from the project 106 # called once for every person removed from the project
97 UsersProject.skip_callback(:destroy, :after, :update_repository) 107 UsersProject.skip_callback(:destroy, :after, :update_repository)
app/controllers/snippets_controller.rb
@@ -16,7 +16,7 @@ class SnippetsController &lt; ProjectResourceController @@ -16,7 +16,7 @@ class SnippetsController &lt; ProjectResourceController
16 respond_to :html 16 respond_to :html
17 17
18 def index 18 def index
19 - @snippets = @project.snippets 19 + @snippets = @project.snippets.fresh
20 end 20 end
21 21
22 def new 22 def new
@@ -62,7 +62,7 @@ class SnippetsController &lt; ProjectResourceController @@ -62,7 +62,7 @@ class SnippetsController &lt; ProjectResourceController
62 redirect_to project_snippets_path(@project) 62 redirect_to project_snippets_path(@project)
63 end 63 end
64 64
65 - def raw 65 + def raw
66 send_data( 66 send_data(
67 @snippet.content, 67 @snippet.content,
68 type: "text/plain", 68 type: "text/plain",
app/decorators/commit_decorator.rb
@@ -76,7 +76,7 @@ class CommitDecorator &lt; ApplicationDecorator @@ -76,7 +76,7 @@ class CommitDecorator &lt; ApplicationDecorator
76 source_name = send "#{options[:source]}_name".to_sym 76 source_name = send "#{options[:source]}_name".to_sym
77 source_email = send "#{options[:source]}_email".to_sym 77 source_email = send "#{options[:source]}_email".to_sym
78 text = if options[:avatar] 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] 79 + avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
80 %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>} 80 %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
81 else 81 else
82 source_name 82 source_name
app/helpers/application_helper.rb
1 require 'digest/md5' 1 require 'digest/md5'
  2 +require 'uri'
2 3
3 module ApplicationHelper 4 module ApplicationHelper
4 5
@@ -30,13 +31,15 @@ module ApplicationHelper @@ -30,13 +31,15 @@ module ApplicationHelper
30 args.any? { |v| v.to_s.downcase == action_name } 31 args.any? { |v| v.to_s.downcase == action_name }
31 end 32 end
32 33
33 - def gravatar_icon(user_email = '', size = 40)  
34 - if Gitlab.config.disable_gravatar? || user_email.blank? 34 + def gravatar_icon(user_email = '', size = nil)
  35 + size = 40 if size.nil? || size <= 0
  36 +
  37 + if !Gitlab.config.gravatar.enabled || user_email.blank?
35 'no_avatar.png' 38 'no_avatar.png'
36 else 39 else
37 - gravatar_prefix = request.ssl? ? "https://secure" : "http://www" 40 + gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
38 user_email.strip! 41 user_email.strip!
39 - "#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm" 42 + sprintf(gravatar_url, {:hash => Digest::MD5.hexdigest(user_email.downcase), :email => URI.escape(user_email), :size => size})
40 end 43 end
41 end 44 end
42 45
@@ -45,7 +48,7 @@ module ApplicationHelper @@ -45,7 +48,7 @@ module ApplicationHelper
45 end 48 end
46 49
47 def web_app_url 50 def web_app_url
48 - "#{request_protocol}://#{Gitlab.config.web_host}/" 51 + "#{request_protocol}://#{Gitlab.config.gitlab.host}/"
49 end 52 end
50 53
51 def last_commit(project) 54 def last_commit(project)
@@ -92,6 +95,7 @@ module ApplicationHelper @@ -92,6 +95,7 @@ module ApplicationHelper
92 { label: "API Help", url: help_api_path }, 95 { label: "API Help", url: help_api_path },
93 { label: "Markdown Help", url: help_markdown_path }, 96 { label: "Markdown Help", url: help_markdown_path },
94 { label: "SSH Keys Help", url: help_ssh_path }, 97 { label: "SSH Keys Help", url: help_ssh_path },
  98 + { label: "Gitlab Rake Tasks Help", url: help_raketasks_path },
95 ] 99 ]
96 100
97 project_nav = [] 101 project_nav = []
app/helpers/issues_helper.rb
@@ -4,28 +4,6 @@ module IssuesHelper @@ -4,28 +4,6 @@ module IssuesHelper
4 project_issues_path project, params 4 project_issues_path project, params
5 end 5 end
6 6
7 - def link_to_issue_assignee(issue)  
8 - project = issue.project  
9 -  
10 - tm = project.team_member_by_id(issue.assignee_id)  
11 - if tm  
12 - link_to issue.assignee_name, project_team_member_path(project, tm), class: "author_link"  
13 - else  
14 - issue.assignee_name  
15 - end  
16 - end  
17 -  
18 - def link_to_issue_author(issue)  
19 - project = issue.project  
20 -  
21 - tm = project.team_member_by_id(issue.author_id)  
22 - if tm  
23 - link_to issue.author_name, project_team_member_path(project, tm), class: "author_link"  
24 - else  
25 - issue.author_name  
26 - end  
27 - end  
28 -  
29 def issue_css_classes issue 7 def issue_css_classes issue
30 classes = "issue" 8 classes = "issue"
31 classes << " closed" if issue.closed 9 classes << " closed" if issue.closed
@@ -52,4 +30,14 @@ module IssuesHelper @@ -52,4 +30,14 @@ module IssuesHelper
52 open: "open" 30 open: "open"
53 } 31 }
54 end 32 end
  33 +
  34 + def labels_autocomplete_source
  35 + labels = @project.issues_labels.order('count DESC')
  36 + labels = labels.map{ |l| { label: l.name, value: l.name } }
  37 + labels.to_json
  38 + end
  39 +
  40 + def issues_active_milestones
  41 + @project.milestones.active.order("id desc").all
  42 + end
55 end 43 end
app/helpers/merge_requests_helper.rb
1 module MergeRequestsHelper 1 module MergeRequestsHelper
2 - def link_to_merge_request_assignee(merge_request)  
3 - project = merge_request.project  
4 -  
5 - tm = project.team_member_by_id(merge_request.assignee_id)  
6 - if tm  
7 - link_to merge_request.assignee_name, project_team_member_path(project, tm), class: "author_link"  
8 - else  
9 - merge_request.assignee_name  
10 - end  
11 - end  
12 -  
13 - def link_to_merge_request_author(merge_request)  
14 - project = merge_request.project  
15 -  
16 - tm = project.team_member_by_id(merge_request.author_id)  
17 - if tm  
18 - link_to merge_request.author_name, project_team_member_path(project, tm), class: "author_link"  
19 - else  
20 - merge_request.author_name  
21 - end  
22 - end  
23 -  
24 def new_mr_path_from_push_event(event) 2 def new_mr_path_from_push_event(event)
25 new_project_merge_request_path( 3 new_project_merge_request_path(
26 event.project, 4 event.project,
@@ -39,7 +17,7 @@ module MergeRequestsHelper @@ -39,7 +17,7 @@ module MergeRequestsHelper
39 classes 17 classes
40 end 18 end
41 19
42 - def ci_status_path  
43 - @project.gitlab_ci_service.commit_badge_path(@merge_request.last_commit.sha) 20 + def ci_build_details_path merge_request
  21 + merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha)
44 end 22 end
45 end 23 end
app/helpers/projects_helper.rb
@@ -8,11 +8,49 @@ module ProjectsHelper @@ -8,11 +8,49 @@ module ProjectsHelper
8 end 8 end
9 9
10 def link_to_project project 10 def link_to_project project
11 - link_to project.name, project 11 + link_to project do
  12 + title = content_tag(:strong, project.name)
  13 +
  14 + if project.namespace
  15 + namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny')
  16 + title = namespace + title
  17 + end
  18 +
  19 + title
  20 + end
  21 + end
  22 +
  23 + def link_to_member(project, author)
  24 + return "(deleted)" unless author
  25 +
  26 + # Build avatar image tag
  27 + avatar = image_tag(gravatar_icon(author.try(:email)), width: 16, class: "lil_av")
  28 +
  29 + # Build name strong tag
  30 + name = content_tag :strong, author.name, class: 'author'
  31 +
  32 + author_html = avatar + name
  33 +
  34 + tm = project.team_member_by_id(author)
  35 +
  36 + content_tag :span, class: 'member-link' do
  37 + if tm
  38 + link_to author_html, project_team_member_path(project, tm), class: "author_link"
  39 + else
  40 + author_html
  41 + end
  42 + end
12 end 43 end
13 44
14 def tm_path team_member 45 def tm_path team_member
15 project_team_member_path(@project, team_member) 46 project_team_member_path(@project, team_member)
16 end 47 end
17 -end  
18 48
  49 + def project_title project
  50 + if project.group
  51 + project.name_with_namespace
  52 + else
  53 + project.name
  54 + end
  55 + end
  56 +end
app/helpers/tab_helper.rb
@@ -72,7 +72,7 @@ module TabHelper @@ -72,7 +72,7 @@ module TabHelper
72 return "active" if current_page?(controller: "projects", action: action, id: @project) 72 return "active" if current_page?(controller: "projects", action: action, id: @project)
73 end 73 end
74 74
75 - if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name 75 + if ['snippets', 'services', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
76 "active" 76 "active"
77 end 77 end
78 end 78 end
app/mailers/notify.rb
@@ -3,11 +3,11 @@ class Notify &lt; ActionMailer::Base @@ -3,11 +3,11 @@ class Notify &lt; ActionMailer::Base
3 add_template_helper ApplicationHelper 3 add_template_helper ApplicationHelper
4 add_template_helper GitlabMarkdownHelper 4 add_template_helper GitlabMarkdownHelper
5 5
6 - default_url_options[:host] = Gitlab.config.web_host  
7 - default_url_options[:protocol] = Gitlab.config.web_protocol  
8 - default_url_options[:port] = Gitlab.config.web_port if Gitlab.config.web_custom_port? 6 + default_url_options[:host] = Gitlab.config.gitlab.host
  7 + default_url_options[:protocol] = Gitlab.config.gitlab.protocol
  8 + default_url_options[:port] = Gitlab.config.gitlab.port if Gitlab.config.gitlab_on_non_standard_port?
9 9
10 - default from: Gitlab.config.email_from 10 + default from: Gitlab.config.gitlab.email_from
11 11
12 12
13 13
@@ -31,6 +31,7 @@ class Notify &lt; ActionMailer::Base @@ -31,6 +31,7 @@ class Notify &lt; ActionMailer::Base
31 def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) 31 def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
32 @issue = Issue.find issue_id 32 @issue = Issue.find issue_id
33 @issue_status = status 33 @issue_status = status
  34 + @project = @issue.project
34 @updated_by = User.find updated_by_user_id 35 @updated_by = User.find updated_by_user_id
35 mail(to: recipient(recipient_id), 36 mail(to: recipient(recipient_id),
36 subject: subject("changed issue ##{@issue.id}", @issue.title)) 37 subject: subject("changed issue ##{@issue.id}", @issue.title))
@@ -102,6 +103,12 @@ class Notify &lt; ActionMailer::Base @@ -102,6 +103,12 @@ class Notify &lt; ActionMailer::Base
102 end 103 end
103 104
104 105
  106 + def project_was_moved_email(user_project_id)
  107 + @users_project = UsersProject.find user_project_id
  108 + @project = @users_project.project
  109 + mail(to: @users_project.user.email,
  110 + subject: subject("project was moved"))
  111 + end
105 112
106 # 113 #
107 # User 114 # User
app/models/ability.rb
@@ -17,9 +17,7 @@ class Ability @@ -17,9 +17,7 @@ class Ability
17 17
18 # Rules based on role in project 18 # Rules based on role in project
19 if project.master_access_for?(user) 19 if project.master_access_for?(user)
20 - # TODO: replace with master rules.  
21 - # Only allow project administration for namespace owners  
22 - rules << project_admin_rules 20 + rules << project_master_rules
23 21
24 elsif project.dev_access_for?(user) 22 elsif project.dev_access_for?(user)
25 rules << project_dev_rules 23 rules << project_dev_rules
@@ -93,13 +91,16 @@ class Ability @@ -93,13 +91,16 @@ class Ability
93 :admin_merge_request, 91 :admin_merge_request,
94 :admin_note, 92 :admin_note,
95 :accept_mr, 93 :accept_mr,
96 - :admin_wiki 94 + :admin_wiki,
  95 + :admin_project
97 ] 96 ]
98 end 97 end
99 98
100 def project_admin_rules 99 def project_admin_rules
101 project_master_rules + [ 100 project_master_rules + [
102 - :admin_project 101 + :change_namespace,
  102 + :rename_project,
  103 + :remove_project
103 ] 104 ]
104 end 105 end
105 106
app/models/commit.rb
@@ -87,14 +87,10 @@ class Commit @@ -87,14 +87,10 @@ class Commit
87 last = project.commit(from.try(:strip)) 87 last = project.commit(from.try(:strip))
88 88
89 if first && last 89 if first && last
90 - commits = [first, last].sort_by(&:created_at)  
91 - younger = commits.first  
92 - older = commits.last  
93 -  
94 - result[:same] = (younger.id == older.id)  
95 - result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}  
96 - result[:diffs] = project.repo.diff(younger.id, older.id) rescue []  
97 - result[:commit] = Commit.new(older) 90 + result[:same] = (first.id == last.id)
  91 + result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
  92 + result[:diffs] = project.repo.diff(last.id, first.id) rescue []
  93 + result[:commit] = Commit.new(first)
98 end 94 end
99 95
100 result 96 result
@@ -163,6 +159,8 @@ class Commit @@ -163,6 +159,8 @@ class Commit
163 while !lines.first.start_with?("diff --git") do 159 while !lines.first.start_with?("diff --git") do
164 lines.shift 160 lines.shift
165 end 161 end
  162 + lines.pop if lines.last =~ /^[\d.]+$/ # Git version
  163 + lines.pop if lines.last == "-- " # end of diff
166 lines.join("\n") 164 lines.join("\n")
167 end 165 end
168 end 166 end
app/models/event.rb
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 # 15 #
16 16
17 class Event < ActiveRecord::Base 17 class Event < ActiveRecord::Base
  18 + include NoteEvent
18 include PushEvent 19 include PushEvent
19 20
20 attr_accessible :project, :action, :data, :author_id, :project_id, 21 attr_accessible :project, :action, :data, :author_id, :project_id,
@@ -58,12 +59,14 @@ class Event &lt; ActiveRecord::Base @@ -58,12 +59,14 @@ class Event &lt; ActiveRecord::Base
58 end 59 end
59 end 60 end
60 61
61 - # Next events currently enabled for system  
62 - # - push  
63 - # - new issue  
64 - # - merge request  
65 - def allowed?  
66 - push? || issue? || merge_request? || membership_changed? 62 + def proper?
  63 + if push?
  64 + true
  65 + elsif membership_changed?
  66 + true
  67 + else
  68 + (issue? || merge_request? || note? || milestone?) && target
  69 + end
67 end 70 end
68 71
69 def project_name 72 def project_name
@@ -94,6 +97,14 @@ class Event &lt; ActiveRecord::Base @@ -94,6 +97,14 @@ class Event &lt; ActiveRecord::Base
94 action == self.class::Reopened 97 action == self.class::Reopened
95 end 98 end
96 99
  100 + def milestone?
  101 + target_type == "Milestone"
  102 + end
  103 +
  104 + def note?
  105 + target_type == "Note"
  106 + end
  107 +
97 def issue? 108 def issue?
98 target_type == "Issue" 109 target_type == "Issue"
99 end 110 end
app/models/gitlab_ci_service.rb
@@ -36,4 +36,22 @@ class GitlabCiService &lt; Service @@ -36,4 +36,22 @@ class GitlabCiService &lt; Service
36 def commit_badge_path sha 36 def commit_badge_path sha
37 project_url + "/status?sha=#{sha}" 37 project_url + "/status?sha=#{sha}"
38 end 38 end
  39 +
  40 + def commit_status_path sha
  41 + project_url + "/builds/#{sha}/status.json?token=#{token}"
  42 + end
  43 +
  44 + def commit_status sha
  45 + response = HTTParty.get(commit_status_path(sha))
  46 +
  47 + if response.code == 200 and response["status"]
  48 + response["status"]
  49 + else
  50 + :error
  51 + end
  52 + end
  53 +
  54 + def build_page sha
  55 + project_url + "/builds/#{sha}"
  56 + end
39 end 57 end
app/models/merge_request.rb
@@ -204,7 +204,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -204,7 +204,7 @@ class MergeRequest &lt; ActiveRecord::Base
204 204
205 def mr_and_commit_notes 205 def mr_and_commit_notes
206 commit_ids = commits.map(&:id) 206 commit_ids = commits.map(&:id)
207 - Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids) 207 + Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
208 end 208 end
209 209
210 # Returns the raw diff for this merge request 210 # Returns the raw diff for this merge request
@@ -220,4 +220,8 @@ class MergeRequest &lt; ActiveRecord::Base @@ -220,4 +220,8 @@ class MergeRequest &lt; ActiveRecord::Base
220 def to_patch 220 def to_patch
221 project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}") 221 project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}")
222 end 222 end
  223 +
  224 + def last_commit_short_sha
  225 + @last_commit_short_sha ||= last_commit.sha[0..10]
  226 + end
223 end 227 end
app/models/milestone.rb
@@ -13,18 +13,26 @@ @@ -13,18 +13,26 @@
13 # 13 #
14 14
15 class Milestone < ActiveRecord::Base 15 class Milestone < ActiveRecord::Base
16 - attr_accessible :title, :description, :due_date, :closed 16 + attr_accessible :title, :description, :due_date, :closed, :author_id_of_changes
  17 + attr_accessor :author_id_of_changes
17 18
18 belongs_to :project 19 belongs_to :project
19 has_many :issues 20 has_many :issues
20 has_many :merge_requests 21 has_many :merge_requests
21 22
  23 + scope :active, where(closed: false)
  24 + scope :closed, where(closed: true)
  25 +
22 validates :title, presence: true 26 validates :title, presence: true
23 validates :project, presence: true 27 validates :project, presence: true
24 validates :closed, inclusion: { in: [true, false] } 28 validates :closed, inclusion: { in: [true, false] }
25 29
26 - def self.active  
27 - where("due_date > ? OR due_date IS NULL", Date.today) 30 + def expired?
  31 + if due_date
  32 + due_date < Date.today
  33 + else
  34 + false
  35 + end
28 end 36 end
29 37
30 def participants 38 def participants
@@ -52,4 +60,20 @@ class Milestone &lt; ActiveRecord::Base @@ -52,4 +60,20 @@ class Milestone &lt; ActiveRecord::Base
52 def expires_at 60 def expires_at
53 "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date 61 "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
54 end 62 end
  63 +
  64 + def can_be_closed?
  65 + open? && issues.opened.count.zero?
  66 + end
  67 +
  68 + def is_empty?
  69 + total_items_count.zero?
  70 + end
  71 +
  72 + def open?
  73 + !closed
  74 + end
  75 +
  76 + def author_id
  77 + author_id_of_changes
  78 + end
55 end 79 end
app/models/namespace.rb
@@ -48,23 +48,30 @@ class Namespace &lt; ActiveRecord::Base @@ -48,23 +48,30 @@ class Namespace &lt; ActiveRecord::Base
48 end 48 end
49 49
50 def ensure_dir_exist 50 def ensure_dir_exist
51 - namespace_dir_path = File.join(Gitlab.config.git_base_path, path) 51 + namespace_dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
52 system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path) 52 system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path)
53 end 53 end
54 54
55 def move_dir 55 def move_dir
56 if path_changed? 56 if path_changed?
57 - old_path = File.join(Gitlab.config.git_base_path, path_was)  
58 - new_path = File.join(Gitlab.config.git_base_path, path) 57 + old_path = File.join(Gitlab.config.gitolite.repos_path, path_was)
  58 + new_path = File.join(Gitlab.config.gitolite.repos_path, path)
59 if File.exists?(new_path) 59 if File.exists?(new_path)
60 raise "Already exists" 60 raise "Already exists"
61 end 61 end
62 - system("mv #{old_path} #{new_path}") 62 +
  63 + if system("mv #{old_path} #{new_path}")
  64 + send_update_instructions
  65 + end
63 end 66 end
64 end 67 end
65 68
66 def rm_dir 69 def rm_dir
67 - dir_path = File.join(Gitlab.config.git_base_path, path) 70 + dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
68 system("rm -rf #{dir_path}") 71 system("rm -rf #{dir_path}")
69 end 72 end
  73 +
  74 + def send_update_instructions
  75 + projects.each(&:send_move_instructions)
  76 + end
70 end 77 end
app/models/note.rb
@@ -19,7 +19,7 @@ require &#39;file_size_validator&#39; @@ -19,7 +19,7 @@ require &#39;file_size_validator&#39;
19 19
20 class Note < ActiveRecord::Base 20 class Note < ActiveRecord::Base
21 attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id, 21 attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
22 - :attachment, :line_code 22 + :attachment, :line_code, :commit_id
23 23
24 attr_accessor :notify 24 attr_accessor :notify
25 attr_accessor :notify_author 25 attr_accessor :notify_author
@@ -35,10 +35,14 @@ class Note &lt; ActiveRecord::Base @@ -35,10 +35,14 @@ class Note &lt; ActiveRecord::Base
35 validates :line_code, format: { with: /\A\d+_\d+_\d+\Z/ }, allow_blank: true 35 validates :line_code, format: { with: /\A\d+_\d+_\d+\Z/ }, allow_blank: true
36 validates :attachment, file_size: { maximum: 10.megabytes.to_i } 36 validates :attachment, file_size: { maximum: 10.megabytes.to_i }
37 37
  38 + validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
  39 + validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
  40 +
38 mount_uploader :attachment, AttachmentUploader 41 mount_uploader :attachment, AttachmentUploader
39 42
40 # Scopes 43 # Scopes
41 - scope :common, ->{ where(noteable_id: nil) } 44 + scope :for_commits, ->{ where(noteable_type: "Commit") }
  45 + scope :common, ->{ where(noteable_id: nil, commit_id: nil) }
42 scope :today, ->{ where("created_at >= :date", date: Date.today) } 46 scope :today, ->{ where("created_at >= :date", date: Date.today) }
43 scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) } 47 scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
44 scope :since, ->(day) { where("created_at >= :date", date: (day)) } 48 scope :since, ->(day) { where("created_at >= :date", date: (day)) }
@@ -122,7 +126,7 @@ class Note &lt; ActiveRecord::Base @@ -122,7 +126,7 @@ class Note &lt; ActiveRecord::Base
122 # override to return commits, which are not active record 126 # override to return commits, which are not active record
123 def noteable 127 def noteable
124 if for_commit? 128 if for_commit?
125 - project.commit(noteable_id) 129 + project.commit(commit_id)
126 else 130 else
127 super 131 super
128 end 132 end
@@ -151,4 +155,12 @@ class Note &lt; ActiveRecord::Base @@ -151,4 +155,12 @@ class Note &lt; ActiveRecord::Base
151 def votable? 155 def votable?
152 for_issue? || (for_merge_request? && !for_diff_line?) 156 for_issue? || (for_merge_request? && !for_diff_line?)
153 end 157 end
  158 +
  159 + def noteable_type_name
  160 + if noteable_type.present?
  161 + noteable_type.downcase
  162 + else
  163 + "wall"
  164 + end
  165 + end
154 end 166 end
app/models/project.rb
@@ -25,6 +25,9 @@ class Project &lt; ActiveRecord::Base @@ -25,6 +25,9 @@ class Project &lt; ActiveRecord::Base
25 include PushObserver 25 include PushObserver
26 include Authority 26 include Authority
27 include Team 27 include Team
  28 + include NamespacedProject
  29 +
  30 + class TransferError < StandardError; end
28 31
29 attr_accessible :name, :path, :description, :default_branch, :issues_enabled, 32 attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
30 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin] 33 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
@@ -36,6 +39,10 @@ class Project &lt; ActiveRecord::Base @@ -36,6 +39,10 @@ class Project &lt; ActiveRecord::Base
36 # Relations 39 # Relations
37 belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" 40 belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
38 belongs_to :namespace 41 belongs_to :namespace
  42 +
  43 + # TODO: replace owner with creator.
  44 + # With namespaces a project owner will be a namespace owner
  45 + # so this field makes sense only for global projects
39 belongs_to :owner, class_name: "User" 46 belongs_to :owner, class_name: "User"
40 has_many :users, through: :users_projects 47 has_many :users, through: :users_projects
41 has_many :events, dependent: :destroy 48 has_many :events, dependent: :destroy
@@ -97,7 +104,7 @@ class Project &lt; ActiveRecord::Base @@ -97,7 +104,7 @@ class Project &lt; ActiveRecord::Base
97 namespace_id = Namespace.find_by_path(id.first).id 104 namespace_id = Namespace.find_by_path(id.first).id
98 where(namespace_id: namespace_id).find_by_path(id.last) 105 where(namespace_id: namespace_id).find_by_path(id.last)
99 else 106 else
100 - find_by_path(id) 107 + where(path: id, namespace_id: nil).last
101 end 108 end
102 end 109 end
103 110
@@ -172,7 +179,7 @@ class Project &lt; ActiveRecord::Base @@ -172,7 +179,7 @@ class Project &lt; ActiveRecord::Base
172 end 179 end
173 180
174 def repo_name 181 def repo_name
175 - denied_paths = %w(gitolite-admin groups projects dashboard) 182 + denied_paths = %w(gitolite-admin admin dashboard groups help profile projects search)
176 183
177 if denied_paths.include?(path) 184 if denied_paths.include?(path)
178 errors.add(:path, "like #{path} is not allowed") 185 errors.add(:path, "like #{path} is not allowed")
@@ -188,7 +195,7 @@ class Project &lt; ActiveRecord::Base @@ -188,7 +195,7 @@ class Project &lt; ActiveRecord::Base
188 end 195 end
189 196
190 def web_url 197 def web_url
191 - [Gitlab.config.url, path].join("/") 198 + [Gitlab.config.gitlab.url, path_with_namespace].join("/")
192 end 199 end
193 200
194 def common_notes 201 def common_notes
@@ -196,15 +203,15 @@ class Project &lt; ActiveRecord::Base @@ -196,15 +203,15 @@ class Project &lt; ActiveRecord::Base
196 end 203 end
197 204
198 def build_commit_note(commit) 205 def build_commit_note(commit)
199 - notes.new(noteable_id: commit.id, noteable_type: "Commit") 206 + notes.new(commit_id: commit.id, noteable_type: "Commit")
200 end 207 end
201 208
202 def commit_notes(commit) 209 def commit_notes(commit)
203 - notes.where(noteable_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""') 210 + notes.where(commit_id: commit.id, noteable_type: "Commit").where('line_code IS NULL OR line_code = ""')
204 end 211 end
205 212
206 def commit_line_notes(commit) 213 def commit_line_notes(commit)
207 - notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL") 214 + notes.where(commit_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
208 end 215 end
209 216
210 def public? 217 def public?
@@ -239,51 +246,11 @@ class Project &lt; ActiveRecord::Base @@ -239,51 +246,11 @@ class Project &lt; ActiveRecord::Base
239 gitlab_ci_service && gitlab_ci_service.active 246 gitlab_ci_service && gitlab_ci_service.active
240 end 247 end
241 248
242 - def path_with_namespace  
243 - if namespace  
244 - namespace.path + '/' + path  
245 - else  
246 - path  
247 - end  
248 - end  
249 -  
250 # For compatibility with old code 249 # For compatibility with old code
251 def code 250 def code
252 path 251 path
253 end 252 end
254 253
255 - def transfer(new_namespace)  
256 - Project.transaction do  
257 - old_namespace = namespace  
258 - self.namespace = new_namespace  
259 -  
260 - old_dir = old_namespace.try(:path) || ''  
261 - new_dir = new_namespace.try(:path) || ''  
262 -  
263 - old_repo = if old_dir.present?  
264 - File.join(old_dir, self.path)  
265 - else  
266 - self.path  
267 - end  
268 -  
269 - Gitlab::ProjectMover.new(self, old_dir, new_dir).execute  
270 -  
271 - git_host.move_repository(old_repo, self)  
272 -  
273 - save!  
274 - end  
275 - end  
276 -  
277 - def name_with_namespace  
278 - @name_with_namespace ||= begin  
279 - if namespace  
280 - namespace.human_name + " / " + name  
281 - else  
282 - name  
283 - end  
284 - end  
285 - end  
286 -  
287 def items_for entity 254 def items_for entity
288 case entity 255 case entity
289 when 'issue' then 256 when 'issue' then
@@ -293,7 +260,9 @@ class Project &lt; ActiveRecord::Base @@ -293,7 +260,9 @@ class Project &lt; ActiveRecord::Base
293 end 260 end
294 end 261 end
295 262
296 - def namespace_owner  
297 - namespace.try(:owner) 263 + def send_move_instructions
  264 + self.users_projects.each do |member|
  265 + Notify.project_was_moved_email(member.id).deliver
  266 + end
298 end 267 end
299 end 268 end
app/models/snippet.rb
@@ -22,7 +22,7 @@ class Snippet &lt; ActiveRecord::Base @@ -22,7 +22,7 @@ class Snippet &lt; ActiveRecord::Base
22 belongs_to :author, class_name: "User" 22 belongs_to :author, class_name: "User"
23 has_many :notes, as: :noteable, dependent: :destroy 23 has_many :notes, as: :noteable, dependent: :destroy
24 24
25 - delegate :name, :email, to: :author, prefix: true 25 + delegate :name, :email, to: :author, prefix: true, allow_nil: true
26 26
27 validates :author, presence: true 27 validates :author, presence: true
28 validates :project, presence: true 28 validates :project, presence: true
app/models/user.rb
@@ -56,12 +56,12 @@ class User &lt; ActiveRecord::Base @@ -56,12 +56,12 @@ class User &lt; ActiveRecord::Base
56 has_many :issues, foreign_key: :author_id, dependent: :destroy 56 has_many :issues, foreign_key: :author_id, dependent: :destroy
57 has_many :notes, foreign_key: :author_id, dependent: :destroy 57 has_many :notes, foreign_key: :author_id, dependent: :destroy
58 has_many :merge_requests, foreign_key: :author_id, dependent: :destroy 58 has_many :merge_requests, foreign_key: :author_id, dependent: :destroy
59 - has_many :my_own_projects, class_name: "Project", foreign_key: :owner_id  
60 has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy 59 has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
61 has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC" 60 has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
62 has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy 61 has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
63 has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy 62 has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
64 63
  64 + validates :name, presence: true
65 validates :bio, length: { within: 0..255 } 65 validates :bio, length: { within: 0..255 }
66 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} 66 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
67 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} 67 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
@@ -123,16 +123,4 @@ class User &lt; ActiveRecord::Base @@ -123,16 +123,4 @@ class User &lt; ActiveRecord::Base
123 self.password = self.password_confirmation = Devise.friendly_token.first(8) 123 self.password = self.password_confirmation = Devise.friendly_token.first(8)
124 end 124 end
125 end 125 end
126 -  
127 - def authorized_groups  
128 - @authorized_groups ||= begin  
129 - groups = Group.where(id: self.projects.pluck(:namespace_id)).all  
130 - groups = groups + self.groups  
131 - groups.uniq  
132 - end  
133 - end  
134 -  
135 - def authorized_projects  
136 - Project.authorized_for(self)  
137 - end  
138 end 126 end
app/models/users_project.rb
@@ -28,6 +28,7 @@ class UsersProject &lt; ActiveRecord::Base @@ -28,6 +28,7 @@ class UsersProject &lt; ActiveRecord::Base
28 28
29 validates :user, presence: true 29 validates :user, presence: true
30 validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" } 30 validates :user_id, uniqueness: { :scope => [:project_id], message: "already exists in project" }
  31 + validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true
31 validates :project, presence: true 32 validates :project, presence: true
32 33
33 delegate :name, :email, to: :user, prefix: true 34 delegate :name, :email, to: :user, prefix: true
app/observers/activity_observer.rb
1 class ActivityObserver < ActiveRecord::Observer 1 class ActivityObserver < ActiveRecord::Observer
2 - observe :issue, :merge_request 2 + observe :issue, :merge_request, :note, :milestone
3 3
4 def after_create(record) 4 def after_create(record)
5 - Event.create(  
6 - project: record.project,  
7 - target_id: record.id,  
8 - target_type: record.class.name,  
9 - action: Event.determine_action(record),  
10 - author_id: record.author_id  
11 - ) 5 + event_author_id = record.author_id
  6 +
  7 + # Skip status notes
  8 + if record.kind_of?(Note) && record.note.include?("_Status changed to ")
  9 + return true
  10 + end
  11 +
  12 + if event_author_id
  13 + Event.create(
  14 + project: record.project,
  15 + target_id: record.id,
  16 + target_type: record.class.name,
  17 + action: Event.determine_action(record),
  18 + author_id: event_author_id
  19 + )
  20 + end
12 end 21 end
13 22
14 def after_save(record) 23 def after_save(record)
15 - if record.changed.include?("closed") 24 + if record.changed.include?("closed") && record.author_id_of_changes
16 Event.create( 25 Event.create(
17 project: record.project, 26 project: record.project,
18 target_id: record.id, 27 target_id: record.id,
app/observers/issue_observer.rb
@@ -16,7 +16,7 @@ class IssueObserver &lt; ActiveRecord::Observer @@ -16,7 +16,7 @@ class IssueObserver &lt; ActiveRecord::Observer
16 if status 16 if status
17 Note.create_status_change_note(issue, current_user, status) 17 Note.create_status_change_note(issue, current_user, status)
18 [issue.author, issue.assignee].compact.each do |recipient| 18 [issue.author, issue.assignee].compact.each do |recipient|
19 - Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) 19 + Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id).deliver
20 end 20 end
21 end 21 end
22 end 22 end
app/observers/note_observer.rb
@@ -21,7 +21,7 @@ class NoteObserver &lt; ActiveRecord::Observer @@ -21,7 +21,7 @@ class NoteObserver &lt; ActiveRecord::Observer
21 # Notifies the whole team except the author of note 21 # Notifies the whole team except the author of note
22 def notify_team(note) 22 def notify_team(note)
23 # Note: wall posts are not "attached" to anything, so fall back to "Wall" 23 # Note: wall posts are not "attached" to anything, so fall back to "Wall"
24 - noteable_type = note.noteable_type || "Wall" 24 + noteable_type = note.noteable_type.presence || "Wall"
25 notify_method = "note_#{noteable_type.underscore}_email".to_sym 25 notify_method = "note_#{noteable_type.underscore}_email".to_sym
26 26
27 if Notify.respond_to? notify_method 27 if Notify.respond_to? notify_method
app/observers/project_observer.rb
@@ -3,7 +3,8 @@ class ProjectObserver &lt; ActiveRecord::Observer @@ -3,7 +3,8 @@ class ProjectObserver &lt; ActiveRecord::Observer
3 project.update_repository 3 project.update_repository
4 end 4 end
5 5
6 - def after_save(project) 6 + def after_update(project)
  7 + project.send_move_instructions if project.namespace_id_changed?
7 end 8 end
8 9
9 def after_destroy(project) 10 def after_destroy(project)
app/roles/account.rb
@@ -47,7 +47,7 @@ module Account @@ -47,7 +47,7 @@ module Account
47 end 47 end
48 48
49 def cared_merge_requests 49 def cared_merge_requests
50 - MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id).opened 50 + MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
51 end 51 end
52 52
53 def project_ids 53 def project_ids
@@ -105,4 +105,20 @@ module Account @@ -105,4 +105,20 @@ module Account
105 def namespace_id 105 def namespace_id
106 namespace.try :id 106 namespace.try :id
107 end 107 end
  108 +
  109 + def authorized_groups
  110 + @authorized_groups ||= begin
  111 + groups = Group.where(id: self.projects.pluck(:namespace_id)).all
  112 + groups = groups + self.groups
  113 + groups.uniq
  114 + end
  115 + end
  116 +
  117 + def authorized_projects
  118 + Project.authorized_for(self)
  119 + end
  120 +
  121 + def my_own_projects
  122 + Project.personal(self)
  123 + end
108 end 124 end
app/roles/namespaced_project.rb 0 → 100644
@@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
  1 +module NamespacedProject
  2 + def transfer(new_namespace)
  3 + Project.transaction do
  4 + old_namespace = namespace
  5 + self.namespace = new_namespace
  6 +
  7 + old_dir = old_namespace.try(:path) || ''
  8 + new_dir = new_namespace.try(:path) || ''
  9 +
  10 + old_repo = if old_dir.present?
  11 + File.join(old_dir, self.path)
  12 + else
  13 + self.path
  14 + end
  15 +
  16 + if Project.where(path: self.path, namespace_id: new_namespace.try(:id)).present?
  17 + raise TransferError.new("Project with same path in target namespace already exists")
  18 + end
  19 +
  20 + Gitlab::ProjectMover.new(self, old_dir, new_dir).execute
  21 +
  22 + git_host.move_repository(old_repo, self)
  23 +
  24 + save!
  25 + end
  26 + rescue Gitlab::ProjectMover::ProjectMoveError => ex
  27 + raise TransferError.new(ex.message)
  28 + end
  29 +
  30 + def name_with_namespace
  31 + @name_with_namespace ||= begin
  32 + if namespace
  33 + namespace.human_name + " / " + name
  34 + else
  35 + name
  36 + end
  37 + end
  38 + end
  39 +
  40 + def namespace_owner
  41 + namespace.try(:owner)
  42 + end
  43 +
  44 + def chief
  45 + if namespace
  46 + namespace_owner
  47 + else
  48 + owner
  49 + end
  50 + end
  51 +
  52 + def path_with_namespace
  53 + if namespace
  54 + namespace.path + '/' + path
  55 + else
  56 + path
  57 + end
  58 + end
  59 +end
app/roles/note_event.rb 0 → 100644
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
  1 +module NoteEvent
  2 + def note_commit_id
  3 + target.commit_id
  4 + end
  5 +
  6 + def note_short_commit_id
  7 + note_commit_id[0..8]
  8 + end
  9 +
  10 + def note_commit?
  11 + target.noteable_type == "Commit"
  12 + end
  13 +
  14 + def note_target
  15 + target.noteable
  16 + end
  17 +
  18 + def note_target_id
  19 + if note_commit?
  20 + target.commit_id
  21 + else
  22 + target.noteable_id.to_s
  23 + end
  24 + end
  25 +
  26 + def wall_note?
  27 + target.noteable_type.blank?
  28 + end
  29 +
  30 + def note_target_type
  31 + if target.noteable_type.present?
  32 + target.noteable_type.titleize
  33 + else
  34 + "Wall"
  35 + end.downcase
  36 + end
  37 +end
app/roles/push_observer.rb
@@ -114,7 +114,7 @@ module PushObserver @@ -114,7 +114,7 @@ module PushObserver
114 id: commit.id, 114 id: commit.id,
115 message: commit.safe_message, 115 message: commit.safe_message,
116 timestamp: commit.date.xmlschema, 116 timestamp: commit.date.xmlschema,
117 - url: "#{Gitlab.config.url}/#{path}/commits/#{commit.id}", 117 + url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",
118 author: { 118 author: {
119 name: commit.author_name, 119 name: commit.author_name,
120 email: commit.author_email 120 email: commit.author_email
app/roles/repository.rb
@@ -45,8 +45,22 @@ module Repository @@ -45,8 +45,22 @@ module Repository
45 end 45 end
46 46
47 def has_post_receive_file? 47 def has_post_receive_file?
48 - hook_file = File.join(path_to_repo, 'hooks', 'post-receive')  
49 - File.exists?(hook_file) 48 + !!hook_file
  49 + end
  50 +
  51 + def valid_post_receive_file?
  52 + valid_hook_file == hook_file
  53 + end
  54 +
  55 + def valid_hook_file
  56 + @valid_hook_file ||= File.read(Rails.root.join('lib', 'hooks', 'post-receive'))
  57 + end
  58 +
  59 + def hook_file
  60 + @hook_file ||= begin
  61 + hook_path = File.join(path_to_repo, 'hooks', 'post-receive')
  62 + File.read(hook_path) if File.exists?(hook_path)
  63 + end
50 end 64 end
51 65
52 # Returns an Array of branch names 66 # Returns an Array of branch names
@@ -83,7 +97,7 @@ module Repository @@ -83,7 +97,7 @@ module Repository
83 end 97 end
84 98
85 def path_to_repo 99 def path_to_repo
86 - File.join(Gitlab.config.git_base_path, "#{path_with_namespace}.git") 100 + File.join(Gitlab.config.gitolite.repos_path, "#{path_with_namespace}.git")
87 end 101 end
88 102
89 def namespace_dir 103 def namespace_dir
@@ -185,7 +199,7 @@ module Repository @@ -185,7 +199,7 @@ module Repository
185 end 199 end
186 200
187 def http_url_to_repo 201 def http_url_to_repo
188 - http_url = [Gitlab.config.url, "/", path_with_namespace, ".git"].join('') 202 + http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
189 end 203 end
190 204
191 # Check if current branch name is marked as protected in the system 205 # Check if current branch name is marked as protected in the system
app/views/admin/groups/_form.html.haml
@@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
1 -= form_for [:admin, @group] do |f|  
2 - - if @group.errors.any?  
3 - .alert-message.block-message.error  
4 - %span= @group.errors.full_messages.first  
5 - .clearfix.group_name_holder  
6 - = f.label :name do  
7 - Group name is  
8 - .input  
9 - = f.text_field :name, placeholder: "Example Group", class: "xxlarge"  
10 -  
11 - .form-actions  
12 - = f.submit 'Save group', class: "btn save-btn"  
app/views/admin/groups/edit.html.haml
1 -%h3.page_title Edit Group  
2 -%br  
3 -= render 'form' 1 +%h3.page_title Rename Group
  2 +%hr
  3 += form_for [:admin, @group] do |f|
  4 + - if @group.errors.any?
  5 + .alert-message.block-message.error
  6 + %span= @group.errors.full_messages.first
  7 + .clearfix.group_name_holder
  8 + = f.label :name do
  9 + Group name is
  10 + .input
  11 + = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
  12 +
  13 +
  14 +
  15 + .clearfix.group_name_holder
  16 + = f.label :path do
  17 + %span.cred Group path is
  18 + .input
  19 + = f.text_field :path, placeholder: "example-group", class: "xxlarge danger"
  20 + %ul.cred
  21 + %li Changing group path can have unintended side effects.
  22 + %li Renaming group path will rename directory for all related projects
  23 + %li It will change web url for access group and group projects.
  24 + %li It will change the git path to repositories under this group.
  25 +
  26 + .form-actions
  27 + = f.submit 'Rename group', class: "btn danger"
  28 + = link_to 'Cancel', admin_groups_path, class: "btn cancel-btn"
app/views/admin/groups/index.html.haml
@@ -12,17 +12,24 @@ @@ -12,17 +12,24 @@
12 12
13 %table 13 %table
14 %thead 14 %thead
15 - %th Name  
16 - %th Path  
17 - %th Projects  
18 - %th Edit  
19 - %th.cred Danger Zone! 15 + %tr
  16 + %th
  17 + Name
  18 + %i.icon-sort-down
  19 + %th Path
  20 + %th Projects
  21 + %th Owner
  22 + %th.cred Danger Zone!
20 23
21 - @groups.each do |group| 24 - @groups.each do |group|
22 %tr 25 %tr
23 - %td= link_to group.name, [:admin, group] 26 + %td
  27 + %strong= link_to group.name, [:admin, group]
24 %td= group.path 28 %td= group.path
25 %td= group.projects.count 29 %td= group.projects.count
26 - %td= link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"  
27 - %td.bgred= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger" 30 + %td
  31 + = link_to group.owner_name, admin_user_path(group.owner_id)
  32 + %td.bgred
  33 + = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn small"
  34 + = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn small danger"
28 = paginate @groups, theme: "admin" 35 = paginate @groups, theme: "admin"
app/views/admin/groups/show.html.haml
1 %h3.page_title 1 %h3.page_title
2 Group: #{@group.name} 2 Group: #{@group.name}
3 - = link_to edit_admin_group_path(@group), class: "btn right" do  
4 - %i.icon-edit  
5 - Edit  
6 3
7 %br 4 %br
8 %table.zebra-striped 5 %table.zebra-striped
@@ -16,36 +13,64 @@ @@ -16,36 +13,64 @@
16 Name: 13 Name:
17 %td 14 %td
18 = @group.name 15 = @group.name
  16 + &nbsp;
  17 + = link_to edit_admin_group_path(@group), class: "btn btn-small right" do
  18 + %i.icon-edit
  19 + Rename
19 %tr 20 %tr
20 %td 21 %td
21 %b 22 %b
22 Path: 23 Path:
23 %td 24 %td
24 - %span.monospace= File.join(Gitlab.config.git_base_path, @group.path) 25 + %span.monospace= File.join(Gitlab.config.gitolite.repos_path, @group.path)
25 %tr 26 %tr
26 %td 27 %td
27 %b 28 %b
28 Owner: 29 Owner:
29 %td 30 %td
30 = @group.owner_name 31 = @group.owner_name
31 -.ui-box  
32 - %h5  
33 - Projects  
34 - %small  
35 - (#{@group.projects.count})  
36 - %ul.unstyled 32 + .right
  33 + = link_to "#", class: "btn btn-small change-owner-link" do
  34 + %i.icon-edit
  35 + Change owner
  36 +
  37 + %tr.change-owner-holder.hide
  38 + %td.bgred
  39 + %b.cred
  40 + New Owner:
  41 + %td.bgred
  42 + = form_for [:admin, @group] do |f|
  43 + = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
  44 + %div
  45 + = f.submit 'Change Owner', class: "btn danger"
  46 + = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
  47 +%fieldset
  48 + %legend Projects (#{@group.projects.count})
  49 + %table
  50 + %thead
  51 + %tr
  52 + %th Project name
  53 + %th Path
  54 + %th Users
  55 + %th.cred Danger Zone!
37 - @group.projects.each do |project| 56 - @group.projects.each do |project|
38 - %li.wll  
39 - %strong  
40 - = link_to project.name, [:admin, project]  
41 - .right  
42 - = link_to 'Remove from group', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Are you sure?', method: :delete, class: "btn danger small"  
43 - .clearfix 57 + %tr
  58 + %td
  59 + = link_to project.name_with_namespace, [:admin, project]
  60 + %td
  61 + %span.monospace= project.path_with_namespace + ".git"
  62 + %td= project.users.count
  63 + %td.bgred
  64 + = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn danger small"
44 65
45 66
46 = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do 67 = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
47 %fieldset 68 %fieldset
48 %legend Move projects to group 69 %legend Move projects to group
  70 + .alert
  71 + You can move only projects with existing repos
  72 + %br
  73 + Group projects will be moved in group directory and will not be accessible by old path
49 .clearfix 74 .clearfix
50 = label_tag :project_ids do 75 = label_tag :project_ids do
51 Projects 76 Projects
@@ -53,3 +78,17 @@ @@ -53,3 +78,17 @@
53 = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' 78 = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
54 .form-actions 79 .form-actions
55 = submit_tag 'Add', class: "btn primary" 80 = submit_tag 'Add', class: "btn primary"
  81 +
  82 +:javascript
  83 + $(function(){
  84 + var modal = $('.change-owner-holder');
  85 + $('.change-owner-link').bind("click", function(){
  86 + $(this).hide();
  87 + modal.show();
  88 + });
  89 + $('.change-owner-cancel-link').bind("click", function(){
  90 + modal.hide();
  91 + $('.change-owner-link').show();
  92 + })
  93 + })
  94 +
app/views/admin/logs/show.html.haml
@@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
3 = link_to "githost.log", "#githost", 'data-toggle' => 'tab' 3 = link_to "githost.log", "#githost", 'data-toggle' => 'tab'
4 %li 4 %li
5 = link_to "application.log", "#application", 'data-toggle' => 'tab' 5 = link_to "application.log", "#application", 'data-toggle' => 'tab'
  6 + %li
  7 + = link_to "production.log", "#production", 'data-toggle' => 'tab'
6 8
7 %p.light To prevent perfomance issues admin logs output the last 2000 lines 9 %p.light To prevent perfomance issues admin logs output the last 2000 lines
8 .tab-content 10 .tab-content
@@ -34,3 +36,17 @@ @@ -34,3 +36,17 @@
34 - Gitlab::AppLogger.read_latest.each do |line| 36 - Gitlab::AppLogger.read_latest.each do |line|
35 %li 37 %li
36 %p= line 38 %p= line
  39 + .tab-pane#production
  40 + .file_holder#README
  41 + .file_title
  42 + %i.icon-file
  43 + production.log
  44 + .right
  45 + = link_to '#', class: 'log-bottom' do
  46 + %i.icon-arrow-down
  47 + Scroll down
  48 + .file_content.logs
  49 + %ol
  50 + - Gitlab::Logger.read_latest_for('production.log').each do |line|
  51 + %li
  52 + %p= line
app/views/admin/projects/_form.html.haml
@@ -19,40 +19,47 @@ @@ -19,40 +19,47 @@
19 .input 19 .input
20 = text_field_tag :ppath, @project.path_to_repo, class: "xlarge", disabled: true 20 = text_field_tag :ppath, @project.path_to_repo, class: "xlarge", disabled: true
21 21
22 - - unless project.new_record? 22 + - if project.repo_exists?
23 .clearfix 23 .clearfix
24 - = f.label :namespace_id  
25 - .input= f.select :namespace_id, namespaces_options(@project.namespace_id), {}, {class: 'chosen'} 24 + = f.label :default_branch, "Default Branch"
  25 + .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;")
26 26
27 - - if project.repo_exists?  
28 - .clearfix  
29 - = f.label :default_branch, "Default Branch"  
30 - .input= f.select(:default_branch, project.heads.map(&:name), {}, style: "width:210px;") 27 + %fieldset.adv_settings
  28 + %legend Features:
31 29
32 - - unless project.new_record?  
33 - %fieldset.adv_settings  
34 - %legend Features: 30 + .clearfix
  31 + = f.label :issues_enabled, "Issues"
  32 + .input= f.check_box :issues_enabled
35 33
36 - .clearfix  
37 - = f.label :issues_enabled, "Issues"  
38 - .input= f.check_box :issues_enabled 34 + .clearfix
  35 + = f.label :merge_requests_enabled, "Merge Requests"
  36 + .input= f.check_box :merge_requests_enabled
39 37
40 - .clearfix  
41 - = f.label :merge_requests_enabled, "Merge Requests"  
42 - .input= f.check_box :merge_requests_enabled 38 + .clearfix
  39 + = f.label :wall_enabled, "Wall"
  40 + .input= f.check_box :wall_enabled
43 41
44 - .clearfix  
45 - = f.label :wall_enabled, "Wall"  
46 - .input= f.check_box :wall_enabled 42 + .clearfix
  43 + = f.label :wiki_enabled, "Wiki"
  44 + .input= f.check_box :wiki_enabled
  45 +
  46 + %fieldset.features
  47 + %legend Transfer:
  48 + .control-group
  49 + = f.label :namespace_id do
  50 + %span Namespace
  51 + .controls
  52 + = f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'}
  53 + %br
  54 + %ul.prepend-top-10.cred
  55 + %li Be careful. Changing project namespace can have unintended side effects
  56 + %li You can transfer project only to namespaces you can manage
  57 + %li You will need to update your local repositories to point to the new location.
47 58
48 - .clearfix  
49 - = f.label :wiki_enabled, "Wiki"  
50 - .input= f.check_box :wiki_enabled  
51 59
52 - - unless project.new_record?  
53 - .actions  
54 - = f.submit 'Save Project', class: "btn save-btn"  
55 - = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn" 60 + .actions
  61 + = f.submit 'Save Project', class: "btn save-btn"
  62 + = link_to 'Cancel', admin_projects_path, class: "btn cancel-btn"
56 63
57 64
58 65
app/views/admin/projects/index.html.haml
1 %h3.page_title 1 %h3.page_title
2 - Projects 2 + Projects (#{@projects.count})
3 = link_to 'New Project', new_project_path, class: "btn small right" 3 = link_to 'New Project', new_project_path, class: "btn small right"
4 %br 4 %br
5 = form_tag admin_projects_path, method: :get, class: 'form-inline' do 5 = form_tag admin_projects_path, method: :get, class: 'form-inline' do
@@ -9,12 +9,15 @@ @@ -9,12 +9,15 @@
9 9
10 %table 10 %table
11 %thead 11 %thead
12 - %th Name  
13 - %th Path  
14 - %th Team Members  
15 - %th Last Commit  
16 - %th Edit  
17 - %th.cred Danger Zone! 12 + %tr
  13 + %th
  14 + Name
  15 + %i.icon-sort-down
  16 + %th Path
  17 + %th Team Members
  18 + %th Last Commit
  19 + %th Edit
  20 + %th.cred Danger Zone!
18 21
19 - @projects.each do |project| 22 - @projects.each do |project|
20 %tr 23 %tr
app/views/admin/projects/show.html.haml
@@ -4,14 +4,24 @@ @@ -4,14 +4,24 @@
4 %i.icon-edit 4 %i.icon-edit
5 Edit 5 Edit
6 6
7 -- if !@project.has_post_receive_file? && @project.has_commits?  
8 - %br  
9 - .alert.alert-error  
10 - %span  
11 - %strong Important!  
12 - Project has commits but missing post-receive file.  
13 - %br  
14 - If you exported project manually - copy post-receive hook to bare repository 7 +- if @project.has_commits?
  8 + - if !@project.has_post_receive_file?
  9 + %br
  10 + .alert.alert-error
  11 + %span
  12 + %strong Project has commits but missing post-receive file.
  13 + %br
  14 + If you exported project manually - make a link of post-receive hook file from gitolite to project repository
  15 + - elsif !@project.valid_post_receive_file?
  16 + %br
  17 + .alert.alert-error
  18 + %span
  19 + %strong Project has invalid post-receive file.
  20 + %br
  21 + 1. Make sure your gitolite instace has latest post-receive file.
  22 + %br
  23 + 2. Make a link of post-receive hook file from gitolite to project repository
  24 +
15 25
16 %br 26 %br
17 %table.zebra-striped 27 %table.zebra-striped
@@ -37,9 +47,12 @@ @@ -37,9 +47,12 @@
37 %tr 47 %tr
38 %td 48 %td
39 %b 49 %b
40 - Path: 50 + Owned by:
41 %td 51 %td
42 - %code= @project.path_to_repo 52 + - if @project.chief
  53 + = link_to @project.chief.name, admin_user_path(@project.chief)
  54 + - else
  55 + (deleted)
43 %tr 56 %tr
44 %td 57 %td
45 %b 58 %b
@@ -49,11 +62,48 @@ @@ -49,11 +62,48 @@
49 %tr 62 %tr
50 %td 63 %td
51 %b 64 %b
  65 + Created at:
  66 + %td
  67 + = @project.created_at.stamp("March 1, 1999")
  68 +
  69 +%table.zebra-striped
  70 + %thead
  71 + %tr
  72 + %th Repository
  73 + %th
  74 + %tr
  75 + %td
  76 + %b
  77 + FS Path:
  78 + %td
  79 + %code= @project.path_to_repo
  80 + %tr
  81 + %td
  82 + %b
  83 + Smart HTTP:
  84 + %td
  85 + = link_to @project.http_url_to_repo
  86 + %tr
  87 + %td
  88 + %b
  89 + SSH:
  90 + %td
  91 + = link_to @project.ssh_url_to_repo
  92 + %tr
  93 + %td
  94 + %b
  95 + Last commit at:
  96 + %td
  97 + = last_commit(@project)
  98 + %tr
  99 + %td
  100 + %b
52 Post Receive File: 101 Post Receive File:
53 %td 102 %td
54 = check_box_tag :post_receive_file, 1, @project.has_post_receive_file?, disabled: true 103 = check_box_tag :post_receive_file, 1, @project.has_post_receive_file?, disabled: true
  104 +
55 %br 105 %br
56 -%h3 106 +%h5
57 Team 107 Team
58 %small 108 %small
59 (#{@project.users_projects.count}) 109 (#{@project.users_projects.count})
@@ -75,7 +125,7 @@ @@ -75,7 +125,7 @@
75 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small" 125 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn danger small"
76 126
77 %br 127 %br
78 -%h3 Add new team member 128 +%h5 Add new team member
79 %br 129 %br
80 = form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do 130 = form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do
81 %table.zebra-striped 131 %table.zebra-striped
app/views/admin/users/index.html.haml
1 %h3.page_title 1 %h3.page_title
2 - Users 2 + Users (#{@admin_users.count})
3 = link_to 'New User', new_admin_user_path, class: "btn small right" 3 = link_to 'New User', new_admin_user_path, class: "btn small right"
4 %br 4 %br
5 5
@@ -21,13 +21,16 @@ @@ -21,13 +21,16 @@
21 21
22 %table 22 %table
23 %thead 23 %thead
24 - %th Admin  
25 - %th Name  
26 - %th Username  
27 - %th Email  
28 - %th Projects  
29 - %th Edit  
30 - %th.cred Danger Zone! 24 + %tr
  25 + %th Admin
  26 + %th
  27 + Name
  28 + %i.icon-sort-down
  29 + %th Username
  30 + %th Email
  31 + %th Projects
  32 + %th Edit
  33 + %th.cred Danger Zone!
31 34
32 - @admin_users.each do |user| 35 - @admin_users.each do |user|
33 %tr 36 %tr
@@ -38,10 +41,13 @@ @@ -38,10 +41,13 @@
38 %td= user.users_projects.count 41 %td= user.users_projects.count
39 %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small" 42 %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small"
40 %td.bgred 43 %td.bgred
41 - - if user.blocked  
42 - = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success" 44 + - if user == current_user
  45 + %span.cred It's you!
43 - else 46 - else
44 - = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"  
45 - = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger" 47 + - if user.blocked
  48 + = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn small success"
  49 + - else
  50 + = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn small danger"
  51 + = link_to 'Destroy', [:admin, user], confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn small danger"
46 52
47 = paginate @admin_users, theme: "admin" 53 = paginate @admin_users, theme: "admin"
app/views/admin/users/show.html.haml
@@ -40,6 +40,12 @@ @@ -40,6 +40,12 @@
40 %tr 40 %tr
41 %td 41 %td
42 %b 42 %b
  43 + Created at:
  44 + %td
  45 + = @admin_user.created_at.stamp("March 1, 1999")
  46 + %tr
  47 + %td
  48 + %b
43 Projects limit: 49 Projects limit:
44 %td 50 %td
45 = @admin_user.projects_limit 51 = @admin_user.projects_limit
@@ -66,7 +72,7 @@ @@ -66,7 +72,7 @@
66 = @admin_user.twitter 72 = @admin_user.twitter
67 73
68 %br 74 %br
69 -%h3 Add User to Projects 75 +%h5 Add User to Projects
70 %br 76 %br
71 = form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do 77 = form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do
72 %table 78 %table
@@ -76,7 +82,7 @@ @@ -76,7 +82,7 @@
76 %th Project Access: 82 %th Project Access:
77 83
78 %tr 84 %tr
79 - %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5' 85 + %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
80 %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3" 86 %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
81 87
82 %tr 88 %tr
@@ -86,8 +92,22 @@ @@ -86,8 +92,22 @@
86 %strong= link_to "here", help_permissions_path, class: "vlink" 92 %strong= link_to "here", help_permissions_path, class: "vlink"
87 %br 93 %br
88 94
  95 +- if @admin_user.groups.present?
  96 + %h5 Owner of groups:
  97 + %br
  98 +
  99 + %table.zebra-striped
  100 + %thead
  101 + %tr
  102 + %th Name
  103 +
  104 + - @admin_user.groups.each do |group|
  105 + %tr
  106 + %td= link_to group.name, admin_group_path(group)
  107 +
  108 +
89 - if @admin_user.projects.present? 109 - if @admin_user.projects.present?
90 - %h3 Projects 110 + %h5 Projects:
91 %br 111 %br
92 112
93 %table.zebra-striped 113 %table.zebra-striped
@@ -101,7 +121,7 @@ @@ -101,7 +121,7 @@
101 - @admin_user.users_projects.each do |tm| 121 - @admin_user.users_projects.each do |tm|
102 - project = tm.project 122 - project = tm.project
103 %tr 123 %tr
104 - %td= link_to project.name, admin_project_path(project) 124 + %td= link_to project.name_with_namespace, admin_project_path(project)
105 %td= tm.project_access_human 125 %td= tm.project_access_human
106 %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small" 126 %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
107 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger" 127 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"