Commit eca823c1c7cef45cc18c6ab36d2327650c85bfc3

Authored by Nihad Abbasov
2 parents 024e0348 8b7e404b

Merge branch 'master' into api

Showing 125 changed files with 1752 additions and 666 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 125 files displayed.

@@ -6,6 +6,7 @@ log/*.log @@ -6,6 +6,7 @@ log/*.log
6 tmp/ 6 tmp/
7 .sass-cache/ 7 .sass-cache/
8 coverage/* 8 coverage/*
  9 +backups/*
9 *.swp 10 *.swp
10 public/uploads/ 11 public/uploads/
11 .rvmrc 12 .rvmrc
1 v 2.7.0 1 v 2.7.0
2 - Issue Labels 2 - Issue Labels
  3 + - Inline diff
  4 + - Git HTTP
  5 + - API
  6 + - UI improved
  7 + - System hooks
  8 + - UI improved
  9 + - Dashboard events endless scroll
  10 + - Source perfomance increased
3 11
4 v 2.6.0 12 v 2.6.0
5 - UI polished 13 - UI polished
@@ -7,7 +7,7 @@ gem "sqlite3" @@ -7,7 +7,7 @@ gem "sqlite3"
7 gem "mysql2" 7 gem "mysql2"
8 8
9 # Auth 9 # Auth
10 -gem "devise", "~> 1.5" 10 +gem "devise", "~> 2.1.0"
11 11
12 # GITLAB patched libs 12 # GITLAB patched libs
13 gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" 13 gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837"
@@ -71,7 +71,6 @@ group :development, :test do @@ -71,7 +71,6 @@ group :development, :test do
71 gem "awesome_print" 71 gem "awesome_print"
72 gem "database_cleaner" 72 gem "database_cleaner"
73 gem "launchy" 73 gem "launchy"
74 - gem "webmock"  
75 end 74 end
76 75
77 group :test do 76 group :test do
@@ -82,4 +81,5 @@ group :test do @@ -82,4 +81,5 @@ group :test do
82 gem "shoulda-matchers" 81 gem "shoulda-matchers"
83 gem 'email_spec' 82 gem 'email_spec'
84 gem 'resque_spec' 83 gem 'resque_spec'
  84 + gem "webmock"
85 end 85 end
@@ -148,10 +148,11 @@ GEM @@ -148,10 +148,11 @@ GEM
148 nokogiri (>= 1.5.0) 148 nokogiri (>= 1.5.0)
149 daemons (1.1.8) 149 daemons (1.1.8)
150 database_cleaner (0.8.0) 150 database_cleaner (0.8.0)
151 - devise (1.5.3) 151 + devise (2.1.2)
152 bcrypt-ruby (~> 3.0) 152 bcrypt-ruby (~> 3.0)
153 - orm_adapter (~> 0.0.3)  
154 - warden (~> 1.1) 153 + orm_adapter (~> 0.1)
  154 + railties (~> 3.1)
  155 + warden (~> 1.2.1)
155 diff-lcs (1.1.3) 156 diff-lcs (1.1.3)
156 drapper (0.8.4) 157 drapper (0.8.4)
157 email_spec (1.2.1) 158 email_spec (1.2.1)
@@ -225,7 +226,7 @@ GEM @@ -225,7 +226,7 @@ GEM
225 omniauth (1.1.0) 226 omniauth (1.1.0)
226 hashie (~> 1.2) 227 hashie (~> 1.2)
227 rack 228 rack
228 - orm_adapter (0.0.7) 229 + orm_adapter (0.3.0)
229 polyglot (0.3.3) 230 polyglot (0.3.3)
230 posix-spawn (0.3.6) 231 posix-spawn (0.3.6)
231 pry (0.9.9.6) 232 pry (0.9.9.6)
@@ -356,7 +357,7 @@ GEM @@ -356,7 +357,7 @@ GEM
356 raindrops (~> 0.7) 357 raindrops (~> 0.7)
357 vegas (0.1.11) 358 vegas (0.1.11)
358 rack (>= 1.0.0) 359 rack (>= 1.0.0)
359 - warden (1.2.0) 360 + warden (1.2.1)
360 rack (>= 1.0) 361 rack (>= 1.0)
361 webmock (1.8.7) 362 webmock (1.8.7)
362 addressable (>= 2.2.7) 363 addressable (>= 2.2.7)
@@ -383,7 +384,7 @@ DEPENDENCIES @@ -383,7 +384,7 @@ DEPENDENCIES
383 colored 384 colored
384 cucumber-rails 385 cucumber-rails
385 database_cleaner 386 database_cleaner
386 - devise (~> 1.5) 387 + devise (~> 2.1.0)
387 drapper 388 drapper
388 email_spec 389 email_spec
389 ffaker 390 ffaker
1 -2.7.0pre 1 +2.7.0
app/assets/images/ajax_loader_tree.gif 0 → 100644

6.38 KB

app/assets/javascripts/application.js
@@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
12 //= require jquery.cookie 12 //= require jquery.cookie
13 //= require jquery.endless-scroll 13 //= require jquery.endless-scroll
14 //= require jquery.highlight 14 //= require jquery.highlight
  15 +//= require jquery.waitforimages
15 //= require bootstrap-modal 16 //= require bootstrap-modal
16 //= require modernizr 17 //= require modernizr
17 //= require chosen-jquery 18 //= require chosen-jquery
@@ -20,10 +21,26 @@ @@ -20,10 +21,26 @@
20 //= require_tree . 21 //= require_tree .
21 22
22 $(document).ready(function(){ 23 $(document).ready(function(){
  24 +
23 $(".one_click_select").live("click", function(){ 25 $(".one_click_select").live("click", function(){
24 $(this).select(); 26 $(this).select();
25 }); 27 });
26 28
  29 +
  30 + $('body').on('ajax:complete, ajax:beforeSend, submit', 'form', function(e){
  31 + var buttons = $('[type="submit"]', this);
  32 + switch( e.type ){
  33 + case 'ajax:beforeSend':
  34 + case 'submit':
  35 + buttons.attr('disabled', 'disabled');
  36 + break;
  37 + case ' ajax:complete':
  38 + default:
  39 + buttons.removeAttr('disabled');
  40 + break;
  41 + }
  42 + })
  43 +
27 $(".account-box").mouseenter(showMenu); 44 $(".account-box").mouseenter(showMenu);
28 $(".account-box").mouseleave(resetMenu); 45 $(".account-box").mouseleave(resetMenu);
29 46
@@ -97,3 +114,8 @@ function showDiff(link) { @@ -97,3 +114,8 @@ function showDiff(link) {
97 return _chosen.apply(this, [default_options]); 114 return _chosen.apply(this, [default_options]);
98 }}) 115 }})
99 })(jQuery); 116 })(jQuery);
  117 +
  118 +
  119 +function ajaxGet(url) {
  120 + $.ajax({type: "GET", url: url, dataType: "script"});
  121 +}
app/assets/javascripts/issues.js
@@ -73,4 +73,25 @@ function issuesPage(){ @@ -73,4 +73,25 @@ function issuesPage(){
73 $("#milestone_id, #assignee_id, #label_name").on("change", function(){ 73 $("#milestone_id, #assignee_id, #label_name").on("change", function(){
74 $(this).closest("form").submit(); 74 $(this).closest("form").submit();
75 }); 75 });
  76 +
  77 + $('body').on('ajax:success', '.close_issue, .reopen_issue, #new_issue', function(){
  78 + var t = $(this),
  79 + totalIssues,
  80 + reopen = t.hasClass('reopen_issue'),
  81 + newIssue = false;
  82 + if( this.id == 'new_issue' ){
  83 + newIssue = true;
  84 + }
  85 + $('.issue_counter, #new_issue').each(function(){
  86 + var issue = $(this);
  87 + totalIssues = parseInt( $(this).html(), 10 );
  88 +
  89 + if( newIssue || ( reopen && issue.closest('.main_menu').length ) ){
  90 + $(this).html( totalIssues+1 );
  91 + }else {
  92 + $(this).html( totalIssues-1 );
  93 + }
  94 + });
  95 +
  96 + });
76 } 97 }
app/assets/javascripts/note.js
@@ -25,11 +25,11 @@ init: @@ -25,11 +25,11 @@ init:
25 $(this).closest('li').fadeOut(); }); 25 $(this).closest('li').fadeOut(); });
26 26
27 $("#new_note").live("ajax:before", function(){ 27 $("#new_note").live("ajax:before", function(){
28 - $("#submit_note").attr("disabled", "disabled"); 28 + $(".submit_note").attr("disabled", "disabled");
29 }) 29 })
30 30
31 $("#new_note").live("ajax:complete", function(){ 31 $("#new_note").live("ajax:complete", function(){
32 - $("#submit_note").removeAttr("disabled"); 32 + $(".submit_note").removeAttr("disabled");
33 }) 33 })
34 34
35 $("#note_note").live("focus", function(){ 35 $("#note_note").live("focus", function(){
app/assets/stylesheets/common.scss
@@ -604,7 +604,11 @@ li.note { @@ -604,7 +604,11 @@ li.note {
604 border-style: solid; 604 border-style: solid;
605 border-width: 1px; 605 border-width: 1px;
606 @include border-radius(4px); 606 @include border-radius(4px);
607 - min-height:42px; 607 + min-height:22px;
  608 +
  609 + .avatar {
  610 + width:24px;
  611 + }
608 } 612 }
609 613
610 .supp_diff_link, 614 .supp_diff_link,
app/assets/stylesheets/gitlab_bootstrap.scss
@@ -202,6 +202,10 @@ a:focus { @@ -202,6 +202,10 @@ a:focus {
202 color:$style_color; 202 color:$style_color;
203 } 203 }
204 204
  205 +.nav-tabs > .active > a {
  206 + font-weight:bold;
  207 +}
  208 +
205 /** COLORS **/ 209 /** COLORS **/
206 .cgray { color:gray; } 210 .cgray { color:gray; }
207 .cred { color:#D12F19; } 211 .cred { color:#D12F19; }
@@ -209,6 +213,7 @@ a:focus { @@ -209,6 +213,7 @@ a:focus {
209 .cblack { color:#111; } 213 .cblack { color:#111; }
210 .cdark { color:#444 } 214 .cdark { color:#444 }
211 .cwhite { color:#fff !important } 215 .cwhite { color:#fff !important }
  216 +.bgred { background: #F2DEDE !important}
212 217
213 /** COMMON STYLES **/ 218 /** COMMON STYLES **/
214 .left { 219 .left {
@@ -299,9 +304,24 @@ table.no-borders { @@ -299,9 +304,24 @@ table.no-borders {
299 } 304 }
300 305
301 .event_label { 306 .event_label {
302 - background: #FCEEC1;  
303 - padding: 2px 2px 0;  
304 - font-family: monospace; 307 + @extend .label;
  308 + background-color: #999;
  309 +
  310 + &.pushed {
  311 + background-color: #3A87AD;
  312 + }
  313 +
  314 + &.opened {
  315 + background-color: #468847;
  316 + }
  317 +
  318 + &.closed {
  319 + background-color: #B94A48;
  320 + }
  321 +
  322 + &.merged {
  323 + background-color: #2A2;
  324 + }
305 } 325 }
306 326
307 img.avatar { 327 img.avatar {
@@ -425,9 +445,10 @@ form { @@ -425,9 +445,10 @@ form {
425 */ 445 */
426 .ui-box { 446 .ui-box {
427 background:#F9F9F9; 447 background:#F9F9F9;
428 - margin-bottom: 40px; 448 + margin-bottom: 25px;
429 @include round-borders-all(4px); 449 @include round-borders-all(4px);
430 border-color: #CCC; 450 border-color: #CCC;
  451 + @include solid_shade;
431 452
432 ul { 453 ul {
433 margin:0; 454 margin:0;
@@ -443,6 +464,13 @@ form { @@ -443,6 +464,13 @@ form {
443 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); 464 background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
444 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); 465 background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
445 466
  467 + &.small {
  468 + line-height: 28px;
  469 + font-size: 14px;
  470 + line-height:28px;
  471 + text-shadow: 0 1px 1px white;
  472 + }
  473 +
446 form { 474 form {
447 padding:9px 0; 475 padding:9px 0;
448 margin:0px; 476 margin:0px;
@@ -511,6 +539,7 @@ form { @@ -511,6 +539,7 @@ form {
511 table.admin-table { 539 table.admin-table {
512 @extend .table-bordered; 540 @extend .table-bordered;
513 @extend .zebra-striped; 541 @extend .zebra-striped;
  542 + @include solid_shade;
514 th { 543 th {
515 border-color: #CCC; 544 border-color: #CCC;
516 border-bottom: 1px solid #bbb; 545 border-bottom: 1px solid #bbb;
@@ -568,6 +597,8 @@ ul.breadcrumb { @@ -568,6 +597,8 @@ ul.breadcrumb {
568 @extend .prepend-top-20; 597 @extend .prepend-top-20;
569 @extend .append-bottom-20; 598 @extend .append-bottom-20;
570 border-width:1px; 599 border-width:1px;
  600 + @include solid_shade;
  601 +
571 602
572 img { max-width: 100%; } 603 img { max-width: 100%; }
573 604
@@ -624,13 +655,166 @@ p { @@ -624,13 +655,166 @@ p {
624 h3.page_title { 655 h3.page_title {
625 color:#456; 656 color:#456;
626 font-size:20px; 657 font-size:20px;
627 - font-weight: 600; 658 + font-weight: normal;
628 line-height: 28px; 659 line-height: 28px;
629 } 660 }
630 661
631 -pre.logs {  
632 - .log {  
633 - font-size:12px;  
634 - line-height:18px; 662 +/**
  663 + * File content holder
  664 + *
  665 + */
  666 +.file_holder {
  667 + border:1px solid #CCC;
  668 + margin-bottom:1em;
  669 + @include solid_shade;
  670 +
  671 + .file_title {
  672 + border-bottom: 1px solid #bbb;
  673 + background:#eee;
  674 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
  675 + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
  676 + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
  677 + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
  678 + margin: 0;
  679 + font-weight: normal;
  680 + font-weight: bold;
  681 + text-align: left;
  682 + color: #666;
  683 + padding: 9px 10px;
  684 + height:18px;
  685 +
  686 + .options {
  687 + float:right;
  688 + margin-top: -5px;
  689 + }
  690 +
  691 + .file_name {
  692 + color:$style_color;
  693 + font-size:14px;
  694 + text-shadow: 0 1px 1px #fff;
  695 + small {
  696 + color:#999;
  697 + font-size:13px;
  698 + }
  699 + }
  700 + }
  701 + .file_content {
  702 + background:#fff;
  703 + font-size: 11px;
  704 +
  705 + &.wiki {
  706 + font-size: 13px;
  707 + code {
  708 + padding:0 4px;
  709 + }
  710 + padding:20px;
  711 + h1, h2 {
  712 + line-height: 46px;
  713 + }
  714 + h3, h4 {
  715 + line-height: 40px;
  716 + }
  717 + }
  718 +
  719 + &.image_file {
  720 + background:#eee;
  721 + text-align:center;
  722 + img {
  723 + padding:100px;
  724 + max-width:300px;
  725 + }
  726 + }
  727 +
  728 + &.blob_file {
  729 +
  730 + }
  731 +
  732 + /**
  733 + * Blame file
  734 + */
  735 + &.blame {
  736 + tr {
  737 + border-bottom: 1px solid #eee;
  738 + }
  739 + td {
  740 + padding:5px;
  741 + }
  742 + .author,
  743 + .blame_commit {
  744 + background:#f5f5f5;
  745 + vertical-align:top;
  746 + }
  747 + .lines {
  748 + pre {
  749 + padding:0;
  750 + margin:0;
  751 + background:none;
  752 + border:none;
  753 + }
  754 + }
  755 + }
  756 +
  757 + &.logs {
  758 + background:#eee;
  759 + max-height: 700px;
  760 + overflow-y: auto;
  761 +
  762 + ol {
  763 + margin-left:40px;
  764 + padding: 10px 0;
  765 + border-left: 1px solid #CCC;
  766 + margin-bottom:0;
  767 + background: white;
  768 + li {
  769 + color:#888;
  770 + p {
  771 + margin:0;
  772 + color:#333;
  773 + line-height:24px;
  774 + padding-left: 10px;
  775 + }
  776 +
  777 + &:hover {
  778 + background:$hover;
  779 + }
  780 + }
  781 + }
  782 + }
  783 +
  784 + /**
  785 + * Code file
  786 + */
  787 + &.code {
  788 + padding:0;
  789 + td.code {
  790 + width: 100%;
  791 + .highlight {
  792 + margin-left: 55px;
  793 + overflow:auto;
  794 + overflow-y:hidden;
  795 + }
  796 + }
  797 + .highlight pre {
  798 + white-space: pre;
  799 + word-wrap:normal;
  800 + }
  801 +
  802 + table.highlighttable {
  803 + border: none;
  804 + }
  805 + body.project-page table.highlighttable td { border: none }
  806 + table.highlighttable tr:hover { background:none;}
  807 +
  808 + table.highlighttable pre{
  809 + line-height:16px !important;
  810 + font-size:12px !important;
  811 + }
  812 +
  813 + table.highlighttable .linenodiv pre {
  814 + text-align: right;
  815 + padding-right: 4px;
  816 + color:#666;
  817 + }
  818 + }
635 } 819 }
636 } 820 }
app/assets/stylesheets/header.scss
@@ -96,7 +96,7 @@ header { @@ -96,7 +96,7 @@ header {
96 */ 96 */
97 .search { 97 .search {
98 float: right; 98 float: right;
99 - margin-right: 55px; 99 + margin-right: 50px;
100 100
101 .search-input { 101 .search-input {
102 @extend .span2; 102 @extend .span2;
@@ -126,10 +126,10 @@ header { @@ -126,10 +126,10 @@ header {
126 cursor: pointer; 126 cursor: pointer;
127 img { 127 img {
128 border-radius: 4px; 128 border-radius: 4px;
129 - right: 0px; 129 + right: 5px;
130 position: absolute; 130 position: absolute;
131 - width: 33px;  
132 - height: 33px; 131 + width: 31px;
  132 + height: 31px;
133 display: block; 133 display: block;
134 top: 0; 134 top: 0;
135 &:after { 135 &:after {
app/assets/stylesheets/main.scss
@@ -31,6 +31,12 @@ $hover: #FDF5D9; @@ -31,6 +31,12 @@ $hover: #FDF5D9;
31 box-shadow: 0 0 3px #ddd; 31 box-shadow: 0 0 3px #ddd;
32 } 32 }
33 33
  34 +@mixin solid_shade {
  35 + -moz-box-shadow: 0 0 0 3px #eee;
  36 + -webkit-box-shadow: 0 0 0 3px #eee;
  37 + box-shadow: 0 0 0 3px #eee;
  38 +}
  39 +
34 @mixin border-radius($radius) { 40 @mixin border-radius($radius) {
35 -moz-border-radius: $radius; 41 -moz-border-radius: $radius;
36 -webkit-border-radius: $radius; 42 -webkit-border-radius: $radius;
@@ -136,7 +142,7 @@ $hover: #FDF5D9; @@ -136,7 +142,7 @@ $hover: #FDF5D9;
136 /** 142 /**
137 * Code (files list) styles. Browsing project files there 143 * Code (files list) styles. Browsing project files there
138 */ 144 */
139 -@import "tree.scss"; 145 +@import "sections/tree.scss";
140 146
141 /** 147 /**
142 * This file represent notes(comments) styles 148 * This file represent notes(comments) styles
app/assets/stylesheets/notes.scss
@@ -63,18 +63,22 @@ p.notify_controls span{ @@ -63,18 +63,22 @@ p.notify_controls span{
63 63
64 tr.line_notes_row { 64 tr.line_notes_row {
65 border-bottom:1px solid #DDD; 65 border-bottom:1px solid #DDD;
  66 + border-left: 7px solid #2A79A3;
  67 +
66 &.reply { 68 &.reply {
67 background:#eee; 69 background:#eee;
68 - 70 + border-left: 7px solid #2A79A3;
  71 + border-top:1px solid #ddd;
69 td { 72 td {
70 padding:7px 10px; 73 padding:7px 10px;
71 } 74 }
72 a.line_note_reply_link { 75 a.line_note_reply_link {
73 @include round-borders-all(4px); 76 @include round-borders-all(4px);
74 - border-color:#aaa;  
75 - background: #bbb;  
76 - padding: 3px 20px; 77 + padding: 3px 10px;
  78 + margin-left:5px;
77 color: white; 79 color: white;
  80 + background: #2A79A3;
  81 + border-color: #2A79A3;
78 } 82 }
79 } 83 }
80 ul { 84 ul {
@@ -95,6 +99,9 @@ tr.line_notes_row { @@ -95,6 +99,9 @@ tr.line_notes_row {
95 td { 99 td {
96 border-bottom:1px solid #ddd; 100 border-bottom:1px solid #ddd;
97 } 101 }
  102 + .actions {
  103 + margin:0;
  104 + }
98 } 105 }
99 106
100 td .line_note_link { 107 td .line_note_link {
app/assets/stylesheets/sections/commits.scss
@@ -101,18 +101,21 @@ @@ -101,18 +101,21 @@
101 margin:50px; 101 margin:50px;
102 padding:1px; 102 padding:1px;
103 max-width:400px; 103 max-width:400px;
104 - }  
105 - &.diff_image_removed {  
106 - img { 104 +
  105 + &.diff_image_removed {
107 border: 1px solid #C00; 106 border: 1px solid #C00;
108 } 107 }
109 - }  
110 108
111 - &.diff_image_added {  
112 - img { 109 + &.diff_image_added {
113 border: 1px solid #0C0;; 110 border: 1px solid #0C0;;
114 } 111 }
115 } 112 }
  113 +
  114 + &.img_compared {
  115 + img {
  116 + max-width:300px;
  117 + }
  118 + }
116 } 119 }
117 } 120 }
118 121
app/assets/stylesheets/sections/merge_requests.scss
@@ -82,3 +82,15 @@ @@ -82,3 +82,15 @@
82 } 82 }
83 } 83 }
84 } 84 }
  85 +
  86 +li.merge_request {
  87 + padding:7px 10px;
  88 + img.avatar {
  89 + width: 32px;
  90 + margin-top: 4px;
  91 + }
  92 + p {
  93 + padding: 0px;
  94 + padding-bottom: 2px;
  95 + }
  96 +}
app/assets/stylesheets/sections/tree.scss 0 → 100644
@@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
  1 +#tree-holder {
  2 + #tree-content-holder {
  3 + float:left;
  4 + width:100%;
  5 + }
  6 + #tree-readme-holder {
  7 + float:left;
  8 + width:100%;
  9 + .readme {
  10 + border:1px solid #ccc;
  11 + padding:12px;
  12 + background: #F7F7F7;
  13 +
  14 + pre {
  15 + overflow: auto;
  16 + }
  17 + }
  18 + }
  19 +
  20 + .tree_progress {
  21 + display:none;
  22 + margin:20px;
  23 + &.loading {
  24 + display:block;
  25 + }
  26 + }
  27 +
  28 + #tree-slider {
  29 + @include border-radius(0);
  30 + .tree-item {
  31 + &:hover {
  32 + td { background: $hover; }
  33 + cursor:pointer;
  34 + }
  35 + }
  36 + }
  37 +
  38 + .tree-item {
  39 + .tree-item-file-name {
  40 + vertical-align:middle;
  41 + font-weight:bold;
  42 + a {
  43 + color:$style_color;
  44 + &:hover {
  45 + color:$blue_link;
  46 + }
  47 + }
  48 +
  49 + img {
  50 + position: relative;
  51 + top:-1px;
  52 + }
  53 + }
  54 + }
  55 +
  56 +
  57 + #tree-slider {
  58 + @include solid_shade;
  59 + width:100%;
  60 +
  61 + border-color:#ccc;
  62 +
  63 + td {
  64 + padding:8px;
  65 + border-color:#f1f1f1;
  66 + background:#fafafa;
  67 + }
  68 +
  69 + tr:first-child td:first-child,
  70 + tr:first-child td:last-child {
  71 + border-radius:0;
  72 + }
  73 +
  74 + th {
  75 + border-color: #CCC;
  76 + border-bottom: 1px solid #bbb;
  77 + background:#eee;
  78 + background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
  79 + background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
  80 + background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
  81 + background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
  82 + }
  83 + }
  84 +
  85 + .tree-commit-link {
  86 + color:#333;
  87 + }
  88 +
  89 + a.tree-commit-link {
  90 + color: #666;
  91 + &:hover {
  92 + text-decoration: underline;
  93 + }
  94 + }
  95 +
  96 +}
app/assets/stylesheets/themes/ui_mars.scss
@@ -70,8 +70,7 @@ @@ -70,8 +70,7 @@
70 } 70 }
71 } 71 }
72 .separator { 72 .separator {
73 - border-color:#444;  
74 - background:#31363E; 73 + display:none;
75 } 74 }
76 75
77 } 76 }
app/assets/stylesheets/tree.scss
@@ -1,232 +0,0 @@ @@ -1,232 +0,0 @@
1 -#tree-holder {  
2 - #tree-content-holder {  
3 - float:left;  
4 - width:100%;  
5 - }  
6 - #tree-readme-holder {  
7 - float:left;  
8 - width:100%;  
9 - .readme {  
10 - border:1px solid #ccc;  
11 - padding:12px;  
12 - background: #F7F7F7;  
13 -  
14 - pre {  
15 - overflow: auto;  
16 - }  
17 - }  
18 - }  
19 -  
20 - .tree_progress {  
21 - display:none;  
22 - margin:20px;  
23 - &.loading {  
24 - display:block;  
25 - }  
26 - }  
27 -  
28 -  
29 - /** FILE CONTENT VIEW **/  
30 - .view_file_content{  
31 - .old_line, .new_line {  
32 - background:#ECECEC;  
33 - color:#777;  
34 - width:15px;  
35 - float:left;  
36 - padding: 0px 10px;  
37 - border-right: 1px solid #ccc;  
38 - }  
39 - .old_line{  
40 - display:none;  
41 - }  
42 - }  
43 -  
44 - .view_file .view_file_header,  
45 - .diff_file .diff_file_header {  
46 - border-bottom: 1px solid #bbb;  
47 - background:#eee;  
48 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));  
49 - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);  
50 - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);  
51 - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);  
52 - margin: 0;  
53 - font-weight: normal;  
54 - font-weight: bold;  
55 - text-align: left;  
56 - color: #666;  
57 - padding: 9px 10px;  
58 - height:18px;  
59 -  
60 - .options {  
61 - float:right;  
62 - margin-top: -5px;  
63 - }  
64 -  
65 - .file_name {  
66 - color:$style_color;  
67 - font-size:14px;  
68 - text-shadow: 0 1px 1px #fff;  
69 - small {  
70 - color:#999;  
71 - font-size:13px;  
72 - }  
73 - }  
74 - }  
75 -  
76 - .view_file {  
77 - border:1px solid #CCC;  
78 - margin-bottom:1em;  
79 -  
80 - .view_file_content {  
81 - background:#fff;  
82 - color:#514721;  
83 - font-size: 11px;  
84 - }  
85 - .view_file_content_image {  
86 - background:#eee;  
87 - text-align:center;  
88 - img {  
89 - padding:100px;  
90 - max-width:300px;  
91 - }  
92 - }  
93 - }  
94 -  
95 - td.code {  
96 - width: 100%;  
97 - .highlight {  
98 - margin-left: 55px;  
99 - overflow:auto;  
100 - overflow-y:hidden;  
101 - }  
102 - }  
103 - .highlight pre {  
104 - white-space: pre;  
105 - word-wrap:normal;  
106 - }  
107 -  
108 - table.highlighttable {  
109 - border: none;  
110 - }  
111 - body.project-page table.highlighttable td { border: none }  
112 - table.highlighttable tr:hover { background:none;}  
113 -  
114 - table.highlighttable pre{  
115 - line-height:16px !important;  
116 - font-size:12px !important;  
117 - }  
118 -  
119 - table.highlighttable .linenodiv pre {  
120 - text-align: right;  
121 - padding-right: 4px;  
122 - color:#666;  
123 - }  
124 -  
125 - #tree-slider {  
126 - @include border-radius(0);  
127 - .tree-item {  
128 - &:hover {  
129 - td { background: $hover; }  
130 - cursor:pointer;  
131 - }  
132 - }  
133 - }  
134 -  
135 - .tree-item {  
136 - .tree-item-file-name {  
137 - vertical-align:middle;  
138 - font-weight:bold;  
139 - a {  
140 - color:$style_color;  
141 - &:hover {  
142 - color:$blue_link;  
143 - }  
144 - }  
145 -  
146 - img {  
147 - position: relative;  
148 - top:-1px;  
149 - }  
150 - }  
151 - }  
152 -  
153 -  
154 - #tree-slider {  
155 - @include shade;  
156 - width:100%;  
157 -  
158 - border-color:#ccc;  
159 -  
160 - td {  
161 - padding:8px;  
162 - border-color:#f1f1f1;  
163 - background:#fafafa;  
164 - }  
165 -  
166 - tr:first-child td:first-child,  
167 - tr:first-child td:last-child {  
168 - border-radius:0;  
169 - }  
170 -  
171 - th {  
172 - border-color: #CCC;  
173 - border-bottom: 1px solid #bbb;  
174 - background:#eee;  
175 - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));  
176 - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);  
177 - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);  
178 - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);  
179 - }  
180 - }  
181 -  
182 - .tree-commit-link {  
183 - color:#333;  
184 - }  
185 -  
186 - #tree-content-holder .view_file{  
187 - @include shade;  
188 - }  
189 -  
190 - #tree-readme-holder .readme {  
191 - @include shade;  
192 - margin-bottom:20px;  
193 - h1, h2 {  
194 - line-height: 56px;  
195 - }  
196 - h3, h4 {  
197 - line-height: 46px;  
198 - }  
199 - }  
200 -  
201 - a.tree-commit-link {  
202 - color: #666;  
203 - &:hover {  
204 - text-decoration: underline;  
205 - }  
206 - }  
207 -  
208 -}  
209 -  
210 -.blame_file {  
211 - .view_file_content {  
212 - tr {  
213 - border-bottom: 1px solid #eee;  
214 - }  
215 - td {  
216 - padding:5px;  
217 - }  
218 - .author,  
219 - .commit {  
220 - background:#f5f5f5;  
221 - vertical-align:top;  
222 - }  
223 - .lines {  
224 - pre {  
225 - padding:0;  
226 - margin:0;  
227 - background:none;  
228 - border:none;  
229 - }  
230 - }  
231 - }  
232 -}  
app/contexts/base_context.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +class BaseContext
  2 + attr_accessor :project, :current_user, :params
  3 +
  4 + def initialize(project, user, params)
  5 + @project, @current_user, @params = project, user, params.dup
  6 + end
  7 +end
  8 +
app/contexts/commit_load.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +class CommitLoad < BaseContext
  2 + def execute
  3 + result = {
  4 + :commit => nil,
  5 + :suppress_diff => false,
  6 + :line_notes => [],
  7 + :notes_count => 0,
  8 + :note => nil
  9 + }
  10 +
  11 + commit = project.commit(params[:id])
  12 +
  13 + if commit
  14 + commit = CommitDecorator.decorate(commit)
  15 + line_notes = project.commit_line_notes(commit)
  16 +
  17 + result[:suppress_diff] = true if commit.diffs.size > 200 && !params[:force_show_diff]
  18 + result[:commit] = commit
  19 + result[:note] = project.build_commit_note(commit)
  20 + result[:line_notes] = line_notes
  21 + result[:notes_count] = line_notes.count + project.commit_notes(commit).count
  22 + end
  23 +
  24 + result
  25 + end
  26 +end
app/contexts/merge_requests_load.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class MergeRequestsLoad < BaseContext
  2 + def execute
  3 + type = params[:f].to_i
  4 +
  5 + merge_requests = project.merge_requests
  6 +
  7 + merge_requests = case type
  8 + when 1 then merge_requests
  9 + when 2 then merge_requests.closed
  10 + when 3 then merge_requests.opened.assigned(current_user)
  11 + else merge_requests.opened
  12 + end.page(params[:page]).per(20)
  13 +
  14 + merge_requests.includes(:author, :project).order("closed, created_at desc")
  15 + end
  16 +end
app/contexts/notes_load.rb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +class NotesLoad < BaseContext
  2 + def execute
  3 + target_type = params[:target_type]
  4 + target_id = params[:target_id]
  5 + first_id = params[:first_id]
  6 + last_id = params[:last_id]
  7 +
  8 +
  9 + @notes = case target_type
  10 + when "commit"
  11 + then project.commit_notes(project.commit(target_id)).fresh.limit(20)
  12 + when "snippet"
  13 + then project.snippets.find(target_id).notes
  14 + when "wall"
  15 + then project.common_notes.order("created_at DESC").fresh.limit(50)
  16 + when "issue"
  17 + then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
  18 + when "merge_request"
  19 + then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
  20 + end
  21 +
  22 + @notes = if last_id
  23 + @notes.where("id > ?", last_id)
  24 + elsif first_id
  25 + @notes.where("id < ?", first_id)
  26 + else
  27 + @notes
  28 + end
  29 + end
  30 +end
app/controllers/admin/hooks_controller.rb 0 → 100644
@@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
  1 +class Admin::HooksController < ApplicationController
  2 + layout "admin"
  3 + before_filter :authenticate_user!
  4 + before_filter :authenticate_admin!
  5 +
  6 + def index
  7 + @hooks = SystemHook.all
  8 + @hook = SystemHook.new
  9 + end
  10 +
  11 + def create
  12 + @hook = SystemHook.new(params[:hook])
  13 +
  14 + if @hook.save
  15 + redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
  16 + else
  17 + @hooks = SystemHook.all
  18 + render :index
  19 + end
  20 + end
  21 +
  22 + def destroy
  23 + @hook = SystemHook.find(params[:id])
  24 + @hook.destroy
  25 +
  26 + redirect_to admin_hooks_path
  27 + end
  28 +
  29 +
  30 + def test
  31 + @hook = SystemHook.find(params[:hook_id])
  32 + data = {
  33 + event_name: "project_create",
  34 + name: "Ruby",
  35 + path: "ruby",
  36 + project_id: 1,
  37 + owner_name: "Someone",
  38 + owner_email: "example@gitlabhq.com"
  39 + }
  40 + @hook.execute(data)
  41 +
  42 + redirect_to :back
  43 + end
  44 +end
app/controllers/admin/mailer_controller.rb
@@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
1 -class Admin::MailerController < ApplicationController  
2 - layout "admin"  
3 - before_filter :authenticate_user!  
4 - before_filter :authenticate_admin!  
5 -  
6 - def preview  
7 -  
8 - end  
9 -  
10 - def preview_note  
11 - @note = Note.first  
12 - @user = @note.author  
13 - @project = @note.project  
14 - case params[:type]  
15 - when "Commit" then  
16 - @commit = @project.commit  
17 - render :file => 'notify/note_commit_email', :layout => 'notify'  
18 - when "Issue" then  
19 - @issue = Issue.first  
20 - render :file => 'notify/note_issue_email', :layout => 'notify'  
21 - else  
22 - render :file => 'notify/note_wall_email', :layout => 'notify'  
23 - end  
24 - rescue  
25 - render :text => "Preview not available"  
26 - end  
27 -  
28 - def preview_user_new  
29 - @user = User.first  
30 - @password = "DHasJKDHAS!"  
31 -  
32 - render :file => 'notify/new_user_email', :layout => 'notify'  
33 - rescue  
34 - render :text => "Preview not available"  
35 - end  
36 -  
37 - def preview_issue_new  
38 - @issue = Issue.first  
39 - @user = @issue.assignee  
40 - @project = @issue.project  
41 - render :file => 'notify/new_issue_email', :layout => 'notify'  
42 - rescue  
43 - render :text => "Preview not available"  
44 - end  
45 -end  
app/controllers/admin/projects_controller.rb
@@ -6,7 +6,7 @@ class Admin::ProjectsController &lt; ApplicationController @@ -6,7 +6,7 @@ class Admin::ProjectsController &lt; ApplicationController
6 def index 6 def index
7 @admin_projects = Project.scoped 7 @admin_projects = Project.scoped
8 @admin_projects = @admin_projects.search(params[:name]) if params[:name].present? 8 @admin_projects = @admin_projects.search(params[:name]) if params[:name].present?
9 - @admin_projects = @admin_projects.page(params[:page]) 9 + @admin_projects = @admin_projects.page(params[:page]).per(20)
10 end 10 end
11 11
12 def show 12 def show
@@ -72,6 +72,6 @@ class Admin::ProjectsController &lt; ApplicationController @@ -72,6 +72,6 @@ class Admin::ProjectsController &lt; ApplicationController
72 @admin_project = Project.find_by_code(params[:id]) 72 @admin_project = Project.find_by_code(params[:id])
73 @admin_project.destroy 73 @admin_project.destroy
74 74
75 - redirect_to admin_projects_url 75 + redirect_to admin_projects_url, notice: 'Project was successfully deleted.'
76 end 76 end
77 end 77 end
app/controllers/application_controller.rb
@@ -52,7 +52,7 @@ class ApplicationController &lt; ActionController::Base @@ -52,7 +52,7 @@ class ApplicationController &lt; ActionController::Base
52 52
53 def layout_by_resource 53 def layout_by_resource
54 if devise_controller? 54 if devise_controller?
55 - "devise" 55 + "devise_layout"
56 else 56 else
57 "application" 57 "application"
58 end 58 end
app/controllers/commits_controller.rb
@@ -26,43 +26,31 @@ class CommitsController &lt; ApplicationController @@ -26,43 +26,31 @@ class CommitsController &lt; ApplicationController
26 end 26 end
27 27
28 def show 28 def show
29 - @commit = project.commit(params[:id])  
30 -  
31 - git_not_found! and return unless @commit  
32 -  
33 - @commit = CommitDecorator.decorate(@commit)  
34 -  
35 - @note = @project.build_commit_note(@commit)  
36 - @comments_allowed = true  
37 - @line_notes = project.commit_line_notes(@commit)  
38 -  
39 - @notes_count = @line_notes.count + project.commit_notes(@commit).count  
40 -  
41 - if @commit.diffs.size > 200 && !params[:force_show_diff]  
42 - @suppress_diff = true 29 + result = CommitLoad.new(project, current_user, params).execute
  30 +
  31 + @commit = result[:commit]
  32 +
  33 + if @commit
  34 + @suppress_diff = result[:suppress_diff]
  35 + @note = result[:note]
  36 + @line_notes = result[:line_notes]
  37 + @notes_count = result[:notes_count]
  38 + @comments_allowed = true
  39 + else
  40 + return git_not_found!
43 end 41 end
  42 +
44 rescue Grit::Git::GitTimeout 43 rescue Grit::Git::GitTimeout
45 render "huge_commit" 44 render "huge_commit"
46 end 45 end
47 46
48 def compare 47 def compare
49 - first = project.commit(params[:to].try(:strip))  
50 - last = project.commit(params[:from].try(:strip)) 48 + result = Commit.compare(project, params[:from], params[:to])
51 49
52 - @diffs = []  
53 - @commits = [] 50 + @commits = result[:commits]
  51 + @commit = result[:commit]
  52 + @diffs = result[:diffs]
54 @line_notes = [] 53 @line_notes = []
55 -  
56 - if first && last  
57 - commits = [first, last].sort_by(&:created_at)  
58 - younger = commits.first  
59 - older = commits.last  
60 -  
61 -  
62 - @commits = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}  
63 - @diffs = project.repo.diff(younger.id, older.id) rescue []  
64 - @commit = Commit.new(older)  
65 - end  
66 end 54 end
67 55
68 def patch 56 def patch
app/controllers/dashboard_controller.rb
@@ -2,15 +2,13 @@ class DashboardController &lt; ApplicationController @@ -2,15 +2,13 @@ class DashboardController &lt; ApplicationController
2 respond_to :html 2 respond_to :html
3 3
4 def index 4 def index
5 - @projects = current_user.projects.includes(:events).order("events.created_at DESC")  
6 - @projects = @projects.page(params[:page]).per(40)  
7 -  
8 - @events = Event.where(:project_id => current_user.projects.map(&:id)).recent.limit(20)  
9 - 5 + @projects = current_user.projects_with_events.page(params[:page]).per(40)
  6 + @events = Event.recent_for_user(current_user).limit(20).offset(params[:offset] || 0)
10 @last_push = current_user.recent_push 7 @last_push = current_user.recent_push
11 8
12 respond_to do |format| 9 respond_to do |format|
13 format.html 10 format.html
  11 + format.js
14 format.atom { render :layout => false } 12 format.atom { render :layout => false }
15 end 13 end
16 end 14 end
app/controllers/hooks_controller.rb
@@ -11,24 +11,24 @@ class HooksController &lt; ApplicationController @@ -11,24 +11,24 @@ class HooksController &lt; ApplicationController
11 respond_to :html 11 respond_to :html
12 12
13 def index 13 def index
14 - @hooks = @project.web_hooks.all  
15 - @hook = WebHook.new 14 + @hooks = @project.hooks.all
  15 + @hook = ProjectHook.new
16 end 16 end
17 17
18 def create 18 def create
19 - @hook = @project.web_hooks.new(params[:hook]) 19 + @hook = @project.hooks.new(params[:hook])
20 @hook.save 20 @hook.save
21 21
22 if @hook.valid? 22 if @hook.valid?
23 redirect_to project_hooks_path(@project) 23 redirect_to project_hooks_path(@project)
24 else 24 else
25 - @hooks = @project.web_hooks.all 25 + @hooks = @project.hooks.all
26 render :index 26 render :index
27 end 27 end
28 end 28 end
29 29
30 def test 30 def test
31 - @hook = @project.web_hooks.find(params[:id]) 31 + @hook = @project.hooks.find(params[:id])
32 commits = @project.commits(@project.default_branch, nil, 3) 32 commits = @project.commits(@project.default_branch, nil, 3)
33 data = @project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}", current_user) 33 data = @project.post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}", current_user)
34 @hook.execute(data) 34 @hook.execute(data)
@@ -37,7 +37,7 @@ class HooksController &lt; ApplicationController @@ -37,7 +37,7 @@ class HooksController &lt; ApplicationController
37 end 37 end
38 38
39 def destroy 39 def destroy
40 - @hook = @project.web_hooks.find(params[:id]) 40 + @hook = @project.hooks.find(params[:id])
41 @hook.destroy 41 @hook.destroy
42 42
43 redirect_to project_hooks_path(@project) 43 redirect_to project_hooks_path(@project)
app/controllers/merge_requests_controller.rb
@@ -24,16 +24,7 @@ class MergeRequestsController &lt; ApplicationController @@ -24,16 +24,7 @@ class MergeRequestsController &lt; ApplicationController
24 24
25 25
26 def index 26 def index
27 - @merge_requests = @project.merge_requests  
28 -  
29 - @merge_requests = case params[:f].to_i  
30 - when 1 then @merge_requests  
31 - when 2 then @merge_requests.closed  
32 - when 3 then @merge_requests.opened.assigned(current_user)  
33 - else @merge_requests.opened  
34 - end.page(params[:page]).per(20)  
35 -  
36 - @merge_requests = @merge_requests.includes(:author, :project).order("closed, created_at desc") 27 + @merge_requests = MergeRequestsLoad.new(project, current_user, params).execute
37 end 28 end
38 29
39 def show 30 def show
app/controllers/notes_controller.rb
@@ -40,25 +40,6 @@ class NotesController &lt; ApplicationController @@ -40,25 +40,6 @@ class NotesController &lt; ApplicationController
40 protected 40 protected
41 41
42 def notes 42 def notes
43 - @notes = case params[:target_type]  
44 - when "commit"  
45 - then project.commit_notes(project.commit((params[:target_id]))).fresh.limit(20)  
46 - when "snippet"  
47 - then project.snippets.find(params[:target_id]).notes  
48 - when "wall"  
49 - then project.common_notes.order("created_at DESC").fresh.limit(50)  
50 - when "issue"  
51 - then project.issues.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)  
52 - when "merge_request"  
53 - then project.merge_requests.find(params[:target_id]).notes.inc_author.order("created_at DESC").limit(20)  
54 - end  
55 -  
56 - @notes = if params[:last_id]  
57 - @notes.where("id > ?", params[:last_id])  
58 - elsif params[:first_id]  
59 - @notes.where("id < ?", params[:first_id])  
60 - else  
61 - @notes  
62 - end 43 + @notes = NotesLoad.new(project, current_user, params).execute
63 end 44 end
64 end 45 end
app/controllers/omniauth_callbacks_controller.rb
1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController 1 class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  2 +
  3 + # Extend the standard message generation to accept our custom exception
  4 + def failure_message
  5 + exception = env["omniauth.error"]
  6 + if exception.class == OmniAuth::Error
  7 + error = exception.message
  8 + else
  9 + error = exception.error_reason if exception.respond_to?(:error_reason)
  10 + error ||= exception.error if exception.respond_to?(:error)
  11 + error ||= env["omniauth.error.type"].to_s
  12 + end
  13 + error.to_s.humanize if error
  14 + end
2 15
3 def ldap 16 def ldap
4 # We only find ourselves here if the authentication to LDAP was successful. 17 # We only find ourselves here if the authentication to LDAP was successful.
app/controllers/refs_controller.rb
@@ -9,7 +9,7 @@ class RefsController &lt; ApplicationController @@ -9,7 +9,7 @@ class RefsController &lt; ApplicationController
9 before_filter :require_non_empty_project 9 before_filter :require_non_empty_project
10 10
11 before_filter :ref 11 before_filter :ref
12 - before_filter :define_tree_vars, :only => [:tree, :blob, :blame] 12 + before_filter :define_tree_vars, :only => [:tree, :blob, :blame, :logs_tree]
13 before_filter :render_full_content 13 before_filter :render_full_content
14 14
15 layout "project" 15 layout "project"
@@ -46,6 +46,18 @@ class RefsController &lt; ApplicationController @@ -46,6 +46,18 @@ class RefsController &lt; ApplicationController
46 end 46 end
47 end 47 end
48 48
  49 + def logs_tree
  50 + contents = @tree.contents
  51 + @logs = contents.map do |content|
  52 + file = params[:path] ? File.join(params[:path], content.name) : content.name
  53 + last_commit = @project.commits(@commit.id, file, 1).last
  54 + {
  55 + :file_name => content.name,
  56 + :commit => last_commit
  57 + }
  58 + end
  59 + end
  60 +
49 def blob 61 def blob
50 if @tree.is_blob? 62 if @tree.is_blob?
51 if @tree.text? 63 if @tree.text?
@@ -79,6 +91,15 @@ class RefsController &lt; ApplicationController @@ -79,6 +91,15 @@ class RefsController &lt; ApplicationController
79 @commit = project.commit(@ref) 91 @commit = project.commit(@ref)
80 @tree = Tree.new(@commit.tree, project, @ref, params[:path]) 92 @tree = Tree.new(@commit.tree, project, @ref, params[:path])
81 @tree = TreeDecorator.new(@tree) 93 @tree = TreeDecorator.new(@tree)
  94 + @hex_path = Digest::SHA1.hexdigest(params[:path] || "/")
  95 +
  96 + if params[:path]
  97 + @history_path = tree_file_project_ref_path(@project, @ref, params[:path])
  98 + @logs_path = logs_file_project_ref_path(@project, @ref, params[:path])
  99 + else
  100 + @history_path = tree_project_ref_path(@project, @ref)
  101 + @logs_path = logs_tree_project_ref_path(@project, @ref)
  102 + end
82 rescue 103 rescue
83 return render_404 104 return render_404
84 end 105 end
app/decorators/event_decorator.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +class EventDecorator < ApplicationDecorator
  2 + decorates :event
  3 +
  4 + def feed_title
  5 + if self.issue?
  6 + "#{self.author_name} #{self.action_name} issue ##{self.target_id}:" + self.issue_title
  7 + elsif self.merge_request?
  8 + "#{self.author_name} #{self.action_name} MR ##{self.target_id}:" + self.merge_request_title
  9 + elsif self.push?
  10 + "#{self.author_name} #{self.push_action_name} #{self.ref_type} " + self.ref_name
  11 + else
  12 + ""
  13 + end
  14 + end
  15 +
  16 + def feed_url
  17 + if self.issue?
  18 + h.project_issue_url(self.project, self.issue)
  19 + elsif self.merge_request?
  20 + h.project_merge_request_url(self.project, self.merge_request)
  21 + elsif self.push?
  22 + h.project_commits_url(self.project, :ref => self.ref_name)
  23 + end
  24 + end
  25 +end
app/helpers/application_helper.rb
@@ -134,4 +134,8 @@ module ApplicationHelper @@ -134,4 +134,8 @@ module ApplicationHelper
134 end 134 end
135 active ? "current" : nil 135 active ? "current" : nil
136 end 136 end
  137 +
  138 + def hexdigest(string)
  139 + Digest::SHA1.hexdigest string
  140 + end
137 end 141 end
app/helpers/tree_helper.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +module TreeHelper
  2 + def tree_icon(content)
  3 + if content.is_a?(Grit::Blob)
  4 + if content.text?
  5 + image_tag "file_txt.png"
  6 + elsif content.image?
  7 + image_tag "file_img.png"
  8 + else
  9 + image_tag "file_bin.png"
  10 + end
  11 + else
  12 + image_tag "file_dir.png"
  13 + end
  14 + end
  15 +
  16 + def tree_hex_class(content)
  17 + "file_#{hexdigest(content.name)}"
  18 + end
  19 +
  20 + def tree_full_path(content)
  21 + if params[:path]
  22 + File.join(params[:path], content.name)
  23 + else
  24 + content.name
  25 + end
  26 + end
  27 +end
app/models/commit.rb
@@ -80,6 +80,29 @@ class Commit @@ -80,6 +80,29 @@ class Commit
80 def commits_between(repo, from, to) 80 def commits_between(repo, from, to)
81 repo.commits_between(from, to).map { |c| Commit.new(c) } 81 repo.commits_between(from, to).map { |c| Commit.new(c) }
82 end 82 end
  83 +
  84 + def compare(project, from, to)
  85 + first = project.commit(to.try(:strip))
  86 + last = project.commit(from.try(:strip))
  87 +
  88 + result = {
  89 + :commits => [],
  90 + :diffs => [],
  91 + :commit => nil
  92 + }
  93 +
  94 + if first && last
  95 + commits = [first, last].sort_by(&:created_at)
  96 + younger = commits.first
  97 + older = commits.last
  98 +
  99 + result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
  100 + result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
  101 + result[:commit] = Commit.new(older)
  102 + end
  103 +
  104 + result
  105 + end
83 end 106 end
84 107
85 def persisted? 108 def persisted?
app/models/event.rb
@@ -28,6 +28,10 @@ class Event &lt; ActiveRecord::Base @@ -28,6 +28,10 @@ class Event &lt; ActiveRecord::Base
28 end 28 end
29 end 29 end
30 30
  31 + def self.recent_for_user user
  32 + where(:project_id => user.projects.map(&:id)).recent
  33 + end
  34 +
31 # Next events currently enabled for system 35 # Next events currently enabled for system
32 # - push 36 # - push
33 # - new issue 37 # - new issue
app/models/merge_request.rb
@@ -22,7 +22,6 @@ class MergeRequest &lt; ActiveRecord::Base @@ -22,7 +22,6 @@ class MergeRequest &lt; ActiveRecord::Base
22 :should_remove_source_branch 22 :should_remove_source_branch
23 23
24 validates_presence_of :project_id 24 validates_presence_of :project_id
25 - validates_presence_of :assignee_id  
26 validates_presence_of :author_id 25 validates_presence_of :author_id
27 validates_presence_of :source_branch 26 validates_presence_of :source_branch
28 validates_presence_of :target_branch 27 validates_presence_of :target_branch
@@ -36,6 +35,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -36,6 +35,7 @@ class MergeRequest &lt; ActiveRecord::Base
36 delegate :name, 35 delegate :name,
37 :email, 36 :email,
38 :to => :assignee, 37 :to => :assignee,
  38 + :allow_nil => true,
39 :prefix => true 39 :prefix => true
40 40
41 validates :title, 41 validates :title,
@@ -128,7 +128,7 @@ class MergeRequest &lt; ActiveRecord::Base @@ -128,7 +128,7 @@ class MergeRequest &lt; ActiveRecord::Base
128 128
129 def unmerged_diffs 129 def unmerged_diffs
130 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)} 130 commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
131 - diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) 131 + diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
132 end 132 end
133 133
134 def last_commit 134 def last_commit
app/models/project.rb
@@ -19,7 +19,7 @@ class Project &lt; ActiveRecord::Base @@ -19,7 +19,7 @@ class Project &lt; ActiveRecord::Base
19 has_many :notes, :dependent => :destroy 19 has_many :notes, :dependent => :destroy
20 has_many :snippets, :dependent => :destroy 20 has_many :snippets, :dependent => :destroy
21 has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key" 21 has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
22 - has_many :web_hooks, :dependent => :destroy 22 + has_many :hooks, :dependent => :destroy, :class_name => "ProjectHook"
23 has_many :wikis, :dependent => :destroy 23 has_many :wikis, :dependent => :destroy
24 has_many :protected_branches, :dependent => :destroy 24 has_many :protected_branches, :dependent => :destroy
25 25
@@ -120,7 +120,7 @@ class Project &lt; ActiveRecord::Base @@ -120,7 +120,7 @@ class Project &lt; ActiveRecord::Base
120 errors.add(:path, " like 'gitolite-admin' is not allowed") 120 errors.add(:path, " like 'gitolite-admin' is not allowed")
121 end 121 end
122 end 122 end
123 - 123 +
124 def self.access_options 124 def self.access_options
125 UsersProject.access_roles 125 UsersProject.access_roles
126 end 126 end
app/models/project_hook.rb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +class ProjectHook < WebHook
  2 + belongs_to :project
  3 +end
app/models/system_hook.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class SystemHook < WebHook
  2 +
  3 + def async_execute(data)
  4 + Resque.enqueue(SystemHookWorker, id, data)
  5 + end
  6 +
  7 + def self.all_hooks_fire(data)
  8 + SystemHook.all.each do |sh|
  9 + sh.async_execute data
  10 + end
  11 + end
  12 +
  13 +end
app/models/user.rb
1 class User < ActiveRecord::Base 1 class User < ActiveRecord::Base
  2 +
2 include Account 3 include Account
3 4
4 - devise :database_authenticatable, :token_authenticatable, 5 + devise :database_authenticatable, :token_authenticatable, :lockable,
5 :recoverable, :rememberable, :trackable, :validatable, :omniauthable 6 :recoverable, :rememberable, :trackable, :validatable, :omniauthable
6 7
7 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, 8 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio,
8 - :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme, 9 + :name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme,
9 :theme_id, :force_random_password 10 :theme_id, :force_random_password
10 11
11 attr_accessor :force_random_password 12 attr_accessor :force_random_password
@@ -15,6 +16,11 @@ class User &lt; ActiveRecord::Base @@ -15,6 +16,11 @@ class User &lt; ActiveRecord::Base
15 has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id 16 has_many :my_own_projects, :class_name => "Project", :foreign_key => :owner_id
16 has_many :keys, :dependent => :destroy 17 has_many :keys, :dependent => :destroy
17 18
  19 + has_many :events,
  20 + :class_name => "Event",
  21 + :foreign_key => :author_id,
  22 + :dependent => :destroy
  23 +
18 has_many :recent_events, 24 has_many :recent_events,
19 :class_name => "Event", 25 :class_name => "Event",
20 :foreign_key => :author_id, 26 :foreign_key => :author_id,
@@ -80,7 +86,8 @@ class User &lt; ActiveRecord::Base @@ -80,7 +86,8 @@ class User &lt; ActiveRecord::Base
80 86
81 def self.find_for_ldap_auth(omniauth_info) 87 def self.find_for_ldap_auth(omniauth_info)
82 name = omniauth_info.name.force_encoding("utf-8") 88 name = omniauth_info.name.force_encoding("utf-8")
83 - email = omniauth_info.email.downcase 89 + email = omniauth_info.email.downcase unless omniauth_info.email.nil?
  90 + raise OmniAuth::Error, "LDAP accounts must provide an email address" if email.nil?
84 91
85 if @user = User.find_by_email(email) 92 if @user = User.find_by_email(email)
86 @user 93 @user
app/models/users_project.rb
@@ -68,7 +68,7 @@ class UsersProject &lt; ActiveRecord::Base @@ -68,7 +68,7 @@ class UsersProject &lt; ActiveRecord::Base
68 end 68 end
69 69
70 def repo_access_human 70 def repo_access_human
71 - "" 71 + self.class.access_roles.invert[self.project_access]
72 end 72 end
73 end 73 end
74 # == Schema Information 74 # == Schema Information
app/models/web_hook.rb
@@ -4,8 +4,6 @@ class WebHook &lt; ActiveRecord::Base @@ -4,8 +4,6 @@ class WebHook &lt; ActiveRecord::Base
4 # HTTParty timeout 4 # HTTParty timeout
5 default_timeout 10 5 default_timeout 10
6 6
7 - belongs_to :project  
8 -  
9 validates :url, 7 validates :url,
10 presence: true, 8 presence: true,
11 format: { 9 format: {
@@ -14,9 +12,8 @@ class WebHook &lt; ActiveRecord::Base @@ -14,9 +12,8 @@ class WebHook &lt; ActiveRecord::Base
14 12
15 def execute(data) 13 def execute(data)
16 WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }) 14 WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" })
17 - rescue  
18 - # There was a problem calling this web hook, let's forget about it.  
19 end 15 end
  16 +
20 end 17 end
21 # == Schema Information 18 # == Schema Information
22 # 19 #
app/observers/mailer_observer.rb
@@ -43,7 +43,7 @@ class MailerObserver &lt; ActiveRecord::Observer @@ -43,7 +43,7 @@ class MailerObserver &lt; ActiveRecord::Observer
43 end 43 end
44 44
45 def new_merge_request(merge_request) 45 def new_merge_request(merge_request)
46 - if merge_request.assignee != current_user 46 + if merge_request.assignee && merge_request.assignee != current_user
47 Notify.new_merge_request_email(merge_request.id).deliver 47 Notify.new_merge_request_email(merge_request.id).deliver
48 end 48 end
49 end 49 end
app/observers/system_hook_observer.rb 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  1 +class SystemHookObserver < ActiveRecord::Observer
  2 + observe :user, :project, :users_project
  3 +
  4 + def after_create(model)
  5 + if model.kind_of? Project
  6 + SystemHook.all_hooks_fire({
  7 + event_name: "project_create",
  8 + name: model.name,
  9 + path: model.path,
  10 + project_id: model.id,
  11 + owner_name: model.owner.name,
  12 + owner_email: model.owner.email,
  13 + created_at: model.created_at
  14 + })
  15 + elsif model.kind_of? User
  16 + SystemHook.all_hooks_fire({
  17 + event_name: "user_create",
  18 + name: model.name,
  19 + email: model.email,
  20 + created_at: model.created_at
  21 + })
  22 +
  23 + elsif model.kind_of? UsersProject
  24 + SystemHook.all_hooks_fire({
  25 + event_name: "user_add_to_team",
  26 + project_name: model.project.name,
  27 + project_path: model.project.path,
  28 + project_id: model.project_id,
  29 + user_name: model.user.name,
  30 + user_email: model.user.email,
  31 + project_access: model.repo_access_human,
  32 + created_at: model.created_at
  33 + })
  34 +
  35 + end
  36 + end
  37 +
  38 + def after_destroy(model)
  39 + if model.kind_of? Project
  40 + SystemHook.all_hooks_fire({
  41 + event_name: "project_destroy",
  42 + name: model.name,
  43 + path: model.path,
  44 + project_id: model.id,
  45 + owner_name: model.owner.name,
  46 + owner_email: model.owner.email,
  47 + })
  48 + elsif model.kind_of? User
  49 + SystemHook.all_hooks_fire({
  50 + event_name: "user_destroy",
  51 + name: model.name,
  52 + email: model.email
  53 + })
  54 +
  55 + elsif model.kind_of? UsersProject
  56 + SystemHook.all_hooks_fire({
  57 + event_name: "user_remove_from_team",
  58 + project_name: model.project.name,
  59 + project_path: model.project.path,
  60 + project_id: model.project_id,
  61 + user_name: model.user.name,
  62 + user_email: model.user.email,
  63 + project_access: model.repo_access_human
  64 + })
  65 + end
  66 + end
  67 +end
app/roles/account.rb
@@ -55,4 +55,8 @@ module Account @@ -55,4 +55,8 @@ module Account
55 # Take only latest one 55 # Take only latest one
56 events = events.recent.limit(1).first 56 events = events.recent.limit(1).first
57 end 57 end
  58 +
  59 + def projects_with_events
  60 + projects.includes(:events).order("events.created_at DESC")
  61 + end
58 end 62 end
app/roles/git_push.rb
@@ -27,7 +27,7 @@ module GitPush @@ -27,7 +27,7 @@ module GitPush
27 true 27 true
28 end 28 end
29 29
30 - def execute_web_hooks(oldrev, newrev, ref, user) 30 + def execute_hooks(oldrev, newrev, ref, user)
31 ref_parts = ref.split('/') 31 ref_parts = ref.split('/')
32 32
33 # Return if this is not a push to a branch (e.g. new commits) 33 # Return if this is not a push to a branch (e.g. new commits)
@@ -35,7 +35,7 @@ module GitPush @@ -35,7 +35,7 @@ module GitPush
35 35
36 data = post_receive_data(oldrev, newrev, ref, user) 36 data = post_receive_data(oldrev, newrev, ref, user)
37 37
38 - web_hooks.each { |web_hook| web_hook.execute(data) } 38 + hooks.each { |hook| hook.execute(data) }
39 end 39 end
40 40
41 def post_receive_data(oldrev, newrev, ref, user) 41 def post_receive_data(oldrev, newrev, ref, user)
@@ -97,7 +97,7 @@ module GitPush @@ -97,7 +97,7 @@ module GitPush
97 self.update_merge_requests(oldrev, newrev, ref, user) 97 self.update_merge_requests(oldrev, newrev, ref, user)
98 98
99 # Execute web hooks 99 # Execute web hooks
100 - self.execute_web_hooks(oldrev, newrev, ref, user) 100 + self.execute_hooks(oldrev, newrev, ref, user)
101 101
102 # Create satellite 102 # Create satellite
103 self.satellite.create unless self.satellite.exists? 103 self.satellite.create unless self.satellite.exists?
app/views/admin/hooks/_data_ex.html.erb 0 → 100644
@@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
  1 +<% data_ex_str = <<eos
  2 +1. Project created:
  3 +{
  4 + "created_at": "2012-07-21T07:30:54Z",
  5 + "event_name": "project_create",
  6 + "name": "StoreCloud",
  7 + "owner_email": "johnsmith@gmail.com",
  8 + "owner_name": "John Smith",
  9 + "path": "storecloud",
  10 + "project_id": 74
  11 +}
  12 +
  13 +2. Project destroyed:
  14 +{
  15 + "event_name": "project_destroy",
  16 + "name": "Underscore",
  17 + "owner_email": "johnsmith@gmail.com",
  18 + "owner_name": "John Smith",
  19 + "path": "underscore",
  20 + "project_id": 73
  21 +}
  22 +
  23 +3. New Team Member:
  24 +{
  25 + "created_at": "2012-07-21T07:30:56Z",
  26 + "event_name": "user_add_to_team",
  27 + "project_access": "Master",
  28 + "project_id": 74,
  29 + "project_name": "StoreCloud",
  30 + "project_path": "storecloud",
  31 + "owner_email": "johnsmith@gmail.com",
  32 + "owner_name": "John Smith",
  33 +}
  34 +
  35 +4. Team Member Removed:
  36 +{
  37 + "created_at": "2012-07-21T07:30:56Z",
  38 + "event_name": "user_remove_from_team",
  39 + "project_access": "Master",
  40 + "project_id": 74,
  41 + "project_name": "StoreCloud",
  42 + "project_path": "storecloud",
  43 + "owner_email": "johnsmith@gmail.com",
  44 + "owner_name": "John Smith",
  45 +}
  46 +
  47 +5. User created:
  48 +{
  49 + "created_at": "2012-07-21T07:44:07Z",
  50 + "email": "js@gitlabhq.com",
  51 + "event_name": "user_create",
  52 + "name": "John Smith"
  53 +}
  54 +
  55 +6. User removed:
  56 +{
  57 + "created_at": "2012-07-21T07:44:07Z",
  58 + "email": "js@gitlabhq.com",
  59 + "event_name": "user_destroy",
  60 + "name": "John Smith"
  61 +}
  62 +
  63 +eos
  64 +%>
  65 +<% js_lexer = Pygments::Lexer[:js] %>
  66 +<%= raw js_lexer.highlight(data_ex_str) %>
app/views/admin/hooks/index.html.haml 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +.alert.alert-info
  2 + %span
  3 + Post receive hooks for binding events.
  4 + %br
  5 + Read more about system hooks
  6 + %strong #{link_to "here", help_system_hooks_path, :class => "vlink"}
  7 +
  8 += form_for @hook, :as => :hook, :url => admin_hooks_path do |f|
  9 + -if @hook.errors.any?
  10 + .alert-message.block-message.error
  11 + - @hook.errors.full_messages.each do |msg|
  12 + %p= msg
  13 + .clearfix
  14 + = f.label :url, "URL:"
  15 + .input
  16 + = f.text_field :url, :class => "text_field xxlarge"
  17 + &nbsp;
  18 + = f.submit "Add System Hook", :class => "btn primary"
  19 +%hr
  20 +
  21 +-if @hooks.any?
  22 + %h3
  23 + Hooks
  24 + %small (#{@hooks.count})
  25 + %br
  26 + %table.admin-table
  27 + %tr
  28 + %th URL
  29 + %th Method
  30 + %th
  31 + - @hooks.each do |hook|
  32 + %tr
  33 + %td
  34 + = link_to admin_hook_path(hook) do
  35 + %strong= hook.url
  36 + = link_to 'Test Hook', admin_hook_test_path(hook), :class => "btn small right"
  37 + %td POST
  38 + %td
  39 + = link_to 'Remove', admin_hook_path(hook), :confirm => 'Are you sure?', :method => :delete, :class => "danger btn small right"
app/views/admin/logs/show.html.haml
1 -%h4  
2 - %i.icon-file  
3 - githost.log  
4 -%pre.logs  
5 - - Gitlab::Logger.read_latest.each do |line|  
6 - %span.log= line 1 +.file_holder#README
  2 + .file_title
  3 + %i.icon-file
  4 + githost.log
  5 + .file_content.logs
  6 + %ol
  7 + - Gitlab::Logger.read_latest.each do |line|
  8 + %li
  9 + %p= line
app/views/admin/mailer/preview.html.haml
@@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
1 -%p This is page with preview for all system emails that are sent to user  
2 -%p Email previews built based on existing Project/Commit/Issue base - so some preview maybe unavailable unless object appear in system  
3 -  
4 -#accordion  
5 - %h3  
6 - %a New user  
7 - %div  
8 - %iframe{ :src=> admin_mailer_preview_user_new_path, :width=>"100%", :height=>"350"}  
9 - %h3  
10 - %a New issue  
11 - %div  
12 - %iframe{ :src=> admin_mailer_preview_issue_new_path, :width=>"100%", :height=>"350"}  
13 - %h3  
14 - %a Commit note  
15 - %div  
16 - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Commit"), :width=>"100%", :height=>"350"}  
17 - %h3  
18 - %a Issue note  
19 - %div  
20 - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Issue"), :width=>"100%", :height=>"350"}  
21 - %h3  
22 - %a Wall note  
23 - %div  
24 - %iframe{ :src=> admin_mailer_preview_note_path(:type => "Wall"), :width=>"100%", :height=>"350"}  
25 -  
26 -:javascript  
27 - $(function() {  
28 - $("#accordion").accordion(); });  
app/views/admin/projects/index.html.haml
@@ -13,8 +13,8 @@ @@ -13,8 +13,8 @@
13 %th Team Members 13 %th Team Members
14 %th Post Receive 14 %th Post Receive
15 %th Last Commit 15 %th Last Commit
16 - %th  
17 - %th 16 + %th Edit
  17 + %th.cred Danger Zone!
18 18
19 - @admin_projects.each do |project| 19 - @admin_projects.each do |project|
20 %tr 20 %tr
@@ -24,5 +24,5 @@ @@ -24,5 +24,5 @@
24 %td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true 24 %td= check_box_tag :post_receive_file, 1, project.has_post_receive_file?, :disabled => true
25 %td= last_commit(project) 25 %td= last_commit(project)
26 %td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small" 26 %td= link_to 'Edit', edit_admin_project_path(project), :id => "edit_#{dom_id(project)}", :class => "btn small"
27 - %td= link_to 'Destroy', [:admin, project], :confirm => 'Are you sure?', :method => :delete, :class => "btn small danger" 27 + %td.bgred= link_to 'Destroy', [:admin, project], :confirm => "REMOVE #{project.name}? Are you sure?", :method => :delete, :class => "btn small danger"
28 = paginate @admin_projects, :theme => "admin" 28 = paginate @admin_projects, :theme => "admin"
app/views/admin/users/_form.html.haml
@@ -50,7 +50,7 @@ @@ -50,7 +50,7 @@
50 50
51 .alert 51 .alert
52 .clearfix 52 .clearfix
53 - %p Give user ability to manage application. 53 + %p Make the user a GitLab administrator.
54 = f.label :admin, :class => "checkbox" do 54 = f.label :admin, :class => "checkbox" do
55 = f.check_box :admin 55 = f.check_box :admin
56 %span Administrator 56 %span Administrator
@@ -59,11 +59,11 @@ @@ -59,11 +59,11 @@
59 - if @admin_user.blocked 59 - if @admin_user.blocked
60 %span 60 %span
61 = link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small" 61 = link_to 'Unblock', unblock_admin_user_path(@admin_user), :method => :put, :class => "btn small"
62 - This user is blocked and is not able to login GitLab 62 + This user is blocked and is not able to login to GitLab
63 - else 63 - else
64 %span 64 %span
65 = link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger" 65 = link_to 'Block', block_admin_user_path(@admin_user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
66 - Blocked user will removed from all projects &amp; will not be able to login to GitLab. 66 + Blocked users will be removed from all projects &amp; will not be able to login to GitLab.
67 .actions 67 .actions
68 = f.submit 'Save', :class => "btn primary" 68 = f.submit 'Save', :class => "btn primary"
69 - if @admin_user.new_record? 69 - if @admin_user.new_record?
app/views/admin/users/index.html.haml
@@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
27 %th Projects 27 %th Projects
28 %th Edit 28 %th Edit
29 %th Blocked 29 %th Blocked
30 - %th 30 + %th.cred Danger Zone!
31 31
32 - @admin_users.each do |user| 32 - @admin_users.each do |user|
33 %tr 33 %tr
@@ -41,6 +41,6 @@ @@ -41,6 +41,6 @@
41 = link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success" 41 = link_to 'Unblock', unblock_admin_user_path(user), :method => :put, :class => "btn small success"
42 - else 42 - else
43 = link_to 'Block', block_admin_user_path(user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger" 43 = link_to 'Block', block_admin_user_path(user), :confirm => 'USER WILL BE BLOCKED! Are you sure?', :method => :put, :class => "btn small danger"
44 - %td= link_to 'Destroy', [:admin, user], :confirm => 'USER WILL BE REMOVED! Are you sure?', :method => :delete, :class => "btn small danger" 44 + %td.bgred= link_to 'Destroy', [:admin, user], :confirm => "USER #{user.name} WILL BE REMOVED! Are you sure?", :method => :delete, :class => "btn small danger"
45 45
46 = paginate @admin_users, :theme => "admin" 46 = paginate @admin_users, :theme => "admin"
app/views/commits/_commits.html.haml
1 - @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits| 1 - @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
2 %div.ui-box 2 %div.ui-box
3 - %h5= day.stamp("28 Aug, 2010") 3 + %h5.small
  4 + %i.icon-calendar
  5 + = day.stamp("28 Aug, 2010")
4 %ul.unstyled= render commits 6 %ul.unstyled= render commits
app/views/commits/_diffs.html.haml
@@ -35,7 +35,13 @@ @@ -35,7 +35,13 @@
35 - if file.text? 35 - if file.text?
36 = render "commits/text_file", :diff => diff, :index => i 36 = render "commits/text_file", :diff => diff, :index => i
37 - elsif file.image? 37 - elsif file.image?
38 - .diff_file_content_image{:class => image_diff_class(diff)}  
39 - %img{:src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} 38 + - if diff.renamed_file || diff.new_file || diff.deleted_file
  39 + .diff_file_content_image
  40 + %img{:class => image_diff_class(diff), :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  41 + - else
  42 + - old_file = (@commit.prev_commit.tree / diff.old_path)
  43 + .diff_file_content_image.img_compared
  44 + %img{:class => "diff_image_removed", :src => "data:#{file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
  45 + %img{:class => "diff_image_added", :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
40 - else 46 - else
41 %p.nothing_here_message No preview for this file type 47 %p.nothing_here_message No preview for this file type
app/views/commits/_head.html.haml
@@ -13,12 +13,12 @@ @@ -13,12 +13,12 @@
13 %li{:class => "#{branches_tab_class}"} 13 %li{:class => "#{branches_tab_class}"}
14 = link_to project_repository_path(@project) do 14 = link_to project_repository_path(@project) do
15 Branches 15 Branches
16 - %span.number= @project.repo.branch_count 16 + %span.badge= @project.repo.branch_count
17 17
18 %li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"} 18 %li{:class => "#{'active' if current_page?(tags_project_repository_path(@project)) }"}
19 = link_to tags_project_repository_path(@project) do 19 = link_to tags_project_repository_path(@project) do
20 Tags 20 Tags
21 - %span.number= @project.repo.tag_count 21 + %span.badge= @project.repo.tag_count
22 22
23 23
24 - if current_page?(project_commits_path(@project)) && current_user.private_token 24 - if current_page?(project_commits_path(@project)) && current_user.private_token
app/views/commits/compare.html.haml
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 = "..." 20 = "..."
21 = text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge" 21 = text_field_tag :to, params[:to], :placeholder => "aa8b4ef", :class => "xlarge"
22 .actions 22 .actions
23 - = submit_tag "Compare", :class => "btn primary" 23 + = submit_tag "Compare", :class => "btn btn-primary"
24 24
25 25
26 - unless @commits.empty? 26 - unless @commits.empty?
app/views/dashboard/index.atom.builder
@@ -8,17 +8,10 @@ xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot;, &quot;xmlns:media&quot; =&gt; &quot;http://sear @@ -8,17 +8,10 @@ xml.feed &quot;xmlns&quot; =&gt; &quot;http://www.w3.org/2005/Atom&quot;, &quot;xmlns:media&quot; =&gt; &quot;http://sear
8 8
9 @events.each do |event| 9 @events.each do |event|
10 if event.allowed? 10 if event.allowed?
  11 + event = EventDecorator.decorate(event)
11 xml.entry do 12 xml.entry do
12 - if event.issue?  
13 - event_link = project_issue_url(event.project, event.issue)  
14 - event_title = event.issue_title  
15 - elsif event.merge_request?  
16 - event_link = project_merge_request_url(event.project, event.merge_request)  
17 - event_title = event.merge_request_title  
18 - elsif event.push?  
19 - event_link = project_commits_url(event.project, :ref => event.ref_name)  
20 - event_title = event.ref_name  
21 - end 13 + event_link = event.feed_url
  14 + event_title = event.feed_title
22 15
23 xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}" 16 xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
24 xml.link :href => event_link 17 xml.link :href => event_link
app/views/dashboard/index.html.haml
@@ -10,9 +10,10 @@ @@ -10,9 +10,10 @@
10 add new key 10 add new key
11 to your profile 11 to your profile
12 - if @events.any? 12 - if @events.any?
13 - = render @events 13 + .content_list= render @events
14 - else 14 - else
15 %h4.nothing_here_message Projects activity will be displayed here 15 %h4.nothing_here_message Projects activity will be displayed here
  16 + .loading.hide
16 .side 17 .side
17 = render "events/event_last_push", :event => @last_push 18 = render "events/event_last_push", :event => @last_push
18 .projects_box 19 .projects_box
@@ -54,3 +55,7 @@ @@ -54,3 +55,7 @@
54 New Project » 55 New Project »
55 - else 56 - else
56 If you will be added to project - it will be displayed here 57 If you will be added to project - it will be displayed here
  58 +
  59 +
  60 +:javascript
  61 + $(function(){ Pager.init(20); });
app/views/dashboard/index.js.haml
1 :plain 1 :plain
2 - $(".projects .activities").append("#{escape_javascript(render(@events))}"); 2 + Pager.append(#{@events.count}, "#{escape_javascript(render(@events))}");
app/views/events/_event_issue.html.haml
1 = image_tag gravatar_icon(event.author_email), :class => "avatar" 1 = image_tag gravatar_icon(event.author_email), :class => "avatar"
2 %strong #{event.author_name} 2 %strong #{event.author_name}
3 -%span.event_label= event.action_name  
4 -&nbsp;issue 3 +%span.event_label{:class => event.action_name}= event.action_name
  4 +issue
5 = link_to project_issue_path(event.project, event.issue) do 5 = link_to project_issue_path(event.project, event.issue) do
6 %strong= truncate event.issue_title 6 %strong= truncate event.issue_title
7 at 7 at
app/views/events/_event_last_push.html.haml
@@ -5,12 +5,9 @@ @@ -5,12 +5,9 @@
5 %span Your pushed to 5 %span Your pushed to
6 = event.ref_type 6 = event.ref_type
7 = link_to project_commits_path(event.project, :ref => event.ref_name) do 7 = link_to project_commits_path(event.project, :ref => event.ref_name) do
8 - %strong= event.ref_name 8 + %strong= truncate(event.ref_name, :length => 28)
9 at 9 at
10 %strong= link_to event.project.name, event.project 10 %strong= link_to event.project.name, event.project
11 - %span.cgray  
12 - = time_ago_in_words(event.created_at)  
13 - ago.  
14 11
15 = link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do 12 = link_to new_mr_path_from_push_event(event), :title => "New Merge Request", :class => "btn very_small primary" do
16 Create Merge Request 13 Create Merge Request
app/views/events/_event_merge_request.html.haml
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 .event_icon= image_tag "event_mr_merged.png" 2 .event_icon= image_tag "event_mr_merged.png"
3 = image_tag gravatar_icon(event.author_email), :class => "avatar" 3 = image_tag gravatar_icon(event.author_email), :class => "avatar"
4 %strong #{event.author_name} 4 %strong #{event.author_name}
5 -%span.event_label= event.action_name  
6 -&nbsp;merge request 5 +%span.event_label{:class => event.action_name}= event.action_name
  6 +merge request
7 = link_to project_merge_request_path(event.project, event.merge_request) do 7 = link_to project_merge_request_path(event.project, event.merge_request) do
8 %strong= truncate event.merge_request_title 8 %strong= truncate event.merge_request_title
9 at 9 at
app/views/events/_event_push.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 .event_icon= image_tag "event_push.png" 2 .event_icon= image_tag "event_push.png"
3 = image_tag gravatar_icon(event.author_email), :class => "avatar" 3 = image_tag gravatar_icon(event.author_email), :class => "avatar"
4 %strong #{event.author_name} 4 %strong #{event.author_name}
5 - %span.event_label= event.push_action_name 5 + %span.event_label.pushed= event.push_action_name
6 = event.ref_type 6 = event.ref_type
7 = link_to project_commits_path(event.project, :ref => event.ref_name) do 7 = link_to project_commits_path(event.project, :ref => event.ref_name) do
8 %strong= event.ref_name 8 %strong= event.ref_name
app/views/help/api.html.haml 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +%h3 API
  2 +.back_link
  3 + = link_to help_path do
  4 + &larr; to index
  5 +%hr
  6 +
  7 +%ol
  8 + %li
  9 + %a{:href => "#README"} README
  10 + %li
  11 + %a{:href => "#projects"} Projects
  12 + %li
  13 + %a{:href => "#users"} Users
  14 +
  15 +.file_holder#README
  16 + .file_title
  17 + %i.icon-file
  18 + README
  19 + .file_content.wiki
  20 + = preserve do
  21 + = markdown File.read(Rails.root.join("doc", "api", "README.md"))
  22 +
  23 +%br
  24 +
  25 +.file_holder#projects
  26 + .file_title
  27 + %i.icon-file
  28 + Projects
  29 + .file_content.wiki
  30 + = preserve do
  31 + = markdown File.read(Rails.root.join("doc", "api", "projects.md"))
  32 +
  33 +%br
  34 +
  35 +.file_holder#users
  36 + .file_title
  37 + %i.icon-file
  38 + Users
  39 + .file_content.wiki
  40 + = preserve do
  41 + = markdown File.read(Rails.root.join("doc", "api", "users.md"))
app/views/help/index.html.haml
@@ -22,3 +22,9 @@ @@ -22,3 +22,9 @@
22 22
23 %li 23 %li
24 %h5= link_to "Web Hooks", help_web_hooks_path 24 %h5= link_to "Web Hooks", help_web_hooks_path
  25 +
  26 + %li
  27 + %h5= link_to "System Hooks", help_system_hooks_path
  28 +
  29 + %li
  30 + %h5= link_to "API", help_api_path
app/views/help/system_hooks.html.haml 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +%h3 System hooks
  2 +.back_link
  3 + = link_to :back do
  4 + &larr; back
  5 +%hr
  6 +
  7 +%p.slead
  8 + Your Gitlab instance can perform HTTP POST request on next event: create_project, delete_project, create_user, delete_user, change_team_member.
  9 + %br
  10 + System Hooks can be used for logging or change information in LDAP server.
  11 + %br
  12 +%h5 Hooks request example:
  13 += render "admin/hooks/data_ex"
app/views/issues/_issues.html.haml
@@ -6,7 +6,9 @@ @@ -6,7 +6,9 @@
6 .row 6 .row
7 .span7= paginate @issues, :remote => true, :theme => "gitlab" 7 .span7= paginate @issues, :remote => true, :theme => "gitlab"
8 .span3.right 8 .span3.right
9 - %span.cgray.right #{@issues.total_count} issues for this filter 9 + %span.cgray.right
  10 + %span.issue_counter #{@issues.total_count}
  11 + issues for this filter
10 - else 12 - else
11 %li 13 %li
12 %h4.nothing_here_message Nothing to show here 14 %h4.nothing_here_message Nothing to show here
app/views/issues/_show.html.haml
@@ -12,9 +12,9 @@ @@ -12,9 +12,9 @@
12 = issue.notes.count 12 = issue.notes.count
13 - if can? current_user, :modify_issue, issue 13 - if can? current_user, :modify_issue, issue
14 - if issue.closed 14 - if issue.closed
15 - = link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped", :remote => true 15 + = link_to 'Reopen', project_issue_path(issue.project, issue, :issue => {:closed => false }, :status_only => true), :method => :put, :class => "btn small grouped reopen_issue", :remote => true
16 - else 16 - else
17 - = link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped", :remote => true 17 + = link_to 'Resolve', project_issue_path(issue.project, issue, :issue => {:closed => true }, :status_only => true), :method => :put, :class => "success btn small grouped close_issue", :remote => true
18 = link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do 18 = link_to edit_project_issue_path(issue.project, issue), :class => "btn small edit-issue-link", :remote => true do
19 %i.icon-edit 19 %i.icon-edit
20 Edit 20 Edit
@@ -35,6 +35,4 @@ @@ -35,6 +35,4 @@
35 &nbsp; 35 &nbsp;
36 36
37 - if issue.upvotes > 0 37 - if issue.upvotes > 0
38 - %span.badge.badge-success= "+#{issue.upvotes}"  
39 -  
40 - 38 + %span.badge.badge-success= "+#{issue.upvotes}"
41 \ No newline at end of file 39 \ No newline at end of file
app/views/issues/index.html.haml
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 .issues_content 2 .issues_content
3 %h3.page_title 3 %h3.page_title
4 Issues 4 Issues
5 - %small (#{@issues.total_count}) 5 + %small (<span class=issue_counter>#{@issues.total_count}</span>)
6 .right 6 .right
7 .span5 7 .span5
8 - if can? current_user, :write_issue, @project 8 - if can? current_user, :write_issue, @project
@@ -45,4 +45,4 @@ @@ -45,4 +45,4 @@
45 :javascript 45 :javascript
46 $(function(){ 46 $(function(){
47 issuesPage(); 47 issuesPage();
48 - }) 48 - })
  49 + })
49 \ No newline at end of file 50 \ No newline at end of file
app/views/keys/new.html.haml
1 -%h3 New key 1 +%h3.page_title New key
2 %hr 2 %hr
3 = render 'form' 3 = render 'form'
4 4
@@ -11,4 +11,4 @@ @@ -11,4 +11,4 @@
11 if( key_mail && key_mail.length > 0 && title.val() == '' ){ 11 if( key_mail && key_mail.length > 0 && title.val() == '' ){
12 $('#key_title').val( key_mail ); 12 $('#key_title').val( key_mail );
13 } 13 }
14 - });  
15 \ No newline at end of file 14 \ No newline at end of file
  15 + });
app/views/layouts/_project_menu.html.haml
@@ -17,14 +17,14 @@ @@ -17,14 +17,14 @@
17 %li{:class => tab_class(:issues)} 17 %li{:class => tab_class(:issues)}
18 = link_to project_issues_filter_path(@project) do 18 = link_to project_issues_filter_path(@project) do
19 Issues 19 Issues
20 - %span.count= @project.issues.opened.count 20 + %span.count.issue_counter= @project.issues.opened.count
21 21
22 - if @project.repo_exists? 22 - if @project.repo_exists?
23 - if @project.merge_requests_enabled 23 - if @project.merge_requests_enabled
24 %li{:class => tab_class(:merge_requests)} 24 %li{:class => tab_class(:merge_requests)}
25 = link_to project_merge_requests_path(@project) do 25 = link_to project_merge_requests_path(@project) do
26 Merge Requests 26 Merge Requests
27 - %span.count= @project.merge_requests.opened.count 27 + %span.count.merge_counter= @project.merge_requests.opened.count
28 28
29 - if @project.wall_enabled 29 - if @project.wall_enabled
30 %li{:class => tab_class(:wall)} 30 %li{:class => tab_class(:wall)}
app/views/layouts/admin.html.haml
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 %li{:class => tab_class(:admin_logs)} 15 %li{:class => tab_class(:admin_logs)}
16 = link_to "Logs", admin_logs_path 16 = link_to "Logs", admin_logs_path
17 %li{:class => tab_class(:admin_emails)} 17 %li{:class => tab_class(:admin_emails)}
18 - = link_to "Emails", admin_emails_path 18 + = link_to "Hooks", admin_hooks_path
19 %li{:class => tab_class(:admin_resque)} 19 %li{:class => tab_class(:admin_resque)}
20 = link_to "Resque", admin_resque_path 20 = link_to "Resque", admin_resque_path
21 21
app/views/layouts/devise.html.haml
@@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
1 -!!! 5  
2 -%html{ :lang => "en"}  
3 - = render "layouts/head"  
4 - %body.ui_basic.login-page  
5 - = render :partial => "layouts/flash"  
6 - .container= yield  
app/views/layouts/devise_layout.html.haml 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +!!! 5
  2 +%html{ :lang => "en"}
  3 + = render "layouts/head"
  4 + %body.ui_basic.login-page
  5 + = render :partial => "layouts/flash"
  6 + .container= yield
app/views/layouts/profile.html.haml
@@ -12,16 +12,17 @@ @@ -12,16 +12,17 @@
12 %li{:class => tab_class(:password)} 12 %li{:class => tab_class(:password)}
13 = link_to "Password", profile_password_path 13 = link_to "Password", profile_password_path
14 14
  15 + %li{:class => tab_class(:ssh_keys)}
  16 + = link_to keys_path do
  17 + SSH Keys
  18 + %span.count= current_user.keys.count
  19 +
15 %li{:class => tab_class(:token)} 20 %li{:class => tab_class(:token)}
16 = link_to "Token", profile_token_path 21 = link_to "Token", profile_token_path
17 22
18 %li{:class => tab_class(:design)} 23 %li{:class => tab_class(:design)}
19 = link_to "Design", profile_design_path 24 = link_to "Design", profile_design_path
20 25
21 - %li{:class => tab_class(:ssh_keys)}  
22 - = link_to keys_path do  
23 - SSH Keys  
24 - %span.count= current_user.keys.count  
25 26
26 .content 27 .content
27 = yield 28 = yield
app/views/merge_requests/_form.html.haml
@@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
5 - @merge_request.errors.full_messages.each do |msg| 5 - @merge_request.errors.full_messages.each do |msg|
6 %li= msg 6 %li= msg
7 7
8 - %h3.padded.cgray 1. Select Branches 8 + %h4.cdark 1. Select Branches
  9 + %br
9 10
10 .row 11 .row
11 .span6 12 .span6
@@ -30,14 +31,21 @@ @@ -30,14 +31,21 @@
30 .bottom_commit 31 .bottom_commit
31 .mr_target_commit 32 .mr_target_commit
32 33
33 - %h3.padded.cgray 2. Fill info 34 + %h4.cdark 2. Fill info
  35 +
34 .clearfix 36 .clearfix
35 - = f.label :assignee_id, "Assign to", :class => "control-label"  
36 - .controls= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px") 37 + .main_box
  38 + .top_box_content
  39 + = f.label :title do
  40 + %strong= "Title *"
  41 + .input= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5
  42 + .middle_box_content
  43 + = f.label :assignee_id do
  44 + %i.icon-user
  45 + Assign to
  46 + .input= f.select(:assignee_id, @project.users.all.collect {|p| [ p.name, p.id ] }, { :include_blank => "Select user" }, :style => "width:250px")
37 47
38 .control-group 48 .control-group
39 - = f.label :title, :class => "control-label"  
40 - .controls= f.text_field :title, :class => "input-xxlarge pad", :maxlength => 255, :rows => 5  
41 49
42 .form-actions 50 .form-actions
43 = f.submit 'Save', :class => "btn-primary btn" 51 = f.submit 'Save', :class => "btn-primary btn"
app/views/merge_requests/_merge_request.html.haml
@@ -15,12 +15,14 @@ @@ -15,12 +15,14 @@
15 &rarr; 15 &rarr;
16 = merge_request.target_branch 16 = merge_request.target_branch
17 = image_tag gravatar_icon(merge_request.author_email), :class => "avatar" 17 = image_tag gravatar_icon(merge_request.author_email), :class => "avatar"
  18 +
  19 + = link_to project_merge_request_path(merge_request.project, merge_request) do
  20 + %p.row_title= truncate(merge_request.title, :length => 80)
  21 +
18 %span.update-author 22 %span.update-author
19 - %strong= merge_request.author_name  
20 - authored 23 + %small.cdark= "##{merge_request.id}"
  24 + authored by #{merge_request.author_name}
21 = time_ago_in_words(merge_request.created_at) 25 = time_ago_in_words(merge_request.created_at)
22 ago 26 ago
23 - if merge_request.upvotes > 0 27 - if merge_request.upvotes > 0
24 %span.badge.badge-success= "+#{merge_request.upvotes}" 28 %span.badge.badge-success= "+#{merge_request.upvotes}"
25 - = link_to project_merge_request_path(merge_request.project, merge_request) do  
26 - %p.row_title= truncate(merge_request.title, :length => 80)  
app/views/merge_requests/edit.html.haml
1 -%h3 1 +%h3.page_title
2 = "Edit merge request #{@merge_request.id}" 2 = "Edit merge request #{@merge_request.id}"
3 %hr 3 %hr
4 = render 'form' 4 = render 'form'
app/views/merge_requests/new.html.haml
1 -%h3 New Merge Request 1 +%h3.page_title New Merge Request
2 %hr 2 %hr
3 = render 'form' 3 = render 'form'
app/views/merge_requests/show/_commits.html.haml
1 - if @commits.present? 1 - if @commits.present?
2 .ui-box 2 .ui-box
3 - %h5 Commits (#{@commits.count}) 3 + %h5
  4 + %i.icon-list
  5 + Commits (#{@commits.count})
4 .merge-request-commits 6 .merge-request-commits
5 - if @commits.count > 8 7 - if @commits.count > 8
6 %ul.first_mr_commits.unstyled 8 %ul.first_mr_commits.unstyled
app/views/merge_requests/show/_mr_box.html.haml
@@ -13,9 +13,10 @@ @@ -13,9 +13,10 @@
13 = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av" 13 = image_tag gravatar_icon(@merge_request.author_email), :width => 16, :class => "lil_av"
14 %strong.author= link_to_merge_request_author(@merge_request) 14 %strong.author= link_to_merge_request_author(@merge_request)
15 15
16 - %cite.cgray and currently assigned to  
17 - = image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"  
18 - %strong.author= link_to_merge_request_assignee(@merge_request) 16 + - if @merge_request.assignee
  17 + %cite.cgray and currently assigned to
  18 + = image_tag gravatar_icon(@merge_request.assignee_email), :width => 16, :class => "lil_av"
  19 + %strong.author= link_to_merge_request_assignee(@merge_request)
19 20
20 21
21 - if @merge_request.closed 22 - if @merge_request.closed
app/views/notes/_form.html.haml
@@ -32,4 +32,4 @@ @@ -32,4 +32,4 @@
32 %span Any file less than 10 MB 32 %span Any file less than 10 MB
33 33
34 34
35 - = f.submit 'Add Comment', :class => "btn primary", :id => "submit_note" 35 + = f.submit 'Add Comment', :class => "btn primary submit_note", :id => "submit_note"
app/views/notes/_per_line_form.html.haml
@@ -24,7 +24,7 @@ @@ -24,7 +24,7 @@
24 = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit" 24 = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
25 %span Commit author 25 %span Commit author
26 .actions 26 .actions
27 - = f.submit 'Add note', :class => "btn primary", :id => "submit_note" 27 + = f.submit 'Add note', :class => "btn primary submit_note", :id => "submit_note"
28 = link_to "Close", "#", :class => "btn hide-button" 28 = link_to "Close", "#", :class => "btn hide-button"
29 29
30 :javascript 30 :javascript
app/views/notes/_reply_button.html.haml
1 %tr.line_notes_row.reply 1 %tr.line_notes_row.reply
2 %td{:colspan => 3} 2 %td{:colspan => 3}
  3 + %i.icon-comment
3 = link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line" 4 = link_to "Reply", "#", :class => "line_note_reply_link", "line_code" => line_code, :title => "Add note for this line"
app/views/refs/_tree.html.haml
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 = render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree } 13 = render :partial => "refs/tree_file", :locals => { :name => tree.name, :content => tree.data, :file => tree }
14 - else 14 - else
15 - contents = tree.contents 15 - contents = tree.contents
16 - %table#tree-slider.bordered-table.table 16 + %table#tree-slider.bordered-table.table{:class => "table_#{@hex_path}" }
17 %thead 17 %thead
18 %th Name 18 %th Name
19 %th Last Update 19 %th Last Update
@@ -29,34 +29,39 @@ @@ -29,34 +29,39 @@
29 %td 29 %td
30 %td 30 %td
31 31
  32 + - index = 0
32 - contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content| 33 - contents.select{ |i| i.is_a?(Grit::Tree)}.each do |content|
33 - = render :partial => "refs/tree_item", :locals => { :content => content } 34 + = render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
34 - contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content| 35 - contents.select{ |i| i.is_a?(Grit::Blob)}.each do |content|
35 - = render :partial => "refs/tree_item", :locals => { :content => content } 36 + = render :partial => "refs/tree_item", :locals => { :content => content, :index => (index += 1) }
36 - contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content| 37 - contents.select{ |i| i.is_a?(Grit::Submodule)}.each do |content|
37 - = render :partial => "refs/submodule_item", :locals => { :content => content } 38 + = render :partial => "refs/submodule_item", :locals => { :content => content, :index => (index += 1) }
38 39
39 - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first 40 - if content = contents.select{ |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }.first
40 - #tree-readme-holder  
41 - %h3= content.name  
42 - .readme 41 + .file_holder#README
  42 + .file_title
  43 + %i.icon-file
  44 + = content.name
  45 + .file_content.wiki
43 - if content.name =~ /\.(md|markdown)$/i 46 - if content.name =~ /\.(md|markdown)$/i
44 = preserve do 47 = preserve do
45 = markdown(content.data) 48 = markdown(content.data)
46 - else 49 - else
47 = simple_format(content.data) 50 = simple_format(content.data)
48 51
49 -- if params[:path]  
50 - - history_path = tree_file_project_ref_path(@project, @ref, params[:path])  
51 -- else  
52 - - history_path = tree_project_ref_path(@project, @ref)  
53 :javascript 52 :javascript
54 $(function(){ 53 $(function(){
55 $('select#branch').selectmenu({style:'popup', width:200}); 54 $('select#branch').selectmenu({style:'popup', width:200});
56 $('select#tag').selectmenu({style:'popup', width:200}); 55 $('select#tag').selectmenu({style:'popup', width:200});
57 $('.project-refs-select').chosen(); 56 $('.project-refs-select').chosen();
58 57
59 - history.pushState({ path: this.path }, '', "#{history_path}") 58 + history.pushState({ path: this.path }, '', "#{@history_path}");
  59 +
  60 + });
  61 +
  62 + // Load last commit log for each file in tree
  63 + $(window).load(function(){
  64 + ajaxGet('#{@logs_path}');
60 }); 65 });
61 66
62 67
app/views/refs/_tree_commit.html.haml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +- if tm
  2 + %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)
  3 += link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link"
app/views/refs/_tree_file.html.haml
1 -.view_file  
2 - .view_file_header 1 +.file_holder
  2 + .file_title
3 %i.icon-file 3 %i.icon-file
4 %span.file_name 4 %span.file_name
5 = name 5 = name
@@ -10,26 +10,28 @@ @@ -10,26 +10,28 @@
10 = link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small" 10 = link_to "blame", blame_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
11 - if file.text? 11 - if file.text?
12 - if name =~ /\.(md|markdown)$/i 12 - if name =~ /\.(md|markdown)$/i
13 - #tree-readme-holder  
14 - .readme  
15 - = preserve do  
16 - = markdown(file.data) 13 + .file_content.wiki
  14 + = preserve do
  15 + = markdown(file.data)
17 - else 16 - else
18 - .view_file_content 17 + .file_content.code
19 - unless file.empty? 18 - unless file.empty?
20 %div{:class => current_user.dark_scheme ? "black" : "white"} 19 %div{:class => current_user.dark_scheme ? "black" : "white"}
21 = preserve do 20 = preserve do
22 = raw file.colorize(options: { linenos: 'True'}) 21 = raw file.colorize(options: { linenos: 'True'})
23 - else 22 - else
24 %h4.nothing_here_message Empty file 23 %h4.nothing_here_message Empty file
  24 +
25 - elsif file.image? 25 - elsif file.image?
26 - .view_file_content_image 26 + .file_content.image_file
27 %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} 27 %img{ :src => "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
  28 +
28 - else 29 - else
29 - %center  
30 - = link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do  
31 - %div.padded  
32 - %br  
33 - = image_tag "download.png", :width => 64  
34 - %h3  
35 - Download (#{file.mb_size}) 30 + .file_content.blob_file
  31 + %center
  32 + = link_to blob_project_ref_path(@project, @ref, :path => params[:path]) do
  33 + %div.padded
  34 + %br
  35 + = image_tag "download.png", :width => 64
  36 + %h3
  37 + Download (#{file.mb_size})
app/views/refs/_tree_item.html.haml
1 -- file = params[:path] ? File.join(params[:path], content.name) : content.name  
2 -- content_commit = @project.commits(@commit.id, file, 1).last  
3 -- return unless content_commit  
4 -%tr{ :class => "tree-item", :url => tree_file_project_ref_path(@project, @ref, file) } 1 +- file = tree_full_path(content)
  2 +%tr{ :class => "tree-item #{tree_hex_class(content)}", :url => tree_file_project_ref_path(@project, @ref, file) }
5 %td.tree-item-file-name 3 %td.tree-item-file-name
6 - - if content.is_a?(Grit::Blob)  
7 - - if content.text?  
8 - = image_tag "file_txt.png"  
9 - - elsif content.image?  
10 - = image_tag "file_img.png"  
11 - - else  
12 - = image_tag "file_bin.png"  
13 - - else  
14 - = image_tag "file_dir.png" 4 + = tree_icon(content)
15 = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true 5 = link_to truncate(content.name, :length => 40), tree_file_project_ref_path(@project, @ref || @commit.id, file), :remote => :true
16 - %td.cgray  
17 - = time_ago_in_words(content_commit.committed_date)  
18 - ago  
19 - %td.commit  
20 - - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)  
21 - - if tm  
22 - %strong= link_to "[#{tm.user_name}]", project_team_member_path(@project, tm)  
23 - = link_to truncate(content_commit.safe_message, :length => tm ? 30 : 50), project_commit_path(@project, content_commit.id), :class => "tree-commit-link" 6 + %td.tree_time_ago.cgray
  7 + - if index == 1
  8 + %span.log_loading
  9 + Loading commit data..
  10 + = image_tag "ajax_loader_tree.gif", :width => 14
  11 + %td.tree_commit
app/views/refs/blame.html.haml
@@ -11,8 +11,8 @@ @@ -11,8 +11,8 @@
11 %li= link 11 %li= link
12 .clear 12 .clear
13 13
14 - .view_file.blame_file  
15 - .view_file_header 14 + .file_holder
  15 + .file_title
16 %i.icon-file 16 %i.icon-file
17 %span.file_name 17 %span.file_name
18 = @tree.name 18 = @tree.name
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank" 21 = link_to "raw", blob_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small", :target => "_blank"
22 = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small" 22 = link_to "history", project_commits_path(@project, :path => params[:path], :ref => @ref), :class => "btn very_small"
23 = link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small" 23 = link_to "source", tree_file_project_ref_path(@project, @ref, :path => params[:path]), :class => "btn very_small"
24 - .view_file_content 24 + .file_content.blame
25 %table 25 %table
26 - @blame.each do |commit, lines| 26 - @blame.each do |commit, lines|
27 - commit = Commit.new(commit) 27 - commit = Commit.new(commit)
@@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
29 %td.author 29 %td.author
30 = image_tag gravatar_icon(commit.author_email, 16) 30 = image_tag gravatar_icon(commit.author_email, 16)
31 = commit.author_name 31 = commit.author_name
32 - %td.commit 32 + %td.blame_commit
33 &nbsp; 33 &nbsp;
34 = link_to project_commit_path(@project, :id => commit.id) do 34 = link_to project_commit_path(@project, :id => commit.id) do
35 %code= commit.id.to_s[0..10] 35 %code= commit.id.to_s[0..10]
@@ -37,8 +37,7 @@ @@ -37,8 +37,7 @@
37 %td.lines 37 %td.lines
38 = preserve do 38 = preserve do
39 %pre 39 %pre
40 - - lines.each do |line|  
41 - = line 40 + = Gitlab::Encode.utf8 lines.join("\n")
42 41
43 :javascript 42 :javascript
44 $(function(){ 43 $(function(){
app/views/refs/logs_tree.js.haml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +- @logs.each do |content_data|
  2 + - file_name = content_data[:file_name]
  3 + - content_commit = content_data[:commit]
  4 + - tm = @project.team_member_by_name_or_email(content_commit.author_email, content_commit.author_name)
  5 +
  6 + :plain
  7 + var row = $("table.table_#{@hex_path} tr.file_#{hexdigest(file_name)}");
  8 + row.find("td.tree_time_ago").html('#{escape_javascript(time_ago_in_words(content_commit.committed_date))} ago');
  9 + row.find("td.tree_commit").html('#{escape_javascript(render("tree_commit", :tm => tm, :content_commit => content_commit))}');
app/views/refs/tree.js.haml
1 :plain 1 :plain
  2 + // Load Files list
2 $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}"); 3 $("#tree-holder").html("#{escape_javascript(render(:partial => "tree", :locals => {:repo => @repo, :commit => @commit, :tree => @tree}))}");
3 $("#tree-content-holder").show("slide", { direction: "right" }, 150); 4 $("#tree-content-holder").show("slide", { direction: "right" }, 150);
4 $('.project-refs-form #path').val("#{params[:path]}"); 5 $('.project-refs-form #path').val("#{params[:path]}");
  6 +
  7 + // Load last commit log for each file in tree
  8 + $('#tree-slider').waitForImages(function() {
  9 + ajaxGet('#{@logs_path}');
  10 + });
app/views/snippets/show.html.haml
@@ -7,16 +7,14 @@ @@ -7,16 +7,14 @@
7 = link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right" 7 = link_to "Edit", edit_project_snippet_path(@project, @snippet), :class => "btn small right"
8 8
9 %br 9 %br
10 -#tree-holder  
11 - #tree-content-holder  
12 - .view_file  
13 - .view_file_header  
14 - %i.icon-file  
15 - %strong= @snippet.file_name  
16 - %span.options  
17 - = link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"  
18 - .view_file_content  
19 - %div{:class => current_user.dark_scheme ? "black" : ""}  
20 - = raw @snippet.colorize(options: { linenos: 'True'}) 10 +.file_holder
  11 + .file_title
  12 + %i.icon-file
  13 + %strong= @snippet.file_name
  14 + %span.options
  15 + = link_to "raw", raw_project_snippet_path(@project, @snippet), :class => "btn very_small", :target => "_blank"
  16 + .file_content.code
  17 + %div{:class => current_user.dark_scheme ? "black" : ""}
  18 + = raw @snippet.colorize(options: { linenos: 'True'})
21 19
22 = render "notes/notes", :tid => @snippet.id, :tt => "snippet" 20 = render "notes/notes", :tid => @snippet.id, :tt => "snippet"
app/workers/system_hook_worker.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +class SystemHookWorker
  2 + @queue = :system_hook
  3 +
  4 + def self.perform(hook_id, data)
  5 + SystemHook.find(hook_id).execute data
  6 + end
  7 +end
config/application.rb
@@ -23,7 +23,7 @@ module Gitlab @@ -23,7 +23,7 @@ module Gitlab
23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 23 # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
24 24
25 # Activate observers that should always be running. 25 # Activate observers that should always be running.
26 - config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer 26 + config.active_record.observers = :mailer_observer, :activity_observer, :project_observer, :key_observer, :issue_observer, :user_observer, :system_hook_observer
27 27
28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 28 # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 29 # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
config/gitlab.yml.example
@@ -21,6 +21,8 @@ email: @@ -21,6 +21,8 @@ email:
21 # Like default project limit for user etc 21 # Like default project limit for user etc
22 app: 22 app:
23 default_projects_limit: 10 23 default_projects_limit: 10
  24 + # backup_path: "/vol/backups" # default: Rails.root + backups/
  25 + # backup_keep_time: 604800 # default: 0 (forever) (in seconds)
24 26
25 27
26 # 28 #