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.

.travis.yml
1 1 env:
  2 + - DB=postgresql
2 3 - DB=mysql
3 4 before_install:
4 5 - sudo apt-get install libicu-dev -y
... ... @@ -18,8 +19,7 @@ services:
18 19 before_script:
19 20 - "cp config/database.yml.$DB config/database.yml"
20 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 23 - "bundle exec rake db:seed_fu RAILS_ENV=test"
24 24 - "sh -e /etc/init.d/xvfb start"
25 25 script: "bundle exec rake travis --trace"
... ...
CHANGELOG
1 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 13 - [API] expose created date for hooks and SSH keys
3 14 - [API] list, create issue notes
4 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.
... ...
Gemfile
... ... @@ -32,7 +32,7 @@ gem 'grit_ext', git: "https://github.com/gitlabhq/grit_ext.git", ref:
32 32 gem "gitolite", '1.1.0'
33 33  
34 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 37 # Language detection
38 38 gem "github-linguist", "~> 2.3.4" , require: "linguist"
... ... @@ -100,7 +100,7 @@ group :assets do
100 100 gem "therubyracer"
101 101  
102 102 gem 'chosen-rails', "0.9.8"
103   - gem 'jquery-atwho-rails', "0.1.6"
  103 + gem 'jquery-atwho-rails', "0.1.7"
104 104 gem "jquery-rails", "2.1.3"
105 105 gem "jquery-ui-rails", "2.0.2"
106 106 gem "modernizr", "2.6.2"
... ... @@ -124,7 +124,7 @@ group :development, :test do
124 124 gem "capybara"
125 125 gem "pry"
126 126 gem "awesome_print"
127   - gem "database_cleaner"
  127 + gem "database_cleaner", ref: "f89c34300e114be99532f14c115b2799a3380ac6", git: "https://github.com/bmabey/database_cleaner.git"
128 128 gem "launchy"
129 129 gem 'factory_girl_rails'
130 130  
... ...
Gemfile.lock
1 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 9 remote: https://github.com/ctran/annotate_models.git
3 10 revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
4 11 specs:
... ... @@ -45,8 +52,8 @@ GIT
45 52  
46 53 GIT
47 54 remote: https://github.com/gitlabhq/pygments.rb.git
48   - revision: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4
49   - ref: 4db80c599067e2d5f23c5c243bf85b8ca0368ad4
  55 + revision: db1da0343adf86b49bdc3add04d02d2e80438d38
  56 + branch: master
50 57 specs:
51 58 pygments.rb (0.3.2)
52 59 posix-spawn (~> 0.3.6)
... ... @@ -140,7 +147,6 @@ GEM
140 147 colorize (0.5.8)
141 148 crack (0.3.1)
142 149 daemons (1.1.9)
143   - database_cleaner (0.9.1)
144 150 devise (2.1.2)
145 151 bcrypt-ruby (~> 3.0)
146 152 orm_adapter (~> 0.1)
... ... @@ -227,7 +233,7 @@ GEM
227 233 httpauth (0.2.0)
228 234 i18n (0.6.1)
229 235 journey (1.0.4)
230   - jquery-atwho-rails (0.1.6)
  236 + jquery-atwho-rails (0.1.7)
231 237 jquery-rails (2.1.3)
232 238 railties (>= 3.1.0, < 5.0)
233 239 thor (~> 0.14)
... ... @@ -458,7 +464,7 @@ DEPENDENCIES
458 464 chosen-rails (= 0.9.8)
459 465 coffee-rails (~> 3.2.2)
460 466 colored
461   - database_cleaner
  467 + database_cleaner!
462 468 devise (~> 2.1.0)
463 469 draper (~> 0.18.0)
464 470 email_spec
... ... @@ -481,7 +487,7 @@ DEPENDENCIES
481 487 guard-spinach
482 488 haml-rails (~> 0.3.5)
483 489 httparty
484   - jquery-atwho-rails (= 0.1.6)
  490 + jquery-atwho-rails (= 0.1.7)
