Commit 4587ab6ff64a255577e02334fa0492b5edcb80ea

Authored by Ariejan de Vroom
2 parents 2677bc3a 98d64925

Merge remote-tracking branch 'upstream/master'

Showing 254 changed files with 4947 additions and 1607 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 254 files displayed.

.foreman 0 → 100644
... ... @@ -0,0 +1 @@
  1 +port: 3000
... ...
.gitignore
1 1 .bundle
2 2 .rbx/
3 3 db/*.sqlite3
  4 +db/*.sqlite3-journal
4 5 log/*.log
5 6 tmp/
6 7 .sass-cache/
7 8 coverage/*
8 9 *.swp
9 10 public/uploads/
  11 +.rvmrc
  12 +.directory
  13 +nohup.out
... ...
... ... @@ -1 +0,0 @@
1   -rvm use 1.9.2-p290
.travis.yml
  1 +before_install: sudo apt-get install libicu-dev -y
1 2 branches:
2 3 only:
3 4 - 'master'
... ...
CHANGELOG
  1 +v 2.1.0
  2 + - Project tab r1
  3 + - Repository tab r1
  4 +
1 5 v 2.0.0
2 6 - gitolite as main git host system
3 7 - merge requests
  8 + - project/repo access
  9 + - link to commit/issue feed
  10 + - design tab
  11 + - improved email notifications
  12 + - restyled dashboard
4 13 - bugfix
5 14  
6 15 v 1.2.2
... ...
Gemfile
... ... @@ -3,9 +3,11 @@ source "http://rubygems.org"
3 3 gem "rails", "3.1.1"
4 4  
5 5 gem "sqlite3"
  6 +gem "rake", "0.9.2.2"
6 7 gem "devise", "1.5.0"
7 8 gem "stamp"
8 9 gem "kaminari"
  10 +gem "haml", "3.1.4"
9 11 gem "haml-rails"
10 12 gem "jquery-rails"
11 13 gem "grit", :git => "https://github.com/gitlabhq/grit.git"
... ... @@ -15,14 +17,17 @@ gem "six"
15 17 gem "therubyracer"
16 18 gem "faker"
17 19 gem "seed-fu", "~> 2.1.0"
18   -gem "pygments.rb", "0.2.3"
  20 +gem "pygments.rb", "0.2.4"
19 21 gem "thin"
20 22 gem "git"
21 23 gem "acts_as_list"
22 24 gem "rdiscount"
23 25 gem "acts-as-taggable-on", "~> 2.1.0"
24 26 gem "drapper"
25   -gem "rchardet19", "~> 1.3.5"
  27 +gem "resque"
  28 +gem "httparty"
  29 +gem "charlock_holmes"
  30 +gem "foreman"
26 31  
27 32 group :assets do
28 33 gem "sass-rails", "~> 3.1.0"
... ... @@ -47,6 +52,7 @@ group :development, :test do
47 52 gem "awesome_print"
48 53 gem "database_cleaner"
49 54 gem "launchy"
  55 + gem "webmock"
50 56 end
51 57  
52 58 group :test do
... ...
Gemfile.lock
... ... @@ -77,6 +77,7 @@ GEM
77 77 xpath (~> 0.1.4)
78 78 carrierwave (0.5.8)
79 79 activesupport (~> 3.0)
  80 + charlock_holmes (0.6.8)
80 81 childprocess (0.2.2)
81 82 ffi (~> 1.0.6)
82 83 coffee-rails (3.1.1)
... ... @@ -87,6 +88,7 @@ GEM
87 88 execjs
88 89 coffee-script-source (1.1.3)
89 90 columnize (0.3.4)
  91 + crack (0.3.1)
90 92 daemons (1.1.4)
91 93 database_cleaner (0.7.0)
92 94 devise (1.5.0)
... ... @@ -102,8 +104,11 @@ GEM
102 104 faker (1.0.1)
103 105 i18n (~> 0.4)
104 106 ffi (1.0.11)
  107 + foreman (0.27.0)
  108 + term-ansicolor (~> 1.0.5)
  109 + thor (>= 0.13.6)
105 110 git (1.2.5)
106   - haml (3.1.3)
  111 + haml (3.1.4)
107 112 haml-rails (0.3.4)
108 113 actionpack (~> 3.0)
109 114 activesupport (~> 3.0)
... ... @@ -111,6 +116,9 @@ GEM
111 116 railties (~> 3.0)
112 117 hashery (1.4.0)
113 118 hike (1.2.1)
  119 + httparty (0.8.1)
  120 + multi_json
  121 + multi_xml
114 122 i18n (0.6.0)
115 123 jquery-rails (1.0.17)
116 124 railties (~> 3.0)
... ... @@ -132,17 +140,20 @@ GEM
132 140 treetop (~> 1.4.8)
133 141 mime-types (1.17.2)
134 142 multi_json (1.0.3)
  143 + multi_xml (0.4.1)
135 144 nokogiri (1.5.0)
136 145 orm_adapter (0.0.5)
137 146 polyglot (0.3.3)
138 147 posix-spawn (0.3.6)
139   - pygments.rb (0.2.3)
140   - rubypython (>= 0.5.1)
  148 + pygments.rb (0.2.4)
  149 + rubypython (~> 0.5.3)
141 150 rack (1.3.5)
142 151 rack-cache (1.1)
143 152 rack (>= 0.4)
144 153 rack-mount (0.8.3)
145 154 rack (>= 1.0.0)
  155 + rack-protection (1.1.4)
  156 + rack
146 157 rack-ssl (1.3.2)
147 158 rack
148 159 rack-test (0.6.1)
... ... @@ -165,10 +176,17 @@ GEM
165 176 rdoc (~> 3.4)
166 177 thor (~> 0.14.6)
167 178 rake (0.9.2.2)
168   - rchardet19 (1.3.5)
169 179 rdiscount (1.6.8)
170 180 rdoc (3.11)
171 181 json (~> 1.4)
  182 + redis (2.2.2)
  183 + redis-namespace (1.0.3)
  184 + redis (< 3.0.0)
  185 + resque (1.19.0)
  186 + multi_json (~> 1.0)
  187 + redis-namespace (~> 1.0.2)
  188 + sinatra (>= 0.9.2)
  189 + vegas (~> 0.1.2)
172 190 rspec (2.7.0)
173 191 rspec-core (~> 2.7.0)
174 192 rspec-expectations (~> 2.7.0)
... ... @@ -220,6 +238,10 @@ GEM
220 238 multi_json (~> 1.0.3)
221 239 simplecov-html (~> 0.5.3)
222 240 simplecov-html (0.5.3)
  241 + sinatra (1.3.1)
  242 + rack (~> 1.3, >= 1.3.4)
  243 + rack-protection (~> 1.1, >= 1.1.2)
  244 + tilt (~> 1.3, >= 1.3.3)
223 245 six (0.2.0)
224 246 sprockets (2.0.3)
225 247 hike (~> 1.2)
... ... @@ -227,6 +249,7 @@ GEM
227 249 tilt (~> 1.1, != 1.3.0)
228 250 sqlite3 (1.3.4)
229 251 stamp (0.1.6)
  252 + term-ansicolor (1.0.7)
230 253 therubyracer (0.9.9)
231 254 libv8 (~> 3.3.10)
232 255 thin (1.3.1)
... ... @@ -244,8 +267,13 @@ GEM
244 267 uglifier (1.1.0)
245 268 execjs (>= 0.3.0)
246 269 multi_json (>= 1.0.2)
  270 + vegas (0.1.8)
  271 + rack (>= 1.0.0)
247 272 warden (1.1.0)
248 273 rack (>= 1.0)
  274 + webmock (1.7.8)
  275 + addressable (~> 2.2, > 2.2.5)
  276 + crack (>= 0.1.7)
249 277 xpath (0.1.4)
250 278 nokogiri (~> 1.3)
251 279  
... ... @@ -261,24 +289,29 @@ DEPENDENCIES
261 289 awesome_print
262 290 capybara
263 291 carrierwave
  292 + charlock_holmes
264 293 coffee-rails (~> 3.1.0)
265 294 database_cleaner
266 295 devise (= 1.5.0)
267 296 drapper
268 297 faker
  298 + foreman
269 299 git
270 300 gitolite!
271 301 grit!
  302 + haml (= 3.1.4)
272 303 haml-rails
  304 + httparty
273 305 jquery-rails
274 306 kaminari
275 307 launchy
276 308 letter_opener
277   - pygments.rb (= 0.2.3)
  309 + pygments.rb (= 0.2.4)
278 310 rails (= 3.1.1)
279 311 rails-footnotes (~> 3.7.5)
280   - rchardet19 (~> 1.3.5)
  312 + rake (= 0.9.2.2)
281 313 rdiscount
  314 + resque
282 315 rspec-rails
283 316 ruby-debug19
284 317 sass-rails (~> 3.1.0)
... ... @@ -292,3 +325,4 @@ DEPENDENCIES
292 325 thin
293 326 turn
294 327 uglifier
  328 + webmock
... ...
Procfile 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +web: bundle exec rails s -p $PORT
  2 +worker: bundle exec rake environment resque:work QUEUE=* VVERBOSE=1
... ...
Procfile.production 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +web: bundle exec rails s -p $PORT -e production
  2 +worker: bundle exec rake environment resque:work RAILS_ENV=production QUEUE=* VVERBOSE=1
... ...
README.md
1 1 # Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://secure.travis-ci.org/gitlabhq/gitlabhq)
2 2  
3   -GitLab is a free Project/Repository management application
4   -
5   -
6   -<img src="http://gitlabhq.com/front.png" width="900" height="471">
  3 +GitLab is a free project and repository management application
7 4  
8 5  
9 6 ## Application details
10 7  
11   -rails 3.1
12   -works only with gitolite
13   -sqlite as default a database
  8 +* rails 3.1
  9 +* works only with gitolite
  10 +* sqlite as default a database
14 11  
15 12 ## Requirements
16 13  
... ... @@ -18,7 +15,7 @@ sqlite as default a database
18 15 * sqlite
19 16 * git
20 17 * gitolite
21   -* pygments lib - `sudo easy_install pygments`
  18 +* redis
22 19  
23 20 ## Install
24 21  
... ... @@ -28,13 +25,11 @@ Checkout wiki pages for installation information, migration, etc.
28 25  
29 26 [Google Group](https://groups.google.com/group/gitlabhq)
30 27  
31   -IRC freenode: #gitlabhq
32   -
33 28 ## Contacts
34 29  
35 30 Twitter:
36 31  
37   - * @gitalbhq
  32 + * @gitlabhq
38 33 * @dzaporozhets
39 34  
40 35 Email
... ... @@ -43,7 +38,5 @@ Email
43 38  
44 39 ## Contribute
45 40  
46   -We are on our way to full open source.
47   -Want to help - create an issue on github and notify us that you are ready to start it.
48   -If approved - fork, code, cover with tests & make pull request.
  41 +Want to help - send a pull request.
49 42 We'll accept good pull requests.
... ...
VERSION
1   -2.0.0
  1 +2.1.0
... ...
app/assets/images/.directory
... ... @@ -1,4 +0,0 @@
1   -[Dolphin]
2   -ShowPreview=true
3   -Timestamp=2011,10,28,13,16,25
4   -Version=2
app/assets/images/Arrow-Left-UI.PNG 0 → 100644

568 Bytes

app/assets/images/Arrow-Right-UI.PNG 0 → 100644

561 Bytes

app/assets/images/Gear-UI.PNG 0 → 100644

940 Bytes

app/assets/images/Home-UI.PNG 0 → 100644

782 Bytes

app/assets/images/Info-UI.PNG 0 → 100644

800 Bytes

app/assets/images/Rss-UI.PNG 0 → 100644

789 Bytes

app/assets/images/Storage-UI.PNG 0 → 100644

737 Bytes

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

823 Bytes

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

333 Bytes

app/assets/images/ajax-loader-facebook.gif 0 → 100644

723 Bytes

app/assets/images/ajax-loader-tree.gif 0 → 100644

2.55 KB

app/assets/images/blueprint_notice.png

4.42 KB

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

16.5 KB

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

3.55 KB

app/assets/images/favicon.png

338 Bytes

app/assets/images/git.png

21.1 KB

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

95.7 KB

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

54.5 KB

app/assets/images/home.png

271 Bytes

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

2.93 KB

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

16.8 KB

app/assets/javascripts/application.js
... ... @@ -16,7 +16,7 @@
16 16 //= require branch-graph
17 17 //= require_tree .
18 18  
19   -$(function(){
  19 +$(document).ready(function(){
20 20 $(".one_click_select").live("click", function(){
21 21 $(this).select();
22 22 });
... ... @@ -27,8 +27,50 @@ $(function(){
27 27 $(".account-box").mouseenter(showMenu);
28 28 $(".account-box").mouseleave(resetMenu);
29 29  
  30 + $("#projects-list .project").live('click', function(e){
  31 + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
  32 + location.href = $(this).attr("url");
  33 + e.stopPropagation();
  34 + return false;
  35 + }
  36 + });
  37 +
  38 + $("#issues-table .issue").live('click', function(e){
  39 + if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
  40 + location.href = $(this).attr("url");
  41 + e.stopPropagation();
  42 + return false;
  43 + }
  44 + });
  45 +
  46 + $(document).keypress(function(e) {
  47 + if( $(e.target).is(":input") ) return;
  48 + switch(e.which) {
  49 + case 115: focusSearch();
  50 + e.preventDefault();
  51 + }
  52 + });
  53 +
30 54 });
31 55  
  56 +function focusSearch() {
  57 + $("#search").focus();
  58 +}
  59 +
  60 +function taggifyForm(){
  61 + var tag_field = $('#tag_field').tagify();
  62 +
  63 + tag_field.tagify('inputField').autocomplete({
  64 + source: '/tags.json'
  65 + });
  66 +
  67 + $('form').submit( function() {
  68 + var tag_field = $('#tag_field')
  69 + tag_field.val( tag_field.tagify('serialize') );
  70 + return true;
  71 + });
  72 +}
  73 +
32 74 function updatePage(data){
33 75 $.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
34 76 }
... ... @@ -40,3 +82,5 @@ function showMenu() {
40 82 function resetMenu() {
41 83 $(this).removeClass("hover");
42 84 }
  85 +
  86 +
... ...
app/assets/javascripts/commits.js
1   -$(document).ready(function(){
2   - $(".day-commits-table li.commit").live('click', function(e){
3   - if(e.target.nodeName != "A") {
4   - location.href = $(this).attr("url");
5   - e.stopPropagation();
6   - return false;
7   - }
8   - });
9   -});
10   -
11 1 var CommitsList = {
  2 + ref:null,
  3 + limit:0,
  4 + offset:0,
12 5  
13   -ref:null,
14   -limit:0,
15   -offset:0,
16   -
17   -init:
18   - function(ref, limit) {
19   - this.ref=ref;
20   - this.limit=limit;
21   - this.offset=limit;
22   - this.initLoadMore();
23   - $('.loading').show();
24   - },
25   -
26   -getOld:
27   - function() {
28   - $('.loading').show();
29   - $.ajax({
30   - type: "GET",
31   - url: location.href,
32   - data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,
33   - complete: function(){ $('.loading').hide()},
34   - dataType: "script"});
35   - },
  6 + init:
  7 + function(ref, limit) {
  8 + $(".day-commits-table li.commit").live('click', function(e){
  9 + if(e.target.nodeName != "A") {
  10 + location.href = $(this).attr("url");
  11 + e.stopPropagation();
  12 + return false;
  13 + }
  14 + });
36 15  
37   -append:
38   - function(count, html) {
39   - $("#commits_list").append(html);
40   - if(count > 0) {
41   - this.offset += count;
  16 + this.ref=ref;
  17 + this.limit=limit;
  18 + this.offset=limit;
42 19 this.initLoadMore();
43   - }
44   - },
  20 + $('.loading').show();
  21 + },
  22 +
  23 + getOld:
  24 + function() {
  25 + $('.loading').show();
  26 + $.ajax({
  27 + type: "GET",
  28 + url: location.href,
  29 + data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,
  30 + complete: function(){ $('.loading').hide()},
  31 + dataType: "script"});
  32 + },
45 33  
46   -initLoadMore:
47   - function() {
48   - $(window).bind('scroll', function(){
49   - if($(window).scrollTop() == $(document).height() - $(window).height()){
50   - $(window).unbind('scroll');
51   - CommitsList.getOld();
  34 + append:
  35 + function(count, html) {
  36 + $("#commits_list").append(html);
  37 + if(count > 0) {
  38 + this.offset += count;
  39 + this.initLoadMore();
52 40 }
53   - });
54   - }
  41 + },
  42 +
  43 + initLoadMore:
  44 + function() {
  45 + $(window).bind('scroll', function(){
  46 + if($(window).scrollTop() == $(document).height() - $(window).height()){
  47 + $(window).unbind('scroll');
  48 + CommitsList.getOld();
  49 + }
  50 + });
  51 + }
55 52 }
... ...
app/assets/javascripts/loader.js 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +var Loader = {
  2 + img_src: "/assets/ajax-loader.gif",
  3 +
  4 + html:
  5 + function(width) {
  6 + img = $("<img>");
  7 + img.attr("width", width);
  8 + img.attr("src", this.img_src);
  9 + return img;
  10 + }
  11 +}
... ...
app/assets/javascripts/merge_requests.js 0 → 100644
... ... @@ -0,0 +1,59 @@
  1 +var MergeRequest = {
  2 + diffs_loaded: false,
  3 + commits_loaded: false,
  4 +
  5 + init:
  6 + function() {
  7 + $(".merge-tabs a").live("click", function() {
  8 + $(".merge-tabs a").removeClass("active");
  9 + $(this).addClass("active");
  10 + });
  11 +
  12 + $(".merge-tabs a.merge-notes-tab").live("click", function() {
  13 + $(".merge-request-commits, .merge-request-diffs").hide();
  14 + $(".merge-request-notes").show();
  15 + });
  16 +
  17 + $(".merge-tabs a.merge-commits-tab").live("click", function() {
  18 + if(!MergeRequest.commits_loaded) {
  19 + MergeRequest.loadCommits();
  20 + }
  21 + $(".merge-request-notes, .merge-request-diffs").hide();
  22 + $(".merge-request-commits").show();
  23 + });
  24 +
  25 + $(".merge-tabs a.merge-diffs-tab").live("click", function() {
  26 + if(!MergeRequest.diffs_loaded) {
  27 + MergeRequest.loadDiff();
  28 + }
  29 + $(".merge-request-notes, .merge-request-commits").hide();
  30 + $(".merge-request-diffs").show();
  31 + });
  32 + },
  33 +
  34 + loadCommits:
  35 + function() {
  36 + $(".dashboard-loader").show();
  37 + $.ajax({
  38 + type: "GET",
  39 + url: $(".merge-commits-tab").attr("data-url"),
  40 + complete: function(){
  41 + MergeRequest.commits_loaded = true;
  42 + $(".merge-request-notes, .merge-request-diffs").hide();
  43 + $(".dashboard-loader").hide()},
  44 + dataType: "script"});
  45 + },
  46 +
  47 + loadDiff:
  48 + function() {
  49 + $(".dashboard-loader").show();
  50 + $.ajax({
  51 + type: "GET",
  52 + url: $(".merge-diffs-tab").attr("data-url"),
  53 + complete: function(){
  54 + MergeRequest.diffs_loaded = true;
  55 + $(".merge-request-notes, .merge-request-commits").hide();
  56 + $(".dashboard-loader").hide()},
  57 + dataType: "script"});
  58 + }
  59 +}
... ...
app/assets/javascripts/merge_requests.js.coffee
... ... @@ -1,3 +0,0 @@
1   -# Place all the behaviors and hooks related to the matching controller here.
2   -# All this logic will automatically be available in application.js.
3   -# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
app/assets/javascripts/projects.js
1   -$(document).ready(function(){
2   - $('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live("click", function() {
3   - history.pushState({ path: this.path }, '', this.href)
4   - })
5   -
6   - $("#tree-slider tr.tree-item").live('click', function(e){
7   - if(e.target.nodeName != "A") {
8   - e.stopPropagation();
9   - link = $(this).find("td.tree-item-file-name a")
10   - link.click();
11   - return false;
12   - }
13   - });
14   -
15   - $("#projects-list .project").live('click', function(e){
16   - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
17   - location.href = $(this).attr("url");
18   - e.stopPropagation();
19   - return false;
20   - }
21   - });
22   -
23   - $("#issues-table .issue").live('click', function(e){
24   - if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
25   - location.href = $(this).attr("url");
26   - e.stopPropagation();
27   - return false;
28   - }
29   - });
30   -
31   - $(document).keypress(function(e) {
32   - if( $(e.target).is(":input") ) return;
33   - switch(e.which) {
34   - case 115: focusSearch();
35   - e.preventDefault();
  1 +var ProjectsList = {
  2 + limit:0,
  3 + offset:0,
  4 +
  5 + init:
  6 + function(limit) {
  7 + this.limit=limit;
  8 + this.offset=limit;
  9 + this.initLoadMore();
  10 + },
  11 +
  12 + getOld:
  13 + function() {
  14 + $('.loading').show();
  15 + $.ajax({
  16 + type: "GET",
  17 + url: location.href,
  18 + data: "limit=" + this.limit + "&offset=" + this.offset,
  19 + complete: function(){ $('.loading').hide()},
  20 + dataType: "script"});
  21 + },
  22 +
  23 + append:
  24 + function(count, html) {
  25 + $(".tile").append(html);
  26 + if(count > 0) {
  27 + this.offset += count;
  28 + this.initLoadMore();
  29 + }
  30 + },
  31 +
  32 + initLoadMore:
  33 + function() {
  34 + $(window).bind('scroll', function(){
  35 + if($(window).scrollTop() == $(document).height() - $(window).height()){
  36 + $(window).unbind('scroll');
  37 + $('.loading').show();
  38 + ProjectsList.getOld();
  39 + }
  40 + });
36 41 }
37   - });
38   -
39   -});
40   -
41   -function focusSearch() {
42   - $("#search").focus();
43 42 }
44   -
45   -function taggifyForm(){
46   - var tag_field = $('#tag_field').tagify();
47   -
48   - tag_field.tagify('inputField').autocomplete({
49   - source: '/tags.json'
50   - });
51   -
52   - $('form').submit( function() {
53   - var tag_field = $('#tag_field')
54   - tag_field.val( tag_field.tagify('serialize') );
55   - return true;
56   - });
57   -}
58   -
... ...
app/assets/javascripts/team.js 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +function backToMembers(){
  2 + $("#team_member_new").hide("slide", { direction: "right" }, 150, function(){
  3 + $("#team-table").show("slide", { direction: "left" }, 150, function() {
  4 + $("#team_member_new").remove();
  5 + $(".add_new").show();
  6 + });
  7 + });
  8 +}
... ...
app/assets/javascripts/tree.js 0 → 100644
... ... @@ -0,0 +1,27 @@
  1 +/**
  2 + * Tree slider for code browse
  3 + *
  4 + */
  5 +var Tree = {
  6 + init:
  7 + function() {
  8 + (new Image).src = "ajax-loader-facebook.gif";
  9 +
  10 + $('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live("click", function() {
  11 + history.pushState({ path: this.path }, '', this.href)
  12 + $("#tree-content-holder").hide("slide", { direction: "left" }, 150)
  13 + })
  14 +
  15 + $("#tree-slider tr.tree-item").live('click', function(e){
  16 + if(e.target.nodeName != "A") {
  17 + link = $(this).find("td.tree-item-file-name a");
  18 + link.trigger("click");
  19 + }
  20 + });
  21 +
  22 + $('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live({
  23 + "ajax:beforeSend": function() { $('.tree_progress').addClass("loading"); },
  24 + "ajax:complete": function() { $('.tree_progress').removeClass("loading"); }
  25 + });
  26 + }
  27 +}
... ...
app/assets/stylesheets/application.css
... ... @@ -7,45 +7,5 @@
7 7 *= require jquery-ui/jquery.tagify
8 8 *= require chosen
9 9 *= require_self
10   - *= require_tree .
  10 + *= require common
11 11 */
12   -
13   -/** COLORS **/
14   -.cgray { color:gray; }
15   -.cred { color:#D12F19; }
16   -.cgreen { color:#44aa22; }
17   -
18   -/** COMMON STYLES **/
19   -.left {
20   - float:left;
21   -}
22   -.right {
23   - float:right;
24   -}
25   -.width-50p{
26   - width:50%;
27   -}
28   -.width-49p{
29   - width:49%;
30   -}
31   -.width-30p{
32   - width:30%;
33   -}
34   -.width-65p{
35   - width:65%;
36   -}
37   -.width-100p{
38   - width:100%;
39   -}
40   -.append-bottom-10 {
41   - margin-bottom:10px;
42   -}
43   -.prepend-top-10 {
44   - margin-top:10px;
45   -}
46   -.no-borders {
47   - border:none;
48   -}
49   -.no-padding {
50   - padding:0 !important;
51   -}
... ...
app/assets/stylesheets/commits.css.scss
  1 +/* Commit Page */
  2 +body.project-page.commits-page .commit-info{float: right;}
  3 +body.project-page.commits-page .commit-info data{
  4 + padding: 4px 10px;
  5 + font-size: 11px;
  6 +}
  7 +body.project-page.commits-page .commit-info data.commit-button{
  8 + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.192, #fff), to(#f4f4f4));
  9 + background-image: -webkit-linear-gradient(#fff 19.2%, #f4f4f4);
  10 + background-image: -moz-linear-gradient(#fff 19.2%, #f4f4f4);
  11 + background-image: -o-linear-gradient(#fff 19.2%, #f4f4f4);
  12 + box-shadow: 0 -1px 0 white inset;
  13 + display: block;
  14 + border: 1px solid #eee;
  15 + border-radius: 5px;
  16 + margin-bottom: 2px;
  17 + position: relative;
  18 + padding-right: 20px;
  19 +}
  20 +
  21 +body.project-page.commits-page .commit-button i{
  22 + background: url('images.png') no-repeat -138px -27px;
  23 + width: 6px;
  24 + height: 9px;
  25 + float: right;
  26 + position: absolute;
  27 + top: 6px;
  28 + right: 5px;
  29 +}
  30 +body.project-page.commits-page .commits-date {display: block; width: 100%; margin-bottom: 20px}
  31 +body.project-page.commits-page .commits-date .data {padding: 0}
  32 +body.project-page.commits-page a.commit{padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
  33 +body.project-page.commits-page .commits-date a.commit {padding: 10px; border-bottom: none; overflow: hidden; display: block;}
  34 +body.project-page.commits-page .commits-date a.commit:last-child{border-bottom: 0}
  35 +body.project-page.commits-page .commits-date a.commit img{float: left; margin-right: 10px;}
  36 +body.project-page.commits-page .commits-date a.commit span.commit-title{display: block;}
  37 +body.project-page.commits-page .commits-date a.commit span.commit-title{margin-bottom: 10px}
  38 +body.project-page.commits-page .commits-date a.commit span.commit-author{color: #999; font-weight: normal; font-style: italic;}
  39 +body.project-page.commits-page .commits-date a.commit span.commit-author strong{font-weight: bold; font-style: normal;}
  40 +
  41 +/* eo Commit Page */
1 42 /** Commit diff view **/
2 43 .diff_file {
3 44 border:1px solid #CCC;
... ... @@ -37,7 +78,7 @@
37 78 padding:0px;
38 79 border:none;
39 80 background:#F7F7F7;
40   - color:#333;
  81 + color:#aaa;
41 82 padding: 0px 5px;
42 83 border-right: 1px solid #ccc;
43 84 text-align:right;
... ... @@ -48,6 +89,7 @@
48 89 float:left;
49 90 width:35px;
50 91 font-weight:normal;
  92 + color:#aaa;
51 93 &:hover {
52 94 text-decoration:underline;
53 95 }
... ... @@ -96,3 +138,54 @@ ul.bordered-list {
96 138 }
97 139  
98 140 ul.bordered-list li:last-child { border:none }
  141 +
  142 +.line_holder {
  143 + &:hover {
  144 + td {
  145 + background: #FFFFCF !important;
  146 + }
  147 + }
  148 +}
  149 +
  150 +.per_line_form {
  151 + font-family: "Helvetica", sans-serif;
  152 + background: #2FA0BB;
  153 +
  154 + td {
  155 + padding:0;
  156 + }
  157 +
  158 + form {
  159 + margin:5px;
  160 + width: 756px;
  161 + border: 1px solid #CCC;
  162 + padding: 20px;
  163 + background: white;
  164 + }
  165 +}
  166 +
  167 +
  168 +tr.line_notes_row {
  169 + font-family: "Helvetica", sans-serif;
  170 + &:hover {
  171 + background:none;
  172 + }
  173 + td {
  174 + margin:0px;
  175 + padding:0px;
  176 + border-bottom:1px solid #DEE2E3;
  177 +
  178 +
  179 + ul {
  180 + display:block;
  181 + list-style:none;
  182 + margin:0px;
  183 + padding:0px;
  184 +
  185 + li {
  186 + border-top:1px solid #DEE2E3;
  187 + padding:10px;
  188 + }
  189 + }
  190 + }
  191 +}
... ...
app/assets/stylesheets/common.scss 0 → 100644
... ... @@ -0,0 +1,115 @@
  1 +$text_color:#222;
  2 +$lite_text_color: #666;
  3 +$link_color:#111;
  4 +$active_link_color:#2FA0BB;
  5 +$active_bg_color:#79C3E0;
  6 +$active_bd_color: #2FA0BB;
  7 +$border_color:#CCC;
  8 +$lite_border_color:#EEE;
  9 +$app_width:980px;
  10 +$app_padding:20px;
  11 +$bg_color: #FFF;
  12 +$styled_border_color: #2FA0BB;
  13 +
  14 +/** MIXINS **/
  15 +@mixin round-borders-bottom($radius) {
  16 + border-top: 1px solid #eaeaea;
  17 + -moz-border-radius-bottomright: $radius;
  18 + -moz-border-radius-bottomleft: $radius;
  19 + border-bottom-right-radius: $radius;
  20 + border-bottom-left-radius: $radius;
  21 + -webkit-border-bottom-left-radius: $radius;
  22 + -webkit-border-bottom-right-radius: $radius;
  23 +}
  24 +
  25 +@mixin round-borders-top($radius) {
  26 + border-top: 1px solid #eaeaea;
  27 + -moz-border-radius-topright: $radius;
  28 + -moz-border-radius-topleft: $radius;
  29 + border-top-right-radius: $radius;
  30 + border-top-left-radius: $radius;
  31 + -webkit-border-top-left-radius: $radius;
  32 + -webkit-border-top-right-radius: $radius;
  33 +}
  34 +
  35 +@mixin round-borders-all($radius) {
  36 + border: 1px solid #eaeaea;
  37 + -moz-border-radius: $radius;
  38 + -webkit-border-radius: $radius;
  39 + border-radius: $radius;
  40 +}
  41 +
  42 +/** COLORS **/
  43 +.cgray { color:gray; }
  44 +.cred { color:#D12F19; }
  45 +.cgreen { color:#44aa22; }
  46 +
  47 +/** COMMON STYLES **/
  48 +.left {
  49 + float:left;
  50 +}
  51 +.right {
  52 + float:right;
  53 +}
  54 +.width-50p{
  55 + width:50%;
  56 +}
  57 +.width-49p{
  58 + width:49%;
  59 +}
  60 +.width-30p{
  61 + width:30%;
  62 +}
  63 +.width-65p{
  64 + width:65%;
  65 +}
  66 +.width-100p{
  67 + width:100%;
  68 +}
  69 +.append-bottom-10 {
  70 + margin-bottom:10px;
  71 +}
  72 +.append-bottom-20 {
  73 + margin-bottom:20px;
  74 +}
  75 +.prepend-top-10 {
  76 + margin-top:10px;
  77 +}
  78 +.no-borders {
  79 + border:none;
  80 +}
  81 +.no-padding {
  82 + padding:0 !important;
  83 +}
  84 +
  85 +/* General */
  86 +
  87 +body.collapsed {
  88 + background-color: $bg_color;
  89 +
  90 + #container{
  91 + margin: auto;
  92 + margin-top:51px;
  93 + width: $app_width;
  94 + border-top: 0;
  95 + background-color: $bg_color;
  96 + }
  97 +}
  98 +
  99 +a {
  100 + color: $link_color;
  101 +}
  102 +
  103 +@import "style.scss";
  104 +@import "projects.css.scss";
  105 +@import "commits.css.scss";
  106 +@import "notes.css.scss";
  107 +@import "merge_requests.css.scss";
  108 +@import "highlight.css.scss";
  109 +@import "highlight.black.css.scss";
  110 +@import "issues.css.scss";
  111 +@import "commits.css.scss";
  112 +
  113 +@import "top_panel.scss";
  114 +@import "dashboard.scss";
  115 +@import "tree.scss";
... ...
app/assets/stylesheets/dashboard.scss 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +body.dashboard-page h2.icon span{ background-position: 9px -69px; }
  2 +body.dashboard-page header{margin-bottom: 0}
  3 +body.dashboard-page .news-feed{margin-left: 285px; min-height: 600px; margin-top: 20px; margin-right:2px; padding:20px;}
  4 +body.dashboard-page .dashboard-content{ position: relative; float: left; width: 100%; height: 100%; }
  5 +body.dashboard-page .news-feed h2{float: left;}
  6 +
  7 +body.dashboard-page aside{
  8 + min-height: 820px; position: relative; top: 0; bottom: 0; right: 0; width: 260px; float: left; border-right: 1px solid $border_color; padding:20px; padding-right:0;
  9 + h4{margin: 0; border-bottom: 1px solid #ccc; padding: 20px 20px 20px 0px; font-size: 11px; font-weight: bold; text-transform: uppercase;}
  10 + h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 2%; margin-top: -4px; display: block;}
  11 + .project-list {list-style: none; margin: 0; padding: 0;}
  12 + .project-list li a {background: white; color: #{$blue_link}; display: block; border-bottom: 1px solid $lite_border_color; padding: 14px 6% 14px 0px;}
  13 + .project-list li a span.project-name{font-size: 14px; display: block; margin-bottom: 8px}
  14 + .project-list li a span.time{color: #666; font-weight: normal; font-size: 11px}
  15 + .project-list li a span.arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999}
  16 +}
  17 +
  18 +body.dashboard-page .news-feed .project-updates {
  19 + margin-bottom: 20px; display: block; width: 100%;
  20 + .data{ padding: 0}
  21 + a.project-update {padding: 10px; overflow: hidden; display: block;}
  22 + a.project-update:last-child{border-bottom: 0}
  23 + a.project-update img{float: left; margin-right: 10px;}
  24 + a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
  25 + a.project-update span.update-title{margin-bottom: 10px}
  26 + a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;}
  27 + a.project-update span.update-author strong{font-weight: bold; font-style: normal;}
  28 +}
  29 +/* eo Dashboard Page */
  30 +
... ...
app/assets/stylesheets/issues.css.scss
... ... @@ -11,8 +11,8 @@
11 11 }
12 12  
13 13 .issues_filter {
14   - margin-top:10px;
15   - .left {
  14 + margin:10px 0;
  15 + .left {
16 16 margin-right:15px;
17 17 }
18 18 }
... ... @@ -72,3 +72,13 @@ body.project-page .edit_snippet table td
72 72 }
73 73 }
74 74  
  75 +
  76 +#issues-table {
  77 + tr {
  78 + border-top: 1px solid $lite_border_color;
  79 + &:first-child {
  80 + border:none;
  81 + }
  82 + }
  83 +
  84 +}
... ...
app/assets/stylesheets/notes.css.scss
... ... @@ -42,3 +42,11 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f
42 42  
43 43  
44 44 .note .note-title { margin-left:55px; }
  45 +
  46 +p.notify_controls input{
  47 + margin: 5px;
  48 +}
  49 +
  50 +p.notify_controls span{
  51 + font-weight: 700;
  52 +}
... ...
app/assets/stylesheets/projects.css.scss
1   -/** MIXINS **/
2   -@mixin round-borders-bottom($radius) {
3   - border-top: 1px solid #eaeaea;
4   - -moz-border-radius-bottomright: $radius;
5   - -moz-border-radius-bottomleft: $radius;
6   - border-bottom-right-radius: $radius;
7   - border-bottom-left-radius: $radius;
8   - -webkit-border-bottom-left-radius: $radius;
9   - -webkit-border-bottom-right-radius: $radius;
10   -}
11   -
12   -@mixin round-borders-top($radius) {
13   - border-top: 1px solid #eaeaea;
14   - -moz-border-radius-topright: $radius;
15   - -moz-border-radius-topleft: $radius;
16   - border-top-right-radius: $radius;
17   - border-top-left-radius: $radius;
18   - -webkit-border-top-left-radius: $radius;
19   - -webkit-border-top-right-radius: $radius;
20   -}
21   -
22   -@mixin round-borders-all($radius) {
23   - border: 1px solid #eaeaea;
24   - -moz-border-radius: $radius;
25   - -webkit-border-radius: $radius;
26   - border-radius: $radius;
  1 +body.project-page h2.icon .project-name, body.project-page h2.icon d{border: 1px solid #eee; padding: 5px 30px 5px 10px; border-radius: 5px; position: relative;}
  2 +body.project-page h2.icon .project-name i.arrow{float: right;
  3 + position: absolute;
  4 + right: 10px;
  5 + top: 13px;
  6 + display: block;
  7 + background: url('images.png') no-repeat -97px -29px;
  8 + width: 4px;
  9 + height: 5px;
  10 +}
  11 +
  12 +body.project-page h2.icon span{ background-position: -78px -68px; }
  13 +body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; padding-bottom: 10px;}
  14 +body.project-page .page-title{margin-bottom: 0}
  15 +
  16 +body.project-page .project-sidebar {
  17 + width: 110px;
  18 + left: 0;
  19 + top: 0;
  20 + height: 100%;
  21 + bottom: 0;
  22 + position: absolute;
  23 + float: left;
  24 + display: inline-block;
  25 + background: #FFF;
  26 + padding: $app_padding;
  27 + padding-right:0px;
  28 + margin: 0;
  29 + border-right: 1px solid $border_color;
  30 +}
  31 +
  32 +body.projects-page input.text.git-url { font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 0 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px; width: 136px}
  33 +body.projects-page input.text.git-url {margin:10px 0 0 }
  34 +.git_url_wrapper { margin-right:50px }
  35 +
  36 +.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); box-shadow:0px 0px 10px rgba(0, 0, 0, .1); }
  37 +
  38 +
  39 +/* New project Page */
  40 +.new-project-page .container table{background: white}
  41 +body.project-page .project-sidebar aside{width: 109px}
  42 +body.project-page .project-sidebar aside a{
  43 + display: block;
  44 + position: relative;
  45 + padding: 15px 10px;
  46 + margin: 10px 0 0 0;
  47 +
  48 +
27 49 }
  50 +body.project-page .project-sidebar aside a span.number{float: right; border-radius: 5px; text-shadow: none; background: rgba(0,0,0,.12); text-align: center; padding: 5px 8px; position: absolute; top: 10px; right: 10px}
  51 +body.project-page .project-sidebar aside a.current {
  52 + color: white;
  53 + background: $active_bg_color;
  54 + border: 1px solid $active_bd_color;
  55 + border-radius:5px;
  56 +
  57 +
  58 + -webkit-border-top-right-radius: 0;
  59 + -webkit-border-bottom-right-radius: 0;
  60 + -moz-border-radius-topright: 0px;
  61 + -moz-border-radius-bottomright: 0px;
  62 + border-top-right-radius: 0;
  63 + border-bottom-right-radius: 0;
  64 + margin-right: -1px;
  65 +}
  66 +body.project-page .project-content{ padding: $app_padding; display: block; margin-left: 130px; min-height: 600px}
  67 +body.project-page .project-content h2{ margin-top: 6px}
  68 +body.project-page .project-content .button.right{margin-left: 20px}
  69 +body.project-page table .commit a{color: #{$blue_link}}
  70 +body.project-page table th, body.project-page table td{ border-bottom: 1px solid #DEE2E3;}
  71 +body.project-page .fixed{position: fixed; }
  72 +
  73 +
  74 +
28 75  
29 76 /** File stat **/
30 77 .file_stats {
... ... @@ -48,90 +95,7 @@ table.round-borders {
48 95 text-align: left;
49 96 }
50 97  
51   -a {
52   - color: #111;
53   -}
54   -
55   -/** FILE CONTENT VIEW **/
56   -.view_file_content{
57   - .old_line, .new_line {
58   - background:#ECECEC;
59   - color:#777;
60   - width:15px;
61   - float:left;
62   - padding: 0px 10px;
63   - border-right: 1px solid #ccc;
64   - }
65   - .old_line{
66   - display:none;
67   - }
68   -}
69   -
70   -.view_file .view_file_header,
71   -.diff_file .diff_file_header {
72   - background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
73   - background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
74   - background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
75   - background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
76   - margin: 0;
77   - font-weight: normal;
78   - font-weight: bold;
79   - text-align: left;
80   - color: #666;
81   - border-bottom: 1px solid #DEE2E3;
82   - padding: 7px 10px;
83   -}
84   -
85   -.view_file {
86   - border:1px solid #CCC;
87   - margin-bottom:1em;
88   -
89   - .view_file_content {
90   - background:#fff;
91   - color:#514721;
92   - font-size: 11px;
93   - }
94   - .view_file_content_image {
95   - background:#eee;
96   - text-align:center;
97   - img {
98   - padding:100px;
99   - max-width:300px;
100   - }
101   - }
102   -}
103   -
104   -td.code {
105   - width: 100%;
106   - .highlight {
107   - margin-left: 55px;
108   - overflow:auto;
109   - overflow-y:hidden;
110   - border-left: 1px solid #DEE2E3;
111   - background: white;
112   - }
113   -}
114   -.highlight pre {
115   - white-space: pre;
116   - word-wrap:normal;
117   -}
118   -
119   -table.highlighttable {
120   - border: none;
121   - background: #F7F7F7;
122   -}
123   -body.project-page table.highlighttable td { border: none }
124   -table.highlighttable tr:hover { background:none;}
125 98  
126   -table.highlighttable pre{
127   - line-height:16px !important;
128   - font-size:12px !important;
129   -}
130   -
131   -table.highlighttable .linenodiv pre {
132   - text-align: right;
133   - padding-right: 4px;
134   -}
135 99  
136 100 /** PROJECTS **/
137 101 input.ssh_project_url {
... ... @@ -157,61 +121,6 @@ input.ssh_project_url {
157 121 clear: both;
158 122 }
159 123  
160   -/** FORM INPUTS **/
161   -.new_merge_request,
162   -.edit_merge_request,
163   -.user_new,
164   -.new_key,
165   -.new_issue,
166   -.new_note,
167   -.edit_user,
168   -.edit_issue,
169   -.new_project,
170   -.new_snippet,
171   -.edit_snippet,
172   -.edit_project {
173   - input[type='text'],
174   - input[type='email'],
175   - input[type='password'],
176   - textarea {
177   - width:400px;
178   - padding:8px;
179   - font-size:14px;
180   - @include round-borders-all(4px);
181   - }
182   -}
183   -
184   -.input_button {
185   - padding:8px;
186   - font-size:14px;
187   - cursor:pointer;
188   - background-color: #F5F5F5;
189   - border-color: #EEEEEE #DEDEDE #DEDEDE #EEEEEE;
190   - border-right: 1px solid #DEDEDE;
191   - border-style: solid;
192   - border-width: 1px;
193   -}
194   -
195   -/** FLASH **/
196   -#flash_container {
197   - height:45px;
198   - position:fixed;
199   - z-index:10001;
200   - top:0px;
201   - width:100%;
202   - margin-bottom:15px;
203   - overflow:hidden;
204   - background:white;
205   - cursor:pointer;
206   - border-bottom:1px solid #777;
207   -
208   - h4 {
209   - color:#444;
210   - font-size:22px;
211   - padding-top:5px;
212   - margin:2px;
213   - }
214   -}
215 124  
216 125 /** Buttons **/
217 126 .lbutton,
... ... @@ -270,7 +179,7 @@ input.ssh_project_url {
270 179  
271 180 body.project-page table .commit {
272 181 a.tree-commit-link {
273   - color:gray;
  182 + color:#444;
274 183 &:hover {
275 184 text-decoration:underline;
276 185 }
... ... @@ -331,7 +240,7 @@ body.project-page table .commit {
331 240 border:none;
332 241 text-shadow:none;
333 242  
334   - &.inline {
  243 + &.inline {
335 244 display:inline;
336 245 }
337 246  
... ... @@ -358,8 +267,12 @@ body.project-page table .commit {
358 267 color:white;
359 268 }
360 269 &.note {
361   - background: #2c5c66;
362   - color:white;
  270 + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
  271 + background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
  272 + background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
  273 + background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
  274 + color: #777;
  275 + border: 1px solid #DEDFE1;
363 276 }
364 277 &.issue {
365 278 background: #D12F19;
... ... @@ -376,7 +289,8 @@ body.project-page table .commit {
376 289 }
377 290  
378 291 #holder {
379   - border: solid 1px #999;
  292 + background:#FAFAFA;
  293 + border: 1px solid #EEE;
380 294 cursor: move;
381 295 height: 70%;
382 296 overflow: hidden;
... ... @@ -428,55 +342,35 @@ body.project-page .team_member_new .span-6, .team_member_edit .span-6{ padding:1
428 342 body.projects-page input.text.git-url.project_list_url { width:165px; }
429 343  
430 344  
  345 +body.project-page table.no-borders th {
  346 + background:none;
  347 + border-bottom:1px solid #CCC;
  348 + color:#333;
  349 +}
431 350  
432 351 body.project-page table.no-borders tr,
433   -body.project-page table.no-borders td{
  352 +body.project-page table.no-borders td{
434 353 border:none;
435 354 }
436 355  
437   -#gitlab-tabs {
438   - .ui-tabs-nav {
439   - border-bottom: 1px solid #DEDFE1;
440   -
441   - li {
442   - background: none;
443   - border:none;
444   - font-size: 16px;
445   - margin: 0;
446   - padding: 0;
447   -
448   - a {
449   - margin: 0;
450   - padding: 10px 16px;
451   - width:150px;
452   - }
453   -
454   - &.ui-tabs-selected {
455   - background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
456   - background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
457   - background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
458   - background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
459   - font-weight: bold;
460   - border:1px solid #DEDFE1;
461   - border-bottom: 1px solid #DEDFE1;
462   - -webkit-border-top-left-radius: 5px;
463   - -webkit-border-top-right-radius: 5px;
464   - -moz-border-radius-topleft: 5px;
465   - -moz-border-radius-topright: 5px;
466   - border-top-left-radius: 5px;
467   - border-top-right-radius: 5px;
468   - }
469   - }
470   - }
471   -}
472   -
473   -.ajax-tab-loading {
  356 +.ajax-tab-loading {
474 357 padding:40px;
475 358 display:none;
476 359 }
477 360  
478 361 #tree-content-holder { float:left; width:100%; }
479 362  
  363 +#tree-readme-holder {
  364 + float:left;
  365 + width:100%;
  366 +
  367 + .readme {
  368 + @include round-borders-all(4px);
  369 + padding: 4px 15px;
  370 + background:#F7F7F7;
  371 + }
  372 +}
  373 +
480 374  
481 375  
482 376 /* Commit Page */
... ... @@ -506,3 +400,173 @@ body.project-page table.no-borders td{
506 400 top: 6px;
507 401 right: 5px;
508 402 }
  403 +.box-arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999; margin: 1.5em 0;}
  404 +
  405 +h4.dash-tabs {
  406 + margin: 0;
  407 + border-bottom: 1px solid #ccc;
  408 + padding: 10px 10px;
  409 + font-size: 11px;
  410 + padding-left:20px;
  411 + font-weight: bold; text-transform: uppercase;
  412 + background: #F7F7F7;
  413 + margin-bottom:20px;
  414 + height:13px;
  415 +
  416 +}
  417 +
  418 +.dash-button {
  419 + border-right: 1px solid #ddd;
  420 + background:none;
  421 + padding: 10px 15px;
  422 + float:left;
  423 + position:relative;
  424 + top:-10px;
  425 + left:0px;
  426 + height:13px;
  427 +
  428 + &:first-child {
  429 + border-left: 1px solid #ddd;
  430 + }
  431 + &.active {
  432 + background: #eaeaea;
  433 + }
  434 +}
  435 +
  436 +
  437 +.dashboard-loader {
  438 + float:right;
  439 + margin-right:30px;
  440 + display:none;
  441 +}
  442 +
  443 +
  444 +.merge-tabs {
  445 + margin: 0;
  446 + border: 1px solid #ccc;
  447 + padding: 5px;
  448 + font-size: 12px;
  449 + background: #F7F7F7;
  450 + margin-bottom:20px;
  451 + height:26px;
  452 +
  453 + -moz-border-radius: 4px;
  454 + -webkit-border-radius: 4px;
  455 + border-radius: 4px;
  456 +
  457 + .tab {
  458 + font-weight: bold;
  459 + border-right: 1px solid #ddd;
  460 + background:none;
  461 + padding: 10px;
  462 + min-width:60px;
  463 + float:left;
  464 + position:relative;
  465 + top:-5px;
  466 + left:-5px;
  467 + height:16px;
  468 + padding-left:34px;
  469 +
  470 + span {
  471 + width: 20px;
  472 + height: 20px;
  473 + display: inline-block;
  474 + position: absolute;
  475 + left: 8px;
  476 + top: 8px;
  477 + }
  478 +
  479 + &.active {
  480 + background: #eaeaea;
  481 + }
  482 + }
  483 +}
  484 +.merge-tabs.repository .tab span{ background: url("images.png") no-repeat -38px -77px; }
  485 +.activities-tab span { background: url("images.png") no-repeat -161px -1px; }
  486 +.stat-tab span,
  487 +.team-tab span,
  488 +.snippets-tab span { background: url("images.png") no-repeat -38px -77px; }
  489 +.files-tab span { background: url("images.png") no-repeat -112px -23px; }
  490 +
  491 +.merge-notes-tab span { background: url("images.png") no-repeat -161px -1px; }
  492 +.merge-commits-tab span { background: url("images.png") no-repeat -86px 1px; }
  493 +.merge-diffs-tab span { background: url("images.png") no-repeat -118px 1px; }
  494 +.merge-tabs .dashboard-loader { padding:8px; }
  495 +
  496 +.user-mention {
  497 + color: #2FA0BB;
  498 + font-weight: bold;
  499 +}
  500 +
  501 +.author {
  502 + color: #999;
  503 +}
  504 +
  505 +
  506 +.red-button{
  507 + border-radius: 5px;
  508 + font-size: 12px;
  509 + font-weight: bold;
  510 + padding: 5px 17px;
  511 + border: 1px solid #999;
  512 + color: #666;
  513 + display: inline-block;
  514 + box-shadow: 0 1px 2px rgba(0,0,0,.3);
  515 + background: #D12F19;
  516 + color: white;
  517 +}
  518 +
  519 +.positive-button{
  520 + border-radius: 5px;
  521 + font-size: 12px;
  522 + font-weight: bold;
  523 + padding: 5px 17px;
  524 + border: 1px solid #999;
  525 + color: #666;
  526 + display: inline-block;
  527 + box-shadow: 0 1px 2px rgba(0,0,0,.3);
  528 + background: #4A2;
  529 + color: white;
  530 +}
  531 +
  532 +
  533 +.dark_scheme_box {
  534 + padding:20px 0;
  535 +
  536 + label {
  537 + float:left;
  538 + box-shadow: 0 0px 5px rgba(0,0,0,.3);
  539 +
  540 + img {
  541 + }
  542 + }
  543 +}
  544 +
  545 +a.project-update.titled {
  546 + position: relative;
  547 + padding-left: 235px !important;
  548 +
  549 + .title-block {
  550 + padding: 10px;
  551 + width: 205px;
  552 + position: absolute;
  553 + left: 0;
  554 + top: 0;
  555 + }
  556 +}
  557 +
  558 +.add_new {
  559 + float: right;
  560 + background: #A6B807;
  561 + color: white;
  562 + padding: 4px 10px;
  563 + @include round-borders-all(4px);
  564 + font-size:11px;
  565 + margin: 10px 0;
  566 +}
  567 +
  568 +
  569 +
  570 +.new-project-hodler {
  571 + padding:20px;
  572 +}
... ...
app/assets/stylesheets/style.scss
... ... @@ -9,7 +9,9 @@ audio:not([controls]) { display: none; }
9 9  
10 10 html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
11 11 body { margin: 0; font-size: 13px; line-height: 1.231; }
12   -body, button, input, select, textarea { font-family: sans-serif; color: #222; }
  12 +body, button, input, select, textarea {
  13 + font-family: "helvetica", "arial", "freesans", "clean", sans-serif;
  14 +color: #222; }
13 15  
14 16 ::-moz-selection { background: #79c3e0; color: #fff; text-shadow: none; }
15 17 ::selection { background: #79c3e0; color: #fff; text-shadow: none; }
... ... @@ -74,9 +76,12 @@ $blue_link: &quot;#2fa0bb&quot;;
74 76 /* eo Vars */
75 77  
76 78 html{ -webkit-font-smoothing:antialiased; }
77   -body{font-size: 12px; background-color: #eee;}
78   -a{text-decoration: none; font-weight: bold; color: #666}
79   -a:hover{color: #333}
  79 +body {
  80 + font-size: 12px;
  81 + background-color: #FFFFFF;
  82 +}
  83 +a{text-decoration: none; font-weight: bold; color: #444}
  84 +a:hover{color: #555}
80 85 /* Typography */
81 86 h1,h2,h3,h4,h5{font-weight: normal; color: #666}
82 87 h2{margin: 1.5em 0}
... ... @@ -122,7 +127,7 @@ table thead th{
122 127 td, th{ padding: .9em 1em; vertical-align: middle; }
123 128  
124 129 table thead .image{width:100px}
125   -table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF}
  130 +.listed_items tr.odd:hover{background-color:#FFFFCF}
126 131 /* eo Tables */
127 132  
128 133 /* Buttons */
... ... @@ -130,7 +135,7 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF}
130 135 border-radius: 5px;
131 136 font-size: 12px;
132 137 font-weight: bold;
133   - padding: 6px 20px;
  138 + padding: 5px 17px;
134 139 border: 1px solid #999;
135 140 color: #666;
136 141 display: inline-block;
... ... @@ -187,12 +192,14 @@ input.button{margin-bottom: 1.5em}
187 192 /* eo Buttons */
188 193  
189 194 /* UI Box */
190   -.ui-box{border: 1px solid #DEDFE1; float: left; border-radius: 5px}
  195 +//.ui-box{border: 1px solid #DEDFE1; float: left; border-radius: 5px}
  196 +.ui-box{float: left;}
191 197 .ui-box h3{
192 198 background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
193 199 background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
194 200 background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
195 201 background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
  202 + background:none;
196 203 margin: 0;
197 204 padding: 1em;
198 205 font-size: 12px;
... ... @@ -215,13 +222,9 @@ input.button{margin-bottom: 1.5em}
215 222  
216 223 .ui-box .data{padding: .5em 1em}
217 224  
218   -.ui-box .buttons{background-color: #f7f8f9; padding: 1em;
219   - -webkit-border-bottom-right-radius: 5px;
220   - -webkit-border-bottom-left-radius: 5px;
221   - -moz-border-radius-bottomright: 5px;
222   - -moz-border-radius-bottomleft: 5px;
223   - border-bottom-right-radius: 5px;
224   - border-bottom-left-radius: 5px;
  225 +.ui-box .buttons{
  226 + padding: 1em;
  227 + border-top:1px solid $lite_border_color;
225 228 }
226 229  
227 230 .ui-box .buttons .button{padding: 8px 9px; font-size: 11px}
... ... @@ -309,8 +312,7 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%}
309 312 input[type="password"],
310 313 textarea
311 314 {
312   - border: 1px solid #FFBBBB;
313   - background: #fff4f6;
  315 + border: 1px solid #D30 !important;
314 316 }
315 317 }
316 318 /* eo Errors */
... ... @@ -328,13 +330,13 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%}
328 330 }
329 331 /* eo InfoBlock */
330 332  
331   -/* General */
332   -#container{background-color: white; overflow: hidden; }
333   -body.collapsed #container{margin: auto; width: 980px; border: 1px solid rgba(0,0,0,.22); border-top: 0; box-shadow: 0 0 0px 4px rgba(0,0,0,.04)}
334   -
335 333 /* Header */
336   -header{background: #474D57 url('bg-header.png') repeat-x bottom; z-index: 10000; height: 44px; padding: 10px 2% 6px 2%; position: relative}
337   -header a{color: white; text-shadow: 0 -1px 0 black}
  334 +header{
  335 + background: #474D57 url('bg-header.png') repeat-x bottom;
  336 + z-index: 10000;
  337 + height: 44px;
  338 + padding: 10px 2% 6px 2%;
  339 +}
338 340 header a:hover{color: #f1f1f1}
339 341 header h1{
340 342 width: 65px;
... ... @@ -359,6 +361,9 @@ header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin
359 361 margin-top: 2px;
360 362 height:30px
361 363 }
  364 +header nav.shorter_nav{
  365 + width: 207px;
  366 +}
362 367 header nav a{padding: 8px 12px 8px 34px; display: inline-block; color: #D6DADF; border-right: 1px solid #31363E; position: relative; box-shadow: 1px 0 0 rgba(255,255,255,.1); margin: 0}
363 368 header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;}
364 369 header nav a:last-child {border: 0; box-shadow: none}
... ... @@ -382,7 +387,7 @@ header nav a.dashboard {
382 387 border-bottom-left-radius: 4px;
383 388 }
384 389  
385   -header nav a.admin{
  390 +header nav a.last_elem{
386 391 -webkit-border-top-right-radius: 4px;
387 392 -webkit-border-bottom-right-radius: 4px;
388 393 -moz-border-radius-topright: 4px;
... ... @@ -391,13 +396,14 @@ header nav a.admin{
391 396 border-bottom-right-radius: 4px;
392 397 }
393 398  
394   -header .search{ display: inline-block; float: right; margin-right: 46px}
  399 +header .search{ display: inline-block; float: right; margin-right: 90px}
395 400  
396 401 header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;}
397 402  
398 403 header nav a.dashboard span{background: url('images.png') no-repeat -161px 0;}
399 404 header nav a.admin span{background: url('images.png') no-repeat -184px 0;}
400 405 header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 7px}
  406 +header nav a.issues span{background: url('images.png') no-repeat -209px -1px; top: 7px}
401 407  
402 408 header .login-top{float: right; width: 180px;
403 409 background-image: -webkit-gradient(linear, 0 0, 0 62, color-stop(0.032, #464c56), to(#363c45));
... ... @@ -413,7 +419,7 @@ header .login-top a.pic{float: left; margin-right: 10px;
413 419 }
414 420 header .login-top a.username{margin-bottom: 5px}
415 421 header .login-top a.logout{color: #ccc}
416   -header{margin-bottom: 0; clear: both; }
  422 +header{margin-bottom: 0; clear: both; position:relative;}
417 423  
418 424 .page-title{background-color: #f1f1f1;display: block; float: left; clear: both; width: 98%; padding: 1% 1%; border-bottom: 1px solid #ccc; box-shadow: 0 -1px 0 white inset; margin-bottom: 1.5em}
419 425 .page-title h1{font-size: 20px; width: 400px; margin: 0; padding-top: 8px }
... ... @@ -421,8 +427,22 @@ header{margin-bottom: 0; clear: both; }
421 427 .right{float: right;}
422 428  
423 429 /* Account box */
424   -header .account-box{position: absolute; right: 0; top: 8px; z-index: 10000; width: 128px; font-size: 11px; float: right; display: block; cursor: pointer;}
425   -header .account-box img{ border-radius: 4px; right: 20px; position: absolute; width: 38px; height: 38px; display: block; box-shadow: 0 1px 2px black}
  430 +header .account-box{
  431 + position: absolute;
  432 + right: 0;
  433 + top: 8px;
  434 + z-index: 10000;
  435 + width: 128px;
  436 + font-size: 11px;
  437 + float: right;
  438 + display: block;
  439 + cursor: pointer;}
  440 +header .account-box img{
  441 + border-radius: 4px;
  442 + right: 20px;
  443 + position: absolute;
  444 + width: 33px; height: 33px;
  445 + display: block; top:0;}
426 446 header .account-box img:after{
427 447 content: " ";
428 448 display: block;
... ... @@ -446,7 +466,8 @@ float: right;
446 466 .account-box.hover{height: 138px;}
447 467  
448 468 .account-box:hover > .account-links{display: block;}
449   -header .account-links{background: white; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); position:relative;}
  469 +header .account-links{
  470 + background: #79C3E0; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); position:relative;}
450 471 header .account-links:before {
451 472 content: ".";
452 473 width:0;
... ... @@ -545,8 +566,22 @@ header .account-links a:last-child{
545 566 }
546 567  
547 568 /* eo Account Box */
548   -input.search-input{float: left; text-shadow: none; width: 116px; background-image: url('icon-search.png') ; background-repeat: no-repeat; background-position: 10px; border-radius: 100px; border: 1px solid rgba(0,0,0,.7); box-shadow: 0 1px 0 rgba(255,255,255,.2), 0 2px 2px rgba(0,0,0,.4) inset ; background-color: #D2D5DA; background-color: rgba(255,255,255,.5); padding: 5px; padding-left: 26px; margin-top: 4px; margin-right: 10px }
549   -input.search-input:focus{ background-color: white; width: 216px;}
  569 +input.search-input{
  570 + float: left;
  571 + text-shadow: none;
  572 + width: 116px;
  573 + background-image: url('icon-search.png') ;
  574 + background-repeat: no-repeat;
  575 + background-position: 10px;
  576 + border-radius: 4px;
  577 + border: 1px solid #AAA;
  578 + background-color: #FFF;
  579 + padding: 5px;
  580 + padding-left: 26px;
  581 + margin-top: 2px;
  582 + margin-right: 10px;
  583 +}
  584 +/*input.search-input:focus{ background-color: white; width: 216px;}*/
550 585 input.search-input::-webkit-input-placeholder {color: #666}
551 586 /* eo Header */
552 587  
... ... @@ -559,127 +594,12 @@ html, body { height: 100%; }
559 594  
560 595  
561 596  
562   -body.dashboard-page h2.icon span{ background-position: 9px -69px; }
563   -body.dashboard-page header{margin-bottom: 0}
564   -body.dashboard-page .news-feed{padding-left: 1em; margin-right: 450px; min-height: 600px; margin-left: 1%}
565   -body.dashboard-page .dashboard-content{ position: relative; float: left; width: 100%; height: 100%; }
566   -body.dashboard-page .news-feed h2{float: left;}
567   -body.dashboard-page aside{ min-height: 820px; position: relative; top: 0; bottom: 0; right: 0; width: 420px; float: right; background-color: #f7f7f7; border-left: 1px solid #ccc }
568   -body.dashboard-page aside h4{margin: 0; border-bottom: 1px solid #ccc; padding: 10px 10px; font-size: 11px; font-weight: bold; text-transform: uppercase;}
569   -body.dashboard-page aside h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 2%; margin-top: -4px; display: block;}
570   -body.dashboard-page aside .project-list {list-style: none; margin: 0; padding: 0;}
571   -body.dashboard-page aside .project-list li a {background: white; color: #{$blue_link}; display: block; border-bottom: 1px solid #eee; padding: 14px 6% 14px 14px;}
572   -body.dashboard-page aside .project-list li a:hover {background: #f1f1f1}
573   -body.dashboard-page aside .project-list li a:hover span.arrow{background-color: #E3E5EA;}
574   -body.dashboard-page aside .project-list li a span.project-name{font-size: 14px; display: block; margin-bottom: 8px}
575   -body.dashboard-page aside .project-list li a span.time{color: #666; font-weight: normal; font-size: 11px}
576   -body.dashboard-page aside .project-list li a span.arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999}
577   -body.dashboard-page .news-feed .project-updates {margin-bottom: 20px; display: block; width: 100%;}
578   -body.dashboard-page .news-feed .project-updates .data{ padding: 0}
579   -body.dashboard-page .news-feed .project-updates a.project-update {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
580   -body.dashboard-page .news-feed .project-updates a.project-update:last-child{border-bottom: 0}
581   -body.dashboard-page .news-feed .project-updates a.project-update img{float: left; margin-right: 10px;}
582   -body.dashboard-page .news-feed .project-updates a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
583   -body.dashboard-page .news-feed .project-updates a.project-update span.update-title{margin-bottom: 10px}
584   -body.dashboard-page .news-feed .project-updates a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;}
585   -body.dashboard-page .news-feed .project-updates a.project-update span.update-author strong{font-weight: bold; font-style: normal;}
586   -/* eo Dashboard Page */
587 597  
588 598 .grey-button.right{margin-top: 20px}
589 599  
590 600 /* Project Page */
591   -
592   -body.project-page h2.icon .project-name, body.project-page h2.icon d{border: 1px solid #eee; padding: 5px 30px 5px 10px; border-radius: 5px; position: relative;}
593   -body.project-page h2.icon .project-name i.arrow{float: right;
594   - position: absolute;
595   - right: 10px;
596   - top: 13px;
597   - display: block;
598   - background: url('images.png') no-repeat -97px -29px;
599   - width: 4px;
600   - height: 5px;
601   -}
602   -
603   -body.project-page h2.icon span{ background-position: -78px -68px; }
604   -body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; padding-bottom: 10px;}
605   -body.project-page .page-title{margin-bottom: 0}
606   -body.project-page .project-sidebar {width: 180px; left: 0; top: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; float: left; display: inline-block; background: #f7f7f7; padding: 20px 0 20px 2%; margin: 0; }
607   -
608   -body.project-page input.text.git-url,
609   -body.projects-page input.text.git-url { font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 0 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px; width: 136px}
610   -body.projects-page input.text.git-url {margin:10px 0 0 }
611   -.git_url_wrapper { margin-right:50px }
612   -
613   -.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); box-shadow:0px 0px 10px rgba(0, 0, 0, .1); }
614   -
615   -body.project-page .project-sidebar aside{width: 179px}
616   -body.project-page .project-sidebar aside a{display: block; position: relative; background: white; padding: 15px 10px; border-bottom: 1px solid #eee}
617   -body.project-page .project-sidebar aside a:first-child{
618   - -webkit-border-top-left-radius: 5px;
619   - -moz-border-radius-topleft: 5px;
620   - border-top-left-radius: 5px;
621   -}
622   -.project-page .project-sidebar aside a:last-child{
623   - -webkit-border-bottom-left-radius: 5px;
624   - -moz-border-radius-bottomleft: 5px;
625   - border-bottom-left-radius: 5px;
626   -}
627   -body.project-page .project-sidebar aside a:hover{background-color: #eee;}
628   -body.project-page .project-sidebar aside a span.number{float: right; border-radius: 5px; text-shadow: none; background: rgba(0,0,0,.12); text-align: center; padding: 5px 8px; position: absolute; top: 10px; right: 10px}
629   -body.project-page .project-sidebar aside a.current{background-color: #79c3e0; color: white; text-shadow: none; border-color: transparent}
630   -body.project-page .project-content{ padding: 20px; display: block; margin-left: 205px; min-height: 600px}
631   -body.project-page .project-content h2{ margin-top: 6px}
632   -body.project-page .project-content .button.right{margin-left: 20px}
633   -body.project-page table .commit a{color: #{$blue_link}}
634   -body.project-page table th, body.project-page table td{ border-bottom: 1px solid #DEE2E3;}
635   -body.project-page .fixed{position: fixed; }
636   -
637   -/* New project Page */
638   -.new-project-page .container{width: 600px; background-color: rgba(0,0,0,.02); margin: auto; border: 1px solid #eee; padding: 0 20px; margin: 30px auto 60px auto; border-radius: 5px}
639   -.new-project-page .container table{background: white}
640 601 /* eo New Project Page */
641 602  
642   -/* Commit Page */
643   -body.project-page.commits-page .commit-info{float: right;}
644   -body.project-page.commits-page .commit-info data{
645   - padding: 4px 10px;
646   - font-size: 11px;
647   -}
648   -body.project-page.commits-page .commit-info data.commit-button{
649   - background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.192, #fff), to(#f4f4f4));
650   - background-image: -webkit-linear-gradient(#fff 19.2%, #f4f4f4);
651   - background-image: -moz-linear-gradient(#fff 19.2%, #f4f4f4);
652   - background-image: -o-linear-gradient(#fff 19.2%, #f4f4f4);
653   - box-shadow: 0 -1px 0 white inset;
654   - display: block;
655   - border: 1px solid #eee;
656   - border-radius: 5px;
657   - margin-bottom: 2px;
658   - position: relative;
659   - padding-right: 20px;
660   -}
661   -
662   -body.project-page.commits-page .commit-button i{
663   - background: url('images.png') no-repeat -138px -27px;
664   - width: 6px;
665   - height: 9px;
666   - float: right;
667   - position: absolute;
668   - top: 6px;
669   - right: 5px;
670   -}
671   -body.project-page.commits-page .commits-date {display: block; width: 100%; margin-bottom: 20px}
672   -body.project-page.commits-page .commits-date .data {padding: 0}
673   -body.project-page.commits-page a.commit{padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
674   -body.project-page.commits-page .commits-date a.commit {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
675   -body.project-page.commits-page .commits-date a.commit:last-child{border-bottom: 0}
676   -body.project-page.commits-page .commits-date a.commit img{float: left; margin-right: 10px;}
677   -body.project-page.commits-page .commits-date a.commit span.commit-title{display: block;}
678   -body.project-page.commits-page .commits-date a.commit span.commit-title{margin-bottom: 10px}
679   -body.project-page.commits-page .commits-date a.commit span.commit-author{color: #999; font-weight: normal; font-style: italic;}
680   -body.project-page.commits-page .commits-date a.commit span.commit-author strong{font-weight: bold; font-style: normal;}
681   -
682   -/* eo Commit Page */
683 603  
684 604 /* eo Project Page */
685 605  
... ... @@ -729,12 +649,154 @@ body.projects-page .browse-code{margin-right: 10px}
729 649 h2, h3 { page-break-after: avoid; }
730 650 }
731 651  
732   -/**
733   - * author:DZ
734   - * date: Nov 09
735   - * fix different fonts for firefox & webkit
736   - */
737 652 body, button, input, select, textarea {
738   - font-family: "Helvetica", sans-serif;
  653 + font-family: "helvetica", "arial", "freesans", "clean", sans-serif;
  654 +}
  655 +
  656 +/** FORM INPUTS **/
  657 +.new_merge_request,
  658 +.edit_merge_request,
  659 +.user_new,
  660 +.new_key,
  661 +.new_issue,
  662 +.new_note,
  663 +.edit_user,
  664 +.edit_issue,
  665 +.new_project,
  666 +.new_snippet,
  667 +.edit_snippet,
  668 +.edit_project {
  669 + input[type='text'],
  670 + input[type='email'],
  671 + input[type='password'],
  672 + textarea {
  673 + width:400px;
  674 + padding:8px;
  675 + font-size:14px;
  676 + @include round-borders-all(4px);
  677 + }
  678 +}
  679 +
  680 +.text_field {
  681 + width:400px;
  682 + padding:8px;
  683 + font-size:14px;
  684 + @include round-borders-all(4px);
  685 +}
  686 +
  687 +.input_button {
  688 + padding:8px;
  689 + font-size:14px;
  690 + cursor:pointer;
  691 + background-color: #F5F5F5;
  692 + border-color: #EEEEEE #DEDEDE #DEDEDE #EEEEEE;
  693 + border-right: 1px solid #DEDEDE;
  694 + border-style: solid;
  695 + border-width: 1px;
  696 +}
  697 +
  698 +/** FLASH **/
  699 +#flash_container {
  700 + height:45px;
  701 + position:fixed;
  702 + z-index:10001;
  703 + top:0px;
  704 + width:100%;
  705 + margin-bottom:15px;
  706 + overflow:hidden;
  707 + background:white;
  708 + cursor:pointer;
  709 + border-bottom:1px solid #777;
  710 +
  711 + h4 {
  712 + color:#444;
  713 + font-size:22px;
  714 + padding-top:5px;
  715 + margin:2px;
  716 + }
  717 +}
  718 +
  719 +
  720 +.errors_holder {
  721 + background:#D30;
  722 + color:#fff;
  723 + @include round-borders-all(4px);
  724 + border:1px solid #a30;
  725 + padding:5px;
  726 + list-style:none;
  727 + font-weight: bold;
  728 + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
  729 +
  730 + li {
  731 + padding:10px;
  732 + }
  733 +}
  734 +
  735 +.notice_holder {
  736 + background:#DDF4FB;
  737 + color:#444;
  738 + border:1px solid #C6EDF9;
  739 + @include round-borders-all(4px);
  740 + padding:5px;
  741 + list-style:none;
  742 + font-weight: bold;
  743 + text-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25);
  744 +
  745 + li {
  746 + padding:10px;
  747 + }
739 748 }
740 749  
  750 +.alert_holder {
  751 + background:#FDF5D9;
  752 + color:#444;
  753 + border:1px solid #FCEEC1;
  754 + @include round-borders-all(4px);
  755 + padding:5px;
  756 + list-style:none;
  757 + font-weight: bold;
  758 + text-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25);
  759 +
  760 + li {
  761 + padding:10px;
  762 + }
  763 +}
  764 +
  765 +.help_content {
  766 + margin:20px;
  767 + margin-top:71px;
  768 +
  769 + h2 {
  770 + margin:0;
  771 + padding:0;
  772 + }
  773 +
  774 + .menu {
  775 + float:left;
  776 + width:20%;
  777 +
  778 + .active {
  779 + color: $active_bd_color;
  780 + }
  781 + }
  782 +
  783 + .content {
  784 + float:right;
  785 + width:78%;
  786 + }
  787 +
  788 + .bash {
  789 + @include round-borders-all(4px);
  790 + background:#eee;
  791 + padding:5px;
  792 + //overflow-x:scroll;
  793 + pre{
  794 + padding:0;
  795 + line-height:2.0;
  796 + margin:0;
  797 + font-family: 'Courier New', 'andale mono','lucida console',monospace;
  798 + color: #333;
  799 + text-align:left;
  800 + }
  801 + }
  802 +}
... ...
app/assets/stylesheets/top_panel.scss 0 → 100644
... ... @@ -0,0 +1,146 @@
  1 +.main_links {
  2 + width:130px;
  3 + float:left;
  4 +
  5 + a {
  6 + float:left;
  7 + }
  8 +}
  9 +
  10 +.dashboard_links {
  11 + padding:7px;
  12 + float:left;
  13 + a {
  14 + margin: 0 14px;
  15 + float: left;
  16 + font-size: 14px;
  17 +
  18 + &.active {
  19 + color:$active_link_color;
  20 + }
  21 + &:hover {
  22 + color:$active_link_color;
  23 + }
  24 + }
  25 +}
  26 +
  27 +.top-tabs {
  28 + margin: 0;
  29 + padding: 5px;
  30 + font-size: 14px;
  31 + padding-bottom:10px;
  32 + margin-bottom:20px;
  33 + height:26px;
  34 + border-bottom:1px solid #ccc;
  35 +
  36 + .tab {
  37 + font-weight: bold;
  38 + background:none;
  39 + padding: 10px;
  40 + float:left;
  41 + padding-left:0px;
  42 + padding-right:40px;
  43 +
  44 + &.active {
  45 + color: $active_link_color;
  46 + }
  47 + }
  48 +}
  49 +
  50 +body header {
  51 + position:absolute;
  52 + width:100%;
  53 + padding:0;
  54 + margin:0;
  55 + top:0;
  56 + left:0;
  57 + background: #999; /* for non-css3 browsers */
  58 + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#EAEAEA'); /* for IE */
  59 + background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#EAEAEA)); /* for webkit browsers */
  60 + background: -moz-linear-gradient(top, #FFFFFF, #EAEAEA); /* for firefox 3.6+ */
  61 + background: -o-linear-gradient(top, #FFFFFF, #EAEAEA); /* for firefox 3.6+ */
  62 + border-bottom: 1px solid #ccc;
  63 +
  64 + height:50px;
  65 +
  66 + .wrapper {
  67 + margin:auto;
  68 + width:$app_width;
  69 + position:relative;
  70 +
  71 + .top_panel_content {
  72 + padding:10px $app_padding;
  73 + }
  74 + }
  75 +
  76 + .project_name {
  77 + float:left;
  78 + width:235px;
  79 + margin-right:30px;
  80 + font-size:16px;
  81 + font-weight:bold;
  82 + padding:8px;
  83 + color:#333;
  84 + }
  85 +
  86 + .git_url_wrapper {
  87 + padding:0px;
  88 + margin:0px;
  89 + float:left;
  90 +
  91 + .git-url {
  92 + padding:0px;
  93 + margin:0px;
  94 + font-size: 12px;
  95 +
  96 + margin-right:10px;
  97 + border-radius: 4px;
  98 + -moz-border-radius: 4px;
  99 +
  100 +
  101 + color: #666;
  102 + border: 1px solid #AAA;
  103 + padding: 0 10px 0 30px;
  104 + background: transparent url('images.png') no-repeat 8px -42px;
  105 + width: 160px;
  106 + height:26px;
  107 + }
  108 + }
  109 +}
  110 +
  111 +.top_panel_holder .chzn-container {
  112 + position:relative;
  113 +
  114 + .chzn-drop {
  115 + margin:7px 0;
  116 + border: 1px solid #CCC;
  117 + min-width: 300px;
  118 +
  119 + .chzn-results {
  120 + max-height:300px;
  121 + }
  122 + }
  123 +
  124 + .chzn-single {
  125 + background:transparent;
  126 + -moz-border-radius: 4px;
  127 + border-radius: 4px;
  128 +
  129 + div {
  130 + background:transparent;
  131 + border-left:none;
  132 + }
  133 +
  134 + span {
  135 + font-weight: normal;
  136 + }
  137 + }
  138 +}
  139 +
  140 +.rss-icon {
  141 + margin:0 15px;
  142 + padding:3px;
  143 + border:1px solid #AAA;
  144 + border-radius:3px;
  145 + float:left;
  146 +}
... ...
app/assets/stylesheets/tree.scss 0 → 100644
... ... @@ -0,0 +1,121 @@
  1 +#tree-breadcrumbs {
  2 + div {
  3 + margin:0;
  4 + margin-bottom:20px;
  5 + float:left;
  6 + font-size:14px;
  7 + }
  8 +}
  9 +
  10 +.tree_progress {
  11 + float:left;
  12 + width:16px;
  13 + height:16px;
  14 + margin:2px 6px;
  15 + &.loading {
  16 + background-position: 0px 0px;
  17 + background: url("ajax-loader-facebook.gif") no-repeat;
  18 + }
  19 +}
  20 +
  21 +
  22 +/** FILE CONTENT VIEW **/
  23 +.view_file_content{
  24 + .old_line, .new_line {
  25 + background:#ECECEC;
  26 + color:#777;
  27 + width:15px;
  28 + float:left;
  29 + padding: 0px 10px;
  30 + border-right: 1px solid #ccc;
  31 + }
  32 + .old_line{
  33 + display:none;
  34 + }
  35 +}
  36 +
  37 +.view_file .view_file_header,
  38 +.diff_file .diff_file_header {
  39 + background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
  40 + background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
  41 + background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
  42 + background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
  43 + margin: 0;
  44 + font-weight: normal;
  45 + font-weight: bold;
  46 + text-align: left;
  47 + color: #666;
  48 + border-bottom: 1px solid #DEE2E3;
  49 + padding: 7px 10px;
  50 +
  51 + .mode_text,
  52 + .file_icon {
  53 + margin-right:15px;
  54 + padding-right:15px;
  55 + border-right:1px solid $lite_border_color;
  56 + float:left;
  57 + color:#aaa;
  58 + }
  59 +
  60 + .file_icon {
  61 + padding-left:15px;
  62 + }
  63 +}
  64 +
  65 +.view_file {
  66 + border:1px solid #CCC;
  67 + margin-bottom:1em;
  68 +
  69 + .view_file_content {
  70 + background:#fff;
  71 + color:#514721;
  72 + font-size: 11px;
  73 + }
  74 + .view_file_content_image {
  75 + background:#eee;
  76 + text-align:center;
  77 + img {
  78 + padding:100px;
  79 + max-width:300px;
  80 + }
  81 + }
  82 +}
  83 +
  84 +td.code {
  85 + width: 100%;
  86 + .highlight {
  87 + margin-left: 55px;
  88 + overflow:auto;
  89 + overflow-y:hidden;
  90 + border-left: 1px solid #DEE2E3;
  91 + background: white;
  92 + }
  93 +}
  94 +.highlight pre {
  95 + white-space: pre;
  96 + word-wrap:normal;
  97 +}
  98 +
  99 +table.highlighttable {
  100 + border: none;
  101 + background: #F7F7F7;
  102 +}
  103 +body.project-page table.highlighttable td { border: none }
  104 +table.highlighttable tr:hover { background:none;}
  105 +
  106 +table.highlighttable pre{
  107 + line-height:16px !important;
  108 + font-size:12px !important;
  109 +}
  110 +
  111 +table.highlighttable .linenodiv pre {
  112 + text-align: right;
  113 + padding-right: 4px;
  114 + color:#888;
  115 +}
  116 +
  117 +.tree-item {
  118 + &:hover {
  119 + background: #FFFFCF;
  120 + }
  121 +}
... ...
app/controllers/admin/projects_controller.rb
... ... @@ -9,6 +9,12 @@ class Admin::ProjectsController &lt; ApplicationController
9 9  
10 10 def show
11 11 @admin_project = Project.find_by_code(params[:id])
  12 +
  13 + @users = if @admin_project.users.empty?
  14 + User
  15 + else
  16 + User.not_in_project(@admin_project)
  17 + end.all
12 18 end
13 19  
14 20 def new
... ... @@ -19,6 +25,19 @@ class Admin::ProjectsController &lt; ApplicationController
19 25 @admin_project = Project.find_by_code(params[:id])
20 26 end
21 27  
  28 + def team_update
  29 + @admin_project = Project.find_by_code(params[:id])
  30 +
  31 + UsersProject.bulk_import(
  32 + @admin_project,
  33 + params[:user_ids],
  34 + params[:project_access],
  35 + params[:repo_access]
  36 + )
  37 +
  38 + redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.'
  39 + end
  40 +
22 41 def create
23 42 @admin_project = Project.new(params[:project])
24 43 @admin_project.owner = current_user
... ...
app/controllers/admin/users_controller.rb
... ... @@ -27,7 +27,6 @@ class Admin::UsersController &lt; ApplicationController
27 27  
28 28 respond_to do |format|
29 29 if @admin_user.save
30   - Notify.new_user_email(@admin_user, params[:user][:password]).deliver
31 30 format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully created.' }
32 31 format.json { render json: @admin_user, status: :created, location: @admin_user }
33 32 else
... ... @@ -39,7 +38,7 @@ class Admin::UsersController &lt; ApplicationController
39 38  
40 39 def update
41 40 admin = params[:user].delete("admin")
42   - if params[:user][:password].empty?
  41 + if params[:user][:password].blank?
43 42 params[:user].delete(:password)
44 43 params[:user].delete(:password_confirmation)
45 44 end
... ...
app/controllers/application_controller.rb
1 1 class ApplicationController < ActionController::Base
2 2 before_filter :authenticate_user!
  3 + before_filter :set_current_user_for_mailer
3 4 protect_from_forgery
4 5 helper_method :abilities, :can?
5 6  
... ... @@ -19,6 +20,10 @@ class ApplicationController &lt; ActionController::Base
19 20 end
20 21 end
21 22  
  23 + def set_current_user_for_mailer
  24 + MailerObserver.current_user = current_user
  25 + end
  26 +
22 27 def abilities
23 28 @abilities ||= Six.new
24 29 end
... ...
app/controllers/commits_controller.rb
... ... @@ -27,6 +27,8 @@ class CommitsController &lt; ApplicationController
27 27 @notes = project.commit_notes(@commit).fresh.limit(20)
28 28 @note = @project.build_commit_note(@commit)
29 29  
  30 + @line_notes = project.commit_line_notes(@commit)
  31 +
30 32 respond_to do |format|
31 33 format.html
32 34 format.js { respond_with_notes }
... ...
app/controllers/dashboard_controller.rb
1 1 class DashboardController < ApplicationController
  2 + respond_to :html
  3 +
2 4 def index
3 5 @projects = current_user.projects.all
4   - @active_projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse
  6 + @active_projects = @projects.select(&:repo_exists?).select(&:last_activity_date_cached).sort_by(&:last_activity_date_cached).reverse
  7 + end
  8 +
  9 + # Get authored or assigned open merge requests
  10 + def merge_requests
  11 + @projects = current_user.projects.all
  12 + @merge_requests = MergeRequest.where("author_id = :id or assignee_id = :id", :id => current_user.id).opened.order("created_at DESC").limit(40)
  13 + end
  14 +
  15 + # Get only assigned issues
  16 + def issues
  17 + @projects = current_user.projects.all
  18 + @user = current_user
  19 + @issues = current_user.assigned_issues.opened.order("created_at DESC").limit(40)
  20 +
  21 + @issues = @issues.includes(:author, :project)
  22 +
  23 + respond_to do |format|
  24 + format.html
  25 + format.atom { render :layout => false }
  26 + end
5 27 end
6 28 end
... ...
app/controllers/deploy_keys_controller.rb 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +class DeployKeysController < ApplicationController
  2 + respond_to :html
  3 + layout "project"
  4 + before_filter :project
  5 +
  6 + # Authorize
  7 + before_filter :add_project_abilities
  8 + before_filter :authorize_admin_project!
  9 +
  10 + def project
  11 + @project ||= Project.find_by_code(params[:project_id])
  12 + end
  13 +
  14 + def index
  15 + @keys = @project.deploy_keys.all
  16 + end
  17 +
  18 + def show
  19 + @key = @project.deploy_keys.find(params[:id])
  20 + end
  21 +
  22 + def new
  23 + @key = @project.deploy_keys.new
  24 +
  25 + respond_with(@key)
  26 + end
  27 +
  28 + def create
  29 + @key = @project.deploy_keys.new(params[:key])
  30 + if @key.save
  31 + redirect_to project_deploy_keys_path(@project)
  32 + else
  33 + render "new"
  34 + end
  35 + end
  36 +
  37 + def destroy
  38 + @key = @project.deploy_keys.find(params[:id])
  39 + @key.destroy
  40 +
  41 + respond_to do |format|
  42 + format.html { redirect_to project_deploy_keys_url }
  43 + format.js { render :nothing => true }
  44 + end
  45 + end
  46 +end
... ...
app/controllers/help_controller.rb 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +class HelpController < ApplicationController
  2 + def index
  3 + end
  4 +end
... ...
app/controllers/hooks_controller.rb 0 → 100644
... ... @@ -0,0 +1,51 @@
  1 +class HooksController < ApplicationController
  2 + before_filter :authenticate_user!
  3 + before_filter :project
  4 + layout "project"
  5 +
  6 + # Authorize
  7 + before_filter :add_project_abilities
  8 + before_filter :authorize_read_project!
  9 + before_filter :authorize_admin_project!, :only => [:new, :create, :destroy]
  10 +
  11 + respond_to :html
  12 +
  13 + def index
  14 + @hooks = @project.web_hooks
  15 + end
  16 +
  17 + def new
  18 + @hook = @project.web_hooks.new
  19 + end
  20 +
  21 + def create
  22 + @hook = @project.web_hooks.new(params[:hook])
  23 + @hook.save
  24 +
  25 + if @hook.valid?
  26 + redirect_to project_hook_path(@project, @hook)
  27 + else
  28 + render :new
  29 + end
  30 + end
  31 +
  32 + def test
  33 + @hook = @project.web_hooks.find(params[:id])
  34 + commits = @project.commits(@project.default_branch, nil, 3)
  35 + data = @project.web_hook_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}")
  36 + @hook.execute(data)
  37 +
  38 + redirect_to :back
  39 + end
  40 +
  41 + def show
  42 + @hook = @project.web_hooks.find(params[:id])
  43 + end
  44 +
  45 + def destroy
  46 + @hook = @project.web_hooks.find(params[:id])
  47 + @hook.destroy
  48 +
  49 + redirect_to project_hooks_path(@project)
  50 + end
  51 +end
... ...
app/controllers/issues_controller.rb
... ... @@ -6,8 +6,18 @@ class IssuesController &lt; ApplicationController
6 6  
7 7 # Authorize
8 8 before_filter :add_project_abilities
  9 +
  10 + # Allow read any issue
9 11 before_filter :authorize_read_issue!
10   - before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update, :sort]
  12 +
  13 + # Allow write(create) issue
  14 + before_filter :authorize_write_issue!, :only => [:new, :create]
  15 +
  16 + # Allow modify issue
  17 + before_filter :authorize_modify_issue!, :only => [:close, :edit, :update, :sort]
  18 +
  19 + # Allow destroy issue
  20 + before_filter :authorize_admin_issue!, :only => [:destroy]
11 21  
12 22 respond_to :js, :html
13 23  
... ... @@ -57,10 +67,7 @@ class IssuesController &lt; ApplicationController
57 67 def create
58 68 @issue = @project.issues.new(params[:issue])
59 69 @issue.author = current_user
60   -
61   - if @issue.save && @issue.assignee != current_user
62   - Notify.new_issue_email(@issue).deliver
63   - end
  70 + @issue.save
64 71  
65 72 respond_with(@issue)
66 73 end
... ... @@ -80,6 +87,7 @@ class IssuesController &lt; ApplicationController
80 87 @issue.destroy
81 88  
82 89 respond_to do |format|
  90 + format.html { redirect_to project_issues_path }
83 91 format.js { render :nothing => true }
84 92 end
85 93 end
... ... @@ -115,4 +123,13 @@ class IssuesController &lt; ApplicationController
115 123 def issue
116 124 @issue ||= @project.issues.find(params[:id])
117 125 end
  126 +
  127 + def authorize_modify_issue!
  128 + can?(current_user, :modify_issue, @issue) ||
  129 + @issue.assignee == current_user
  130 + end
  131 +
  132 + def authorize_admin_issue!
  133 + can?(current_user, :admin_issue, @issue)
  134 + end
118 135 end
... ...
app/controllers/keys_controller.rb
... ... @@ -6,6 +6,10 @@ class KeysController &lt; ApplicationController
6 6 @keys = current_user.keys.all
7 7 end
8 8  
  9 + def show
  10 + @key = current_user.keys.find(params[:id])
  11 + end
  12 +
9 13 def new
10 14 @key = current_user.keys.new
11 15  
... ...
app/controllers/merge_requests_controller.rb
... ... @@ -6,11 +6,28 @@ class MergeRequestsController &lt; ApplicationController
6 6  
7 7 # Authorize
8 8 before_filter :add_project_abilities
9   - before_filter :authorize_read_project!
10   - before_filter :authorize_write_project!, :only => [:new, :create, :edit, :update]
  9 +
  10 + # Allow read any merge_request
  11 + before_filter :authorize_read_merge_request!
  12 +
  13 + # Allow write(create) merge_request
  14 + before_filter :authorize_write_merge_request!, :only => [:new, :create]
  15 +
  16 + # Allow modify merge_request
  17 + before_filter :authorize_modify_merge_request!, :only => [:close, :edit, :update, :sort]
  18 +
  19 + # Allow destroy merge_request
  20 + before_filter :authorize_admin_merge_request!, :only => [:destroy]
11 21  
12 22 def index
13 23 @merge_requests = @project.merge_requests
  24 +
  25 + @merge_requests = case params[:f].to_i
  26 + when 2 then @merge_requests.closed
  27 + else @merge_requests.opened
  28 + end
  29 +
  30 + @merge_requests = @merge_requests.includes(:author, :project)
14 31 end
15 32  
16 33 def show
... ... @@ -30,14 +47,12 @@ class MergeRequestsController &lt; ApplicationController
30 47  
31 48 def commits
32 49 @commits = @project.repo.commits_between(@merge_request.target_branch, @merge_request.source_branch).map {|c| Commit.new(c)}
33   - render :template => "merge_requests/_commits", :layout => false
34 50 end
35 51  
36 52 def diffs
37 53 @diffs = @merge_request.diffs
38 54 @commit = @merge_request.last_commit
39   -
40   - render :template => "merge_requests/_diffs", :layout => false
  55 + @line_notes = []
41 56 end
42 57  
43 58 def new
... ... @@ -88,4 +103,13 @@ class MergeRequestsController &lt; ApplicationController
88 103 def merge_request
89 104 @merge_request ||= @project.merge_requests.find(params[:id])
90 105 end
  106 +
  107 + def authorize_modify_merge_request!
  108 + can?(current_user, :modify_merge_request, @merge_request) ||
  109 + @merge_request.assignee == current_user
  110 + end
  111 +
  112 + def authorize_admin_merge_request!
  113 + can?(current_user, :admin_merge_request, @merge_request)
  114 + end
91 115 end
... ...
app/controllers/notes_controller.rb
... ... @@ -3,6 +3,8 @@ class NotesController &lt; ApplicationController
3 3  
4 4 # Authorize
5 5 before_filter :add_project_abilities
  6 +
  7 + before_filter :authorize_read_note!
6 8 before_filter :authorize_write_note!, :only => [:create]
7 9  
8 10 respond_to :js
... ... @@ -10,10 +12,9 @@ class NotesController &lt; ApplicationController
10 12 def create
11 13 @note = @project.notes.new(params[:note])
12 14 @note.author = current_user
13   -
14   - if @note.save
15   - notify if params[:notify] == '1'
16   - end
  15 + @note.notify = true if params[:notify] == '1'
  16 + @note.notify_author = true if params[:notify_author] == '1'
  17 + @note.save
17 18  
18 19 respond_to do |format|
19 20 format.html {redirect_to :back}
... ... @@ -33,22 +34,4 @@ class NotesController &lt; ApplicationController
33 34 end
34 35 end
35 36  
36   - protected
37   -
38   - def notify
39   - @project.users.reject { |u| u.id == current_user.id } .each do |u|
40   - case @note.noteable_type
41   - when "Commit" then
42   - Notify.note_commit_email(u, @note).deliver
43   - when "Issue" then
44   - Notify.note_issue_email(u, @note).deliver
45   - when "MergeRequest"
46   - true # someone should write email notification
47   - when "Snippet"
48   - true
49   - else
50   - Notify.note_wall_email(u, @note).deliver
51   - end
52   - end
53   - end
54 37 end
... ...
app/controllers/profile_controller.rb
... ... @@ -4,10 +4,14 @@ class ProfileController &lt; ApplicationController
4 4 @user = current_user
5 5 end
6 6  
7   - def social_update
  7 + def design
  8 + @user = current_user
  9 + end
  10 +
  11 + def update
8 12 @user = current_user
9 13 @user.update_attributes(params[:user])
10   - redirect_to [:profile]
  14 + redirect_to :back
11 15 end
12 16  
13 17 def password
... ...
app/controllers/projects_controller.rb
... ... @@ -9,12 +9,10 @@ class ProjectsController &lt; ApplicationController
9 9 before_filter :authorize_read_project!, :except => [:index, :new, :create]
10 10 before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy]
11 11 before_filter :require_non_empty_project, :only => [:blob, :tree, :graph]
12   - before_filter :load_refs, :only => :tree # load @branch, @tag & @ref
13 12  
14 13 def index
15   - source = current_user.projects
16   - source = source.tagged_with(params[:tag]) unless params[:tag].blank?
17   - @projects = source.all
  14 + @limit, @offset = (params[:limit] || 16), (params[:offset] || 0)
  15 + @projects = current_user.projects.limit(@limit).offset(@offset)
18 16 end
19 17  
20 18 def new
... ... @@ -59,7 +57,7 @@ class ProjectsController &lt; ApplicationController
59 57 def update
60 58 respond_to do |format|
61 59 if project.update_attributes(params[:project])
62   - format.html { redirect_to project, :notice => 'Project was successfully updated.' }
  60 + format.html { redirect_to info_project_path(project), :notice => 'Project was successfully updated.' }
63 61 format.js
64 62 else
65 63 format.html { render action: "edit" }
... ... @@ -71,7 +69,14 @@ class ProjectsController &lt; ApplicationController
71 69 def show
72 70 return render "projects/empty" unless @project.repo_exists? && @project.has_commits?
73 71 limit = (params[:limit] || 20).to_i
74   - @activities = @project.cached_updates(limit)
  72 + @activities = @project.activities(limit)#updates_wo_repo(limit)
  73 + end
  74 +
  75 + def files
  76 + @notes = @project.notes.where("attachment != 'NULL'").order("created_at DESC").limit(100)
  77 + end
  78 +
  79 + def info
75 80 end
76 81  
77 82 #
... ... @@ -94,7 +99,11 @@ class ProjectsController &lt; ApplicationController
94 99 end
95 100  
96 101 def destroy
  102 + # Disable the UsersProject update_repository call, otherwise it will be
  103 + # called once for every person removed from the project
  104 + UsersProject.skip_callback(:destroy, :after, :update_repository)
97 105 project.destroy
  106 + UsersProject.set_callback(:destroy, :after, :update_repository)
98 107  
99 108 respond_to do |format|
100 109 format.html { redirect_to projects_url }
... ...
app/controllers/repositories_controller.rb 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +class RepositoriesController < ApplicationController
  2 + before_filter :project
  3 +
  4 + # Authorize
  5 + before_filter :add_project_abilities
  6 + before_filter :authorize_read_project!
  7 + before_filter :require_non_empty_project
  8 +
  9 + layout "project"
  10 +
  11 + def show
  12 + @activities = @project.commits_with_refs(20)
  13 + end
  14 +
  15 + def branches
  16 + @branches = @project.repo.heads.sort_by(&:name)
  17 + end
  18 +
  19 + def tags
  20 + @tags = @project.repo.tags.sort_by(&:name).reverse
  21 + end
  22 +end
... ...
app/controllers/snippets_controller.rb
... ... @@ -5,8 +5,18 @@ class SnippetsController &lt; ApplicationController
5 5  
6 6 # Authorize
7 7 before_filter :add_project_abilities
  8 +
  9 + # Allow read any snippet
8 10 before_filter :authorize_read_snippet!
9   - before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort]
  11 +
  12 + # Allow write(create) snippet
  13 + before_filter :authorize_write_snippet!, :only => [:new, :create]
  14 +
  15 + # Allow modify snippet
  16 + before_filter :authorize_modify_snippet!, :only => [:edit, :update]
  17 +
  18 + # Allow destroy snippet
  19 + before_filter :authorize_admin_snippet!, :only => [:destroy]
10 20  
11 21 respond_to :html
12 22  
... ... @@ -60,4 +70,14 @@ class SnippetsController &lt; ApplicationController
60 70  
61 71 redirect_to project_snippets_path(@project)
62 72 end
  73 +
  74 + protected
  75 +
  76 + def authorize_modify_snippet!
  77 + can?(current_user, :modify_snippet, @snippet)
  78 + end
  79 +
  80 + def authorize_admin_snippet!
  81 + can?(current_user, :admin_snippet, @snippet)
  82 + end
63 83 end
... ...
app/controllers/team_members_controller.rb
... ... @@ -5,7 +5,7 @@ class TeamMembersController &lt; ApplicationController
5 5 # Authorize
6 6 before_filter :add_project_abilities
7 7 before_filter :authorize_read_project!
8   - before_filter :authorize_admin_project!, :only => [:new, :create, :destroy, :update]
  8 + before_filter :authorize_admin_project!, :except => [:show]
9 9  
10 10 def show
11 11 @team_member = project.users_projects.find(params[:id])
... ... @@ -18,7 +18,11 @@ class TeamMembersController &lt; ApplicationController
18 18 def create
19 19 @team_member = UsersProject.new(params[:team_member])
20 20 @team_member.project = project
21   - @team_member.save
  21 + if @team_member.save
  22 + redirect_to team_project_path(@project)
  23 + else
  24 + render "new"
  25 + end
22 26 end
23 27  
24 28 def update
... ...
app/decorators/tree_decorator.rb
... ... @@ -6,7 +6,7 @@ class TreeDecorator &lt; ApplicationDecorator
6 6 part_path = ""
7 7 parts = path.split("\/")
8 8  
9   - parts = parts[0...-1] if is_blob?
  9 + #parts = parts[0...-1] if is_blob?
10 10  
11 11 yield(h.link_to("..", "#", :remote => :true)) if parts.count > max_links
12 12  
... ... @@ -32,4 +32,13 @@ class TreeDecorator &lt; ApplicationDecorator
32 32 def history_path
33 33 h.project_commits_path(project, :path => path, :ref => ref)
34 34 end
  35 +
  36 + def mb_size
  37 + size = (tree.size / 1024)
  38 + if size < 1024
  39 + "#{size} KB"
  40 + else
  41 + "#{size/1024} MB"
  42 + end
  43 + end
35 44 end
... ...
app/helpers/application_helper.rb
1 1 require 'digest/md5'
2 2 module ApplicationHelper
3 3  
4   - def gravatar_icon(user_email)
  4 + def gravatar_icon(user_email, size = 40)
5 5 gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com"
6   - "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon"
  6 + "#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=#{size}&d=identicon"
7 7 end
8 8  
9 9 def fixed_mode?
... ... @@ -48,11 +48,11 @@ module ApplicationHelper
48 48  
49 49 def grouped_options_refs(destination = :tree)
50 50 options = [
51   - ["Branch", @repo.heads.map(&:name) ],
  51 + ["Branch", @project.repo.heads.map(&:name) ],
52 52 [ "Tag", @project.tags ]
53 53 ]
54 54  
55   - grouped_options_for_select(options, @ref)
  55 + grouped_options_for_select(options, @ref || @project.default_branch)
56 56 end
57 57  
58 58 def markdown(text)
... ... @@ -82,4 +82,15 @@ module ApplicationHelper
82 82 [projects, default_nav, project_nav].flatten.to_json
83 83 end
84 84  
  85 + def project_layout
  86 + @project && !@project.new_record?
  87 + end
  88 +
  89 + def profile_layout
  90 + controller.controller_name == "dashboard" || current_page?(projects_path) || controller.controller_name == "profile" || controller.controller_name == "keys"
  91 + end
  92 +
  93 + def help_layout
  94 + controller.controller_name == "help"
  95 + end
85 96 end
... ...
app/helpers/commits_helper.rb
1 1 module CommitsHelper
2   - include Utils::CharEncode
3   -
4 2 def old_line_number(line, i)
5 3  
6 4 end
... ... @@ -25,4 +23,30 @@ module CommitsHelper
25 23 link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit),
26 24 :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link"
27 25 end
  26 +
  27 + def commit_msg_with_link_to_issues(project, message)
  28 + return '' unless message
  29 + out = ''
  30 + message.split(/(#[0-9]+)/m).each do |m|
  31 + if m =~ /(#([0-9]+))/m
  32 + begin
  33 + issue = project.issues.find($2)
  34 + out += link_to($1, project_issue_path(project, $2))
  35 + rescue
  36 + out += $1
  37 + end
  38 + else
  39 + out += m
  40 + end
  41 + end
  42 + preserve out
  43 + end
  44 +
  45 + def build_line_code(line, index, line_new, line_old)
  46 + if diff_line_class(line) == "new"
  47 + "NEW_#{index}_#{line_new}"
  48 + else
  49 + "OLD_#{index}_#{line_old}"
  50 + end
  51 + end
28 52 end
... ...
app/helpers/dashboard_helper.rb
... ... @@ -10,6 +10,7 @@ module DashboardHelper
10 10 when "Issue" then project_issue_path(project, note.noteable_id)
11 11 when "Snippet" then project_snippet_path(project, note.noteable_id)
12 12 when "Commit" then project_commit_path(project, :id => note.noteable_id)
  13 + when "MergeRequest" then project_merge_request_path(project, note.noteable_id)
13 14 else wall_project_path(project)
14 15 end
15 16 else wall_project_path(project)
... ...
app/helpers/projects_helper.rb
... ... @@ -16,12 +16,26 @@ module ProjectsHelper
16 16 nil
17 17 end
18 18  
19   - # expires in 360 days
20   - def switch_colorscheme_link(opts)
21   - if cookies[:colorschema].blank?
22   - link_to_function "paint it black!", "$.cookie('colorschema','black', {expires:360}); window.location.reload()", opts
23   - else
24   - link_to_function "paint it white!", "$.cookie('colorschema','', {expires:360}); window.location.reload()", opts
  19 + def project_tab_class
  20 + [:show, :files, :team, :edit, :update, :info].each do |action|
  21 + return "current" if current_page?(:controller => "projects", :action => action, :id => @project)
  22 + end
  23 +
  24 + if controller.controller_name == "snippets" ||
  25 + controller.controller_name == "team_members"
  26 + "current"
  27 + end
  28 + end
  29 +
  30 + def tree_tab_class
  31 + controller.controller_name == "refs" ?
  32 + "current" : nil
  33 + end
  34 +
  35 + def repository_tab_class
  36 + if controller.controller_name == "repositories" ||
  37 + controller.controller_name == "hooks"
  38 + "current"
25 39 end
26 40 end
27 41 end
... ...
app/helpers/user_issues_helper.rb 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 +module UserIssuesHelper
  2 +end
... ...
app/helpers/user_merge_requests_helper.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +module UserMergeRequestsHelper
  2 +end
  3 +
... ...
app/mailers/notify.rb
... ... @@ -28,7 +28,16 @@ class Notify &lt; ActionMailer::Base
28 28 @note = note
29 29 @project = note.project
30 30 @commit = @project.repo.commits(note.noteable_id).first
31   - mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ")
  31 + return unless ( note.notify or ( note.notify_author and @commit.author.email == @user.email ) )
  32 + mail(:to => @user.email, :subject => "gitlab | note for commit | #{@note.project.name} ")
  33 + end
  34 +
  35 + def note_merge_request_email(user, note)
  36 + @user = user
  37 + @note = note
  38 + @project = note.project
  39 + @merge_request = note.noteable
  40 + mail(:to => @user.email, :subject => "gitlab | note for merge request | #{@note.project.name} ")
32 41 end
33 42  
34 43 def note_issue_email(user, note)
... ... @@ -36,6 +45,29 @@ class Notify &lt; ActionMailer::Base
36 45 @note = note
37 46 @project = note.project
38 47 @issue = note.noteable
39   - mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ")
  48 + mail(:to => @user.email, :subject => "gitlab | note for issue #{@issue.id} | #{@note.project.name} ")
  49 + end
  50 +
  51 + def new_merge_request_email(merge_request)
  52 + @user = merge_request.assignee
  53 + @merge_request = merge_request
  54 + @project = merge_request.project
  55 + mail(:to => @user.email, :subject => "gitlab | new merge request | #{@merge_request.title} ")
  56 + end
  57 +
  58 + def changed_merge_request_email(user, merge_request)
  59 + @user = user
  60 + @assignee_was ||= User.find(merge_request.assignee_id_was)
  61 + @merge_request = merge_request
  62 + @project = merge_request.project
  63 + mail(:to => @user.email, :subject => "gitlab | merge request changed | #{@merge_request.title} ")
  64 + end
  65 +
  66 + def changed_issue_email(user, issue)
  67 + @user = user
  68 + @assignee_was ||= User.find(issue.assignee_id_was)
  69 + @issue = issue
  70 + @project = issue.project
  71 + mail(:to => @user.email, :subject => "gitlab | changed issue | #{@issue.title} ")
40 72 end
41 73 end
... ...
app/models/ability.rb
... ... @@ -19,7 +19,7 @@ class Ability
19 19 :read_team_member,
20 20 :read_merge_request,
21 21 :read_note
22   - ] if project.readers.include?(user)
  22 + ] if project.allow_read_for?(user)
23 23  
24 24 rules << [
25 25 :write_project,
... ... @@ -27,16 +27,18 @@ class Ability
27 27 :write_snippet,
28 28 :write_merge_request,
29 29 :write_note
30   - ] if project.writers.include?(user)
  30 + ] if project.allow_write_for?(user)
31 31  
32 32 rules << [
  33 + :modify_issue,
  34 + :modify_snippet,
33 35 :admin_project,
34 36 :admin_issue,
35 37 :admin_snippet,
36 38 :admin_team_member,
37 39 :admin_merge_request,
38 40 :admin_note
39   - ] if project.admins.include?(user)
  41 + ] if project.allow_admin_for?(user)
40 42  
41 43 rules.flatten
42 44 end
... ... @@ -48,6 +50,7 @@ class Ability
48 50 [
49 51 :"read_#{name}",
50 52 :"write_#{name}",
  53 + :"modify_#{name}",
51 54 :"admin_#{name}"
52 55 ]
53 56 else
... ...
app/models/commit.rb
1 1 class Commit
2   - include Utils::CharEncode
3 2  
4 3 attr_accessor :commit
5 4 attr_accessor :head
  5 + attr_accessor :refs
6 6  
7 7 delegate :message,
8 8 :committed_date,
... ... @@ -22,7 +22,7 @@ class Commit
22 22 end
23 23  
24 24 def safe_message
25   - encode(message)
  25 + message
26 26 end
27 27  
28 28 def created_at
... ... @@ -30,11 +30,11 @@ class Commit
30 30 end
31 31  
32 32 def author_email
33   - encode(author.email)
  33 + author.email
34 34 end
35 35  
36 36 def author_name
37   - encode(author.name)
  37 + author.name
38 38 end
39 39  
40 40 def prev_commit
... ...
app/models/issue.rb
... ... @@ -2,7 +2,7 @@ class Issue &lt; ActiveRecord::Base
2 2 belongs_to :project
3 3 belongs_to :author, :class_name => "User"
4 4 belongs_to :assignee, :class_name => "User"
5   - has_many :notes, :as => :noteable
  5 + has_many :notes, :as => :noteable, :dependent => :destroy
6 6  
7 7 attr_protected :author, :author_id, :project, :project_id
8 8  
... ... @@ -59,5 +59,6 @@ end
59 59 # closed :boolean default(FALSE), not null
60 60 # position :integer default(0)
61 61 # critical :boolean default(FALSE), not null
  62 +# branch_name :string(255)
62 63 #
63 64  
... ...
app/models/key.rb
1 1 class Key < ActiveRecord::Base
2 2 belongs_to :user
  3 + belongs_to :project
3 4  
4 5 validates :title,
5 6 :presence => true,
... ... @@ -15,32 +16,38 @@ class Key &lt; ActiveRecord::Base
15 16 after_destroy :repository_delete_key
16 17  
17 18 def set_identifier
18   - self.identifier = "#{user.identifier}_#{Time.now.to_i}"
  19 + if is_deploy_key
  20 + self.identifier = "deploy_#{project.code}_#{Time.now.to_i}"
  21 + else
  22 + self.identifier = "#{user.identifier}_#{Time.now.to_i}"
  23 + end
19 24 end
20 25  
21 26 def update_repository
22 27 Gitlabhq::GitHost.system.new.configure do |c|
23 28 c.update_keys(identifier, key)
24   -
25   - projects.each do |project|
26   - c.update_project(project.path, project)
27   - end
  29 + c.update_projects(projects)
28 30 end
29 31 end
30 32  
31 33 def repository_delete_key
32 34 Gitlabhq::GitHost.system.new.configure do |c|
33 35 c.delete_key(identifier)
34   -
35   - projects.each do |project|
36   - c.update_project(project.path, project)
37   - end
  36 + c.update_projects(projects)
38 37 end
39 38 end
  39 +
  40 + def is_deploy_key
  41 + true if project_id
  42 + end
40 43  
41 44 #projects that has this key
42 45 def projects
43   - user.projects
  46 + if is_deploy_key
  47 + [project]
  48 + else
  49 + user.projects
  50 + end
44 51 end
45 52 end
46 53 # == Schema Information
... ... @@ -48,11 +55,12 @@ end
48 55 # Table name: keys
49 56 #
50 57 # id :integer not null, primary key
51   -# user_id :integer not null
  58 +# user_id :integer
52 59 # created_at :datetime
53 60 # updated_at :datetime
54 61 # key :text
55 62 # title :string(255)
56 63 # identifier :string(255)
  64 +# project_id :integer
57 65 #
58 66  
... ...
app/models/mailer_observer.rb 0 → 100644
... ... @@ -0,0 +1,88 @@
  1 +class MailerObserver < ActiveRecord::Observer
  2 + observe :issue, :user, :note, :merge_request
  3 + cattr_accessor :current_user
  4 +
  5 + def after_create(model)
  6 + new_issue(model) if model.kind_of?(Issue)
  7 + new_user(model) if model.kind_of?(User)
  8 + new_note(model) if model.kind_of?(Note)
  9 + new_merge_request(model) if model.kind_of?(MergeRequest)
  10 + end
  11 +
  12 + def after_update(model)
  13 + changed_merge_request(model) if model.kind_of?(MergeRequest)
  14 + changed_issue(model) if model.kind_of?(Issue)
  15 + end
  16 +
  17 + protected
  18 +
  19 + def new_issue(issue)
  20 + if issue.assignee != current_user
  21 + Notify.new_issue_email(issue).deliver
  22 + end
  23 + end
  24 +
  25 + def new_user(user)
  26 + Notify.new_user_email(user, user.password).deliver
  27 + end
  28 +
  29 + def new_note(note)
  30 + return unless note.notify or note.notify_author
  31 + note.project.users.reject { |u| u.id == current_user.id } .each do |u|
  32 + case note.noteable_type
  33 + when "Commit" then
  34 + Notify.note_commit_email(u, note).deliver
  35 + when "Issue" then
  36 + Notify.note_issue_email(u, note).deliver
  37 + when "MergeRequest" then
  38 + Notify.note_merge_request_email(u, note).deliver
  39 + when "Snippet"
  40 + true
  41 + else
  42 + Notify.note_wall_email(u, note).deliver
  43 + end
  44 + end
  45 + end
  46 +
  47 + def new_merge_request(merge_request)
  48 + if merge_request.assignee != current_user
  49 + Notify.new_merge_request_email(merge_request).deliver
  50 + end
  51 + end
  52 +
  53 + def changed_merge_request(merge_request)
  54 + if merge_request.assignee_id_changed?
  55 + recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
  56 + recipients_ids.delete current_user.id
  57 +
  58 + User.find(recipients_ids).each do |user|
  59 + Notify.changed_merge_request_email(user, merge_request).deliver
  60 + end
  61 + end
  62 +
  63 + if merge_request.closed_changed?
  64 + note = Note.new(:noteable => merge_request, :project => merge_request.project)
  65 + note.author = current_user
  66 + note.note = "_Status changed to #{merge_request.closed ? 'closed' : 'reopened'}_"
  67 + note.save()
  68 + end
  69 + end
  70 +
  71 + def changed_issue(issue)
  72 + if issue.assignee_id_changed?
  73 + recipients_ids = issue.assignee_id_was, issue.assignee_id
  74 + recipients_ids.delete current_user.id
  75 +
  76 + User.find(recipients_ids).each do |user|
  77 + Notify.changed_issue_email(user, issue).deliver
  78 + end
  79 + end
  80 +
  81 + if issue.closed_changed?
  82 + note = Note.new(:noteable => issue, :project => issue.project)
  83 + note.author = current_user
  84 + note.note = "_Status changed to #{issue.closed ? 'closed' : 'reopened'}_"
  85 + note.save()
  86 + end
  87 + end
  88 +end
... ...
app/models/merge_request.rb
... ... @@ -2,7 +2,7 @@ class MergeRequest &lt; ActiveRecord::Base
2 2 belongs_to :project
3 3 belongs_to :author, :class_name => "User"
4 4 belongs_to :assignee, :class_name => "User"
5   - has_many :notes, :as => :noteable
  5 + has_many :notes, :as => :noteable, :dependent => :destroy
6 6  
7 7 attr_protected :author, :author_id, :project, :project_id
8 8  
... ... @@ -35,12 +35,27 @@ class MergeRequest &lt; ActiveRecord::Base
35 35 end
36 36  
37 37 def diffs
38   - commit = project.commit(source_branch)
39 38 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
40   - diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id)
  39 + diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
41 40 end
42 41  
43 42 def last_commit
44 43 project.commit(source_branch)
45 44 end
46 45 end
  46 +# == Schema Information
  47 +#
  48 +# Table name: merge_requests
  49 +#
  50 +# id :integer not null, primary key
  51 +# target_branch :string(255) not null
  52 +# source_branch :string(255) not null
  53 +# project_id :integer not null
  54 +# author_id :integer
  55 +# assignee_id :integer
  56 +# title :string(255)
  57 +# closed :boolean default(FALSE), not null
  58 +# created_at :datetime
  59 +# updated_at :datetime
  60 +#
  61 +
... ...
app/models/note.rb
... ... @@ -13,6 +13,8 @@ class Note &lt; ActiveRecord::Base
13 13 :prefix => true
14 14  
15 15 attr_protected :author, :author_id
  16 + attr_accessor :notify
  17 + attr_accessor :notify_author
16 18  
17 19 validates_presence_of :project
18 20  
... ... @@ -35,6 +37,43 @@ class Note &lt; ActiveRecord::Base
35 37 scope :inc_author, includes(:author)
36 38  
37 39 mount_uploader :attachment, AttachmentUploader
  40 +
  41 + def notify
  42 + @notify ||= false
  43 + end
  44 +
  45 + def notify_author
  46 + @notify_author ||= false
  47 + end
  48 +
  49 + def target
  50 + if noteable_type == "Commit"
  51 + project.commit(noteable_id)
  52 + else
  53 + noteable
  54 + end
  55 + # Temp fix to prevent app crash
  56 + # if note commit id doesnt exist
  57 + rescue
  58 + nil
  59 + end
  60 +
  61 + def line_file_id
  62 + @line_file_id ||= line_code.split("_")[1].to_i if line_code
  63 + end
  64 +
  65 + def line_type_id
  66 + @line_type_id ||= line_code.split("_").first if line_code
  67 + end
  68 +
  69 + def line_number
  70 + @line_number ||= line_code.split("_").last.to_i if line_code
  71 + end
  72 +
  73 + def for_line?(file_id, old_line, new_line)
  74 + line_file_id == file_id &&
  75 + ((line_type_id == "NEW" && line_number == new_line) || (line_type_id == "OLD" && line_number == old_line ))
  76 + end
38 77 end
39 78 # == Schema Information
40 79 #
... ... @@ -49,5 +88,6 @@ end
49 88 # updated_at :datetime
50 89 # project_id :integer
51 90 # attachment :string(255)
  91 +# line_code :string(255)
52 92 #
53 93  
... ...
app/models/project.rb
... ... @@ -14,6 +14,8 @@ class Project &lt; ActiveRecord::Base
14 14 has_many :users, :through => :users_projects
15 15 has_many :notes, :dependent => :destroy
16 16 has_many :snippets, :dependent => :destroy
  17 + has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
  18 + has_many :web_hooks, :dependent => :destroy
17 19  
18 20 acts_as_taggable
19 21  
... ... @@ -25,8 +27,8 @@ class Project &lt; ActiveRecord::Base
25 27 validates :path,
26 28 :uniqueness => true,
27 29 :presence => true,
28   - :format => { :with => /^[a-zA-Z0-9_\-]*$/,
29   - :message => "only letters, digits & '_' '-' allowed" },
  30 + :format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
  31 + :message => "only letters, digits & '_' '-' '.' allowed" },
30 32 :length => { :within => 0..255 }
31 33  
32 34 validates :description,
... ... @@ -35,8 +37,8 @@ class Project &lt; ActiveRecord::Base
35 37 validates :code,
36 38 :presence => true,
37 39 :uniqueness => true,
38   - :format => { :with => /^[a-zA-Z0-9_\-]*$/,
39   - :message => "only letters, digits & '_' '-' allowed" },
  40 + :format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
  41 + :message => "only letters, digits & '_' '-' '.' allowed" },
40 42 :length => { :within => 3..255 }
41 43  
42 44 validates :owner,
... ... @@ -52,6 +54,9 @@ class Project &lt; ActiveRecord::Base
52 54  
53 55 scope :public_only, where(:private_flag => false)
54 56  
  57 + def self.active
  58 + joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
  59 + end
55 60  
56 61 def self.access_options
57 62 {
... ... @@ -75,21 +80,76 @@ class Project &lt; ActiveRecord::Base
75 80 :repo_exists?,
76 81 :commit,
77 82 :commits,
  83 + :commits_with_refs,
78 84 :tree,
79 85 :heads,
80 86 :commits_since,
81 87 :fresh_commits,
  88 + :commits_between,
82 89 :to => :repository, :prefix => nil
83 90  
84 91 def to_param
85 92 code
86 93 end
87 94  
  95 + def web_url
  96 + [GIT_HOST['host'], code].join("/")
  97 + end
  98 +
  99 + def execute_web_hooks(oldrev, newrev, ref)
  100 + ref_parts = ref.split('/')
  101 +
  102 + # Return if this is not a push to a branch (e.g. new commits)
  103 + return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000"
  104 +
  105 + data = web_hook_data(oldrev, newrev, ref)
  106 + web_hooks.each { |web_hook| web_hook.execute(data) }
  107 + end
  108 +
  109 + def web_hook_data(oldrev, newrev, ref)
  110 + data = {
  111 + before: oldrev,
  112 + after: newrev,
  113 + ref: ref,
  114 + repository: {
  115 + name: name,
  116 + url: web_url,
  117 + description: description,
  118 + homepage: web_url,
  119 + private: private?
  120 + },
  121 + commits: []
  122 + }
  123 +
  124 + commits_between(oldrev, newrev).each do |commit|
  125 + data[:commits] << {
  126 + id: commit.id,
  127 + message: commit.safe_message,
  128 + timestamp: commit.date.xmlschema,
  129 + url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}",
  130 + author: {
  131 + name: commit.author_name,
  132 + email: commit.author_email
  133 + }
  134 + }
  135 + end
  136 +
  137 + data
  138 + end
  139 +
88 140 def team_member_by_name_or_email(email = nil, name = nil)
89 141 user = users.where("email like ? or name like ?", email, name).first
90 142 users_projects.find_by_user_id(user.id) if user
91 143 end
92 144  
  145 + def team_member_by_id(user_id)
  146 + users_projects.find_by_user_id(user_id)
  147 + end
  148 +
  149 + def fresh_merge_requests(n)
  150 + merge_requests.includes(:project, :author).order("created_at desc").first(n)
  151 + end
  152 +
93 153 def fresh_issues(n)
94 154 issues.includes(:project, :author).order("created_at desc").first(n)
95 155 end
... ... @@ -107,7 +167,11 @@ class Project &lt; ActiveRecord::Base
107 167 end
108 168  
109 169 def commit_notes(commit)
110   - notes.where(:noteable_id => commit.id, :noteable_type => "Commit")
  170 + notes.where(:noteable_id => commit.id, :noteable_type => "Commit", :line_code => nil)
  171 + end
  172 +
  173 + def commit_line_notes(commit)
  174 + notes.where(:noteable_id => commit.id, :noteable_type => "Commit").where("line_code is not null")
111 175 end
112 176  
113 177 def has_commits?
... ... @@ -136,7 +200,7 @@ class Project &lt; ActiveRecord::Base
136 200 def repository_readers
137 201 keys = Key.joins({:user => :users_projects}).
138 202 where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R)
139   - keys.map(&:identifier)
  203 + keys.map(&:identifier) + deploy_keys.map(&:identifier)
140 204 end
141 205  
142 206 def repository_writers
... ... @@ -157,6 +221,18 @@ class Project &lt; ActiveRecord::Base
157 221 @admins ||= users_projects.includes(:user).where(:project_access => PROJECT_RWA).map(&:user)
158 222 end
159 223  
  224 + def allow_read_for?(user)
  225 + !users_projects.where(:user_id => user.id, :project_access => [PROJECT_R, PROJECT_RW, PROJECT_RWA]).empty?
  226 + end
  227 +
  228 + def allow_write_for?(user)
  229 + !users_projects.where(:user_id => user.id, :project_access => [PROJECT_RW, PROJECT_RWA]).empty?
  230 + end
  231 +
  232 + def allow_admin_for?(user)
  233 + !users_projects.where(:user_id => user.id, :project_access => [PROJECT_RWA]).empty? || owner_id == user.id
  234 + end
  235 +
160 236 def root_ref
161 237 default_branch || "master"
162 238 end
... ... @@ -179,6 +255,24 @@ class Project &lt; ActiveRecord::Base
179 255 last_activity.try(:created_at)
180 256 end
181 257  
  258 + def last_activity_date_cached(expire = 1.hour)
  259 + activity_date_key = "project_#{id}_activity_date"
  260 +
  261 + cached_activities = Rails.cache.read(activity_date_key)
  262 + if cached_activities
  263 + activity_date = if cached_activities == "Never"
  264 + nil
  265 + else
  266 + cached_activities
  267 + end
  268 + else
  269 + activity_date = last_activity_date
  270 + Rails.cache.write(activity_date_key, activity_date || "Never", :expires_in => expire)
  271 + end
  272 +
  273 + activity_date
  274 + end
  275 +
182 276 # Get project updates from cache
183 277 # or calculate.
184 278 def cached_updates(limit, expire = 2.minutes)
... ... @@ -188,7 +282,7 @@ class Project &lt; ActiveRecord::Base
188 282 activities = cached_activities
189 283 else
190 284 activities = updates(limit)
191   - Rails.cache.write(activities_key, activities, :expires_in => 60.seconds)
  285 + Rails.cache.write(activities_key, activities, :expires_in => expire)
192 286 end
193 287  
194 288 activities
... ... @@ -206,6 +300,16 @@ class Project &lt; ActiveRecord::Base
206 300 end[0...n]
207 301 end
208 302  
  303 + def activities(n=3)
  304 + [
  305 + fresh_issues(n),
  306 + fresh_merge_requests(n),
  307 + notes.inc_author_project.where("noteable_type is not null").order("created_at desc").first(n)
  308 + ].compact.flatten.sort do |x, y|
  309 + y.created_at <=> x.created_at
  310 + end[0...n]
  311 + end
  312 +
209 313 def check_limit
210 314 unless owner.can_create_project?
211 315 errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
... ... @@ -231,14 +335,15 @@ end
231 335 #
232 336 # Table name: projects
233 337 #
234   -# id :integer not null, primary key
235   -# name :string(255)
236   -# path :string(255)
237   -# description :text
238   -# created_at :datetime
239   -# updated_at :datetime
240   -# private_flag :boolean default(TRUE), not null
241   -# code :string(255)
242   -# owner_id :integer
  338 +# id :integer not null, primary key
  339 +# name :string(255)
  340 +# path :string(255)
  341 +# description :text
  342 +# created_at :datetime
  343 +# updated_at :datetime
  344 +# private_flag :boolean default(TRUE), not null
  345 +# code :string(255)
  346 +# owner_id :integer
  347 +# default_branch :string(255) default("master"), not null
243 348 #
244 349  
... ...
app/models/repository.rb
... ... @@ -31,6 +31,22 @@ class Repository
31 31 project.id
32 32 end
33 33  
  34 + def write_hooks
  35 + %w(post-receive).each do |hook|
  36 + write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook")))
  37 + end
  38 + end
  39 +
  40 + def write_hook(name, content)
  41 + hook_file = File.join(project.path_to_repo, 'hooks', name)
  42 +
  43 + File.open(hook_file, 'w') do |f|
  44 + f.write(content)
  45 + end
  46 +
  47 + File.chmod(0775, hook_file)
  48 + end
  49 +
34 50 def repo
35 51 @repo ||= Grit::Repo.new(project.path_to_repo)
36 52 end
... ... @@ -47,6 +63,8 @@ class Repository
47 63 Gitlabhq::GitHost.system.new.configure do |c|
48 64 c.update_project(path, project)
49 65 end
  66 +
  67 + write_hooks if File.exists?(project.path_to_repo)
50 68 end
51 69  
52 70 def destroy_repository
... ... @@ -56,7 +74,9 @@ class Repository
56 74 end
57 75  
58 76 def repo_exists?
59   - repo rescue false
  77 + @repo_exists ||= (repo && !repo.branches.empty?)
  78 + rescue
  79 + @repo_exists = false
60 80 end
61 81  
62 82 def tags
... ... @@ -94,6 +114,16 @@ class Repository
94 114 commits[0...n]
95 115 end
96 116  
  117 + def commits_with_refs(n = 20)
  118 + commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
  119 +
  120 + commits.sort! do |x, y|
  121 + y.committed_date <=> x.committed_date
  122 + end
  123 +
  124 + commits[0..n]
  125 + end
  126 +
97 127 def commits_since(date)
98 128 commits = heads.map do |h|
99 129 repo.log(h.name, nil, :since => date).each { |c| Commit.new(c, h) }
... ... @@ -115,4 +145,8 @@ class Repository
115 145 repo.commits(ref)
116 146 end.map{ |c| Commit.new(c) }
117 147 end
  148 +
  149 + def commits_between(from, to)
  150 + repo.commits_between(from, to).map { |c| Commit.new(c) }
  151 + end
118 152 end
... ...
app/models/snippet.rb
... ... @@ -3,7 +3,7 @@ class Snippet &lt; ActiveRecord::Base
3 3  
4 4 belongs_to :project
5 5 belongs_to :author, :class_name => "User"
6   - has_many :notes, :as => :noteable
  6 + has_many :notes, :as => :noteable, :dependent => :destroy
7 7  
8 8 delegate :name,
9 9 :email,
... ... @@ -28,6 +28,7 @@ class Snippet &lt; ActiveRecord::Base
28 28  
29 29 scope :fresh, order("created_at DESC")
30 30 scope :non_expired, where(["expires_at IS NULL OR expires_at > ?", Time.current])
  31 + scope :expired, where(["expires_at IS NOT NULL AND expires_at < ?", Time.current])
31 32  
32 33 def self.content_types
33 34 [
... ...
app/models/tree.rb
... ... @@ -7,6 +7,8 @@ class Tree
7 7 :name,
8 8 :data,
9 9 :mime_type,
  10 + :mode,
  11 + :size,
10 12 :text?,
11 13 :colorize,
12 14 :to => :tree
... ...
app/models/user.rb
... ... @@ -6,7 +6,7 @@ class User &lt; ActiveRecord::Base
6 6  
7 7 # Setup accessible (or protected) attributes for your model
8 8 attr_accessible :email, :password, :password_confirmation, :remember_me,
9   - :name, :projects_limit, :skype, :linkedin, :twitter
  9 + :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme
10 10  
11 11 has_many :users_projects, :dependent => :destroy
12 12 has_many :projects, :through => :users_projects
... ... @@ -25,6 +25,20 @@ class User &lt; ActiveRecord::Base
25 25 :foreign_key => :assignee_id,
26 26 :dependent => :destroy
27 27  
  28 + has_many :merge_requests,
  29 + :foreign_key => :author_id,
  30 + :dependent => :destroy
  31 +
  32 + has_many :assigned_merge_requests,
  33 + :class_name => "MergeRequest",
  34 + :foreign_key => :assignee_id,
  35 + :dependent => :destroy
  36 +
  37 + validates :projects_limit,
  38 + :presence => true,
  39 + :numericality => {:greater_than_or_equal_to => 0}
  40 +
  41 +
28 42 before_create :ensure_authentication_token
29 43 alias_attribute :private_token, :authentication_token
30 44 scope :not_in_project, lambda { |project| where("id not in (:ids)", :ids => project.users.map(&:id) ) }
... ... @@ -37,8 +51,12 @@ class User &lt; ActiveRecord::Base
37 51 admin
38 52 end
39 53  
  54 + def require_ssh_key?
  55 + keys.count == 0
  56 + end
  57 +
40 58 def can_create_project?
41   - projects_limit >= my_own_projects.count
  59 + projects_limit > my_own_projects.count
42 60 end
43 61  
44 62 def last_activity_project
... ... @@ -69,5 +87,6 @@ end
69 87 # linkedin :string(255) default(""), not null
70 88 # twitter :string(255) default(""), not null
71 89 # authentication_token :string(255)
  90 +# dark_scheme :boolean default(FALSE), not null
72 91 #
73 92  
... ...
app/models/users_project.rb
... ... @@ -13,6 +13,20 @@ class UsersProject &lt; ActiveRecord::Base
13 13  
14 14 delegate :name, :email, :to => :user, :prefix => true
15 15  
  16 + def self.bulk_import(project, user_ids, project_access, repo_access)
  17 + UsersProject.transaction do
  18 + user_ids.each do |user_id|
  19 + users_project = UsersProject.new(
  20 + :repo_access => repo_access,
  21 + :project_access => project_access,
  22 + :user_id => user_id
  23 + )
  24 + users_project.project = project
  25 + users_project.save
  26 + end
  27 + end
  28 + end
  29 +
16 30 def update_repository
17 31 Gitlabhq::GitHost.system.new.configure do |c|
18 32 c.update_project(project.path, project)
... ... @@ -23,13 +37,12 @@ end
23 37 #
24 38 # Table name: users_projects
25 39 #
26   -# id :integer not null, primary key
27   -# user_id :integer not null
28   -# project_id :integer not null
29   -# read :boolean default(FALSE)
30   -# write :boolean default(FALSE)
31   -# admin :boolean default(FALSE)
32   -# created_at :datetime
33   -# updated_at :datetime
  40 +# id :integer not null, primary key
  41 +# user_id :integer not null
  42 +# project_id :integer not null
  43 +# created_at :datetime
  44 +# updated_at :datetime
  45 +# repo_access :integer default(0), not null
  46 +# project_access :integer default(0), not null
34 47 #
35 48  
... ...
app/models/web_hook.rb 0 → 100644
... ... @@ -0,0 +1,31 @@
  1 +class WebHook < ActiveRecord::Base
  2 + include HTTParty
  3 +
  4 + # HTTParty timeout
  5 + default_timeout 10
  6 +
  7 + belongs_to :project
  8 +
  9 + validates :url,
  10 + presence: true,
  11 + format: {
  12 + with: URI::regexp(%w(http https)),
  13 + message: "should be a valid url" }
  14 +
  15 + def execute(data)
  16 + WebHook.post(url, body: data.to_json)
  17 + rescue
  18 + # There was a problem calling this web hook, let's forget about it.
  19 + end
  20 +end
  21 +# == Schema Information
  22 +#
  23 +# Table name: web_hooks
  24 +#
  25 +# id :integer not null, primary key
  26 +# url :string(255)
  27 +# project_id :integer
  28 +# created_at :datetime
  29 +# updated_at :datetime
  30 +#
  31 +
... ...
app/views/admin/projects/show.html.haml
... ... @@ -38,6 +38,23 @@
38 38  
39 39 %h2 Team
40 40  
  41 + = form_tag team_update_admin_project_path(@admin_project), :class => "bulk_import", :method => :put do
  42 + %table
  43 + %thead
  44 + %tr
  45 + %th Users
  46 + %th Project Access:
  47 + %th Repo Access:
  48 +
  49 + %tr
  50 + %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), :multiple => true
  51 + %td= select_tag :project_access, options_for_select(Project.access_options), :class => "project-access-select"
  52 + %td= select_tag :repo_access, options_for_select(Repository.access_options), :class => "repo-access-select"
  53 +
  54 + %tr
  55 + %td{ :colspan => 3 }
  56 + = submit_tag 'Add', :class => "positive-button"
  57 +
41 58 %table.round-borders
42 59 %thead
43 60 %tr
... ... @@ -52,8 +69,22 @@
52 69 %td
53 70 = link_to tm.user_name, admin_team_member_path(tm)
54 71 %td= time_ago_in_words(tm.updated_at) + " ago"
55   - %td= select_tag :project_access, options_for_select(Project.access_options, tm.project_access), :class => "project-access-select", :disabled => :disabled
56   - %td= select_tag :repo_access, options_for_select(Repository.access_options, tm.repo_access), :class => "repo-access-select", :disabled => :disabled
  72 + %td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), :class => "project-access-select", :disabled => :disabled
  73 + %td= select_tag :tm_repo_access, options_for_select(Repository.access_options, tm.repo_access), :class => "repo-access-select", :disabled => :disabled
57 74 %td= link_to 'Destroy', admin_team_member_path(tm), :confirm => 'Are you sure?', :method => :delete
58 75  
59   - = link_to 'New Team Member', new_admin_team_member_path(:team_member => {:project_id => @admin_project.id}), :class => "grey-button"
  76 +:css
  77 + form select {
  78 + width:150px;
  79 + }
  80 +
  81 + #user_ids {
  82 + width:300px;
  83 + }
  84 +
  85 +
  86 +:javascript
  87 + $('select#user_ids').chosen();
  88 + $('select#repo_access').chosen();
  89 + $('select#project_access').chosen();
  90 +
... ...
app/views/admin/projects/team.html.haml 0 → 100644
app/views/commits/_commits.html.haml
... ... @@ -17,7 +17,7 @@
17 17 = image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;"
18 18 %span.commit-title
19 19 %strong
20   - = truncate(commit.safe_message, :length => 60)
  20 + = truncate(commit.safe_message, :length => 70)
21 21 %span.commit-author
22 22 %strong= commit.author_name
23 23 = time_ago_in_words(commit.committed_date)
... ...
app/views/commits/_text_file.html.haml
1 1 %table
2 2 - line_old = 0
3 3 - line_new = 0
4   - - diff_str = encode(diff.diff)
  4 + - diff_str = diff.diff
5 5 - lines_arr = diff_str.lines.to_a
6 6 - lines_arr.each do |line|
7 7 - next if line.match(/^--- \/dev\/null/)
8 8 - next if line.match(/^--- a/)
9 9 - next if line.match(/^\+\+\+ b/)
10 10 - if line.match(/^@@ -/)
  11 + - unless line_old.zero? && line_new.zero?
  12 + %tr.line_holder
  13 + %td.old_line= "..."
  14 + %td.new_line= "..."
  15 + %td.line_content &nbsp;
  16 +
11 17 - line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
12 18 - line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
13 19 - next
... ... @@ -18,7 +24,11 @@
18 24 = link_to raw(diff_line_class(line) == "new" ? "&nbsp;" : line_old), "#OLD#{index}-#{line_old}", :id => "OLD#{index}-#{line_old}"
19 25 %td.new_line
20 26 = link_to raw(diff_line_class(line) == "old" ? "&nbsp;" : line_new) , "#NEW#{index}-#{line_new}", :id => "NEW#{index}-#{line_new}"
21   - %td.line_content{:class => diff_line_class(full_line)}= raw "#{full_line} &nbsp;"
  27 + %td.line_content{:class => "#{diff_line_class(full_line)} #{build_line_code(line, index, line_new, line_old)}", "line_code" => build_line_code(line, index, line_new, line_old)}= raw "#{full_line} &nbsp;"
  28 + - comments = @line_notes.select { |n| n.for_line?(index, line_old, line_new) }.sort_by(&:created_at).reverse
  29 + - unless comments.empty?
  30 + - comments.each do |note|
  31 + = render "notes/per_line_show", :note => note
22 32 - if line[0] == "+"
23 33 - line_new += 1
24 34 - elsif line[0] == "-"
... ...
app/views/commits/index.html.haml
1 1 - content_for(:body_class, "project-page commits-page")
  2 +- if current_user.private_token
  3 + = content_for :rss_icon do
  4 + .rss-icon
  5 + = link_to project_commits_path(@project, :atom, { :private_token => current_user.private_token, :ref => @ref }) do
  6 + = image_tag "Rss-UI.PNG", :width => 22, :title => "feed"
2 7  
3   --#%a.right.button{:href => "#"} Download
4   --#-if can? current_user, :admin_project, @project
5   - %a.right.button.blue{:href => "#"} EDIT
6   -%h2.icon
7   - %span
8   - %d
  8 +- if params[:path]
  9 + %h2
9 10 = link_to project_commits_path(@project) do
10   - = @project.name
11   - - if params[:path]
12   - \/
13   - %a{:href => "#"}= params[:path].split("/").join(" / ")
14   -
15   -.right= render :partial => "projects/refs", :locals => { :destination => :commits }
  11 + = @project.code
  12 + \/
  13 + %a{:href => "#"}= params[:path].split("/").join(" / ")
16 14  
17 15 %div{:id => dom_id(@project)}
18 16 #commits_list= render "commits"
... ...
app/views/commits/show.html.haml
... ... @@ -18,10 +18,21 @@
18 18  
19 19 %hr
20 20 %pre.commit_message
21   - = preserve @commit.safe_message
22   -
  21 + = commit_msg_with_link_to_issues(@project, @commit.safe_message)
23 22 .clear
24 23 %br
25 24  
26 25 = render "commits/diff"
27 26 = render "notes/notes"
  27 += render "notes/per_line_form"
  28 +
  29 +
  30 +:javascript
  31 + $(document).ready(function(){
  32 + $(".line_content").live("dblclick", function(e) {
  33 + var form = $(".per_line_form");
  34 + $(this).parent().after(form);
  35 + form.find("#note_line_code").val($(this).attr("line_code"));
  36 + form.show();
  37 + });
  38 + });
... ...
app/views/dashboard/_issues_feed.html.haml 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +#feeds_content_holder
  2 + - unless @issues.empty?
  3 + .project-box.project-updates.ui-box.ui-box-small.ui-box-big
  4 + .data
  5 + - @issues.each do |update|
  6 + %a.project-update{:href => dashboard_feed_path(update.project, update)}
  7 + %strong.issue-number= "##{update.id}"
  8 + %span.update-title
  9 + = truncate update.title, :length => 35
  10 + .right= truncate update.project.name
  11 + %span.update-author
  12 + %strong= update.author_name
  13 + authored
  14 + = time_ago_in_words(update.created_at)
  15 + ago
  16 + .right
  17 + - if update.critical
  18 + %span.tag.high critical
  19 + - if update.today?
  20 + %span.tag.today today
  21 +
  22 + - else
  23 + %h2
  24 + No assigned
  25 + %span.tag.open open
  26 + issues
... ...
app/views/dashboard/_menu.html.haml 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +-#%h4.dash-tabs
  2 + = link_to "Activities", dashboard_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_path) || current_page?(root_path) }", :id => "activities_slide"
  3 + = link_to "Issues", dashboard_issues_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_issues_path)}", :id => "issues_slide"
  4 + = link_to "Merge Requests", dashboard_merge_requests_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide"
  5 + = image_tag "ajax-loader-facebook.gif", :class => "dashboard-loader"
  6 +
  7 +:javascript
  8 + $(function(){
  9 + $(".dash-button").live("click", function() {
  10 + $(".dash-button").removeClass("active");
  11 + $(this).addClass("active");
  12 + });
  13 +
  14 + $(".dash-button").live("ajax:before", function() {
  15 + $(".dashboard-loader").show();
  16 + });
  17 +
  18 + $(".dash-button").live("ajax:complete", function() {
  19 + $(".dashboard-loader").hide();
  20 + });
  21 + });
... ...
app/views/dashboard/_merge_requests_feed.html.haml 0 → 100644
... ... @@ -0,0 +1,24 @@
  1 +#feeds_content_holder
  2 + - unless @merge_requests.empty?
  3 + .project-box.project-updates.ui-box.ui-box-small.ui-box-big
  4 + .data
  5 + - @merge_requests.each do |update|
  6 + %a.project-update{:href => project_merge_request_path(update.project, update)}
  7 + = image_tag gravatar_icon(update.author_email), :class => "left", :width => 40
  8 + %span.update-title
  9 + = truncate update.title, :length => 35
  10 + .right= truncate update.project.name
  11 + %span.update-author
  12 + %strong= update.author_name
  13 + authored
  14 + = time_ago_in_words(update.created_at)
  15 + ago
  16 + .right
  17 + %span.tag.commit= update.source_branch
  18 + &rarr;
  19 + %span.tag.commit= update.target_branch
  20 + - else
  21 + %h2
  22 + No authored or assigned
  23 + %span.tag.open open
  24 + merge requests
... ...
app/views/dashboard/_projects_feed.html.haml 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +#feeds_content_holder
  2 + - @active_projects.first(3).each do |project|
  3 + .project-box.project-updates.ui-box.ui-box-small.ui-box-big
  4 + = link_to project do
  5 + %h3= project.name
  6 + .data
  7 + - project.updates(3).each do |update|
  8 + %a.project-update{:href => dashboard_feed_path(project, update)}
  9 + = image_tag gravatar_icon(update.author_email), :class => "left", :width => 40
  10 + %span.update-title
  11 + = dashboard_feed_title(update)
  12 + %span.update-author
  13 + %strong= update.author_name
  14 + authored
  15 + = time_ago_in_words(update.created_at)
  16 + ago
  17 + .right
  18 + - klass = update.class.to_s.split("::").last.downcase
  19 + %span.tag{ :class => klass }= klass
  20 +
... ...