485 491 jquery-rails (= 2.1.3)
486 492 jquery-ui-rails (= 2.0.2)
487 493 kaminari (~> 0.14.1)
... ...
README.md
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 3 GitLab is a free project and repository management application
4 4  
... ...
VERSION
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 1 # Creates the variables for setting up GFM auto-completion
2 2  
3 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 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 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 1 function initIssuesSearch() {
42 2 var href = $('#issue_search_form').attr('action');
43 3 var last_terms = '';
... ... @@ -76,23 +36,15 @@ function issuesPage(){
76 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 40 var t = $(this),
85 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 44 var issue = $(this);
93 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 48 $(this).html( totalIssues+1 );
97 49 }else {
98 50 $(this).html( totalIssues-1 );
... ... @@ -126,20 +78,3 @@ function issuesCheckChanged() {
126 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 7 window.ajaxGet = (url) ->
8 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 22 # Disable button if text field is empty
11 23 window.disableButtonIfEmptyField = (field_selector, button_selector) ->
12 24 field = $(field_selector)
... ...
app/assets/javascripts/merge_requests.js
... ... @@ -26,6 +26,12 @@ var MergeRequest = {
26 26 self.showState(data.state);
27 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 37 initTabs:
... ... @@ -79,6 +85,11 @@ var MergeRequest = {
79 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 94 loadDiff:
84 95 function() {
... ...
app/assets/javascripts/projects.js.coffee
... ... @@ -18,10 +18,3 @@ $ -&gt;
18 18 # Ref switcher
19 19 $('.project-refs-select').on 'change', ->
20 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   -/*
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 @@
  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 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 16 .visible_link,
25 17 .author_link {
26 18 color: $link_color;
27 19 }
28 20  
29   -.help li { color:#111 }
  21 +.help li { color:$style_color; }
30 22  
31 23 .back_link {
32 24 text-decoration: underline;
... ... @@ -65,6 +57,9 @@ table a code {
65 57 background: url(ajax_loader.gif) no-repeat center center;
66 58 width: 40px;
67 59 height: 40px;
  60 + &.loading-gray {
  61 + background: url(ajax_loader_gray.gif) no-repeat center center;
  62 + }
68 63 }
69 64  
70 65 /** FLASH message **/
... ... @@ -96,28 +91,17 @@ table a code {
96 91 margin-right:50px
97 92 }
98 93  
99   -.handle:hover {
100   - cursor: move;
101   -}
102   -
103 94 span.update-author {
104 95 display: block;
105   -}
106   -span.update-author {
107 96 color: #999;
108 97 font-weight: normal;
109 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 105 .dashboard-loader {
122 106 float: left;
123 107 margin: 10px;
... ... @@ -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 251 .line_holder {
283 252 &:hover {
284 253 td {
... ... @@ -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 289 /* Fix for readme code (stopped it from being yellow) */
413 290 .readme {
... ... @@ -420,7 +297,6 @@ p.time {
420 297 }
421 298 }
422 299  
423   -
424 300 .highlight_word {
425 301 background: #EEDC94;
426 302 }
... ... @@ -428,23 +304,16 @@ p.time {
428 304 .status_info {
429 305 font-size: 14px;
430 306 padding: 5px 15px;
431   - line-height: 24px;
432   - width: 60px;
  307 + line-height: 26px;
433 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 314 &.error {
444 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 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 335 // Fixes alignment on notes.
477 336 .new_note {
478 337 label {
... ... @@ -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 519 .float-link {
... ...
app/assets/stylesheets/fonts.scss
... ... @@ -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 0 \ No newline at end of file
app/assets/stylesheets/gitlab_bootstrap.scss 0 → 100644
... ... @@ -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 31 .middle_box_content,
32 32 .bottom_box_content {
33 33 padding: 15px;
  34 + word-wrap: break-word;
34 35  
35 36 pre {
36 37 background: none !important;
... ... @@ -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 53 .middle_box_content {
44 54 @include border-radius(0);
45 55 border: none;
... ... @@ -64,7 +74,7 @@
64 74  
65 75 border: 1px solid #eaeaea;
66 76 @include border-radius(4px);
67   -
  77 +
68 78 border-color: #CCC;
69 79 @include solid-shade;
70 80  
... ... @@ -83,6 +93,10 @@
83 93 border-top: 1px solid #eaeaea;
84 94 border-bottom: 1px solid #bbb;
85 95  
  96 + > a {
  97 + text-shadow: 0 1px 1px #fff;
  98 + }
  99 +
86 100 &.small {
87 101 line-height: 28px;
88 102 font-size: 14px;
... ... @@ -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 155 .ui-box-body {
155 156 padding: 10px;
156 157 }
... ...
app/assets/stylesheets/gitlab_bootstrap/common.scss
... ... @@ -10,11 +10,6 @@
10 10 /** COMMON CLASSES **/
11 11 .left { float:left }
12 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 13 .append-bottom-10 { margin-bottom:10px }
19 14 .append-bottom-20 { margin-bottom:20px }
20 15 .prepend-top-10 { margin-top:10px }
... ... @@ -30,6 +25,7 @@
30 25 .borders { border: 1px solid #ccc; @include shade; }
31 26 .hint { font-style: italic; color: #999; }
32 27 .light { color: #888 }
  28 +.tiny { font-weight: normal }
33 29  
34 30 /** PILLS & TABS**/
35 31 .nav-pills a:hover { background-color: #888; }
... ... @@ -99,18 +95,21 @@ input[type=&#39;search&#39;].search-text-input {
99 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 43 padding: 0 4px;
44 44 }
45 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 @@
  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 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 9 background-color: #FFF;
10   - padding: 10px 5px;
  10 + padding: 10px;
11 11 min-height: 20px;
12 12 border-bottom: 1px solid #eee;
13 13 border-bottom: 1px solid rgba(0, 0, 0, 0.05);
14 14  
  15 + &.disabled {
  16 + color: #888;
  17 + }
  18 +
15 19 &.smoke { background-color: #f5f5f5; }
  20 +
16 21 &:hover {
17 22 background: $hover;
18 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 36 .author { color: #999; }
22 37  
23 38 p {
... ... @@ -29,6 +44,11 @@ ul {
29 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 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 @@
  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 @@
  1 +/** Colors **/
  2 +$primary_color: #2FA0BB;
  3 +$link_color: #3A89A3;
  4 +$style_color: #474D57;
  5 +$hover: #D9EDF7;
... ...
app/assets/stylesheets/highlight/dark.scss
1 1 .black .highlight {
  2 + background-color: #333;
2 3 pre {
3   - background-color: #333;
4 4 color: #eee;
  5 + background: inherit;
5 6 }
6 7  
7 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 1 /* Interaction Cues
26 2 ----------------------------------*/
27 3 .ui-state-disabled { cursor: default !important; }
... ... @@ -141,26 +117,6 @@
141 117 .ui-widget-overlay { background: #262b33; opacity: .70;filter:Alpha(Opacity=70); }
142 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 120 * jQuery UI Selectable 1.8.7
165 121 *
166 122 * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
... ... @@ -240,34 +196,7 @@
240 196 cursor: pointer;
241 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 201 * jQuery UI Datepicker 1.8.7
273 202 *
... ... @@ -326,45 +255,3 @@
326 255 .ui-datepicker table .ui-state-highlight { border-color: #ADE; }
327 256 .ui-datepicker-calendar .ui-state-default { background: transparent; border-color: #FFF; }
328 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   -/** 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   -/**
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 0 \ No newline at end of file
app/assets/stylesheets/sections/commits.scss
... ... @@ -232,8 +232,6 @@
232 232  
233 233 /** COMMIT ROW **/
234 234 .commit {
235   - @extend .wll;
236   -
237 235 .browse_code_link_holder {
238 236 @extend .span2;
239 237 float: right;
... ... @@ -305,3 +303,17 @@
305 303 color: #fff;
306 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 31 *
32 32 */
33 33 .event-item {
34   - min-height: 40px;
35 34 border-bottom: 1px solid #eee;
36 35 .event-title {
37 36 color: #333;
... ... @@ -50,14 +49,18 @@
50 49 }
51 50 }
52 51 .avatar {
53   - width: 32px;
  52 + position: relative;
  53 + top: -3px;
54 54 }
55 55 .event_icon {
  56 + position: relative;
56 57 float: right;
57 58 border: 1px solid #EEE;
58 59 padding: 5px;
59 60 @include border-radius(5px);
60 61 background: #F9F9F9;
  62 + margin-left: 10px;
  63 + top: -6px;
61 64 img {
62 65 width: 20px;
63 66 }
... ... @@ -71,9 +74,8 @@
71 74 }
72 75 }
73 76  
74   - padding: 15px 5px;
  77 + padding: 16px 5px;
75 78 &:last-child { border:none }
76   - .wll:hover { background:none }
77 79  
78 80 .event_commits {
79 81 margin-top: 5px;
... ...
app/assets/stylesheets/sections/header.scss
... ... @@ -44,14 +44,9 @@ header {
44 44 background: url('logo_dark.png') no-repeat 0px 2px;
45 45 float: left;
46 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 47 padding-left: 45px;
53 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 61 float: left;
67 62 margin: 0;
68 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 162 display: none;
173 163 z-index: 100000;
174 164 @include border-radius(4px);
175   - width: 100px;
  165 + width: 130px;
176 166 position: absolute;
177 167 right: 5px;
178 168 top: 38px;
... ... @@ -181,7 +171,7 @@ header {
181 171 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
182 172 a {
183 173 color: #fff;
184   - padding: 7px 10px;
  174 + padding: 12px 15px;
185 175 display: block;
186 176 text-shadow: none;
187 177 border-bottom: 1px solid #666;
... ... @@ -204,8 +194,8 @@ header {
204 194 }
205 195 &:last-child {
206 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 121 #update_status {
122 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 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 3 *
4 4 */
5 5 ul.main_menu {
6   - @include border-radius(4px);
7 6 margin: auto;
8 7 margin: 30px 0;
9   - border: 1px solid #BBB;
  8 + margin-top: 10px;
  9 + border-bottom: 1px solid #DDD;
10 10 height: 37px;
11   - @include bg-gray-gradient;
12 11 position: relative;
13 12 overflow: hidden;
14   - @include shade;
15 13 .count {
16 14 position: relative;
17 15 top: -1px;
... ... @@ -24,9 +22,6 @@ ul.main_menu {
24 22 line-height: 14px;
25 23 text-align: center;
26 24 color: #777;
27   - background: #f2f2f2;
28   - border-top: 1px solid #CCC;
29   - @include border-radius(8px);
30 25 }
31 26 .label {
32 27 background: $hover;
... ... @@ -38,23 +33,10 @@ ul.main_menu {
38 33 margin: 0;
39 34 display: table-cell;
40 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 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 55 a {
74 56 display: block;
75 57 text-align: center;
76   - font-weight: bold;
  58 + font-weight: normal;
77 59 height: 35px;
78 60 line-height: 36px;
79   - color: $style_color;
  61 + color: #777;
80 62 text-shadow: 0 1px 1px white;
81 63 padding: 0 10px;
82 64 }
... ...
app/assets/stylesheets/sections/projects.scss
... ... @@ -4,12 +4,11 @@
4 4 }
5 5  
6 6 .side {
7   - @extend .span4;
8 7 @extend .right;
9 8  
10 9 .groups_box,
11 10 .projects_box {
12   - h5 {
  11 + > h5 {
13 12 color: $style_color;
14 13 font-size: 16px;
15 14 text-shadow: 0 1px 1px #fff;
... ... @@ -17,37 +16,22 @@
17 16 line-height: 32px;
18 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 35 @extend .ui-box;
52 36 }
53 37 }
... ... @@ -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 @@
  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 @@
  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 4 *
5 5 */
6 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 7 .app_logo {
20 8 .separator {
21 9 margin-left: 0;
... ...
app/assets/stylesheets/variables.scss
... ... @@ -1,5 +0,0 @@
1   -/** Colors **/
2   -$primary_color: #2FA0BB;
3   -$link_color: #3A89A3;
4   -$style_color: #474D57;
5   -$hover: #D9EDF7;
6 0 \ No newline at end of file
app/contexts/project_update_context.rb
... ... @@ -2,7 +2,9 @@ class ProjectUpdateContext &lt; BaseContext
2 2 def execute(role = :default)
3 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 8 if namespace_id == Namespace.global_id
7 9 if project.namespace.present?
8 10 # Transfer to global namespace from anyone
... ...
app/controllers/admin/groups_controller.rb
... ... @@ -2,7 +2,7 @@ class Admin::GroupsController &lt; AdminController
2 2 before_filter :group, only: [:edit, :show, :update, :destroy, :project_update]
3 3  
4 4 def index
5   - @groups = Group.scoped
  5 + @groups = Group.order('name ASC')
6 6 @groups = @groups.search(params[:name]) if params[:name].present?
7 7 @groups = @groups.page(params[:page]).per(20)
8 8 end
... ... @@ -11,6 +11,7 @@ class Admin::GroupsController &lt; AdminController
11 11 @projects = Project.scoped
12 12 @projects = @projects.not_in_group(@group) if @group.projects.present?
13 13 @projects = @projects.all
  14 + @projects.reject!(&:empty_repo?)
14 15 end
15 16  
16 17 def new
... ...
app/controllers/admin/projects_controller.rb
... ... @@ -4,12 +4,13 @@ class Admin::ProjectsController &lt; AdminController
4 4 def index
5 5 @projects = Project.scoped
6 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 8 @projects = @projects.search(params[:name]) if params[:name].present?
8 9 @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20)
9 10 end
10 11  
11 12 def show
12   - @users = User.scoped
  13 + @users = User.active
13 14 @users = @users.not_in_project(@project) if @project.users.present?
14 15 @users = @users.all
15 16 end
... ...
app/controllers/admin/users_controller.rb
... ... @@ -3,7 +3,7 @@ class Admin::UsersController &lt; AdminController
3 3 @admin_users = User.scoped
4 4 @admin_users = @admin_users.filter(params[:filter])
5 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 7 end
8 8  
9 9 def show
... ... @@ -30,7 +30,7 @@ class Admin::UsersController &lt; AdminController
30 30  
31 31  
32 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 34 end
35 35  
36 36 def edit
... ...
app/controllers/application_controller.rb
... ... @@ -112,6 +112,10 @@ class ApplicationController &lt; ActionController::Base
112 112 render file: Rails.root.join("public", "404"), layout: false, status: "404"
113 113 end
114 114  
  115 + def render_403
  116 + render file: Rails.root.join("public", "403"), layout: false, status: "403"
  117 + end
  118 +
115 119 def require_non_empty_project
116 120 redirect_to @project if @project.empty_repo?
117 121 end
... ...
app/controllers/dashboard_controller.rb
... ... @@ -7,6 +7,8 @@ class DashboardController &lt; ApplicationController
7 7 def index
8 8 @groups = current_user.authorized_groups
9 9  
  10 + @has_authorized_projects = @projects.count > 0
  11 +
10 12 @projects = case params[:scope]
11 13 when 'personal' then
12 14 @projects.personal(current_user)
... ...
app/controllers/groups_controller.rb
... ... @@ -21,7 +21,7 @@ class GroupsController &lt; ApplicationController
21 21  
22 22 # Get authored or assigned open merge requests
23 23 def merge_requests
24   - @merge_requests = current_user.cared_merge_requests
  24 + @merge_requests = current_user.cared_merge_requests.opened
25 25 @merge_requests = @merge_requests.of_group(@group).recent.page(params[:page]).per(20)
26 26 end
27 27  
... ... @@ -49,6 +49,7 @@ class GroupsController &lt; ApplicationController
49 49 def people
50 50 @project = group.projects.find(params[:project_id]) if params[:project_id]
51 51 @users = @project ? @project.users : group.users
  52 + @users.sort_by!(&:name)
52 53  
53 54 if @project
54 55 @team_member = @project.users_projects.new
... ...
app/controllers/issues_controller.rb
1 1 class IssuesController < ProjectResourceController
2 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 5 # Allow read any issue
6 6 before_filter :authorize_read_issue!
... ... @@ -11,9 +11,6 @@ class IssuesController &lt; ProjectResourceController
11 11 # Allow modify issue
12 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 14 respond_to :js, :html
18 15  
19 16 def index
... ... @@ -79,15 +76,6 @@ class IssuesController &lt; ProjectResourceController
79 76 end
80 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 79 def sort
92 80 return render_404 unless can?(current_user, :admin_issue, @project)
93 81  
... ...
app/controllers/merge_requests_controller.rb
1 1 class MergeRequestsController < ProjectResourceController
2 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 4 before_filter :validates_merge_request, only: [:show, :diffs]
5 5 before_filter :define_show_vars, only: [:show, :diffs]
6 6  
... ... @@ -13,9 +13,6 @@ class MergeRequestsController &lt; ProjectResourceController
13 13 # Allow modify merge_request
14 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 16 def index
20 17 @merge_requests = MergeRequestsLoadContext.new(project, current_user, params).execute
21 18 end
... ... @@ -90,14 +87,6 @@ class MergeRequestsController &lt; ProjectResourceController
90 87 end
91 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 90 def branch_from
102 91 @commit = project.commit(params[:ref])
103 92 @commit = CommitDecorator.decorate(@commit)
... ... @@ -108,6 +97,13 @@ class MergeRequestsController &lt; ProjectResourceController
108 97 @commit = CommitDecorator.decorate(@commit)
109 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 107 protected
112 108  
113 109 def merge_request
... ...
app/controllers/milestones_controller.rb
... ... @@ -12,11 +12,12 @@ class MilestonesController &lt; ProjectResourceController
12 12  
13 13 def index
14 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 18 end
18 19  
19   - @milestones = @milestones.includes(:project).order("due_date")
  20 + @milestones = @milestones.includes(:project)
20 21 @milestones = @milestones.page(params[:page]).per(20)
21 22 end
22 23  
... ... @@ -42,6 +43,7 @@ class MilestonesController &lt; ProjectResourceController
42 43  
43 44 def create
44 45 @milestone = @project.milestones.new(params[:milestone])
  46 + @milestone.author_id_of_changes = current_user.id
45 47  
46 48 if @milestone.save
47 49 redirect_to project_milestone_path(@project, @milestone)
... ... @@ -51,7 +53,7 @@ class MilestonesController &lt; ProjectResourceController
51 53 end
52 54  
53 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 58 respond_to do |format|
57 59 format.js
... ...
app/controllers/omniauth_callbacks_controller.rb
1 1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController
2   - Gitlab.config.omniauth_providers.each do |provider|
  2 + Gitlab.config.omniauth.providers.each do |provider|
3 3 define_method provider['name'] do
4 4 handle_omniauth
5 5 end
... ...
app/controllers/projects_controller.rb
... ... @@ -46,6 +46,10 @@ class ProjectsController &lt; ProjectResourceController
46 46 format.js
47 47 end
48 48 end
  49 +
  50 + rescue Project::TransferError => ex
  51 + @error = ex
  52 + render :update_failed
49 53 end
50 54  
51 55 def show
... ... @@ -54,12 +58,12 @@ class ProjectsController &lt; ProjectResourceController
54 58  
55 59 respond_to do |format|
56 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 67 end
64 68 format.js
65 69 end
... ... @@ -86,12 +90,18 @@ class ProjectsController &lt; ProjectResourceController
86 90 end
87 91  
88 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 100 end
93 101  
94 102 def destroy
  103 + return access_denied! unless can?(current_user, :remove_project, project)
  104 +
95 105 # Disable the UsersProject update_repository call, otherwise it will be
96 106 # called once for every person removed from the project
97 107 UsersProject.skip_callback(:destroy, :after, :update_repository)
... ...
app/controllers/snippets_controller.rb
... ... @@ -16,7 +16,7 @@ class SnippetsController &lt; ProjectResourceController
16 16 respond_to :html
17 17  
18 18 def index
19   - @snippets = @project.snippets
  19 + @snippets = @project.snippets.fresh
20 20 end
21 21  
22 22 def new
... ... @@ -62,7 +62,7 @@ class SnippetsController &lt; ProjectResourceController
62 62 redirect_to project_snippets_path(@project)
63 63 end
64 64  
65   - def raw
  65 + def raw
66 66 send_data(
67 67 @snippet.content,
68 68 type: "text/plain",
... ...
app/decorators/commit_decorator.rb
... ... @@ -76,7 +76,7 @@ class CommitDecorator &lt; ApplicationDecorator
76 76 source_name = send "#{options[:source]}_name".to_sym
77 77 source_email = send "#{options[:source]}_email".to_sym
78 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 80 %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
81 81 else
82 82 source_name
... ...
app/helpers/application_helper.rb
1 1 require 'digest/md5'
  2 +require 'uri'
2 3  
3 4 module ApplicationHelper
4 5  
... ... @@ -30,13 +31,15 @@ module ApplicationHelper
30 31 args.any? { |v| v.to_s.downcase == action_name }
31 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 38 'no_avatar.png'
36 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 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 43 end
41 44 end
42 45  
... ... @@ -45,7 +48,7 @@ module ApplicationHelper
45 48 end
46 49  
47 50 def web_app_url
48   - "#{request_protocol}://#{Gitlab.config.web_host}/"
  51 + "#{request_protocol}://#{Gitlab.config.gitlab.host}/"
49 52 end
50 53  
51 54 def last_commit(project)
... ... @@ -92,6 +95,7 @@ module ApplicationHelper
92 95 { label: "API Help", url: help_api_path },
93 96 { label: "Markdown Help", url: help_markdown_path },
94 97 { label: "SSH Keys Help", url: help_ssh_path },
  98 + { label: "Gitlab Rake Tasks Help", url: help_raketasks_path },
95 99 ]
96 100  
97 101 project_nav = []
... ...
app/helpers/issues_helper.rb
... ... @@ -4,28 +4,6 @@ module IssuesHelper
4 4 project_issues_path project, params
5 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 7 def issue_css_classes issue
30 8 classes = "issue"
31 9 classes << " closed" if issue.closed
... ... @@ -52,4 +30,14 @@ module IssuesHelper
52 30 open: "open"
53 31 }
54 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 43 end
... ...
app/helpers/merge_requests_helper.rb
1 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 2 def new_mr_path_from_push_event(event)
25 3 new_project_merge_request_path(
26 4 event.project,
... ... @@ -39,7 +17,7 @@ module MergeRequestsHelper
39 17 classes
40 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 22 end
45 23 end
... ...
app/helpers/projects_helper.rb
... ... @@ -8,11 +8,49 @@ module ProjectsHelper
8 8 end
9 9  
10 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 43 end
13 44  
14 45 def tm_path team_member
15 46 project_team_member_path(@project, team_member)
16 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 72 return "active" if current_page?(controller: "projects", action: action, id: @project)
73 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 76 "active"
77 77 end
78 78 end
... ...
app/mailers/notify.rb
... ... @@ -3,11 +3,11 @@ class Notify &lt; ActionMailer::Base
3 3 add_template_helper ApplicationHelper
4 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 31 def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
32 32 @issue = Issue.find issue_id
33 33 @issue_status = status
  34 + @project = @issue.project
34 35 @updated_by = User.find updated_by_user_id
35 36 mail(to: recipient(recipient_id),
36 37 subject: subject("changed issue ##{@issue.id}", @issue.title))
... ... @@ -102,6 +103,12 @@ class Notify &lt; ActionMailer::Base
102 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 114 # User
... ...
app/models/ability.rb
... ... @@ -17,9 +17,7 @@ class Ability
17 17  
18 18 # Rules based on role in project
19 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 22 elsif project.dev_access_for?(user)
25 23 rules << project_dev_rules
... ... @@ -93,13 +91,16 @@ class Ability
93 91 :admin_merge_request,
94 92 :admin_note,
95 93 :accept_mr,
96   - :admin_wiki
  94 + :admin_wiki,
  95 + :admin_project
97 96 ]
98 97 end
99 98  
100 99 def project_admin_rules
101 100 project_master_rules + [
102   - :admin_project
  101 + :change_namespace,
  102 + :rename_project,
  103 + :remove_project
103 104 ]
104 105 end
105 106  
... ...
app/models/commit.rb
... ... @@ -87,14 +87,10 @@ class Commit
87 87 last = project.commit(from.try(:strip))
88 88  
89 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 94 end
99 95  
100 96 result
... ... @@ -163,6 +159,8 @@ class Commit
163 159 while !lines.first.start_with?("diff --git") do
164 160 lines.shift
165 161 end
  162 + lines.pop if lines.last =~ /^[\d.]+$/ # Git version
  163 + lines.pop if lines.last == "-- " # end of diff
166 164 lines.join("\n")
167 165 end
168 166 end
... ...
app/models/event.rb
... ... @@ -15,6 +15,7 @@
15 15 #
16 16  
17 17 class Event < ActiveRecord::Base
  18 + include NoteEvent
18 19 include PushEvent
19 20  
20 21 attr_accessible :project, :action, :data, :author_id, :project_id,
... ... @@ -58,12 +59,14 @@ class Event &lt; ActiveRecord::Base
58 59 end
59 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 70 end
68 71  
69 72 def project_name
... ... @@ -94,6 +97,14 @@ class Event &lt; ActiveRecord::Base
94 97 action == self.class::Reopened
95 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 108 def issue?
98 109 target_type == "Issue"
99 110 end
... ...
app/models/gitlab_ci_service.rb
... ... @@ -36,4 +36,22 @@ class GitlabCiService &lt; Service
36 36 def commit_badge_path sha
37 37 project_url + "/status?sha=#{sha}"
38 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 57 end
... ...
app/models/merge_request.rb
... ... @@ -204,7 +204,7 @@ class MergeRequest &lt; ActiveRecord::Base
204 204  
205 205 def mr_and_commit_notes
206 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 208 end
209 209  
210 210 # Returns the raw diff for this merge request
... ... @@ -220,4 +220,8 @@ class MergeRequest &lt; ActiveRecord::Base
220 220 def to_patch
221 221 project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}")
222 222 end
  223 +
  224 + def last_commit_short_sha
  225 + @last_commit_short_sha ||= last_commit.sha[0..10]
  226 + end
223 227 end
... ...
app/models/milestone.rb
... ... @@ -13,18 +13,26 @@
13 13 #
14 14  
15 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 19 belongs_to :project
19 20 has_many :issues
20 21 has_many :merge_requests
21 22  
  23 + scope :active, where(closed: false)
  24 + scope :closed, where(closed: true)
  25 +
22 26 validates :title, presence: true
23 27 validates :project, presence: true
24 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 36 end
29 37  
30 38 def participants
... ... @@ -52,4 +60,20 @@ class Milestone &lt; ActiveRecord::Base
52 60 def expires_at
53 61 "expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
54 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 79 end
... ...
app/models/namespace.rb
... ... @@ -48,23 +48,30 @@ class Namespace &lt; ActiveRecord::Base
48 48 end
49 49  
50 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 52 system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path)
53 53 end
54 54  
55 55 def move_dir
56 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 59 if File.exists?(new_path)
60 60 raise "Already exists"
61 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 66 end
64 67 end
65 68  
66 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 71 system("rm -rf #{dir_path}")
69 72 end
  73 +
  74 + def send_update_instructions
  75 + projects.each(&:send_move_instructions)
  76 + end
70 77 end
... ...
app/models/note.rb
... ... @@ -19,7 +19,7 @@ require &#39;file_size_validator&#39;
19 19  
20 20 class Note < ActiveRecord::Base
21 21 attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
22   - :attachment, :line_code
  22 + :attachment, :line_code, :commit_id
23 23  
24 24 attr_accessor :notify
25 25 attr_accessor :notify_author
... ... @@ -35,10 +35,14 @@ class Note &lt; ActiveRecord::Base
35 35 validates :line_code, format: { with: /\A\d+_\d+_\d+\Z/ }, allow_blank: true
36 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 41 mount_uploader :attachment, AttachmentUploader
39 42  
40 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 46 scope :today, ->{ where("created_at >= :date", date: Date.today) }
43 47 scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
44 48 scope :since, ->(day) { where("created_at >= :date", date: (day)) }
... ... @@ -122,7 +126,7 @@ class Note &lt; ActiveRecord::Base
122 126 # override to return commits, which are not active record
123 127 def noteable
124 128 if for_commit?
125   - project.commit(noteable_id)
  129 + project.commit(commit_id)
126 130 else
127 131 super
128 132 end
... ... @@ -151,4 +155,12 @@ class Note &lt; ActiveRecord::Base
151 155 def votable?
152 156 for_issue? || (for_merge_request? && !for_diff_line?)
153 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 166 end
... ...
app/models/project.rb
... ... @@ -25,6 +25,9 @@ class Project &lt; ActiveRecord::Base
25 25 include PushObserver
26 26 include Authority
27 27 include Team
  28 + include NamespacedProject
  29 +
  30 + class TransferError < StandardError; end
28 31  
29 32 attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
30 33 :wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
... ... @@ -36,6 +39,10 @@ class Project &lt; ActiveRecord::Base
36 39 # Relations
37 40 belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
38 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 46 belongs_to :owner, class_name: "User"
40 47 has_many :users, through: :users_projects
41 48 has_many :events, dependent: :destroy
... ... @@ -97,7 +104,7 @@ class Project &lt; ActiveRecord::Base
97 104 namespace_id = Namespace.find_by_path(id.first).id
98 105 where(namespace_id: namespace_id).find_by_path(id.last)
99 106 else
100   - find_by_path(id)
  107 + where(path: id, namespace_id: nil).last
101 108 end
102 109 end
103 110  
... ... @@ -172,7 +179,7 @@ class Project &lt; ActiveRecord::Base
172 179 end
173 180  
174 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 184 if denied_paths.include?(path)
178 185 errors.add(:path, "like #{path} is not allowed")
... ... @@ -188,7 +195,7 @@ class Project &lt; ActiveRecord::Base
188 195 end
189 196  
190 197 def web_url
191   - [Gitlab.config.url, path].join("/")
  198 + [Gitlab.config.gitlab.url, path_with_namespace].join("/")
192 199 end
193 200  
194 201 def common_notes
... ... @@ -196,15 +203,15 @@ class Project &lt; ActiveRecord::Base
196 203 end
197 204  
198 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 207 end
201 208  
202 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 211 end
205 212  
206 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 215 end
209 216  
210 217 def public?
... ... @@ -239,51 +246,11 @@ class Project &lt; ActiveRecord::Base
239 246 gitlab_ci_service && gitlab_ci_service.active
240 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 249 # For compatibility with old code
251 250 def code
252 251 path
253 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 254 def items_for entity
288 255 case entity
289 256 when 'issue' then
... ... @@ -293,7 +260,9 @@ class Project &lt; ActiveRecord::Base
293 260 end
294 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 267 end
299 268 end
... ...
app/models/snippet.rb
... ... @@ -22,7 +22,7 @@ class Snippet &lt; ActiveRecord::Base
22 22 belongs_to :author, class_name: "User"
23 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 27 validates :author, presence: true
28 28 validates :project, presence: true
... ...
app/models/user.rb
... ... @@ -56,12 +56,12 @@ class User &lt; ActiveRecord::Base
56 56 has_many :issues, foreign_key: :author_id, dependent: :destroy
57 57 has_many :notes, foreign_key: :author_id, dependent: :destroy
58 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 59 has_many :events, class_name: "Event", foreign_key: :author_id, dependent: :destroy
61 60 has_many :recent_events, class_name: "Event", foreign_key: :author_id, order: "id DESC"
62 61 has_many :assigned_issues, class_name: "Issue", foreign_key: :assignee_id, dependent: :destroy
63 62 has_many :assigned_merge_requests, class_name: "MergeRequest", foreign_key: :assignee_id, dependent: :destroy
64 63  
  64 + validates :name, presence: true
65 65 validates :bio, length: { within: 0..255 }
66 66 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
67 67 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
... ... @@ -123,16 +123,4 @@ class User &lt; ActiveRecord::Base
123 123 self.password = self.password_confirmation = Devise.friendly_token.first(8)
124 124 end
125 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 126 end
... ...
app/models/users_project.rb
... ... @@ -28,6 +28,7 @@ class UsersProject &lt; ActiveRecord::Base
28 28  
29 29 validates :user, presence: true
30 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 32 validates :project, presence: true
32 33  
33 34 delegate :name, :email, to: :user, prefix: true
... ...
app/observers/activity_observer.rb
1 1 class ActivityObserver < ActiveRecord::Observer
2   - observe :issue, :merge_request
  2 + observe :issue, :merge_request, :note, :milestone
3 3  
4 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 21 end
13 22  
14 23 def after_save(record)
15   - if record.changed.include?("closed")
  24 + if record.changed.include?("closed") && record.author_id_of_changes
16 25 Event.create(
17 26 project: record.project,
18 27 target_id: record.id,
... ...
app/observers/issue_observer.rb
... ... @@ -16,7 +16,7 @@ class IssueObserver &lt; ActiveRecord::Observer
16 16 if status
17 17 Note.create_status_change_note(issue, current_user, status)
18 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 20 end
21 21 end
22 22 end
... ...
app/observers/note_observer.rb
... ... @@ -21,7 +21,7 @@ class NoteObserver &lt; ActiveRecord::Observer
21 21 # Notifies the whole team except the author of note
22 22 def notify_team(note)
23 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 25 notify_method = "note_#{noteable_type.underscore}_email".to_sym
26 26  
27 27 if Notify.respond_to? notify_method
... ...
app/observers/project_observer.rb
... ... @@ -3,7 +3,8 @@ class ProjectObserver &lt; ActiveRecord::Observer
3 3 project.update_repository
4 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 8 end
8 9  
9 10 def after_destroy(project)
... ...
app/roles/account.rb
... ... @@ -47,7 +47,7 @@ module Account
47 47 end
48 48  
49 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 51 end
52 52  
53 53 def project_ids
... ... @@ -105,4 +105,20 @@ module Account
105 105 def namespace_id
106 106 namespace.try :id
107 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 124 end
... ...
app/roles/namespaced_project.rb 0 → 100644
... ... @@ -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 @@
  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 114 id: commit.id,
115 115 message: commit.safe_message,
116 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 118 author: {
119 119 name: commit.author_name,
120 120 email: commit.author_email
... ...
app/roles/repository.rb
... ... @@ -45,8 +45,22 @@ module Repository
45 45 end
46 46  
47 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 64 end
51 65  
52 66 # Returns an Array of branch names
... ... @@ -83,7 +97,7 @@ module Repository
83 97 end
84 98  
85 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 101 end
88 102  
89 103 def namespace_dir
... ... @@ -185,7 +199,7 @@ module Repository
185 199 end
186 200  
187 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 203 end
190 204  
191 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   -= 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 12  
13 13 %table
14 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 24 - @groups.each do |group|
22 25 %tr
23   - %td= link_to group.name, [:admin, group]
  26 + %td
  27 + %strong= link_to group.name, [:admin, group]
24 28 %td= group.path
25 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 35 = paginate @groups, theme: "admin"
... ...
app/views/admin/groups/show.html.haml
1 1 %h3.page_title
2 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 4 %br
8 5 %table.zebra-striped
... ... @@ -16,36 +13,64 @@
16 13 Name:
17 14 %td
18 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 20 %tr
20 21 %td
21 22 %b
22 23 Path:
23 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 26 %tr
26 27 %td
27 28 %b
28 29 Owner:
29 30 %td
30 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 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 67 = form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
47 68 %fieldset
48 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 74 .clearfix
50 75 = label_tag :project_ids do
51 76 Projects
... ... @@ -53,3 +78,17 @@
53 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 79 .form-actions
55 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 3 = link_to "githost.log", "#githost", 'data-toggle' => 'tab'
4 4 %li
5 5 = link_to "application.log", "#application", 'data-toggle' => 'tab'
  6 + %li
  7 + = link_to "production.log", "#production", 'data-toggle' => 'tab'
6 8  
7 9 %p.light To prevent perfomance issues admin logs output the last 2000 lines
8 10 .tab-content
... ... @@ -34,3 +36,17 @@
34 36 - Gitlab::AppLogger.read_latest.each do |line|
35 37 %li
36 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 19 .input
20 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 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 1 %h3.page_title
2   - Projects
  2 + Projects (#{@projects.count})
3 3 = link_to 'New Project', new_project_path, class: "btn small right"
4 4 %br
5 5 = form_tag admin_projects_path, method: :get, class: 'form-inline' do
... ... @@ -9,12 +9,15 @@
9 9  
10 10 %table
11 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 22 - @projects.each do |project|
20 23 %tr
... ...
app/views/admin/projects/show.html.haml
... ... @@ -4,14 +4,24 @@
4 4 %i.icon-edit
5 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 26 %br
17 27 %table.zebra-striped
... ... @@ -37,9 +47,12 @@
37 47 %tr
38 48 %td
39 49 %b
40   - Path:
  50 + Owned by:
41 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 56 %tr
44 57 %td
45 58 %b
... ... @@ -49,11 +62,48 @@
49 62 %tr
50 63 %td
51 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 101 Post Receive File:
53 102 %td
54 103 = check_box_tag :post_receive_file, 1, @project.has_post_receive_file?, disabled: true
  104 +
55 105 %br
56   -%h3
  106 +%h5
57 107 Team
58 108 %small
59 109 (#{@project.users_projects.count})
... ... @@ -75,7 +125,7 @@
75 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 127 %br
78   -%h3 Add new team member
  128 +%h5 Add new team member
79 129 %br
80 130 = form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do
81 131 %table.zebra-striped
... ...
app/views/admin/users/index.html.haml
1 1 %h3.page_title
2   - Users
  2 + Users (#{@admin_users.count})
3 3 = link_to 'New User', new_admin_user_path, class: "btn small right"
4 4 %br
5 5  
... ... @@ -21,13 +21,16 @@
21 21  
22 22 %table
23 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 35 - @admin_users.each do |user|
33 36 %tr
... ... @@ -38,10 +41,13 @@
38 41 %td= user.users_projects.count
39 42 %td= link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn small"
40 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 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 53 = paginate @admin_users, theme: "admin"
... ...
app/views/admin/users/show.html.haml
... ... @@ -40,6 +40,12 @@
40 40 %tr
41 41 %td
42 42 %b
  43 + Created at:
  44 + %td
  45 + = @admin_user.created_at.stamp("March 1, 1999")
  46 + %tr
  47 + %td
  48 + %b
43 49 Projects limit:
44 50 %td
45 51 = @admin_user.projects_limit
... ... @@ -66,7 +72,7 @@
66 72 = @admin_user.twitter
67 73  
68 74 %br
69   -%h3 Add User to Projects
  75 +%h5 Add User to Projects
70 76 %br
71 77 = form_tag team_update_admin_user_path(@admin_user), class: "bulk_import", method: :put do
72 78 %table
... ... @@ -76,7 +82,7 @@
76 82 %th Project Access:
77 83  
78 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 86 %td= select_tag :project_access, options_for_select(Project.access_options), class: "project-access-select chosen span3"
81 87  
82 88 %tr
... ... @@ -86,8 +92,22 @@
86 92 %strong= link_to "here", help_permissions_path, class: "vlink"
87 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 109 - if @admin_user.projects.present?
90   - %h3 Projects
  110 + %h5 Projects:
91 111 %br
92 112  
93 113 %table.zebra-striped
... ... @@ -101,7 +121,7 @@
101 121 - @admin_user.users_projects.each do |tm|
102 122 - project = tm.project
103 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 125 %td= tm.project_access_human
106 126 %td= link_to 'Edit Access', edit_admin_team_member_path(tm), class: "btn small"
107 127 %td= link_to 'Remove from team', admin_team_member_path(tm), confirm: 'Are you sure?', method: :delete, class: "btn small danger"
... ...