Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Ariejan de Vroom 2012-01-23 09:38:49 +01:00
commit 4587ab6ff6
254 changed files with 4941 additions and 1601 deletions

1
.foreman Normal file
View file

@ -0,0 +1 @@
port: 3000

4
.gitignore vendored
View file

@ -1,9 +1,13 @@
.bundle
.rbx/
db/*.sqlite3
db/*.sqlite3-journal
log/*.log
tmp/
.sass-cache/
coverage/*
*.swp
public/uploads/
.rvmrc
.directory
nohup.out

1
.rvmrc
View file

@ -1 +0,0 @@
rvm use 1.9.2-p290

View file

@ -1,3 +1,4 @@
before_install: sudo apt-get install libicu-dev -y
branches:
only:
- 'master'

View file

@ -1,6 +1,15 @@
v 2.1.0
- Project tab r1
- Repository tab r1
v 2.0.0
- gitolite as main git host system
- merge requests
- project/repo access
- link to commit/issue feed
- design tab
- improved email notifications
- restyled dashboard
- bugfix
v 1.2.2

10
Gemfile
View file

@ -3,9 +3,11 @@ source "http://rubygems.org"
gem "rails", "3.1.1"
gem "sqlite3"
gem "rake", "0.9.2.2"
gem "devise", "1.5.0"
gem "stamp"
gem "kaminari"
gem "haml", "3.1.4"
gem "haml-rails"
gem "jquery-rails"
gem "grit", :git => "https://github.com/gitlabhq/grit.git"
@ -15,14 +17,17 @@ gem "six"
gem "therubyracer"
gem "faker"
gem "seed-fu", "~> 2.1.0"
gem "pygments.rb", "0.2.3"
gem "pygments.rb", "0.2.4"
gem "thin"
gem "git"
gem "acts_as_list"
gem "rdiscount"
gem "acts-as-taggable-on", "~> 2.1.0"
gem "drapper"
gem "rchardet19", "~> 1.3.5"
gem "resque"
gem "httparty"
gem "charlock_holmes"
gem "foreman"
group :assets do
gem "sass-rails", "~> 3.1.0"
@ -47,6 +52,7 @@ group :development, :test do
gem "awesome_print"
gem "database_cleaner"
gem "launchy"
gem "webmock"
end
group :test do

View file

@ -77,6 +77,7 @@ GEM
xpath (~> 0.1.4)
carrierwave (0.5.8)
activesupport (~> 3.0)
charlock_holmes (0.6.8)
childprocess (0.2.2)
ffi (~> 1.0.6)
coffee-rails (3.1.1)
@ -87,6 +88,7 @@ GEM
execjs
coffee-script-source (1.1.3)
columnize (0.3.4)
crack (0.3.1)
daemons (1.1.4)
database_cleaner (0.7.0)
devise (1.5.0)
@ -102,8 +104,11 @@ GEM
faker (1.0.1)
i18n (~> 0.4)
ffi (1.0.11)
foreman (0.27.0)
term-ansicolor (~> 1.0.5)
thor (>= 0.13.6)
git (1.2.5)
haml (3.1.3)
haml (3.1.4)
haml-rails (0.3.4)
actionpack (~> 3.0)
activesupport (~> 3.0)
@ -111,6 +116,9 @@ GEM
railties (~> 3.0)
hashery (1.4.0)
hike (1.2.1)
httparty (0.8.1)
multi_json
multi_xml
i18n (0.6.0)
jquery-rails (1.0.17)
railties (~> 3.0)
@ -132,17 +140,20 @@ GEM
treetop (~> 1.4.8)
mime-types (1.17.2)
multi_json (1.0.3)
multi_xml (0.4.1)
nokogiri (1.5.0)
orm_adapter (0.0.5)
polyglot (0.3.3)
posix-spawn (0.3.6)
pygments.rb (0.2.3)
rubypython (>= 0.5.1)
pygments.rb (0.2.4)
rubypython (~> 0.5.3)
rack (1.3.5)
rack-cache (1.1)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-protection (1.1.4)
rack
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
@ -165,10 +176,17 @@ GEM
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2.2)
rchardet19 (1.3.5)
rdiscount (1.6.8)
rdoc (3.11)
json (~> 1.4)
redis (2.2.2)
redis-namespace (1.0.3)
redis (< 3.0.0)
resque (1.19.0)
multi_json (~> 1.0)
redis-namespace (~> 1.0.2)
sinatra (>= 0.9.2)
vegas (~> 0.1.2)
rspec (2.7.0)
rspec-core (~> 2.7.0)
rspec-expectations (~> 2.7.0)
@ -220,6 +238,10 @@ GEM
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
sinatra (1.3.1)
rack (~> 1.3, >= 1.3.4)
rack-protection (~> 1.1, >= 1.1.2)
tilt (~> 1.3, >= 1.3.3)
six (0.2.0)
sprockets (2.0.3)
hike (~> 1.2)
@ -227,6 +249,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.4)
stamp (0.1.6)
term-ansicolor (1.0.7)
therubyracer (0.9.9)
libv8 (~> 3.3.10)
thin (1.3.1)
@ -244,8 +267,13 @@ GEM
uglifier (1.1.0)
execjs (>= 0.3.0)
multi_json (>= 1.0.2)
vegas (0.1.8)
rack (>= 1.0.0)
warden (1.1.0)
rack (>= 1.0)
webmock (1.7.8)
addressable (~> 2.2, > 2.2.5)
crack (>= 0.1.7)
xpath (0.1.4)
nokogiri (~> 1.3)
@ -261,24 +289,29 @@ DEPENDENCIES
awesome_print
capybara
carrierwave
charlock_holmes
coffee-rails (~> 3.1.0)
database_cleaner
devise (= 1.5.0)
drapper
faker
foreman
git
gitolite!
grit!
haml (= 3.1.4)
haml-rails
httparty
jquery-rails
kaminari
launchy
letter_opener
pygments.rb (= 0.2.3)
pygments.rb (= 0.2.4)
rails (= 3.1.1)
rails-footnotes (~> 3.7.5)
rchardet19 (~> 1.3.5)
rake (= 0.9.2.2)
rdiscount
resque
rspec-rails
ruby-debug19
sass-rails (~> 3.1.0)
@ -292,3 +325,4 @@ DEPENDENCIES
thin
turn
uglifier
webmock

2
Procfile Normal file
View file

@ -0,0 +1,2 @@
web: bundle exec rails s -p $PORT
worker: bundle exec rake environment resque:work QUEUE=* VVERBOSE=1

2
Procfile.production Normal file
View file

@ -0,0 +1,2 @@
web: bundle exec rails s -p $PORT -e production
worker: bundle exec rake environment resque:work RAILS_ENV=production QUEUE=* VVERBOSE=1

View file

@ -1,16 +1,13 @@
# Welcome to GitLab [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://secure.travis-ci.org/gitlabhq/gitlabhq)
GitLab is a free Project/Repository management application
<img src="http://gitlabhq.com/front.png" width="900" height="471">
GitLab is a free project and repository management application
## Application details
rails 3.1
works only with gitolite
sqlite as default a database
* rails 3.1
* works only with gitolite
* sqlite as default a database
## Requirements
@ -18,7 +15,7 @@ sqlite as default a database
* sqlite
* git
* gitolite
* pygments lib - `sudo easy_install pygments`
* redis
## Install
@ -28,13 +25,11 @@ Checkout wiki pages for installation information, migration, etc.
[Google Group](https://groups.google.com/group/gitlabhq)
IRC freenode: #gitlabhq
## Contacts
Twitter:
* @gitalbhq
* @gitlabhq
* @dzaporozhets
Email
@ -43,7 +38,5 @@ Email
## Contribute
We are on our way to full open source.
Want to help - create an issue on github and notify us that you are ready to start it.
If approved - fork, code, cover with tests & make pull request.
Want to help - send a pull request.
We'll accept good pull requests.

View file

@ -1 +1 @@
2.0.0
2.1.0

View file

@ -1,4 +0,0 @@
[Dolphin]
ShowPreview=true
Timestamp=2011,10,28,13,16,25
Version=2

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 800 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

BIN
app/assets/images/dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

BIN
app/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
app/assets/images/white.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -16,7 +16,7 @@
//= require branch-graph
//= require_tree .
$(function(){
$(document).ready(function(){
$(".one_click_select").live("click", function(){
$(this).select();
});
@ -27,8 +27,50 @@ $(function(){
$(".account-box").mouseenter(showMenu);
$(".account-box").mouseleave(resetMenu);
$("#projects-list .project").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
$("#issues-table .issue").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
$(document).keypress(function(e) {
if( $(e.target).is(":input") ) return;
switch(e.which) {
case 115: focusSearch();
e.preventDefault();
}
});
});
function focusSearch() {
$("#search").focus();
}
function taggifyForm(){
var tag_field = $('#tag_field').tagify();
tag_field.tagify('inputField').autocomplete({
source: '/tags.json'
});
$('form').submit( function() {
var tag_field = $('#tag_field')
tag_field.val( tag_field.tagify('serialize') );
return true;
});
}
function updatePage(data){
$.ajax({type: "GET", url: location.href, data: data, dataType: "script"});
}
@ -40,3 +82,5 @@ function showMenu() {
function resetMenu() {
$(this).removeClass("hover");
}

View file

@ -1,55 +1,52 @@
$(document).ready(function(){
$(".day-commits-table li.commit").live('click', function(e){
if(e.target.nodeName != "A") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
});
var CommitsList = {
ref:null,
limit:0,
offset:0,
ref:null,
limit:0,
offset:0,
init:
function(ref, limit) {
$(".day-commits-table li.commit").live('click', function(e){
if(e.target.nodeName != "A") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
init:
function(ref, limit) {
this.ref=ref;
this.limit=limit;
this.offset=limit;
this.initLoadMore();
$('.loading').show();
},
getOld:
function() {
$('.loading').show();
$.ajax({
type: "GET",
url: location.href,
data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,
complete: function(){ $('.loading').hide()},
dataType: "script"});
},
append:
function(count, html) {
$("#commits_list").append(html);
if(count > 0) {
this.offset += count;
this.ref=ref;
this.limit=limit;
this.offset=limit;
this.initLoadMore();
}
},
$('.loading').show();
},
initLoadMore:
function() {
$(window).bind('scroll', function(){
if($(window).scrollTop() == $(document).height() - $(window).height()){
$(window).unbind('scroll');
CommitsList.getOld();
getOld:
function() {
$('.loading').show();
$.ajax({
type: "GET",
url: location.href,
data: "limit=" + this.limit + "&offset=" + this.offset + "&ref=" + this.ref,
complete: function(){ $('.loading').hide()},
dataType: "script"});
},
append:
function(count, html) {
$("#commits_list").append(html);
if(count > 0) {
this.offset += count;
this.initLoadMore();
}
});
}
},
initLoadMore:
function() {
$(window).bind('scroll', function(){
if($(window).scrollTop() == $(document).height() - $(window).height()){
$(window).unbind('scroll');
CommitsList.getOld();
}
});
}
}

View file

@ -0,0 +1,11 @@
var Loader = {
img_src: "/assets/ajax-loader.gif",
html:
function(width) {
img = $("<img>");
img.attr("width", width);
img.attr("src", this.img_src);
return img;
}
}

View file

@ -0,0 +1,59 @@
var MergeRequest = {
diffs_loaded: false,
commits_loaded: false,
init:
function() {
$(".merge-tabs a").live("click", function() {
$(".merge-tabs a").removeClass("active");
$(this).addClass("active");
});
$(".merge-tabs a.merge-notes-tab").live("click", function() {
$(".merge-request-commits, .merge-request-diffs").hide();
$(".merge-request-notes").show();
});
$(".merge-tabs a.merge-commits-tab").live("click", function() {
if(!MergeRequest.commits_loaded) {
MergeRequest.loadCommits();
}
$(".merge-request-notes, .merge-request-diffs").hide();
$(".merge-request-commits").show();
});
$(".merge-tabs a.merge-diffs-tab").live("click", function() {
if(!MergeRequest.diffs_loaded) {
MergeRequest.loadDiff();
}
$(".merge-request-notes, .merge-request-commits").hide();
$(".merge-request-diffs").show();
});
},
loadCommits:
function() {
$(".dashboard-loader").show();
$.ajax({
type: "GET",
url: $(".merge-commits-tab").attr("data-url"),
complete: function(){
MergeRequest.commits_loaded = true;
$(".merge-request-notes, .merge-request-diffs").hide();
$(".dashboard-loader").hide()},
dataType: "script"});
},
loadDiff:
function() {
$(".dashboard-loader").show();
$.ajax({
type: "GET",
url: $(".merge-diffs-tab").attr("data-url"),
complete: function(){
MergeRequest.diffs_loaded = true;
$(".merge-request-notes, .merge-request-commits").hide();
$(".dashboard-loader").hide()},
dataType: "script"});
}
}

View file

@ -1,3 +0,0 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

View file

@ -1,58 +1,42 @@
$(document).ready(function(){
$('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live("click", function() {
history.pushState({ path: this.path }, '', this.href)
})
var ProjectsList = {
limit:0,
offset:0,
$("#tree-slider tr.tree-item").live('click', function(e){
if(e.target.nodeName != "A") {
e.stopPropagation();
link = $(this).find("td.tree-item-file-name a")
link.click();
return false;
init:
function(limit) {
this.limit=limit;
this.offset=limit;
this.initLoadMore();
},
getOld:
function() {
$('.loading').show();
$.ajax({
type: "GET",
url: location.href,
data: "limit=" + this.limit + "&offset=" + this.offset,
complete: function(){ $('.loading').hide()},
dataType: "script"});
},
append:
function(count, html) {
$(".tile").append(html);
if(count > 0) {
this.offset += count;
this.initLoadMore();
}
},
initLoadMore:
function() {
$(window).bind('scroll', function(){
if($(window).scrollTop() == $(document).height() - $(window).height()){
$(window).unbind('scroll');
$('.loading').show();
ProjectsList.getOld();
}
});
}
});
$("#projects-list .project").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
$("#issues-table .issue").live('click', function(e){
if(e.target.nodeName != "A" && e.target.nodeName != "INPUT") {
location.href = $(this).attr("url");
e.stopPropagation();
return false;
}
});
$(document).keypress(function(e) {
if( $(e.target).is(":input") ) return;
switch(e.which) {
case 115: focusSearch();
e.preventDefault();
}
});
});
function focusSearch() {
$("#search").focus();
}
function taggifyForm(){
var tag_field = $('#tag_field').tagify();
tag_field.tagify('inputField').autocomplete({
source: '/tags.json'
});
$('form').submit( function() {
var tag_field = $('#tag_field')
tag_field.val( tag_field.tagify('serialize') );
return true;
});
}

View file

@ -0,0 +1,8 @@
function backToMembers(){
$("#team_member_new").hide("slide", { direction: "right" }, 150, function(){
$("#team-table").show("slide", { direction: "left" }, 150, function() {
$("#team_member_new").remove();
$(".add_new").show();
});
});
}

View file

@ -0,0 +1,27 @@
/**
* Tree slider for code browse
*
*/
var Tree = {
init:
function() {
(new Image).src = "ajax-loader-facebook.gif";
$('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live("click", function() {
history.pushState({ path: this.path }, '', this.href)
$("#tree-content-holder").hide("slide", { direction: "left" }, 150)
})
$("#tree-slider tr.tree-item").live('click', function(e){
if(e.target.nodeName != "A") {
link = $(this).find("td.tree-item-file-name a");
link.trigger("click");
}
});
$('#tree-slider td.tree-item-file-name a, #tree-breadcrumbs a').live({
"ajax:beforeSend": function() { $('.tree_progress').addClass("loading"); },
"ajax:complete": function() { $('.tree_progress').removeClass("loading"); }
});
}
}

View file

@ -7,45 +7,5 @@
*= require jquery-ui/jquery.tagify
*= require chosen
*= require_self
*= require_tree .
*= require common
*/
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
.cgreen { color:#44aa22; }
/** COMMON STYLES **/
.left {
float:left;
}
.right {
float:right;
}
.width-50p{
width:50%;
}
.width-49p{
width:49%;
}
.width-30p{
width:30%;
}
.width-65p{
width:65%;
}
.width-100p{
width:100%;
}
.append-bottom-10 {
margin-bottom:10px;
}
.prepend-top-10 {
margin-top:10px;
}
.no-borders {
border:none;
}
.no-padding {
padding:0 !important;
}

View file

@ -1,3 +1,44 @@
/* Commit Page */
body.project-page.commits-page .commit-info{float: right;}
body.project-page.commits-page .commit-info data{
padding: 4px 10px;
font-size: 11px;
}
body.project-page.commits-page .commit-info data.commit-button{
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.192, #fff), to(#f4f4f4));
background-image: -webkit-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -moz-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -o-linear-gradient(#fff 19.2%, #f4f4f4);
box-shadow: 0 -1px 0 white inset;
display: block;
border: 1px solid #eee;
border-radius: 5px;
margin-bottom: 2px;
position: relative;
padding-right: 20px;
}
body.project-page.commits-page .commit-button i{
background: url('images.png') no-repeat -138px -27px;
width: 6px;
height: 9px;
float: right;
position: absolute;
top: 6px;
right: 5px;
}
body.project-page.commits-page .commits-date {display: block; width: 100%; margin-bottom: 20px}
body.project-page.commits-page .commits-date .data {padding: 0}
body.project-page.commits-page a.commit{padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.project-page.commits-page .commits-date a.commit {padding: 10px; border-bottom: none; overflow: hidden; display: block;}
body.project-page.commits-page .commits-date a.commit:last-child{border-bottom: 0}
body.project-page.commits-page .commits-date a.commit img{float: left; margin-right: 10px;}
body.project-page.commits-page .commits-date a.commit span.commit-title{display: block;}
body.project-page.commits-page .commits-date a.commit span.commit-title{margin-bottom: 10px}
body.project-page.commits-page .commits-date a.commit span.commit-author{color: #999; font-weight: normal; font-style: italic;}
body.project-page.commits-page .commits-date a.commit span.commit-author strong{font-weight: bold; font-style: normal;}
/* eo Commit Page */
/** Commit diff view **/
.diff_file {
border:1px solid #CCC;
@ -37,7 +78,7 @@
padding:0px;
border:none;
background:#F7F7F7;
color:#333;
color:#aaa;
padding: 0px 5px;
border-right: 1px solid #ccc;
text-align:right;
@ -48,6 +89,7 @@
float:left;
width:35px;
font-weight:normal;
color:#aaa;
&:hover {
text-decoration:underline;
}
@ -96,3 +138,54 @@ ul.bordered-list {
}
ul.bordered-list li:last-child { border:none }
.line_holder {
&:hover {
td {
background: #FFFFCF !important;
}
}
}
.per_line_form {
font-family: "Helvetica", sans-serif;
background: #2FA0BB;
td {
padding:0;
}
form {
margin:5px;
width: 756px;
border: 1px solid #CCC;
padding: 20px;
background: white;
}
}
tr.line_notes_row {
font-family: "Helvetica", sans-serif;
&:hover {
background:none;
}
td {
margin:0px;
padding:0px;
border-bottom:1px solid #DEE2E3;
ul {
display:block;
list-style:none;
margin:0px;
padding:0px;
li {
border-top:1px solid #DEE2E3;
padding:10px;
}
}
}
}

View file

@ -0,0 +1,115 @@
$text_color:#222;
$lite_text_color: #666;
$link_color:#111;
$active_link_color:#2FA0BB;
$active_bg_color:#79C3E0;
$active_bd_color: #2FA0BB;
$border_color:#CCC;
$lite_border_color:#EEE;
$app_width:980px;
$app_padding:20px;
$bg_color: #FFF;
$styled_border_color: #2FA0BB;
/** MIXINS **/
@mixin round-borders-bottom($radius) {
border-top: 1px solid #eaeaea;
-moz-border-radius-bottomright: $radius;
-moz-border-radius-bottomleft: $radius;
border-bottom-right-radius: $radius;
border-bottom-left-radius: $radius;
-webkit-border-bottom-left-radius: $radius;
-webkit-border-bottom-right-radius: $radius;
}
@mixin round-borders-top($radius) {
border-top: 1px solid #eaeaea;
-moz-border-radius-topright: $radius;
-moz-border-radius-topleft: $radius;
border-top-right-radius: $radius;
border-top-left-radius: $radius;
-webkit-border-top-left-radius: $radius;
-webkit-border-top-right-radius: $radius;
}
@mixin round-borders-all($radius) {
border: 1px solid #eaeaea;
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
border-radius: $radius;
}
/** COLORS **/
.cgray { color:gray; }
.cred { color:#D12F19; }
.cgreen { color:#44aa22; }
/** COMMON STYLES **/
.left {
float:left;
}
.right {
float:right;
}
.width-50p{
width:50%;
}
.width-49p{
width:49%;
}
.width-30p{
width:30%;
}
.width-65p{
width:65%;
}
.width-100p{
width:100%;
}
.append-bottom-10 {
margin-bottom:10px;
}
.append-bottom-20 {
margin-bottom:20px;
}
.prepend-top-10 {
margin-top:10px;
}
.no-borders {
border:none;
}
.no-padding {
padding:0 !important;
}
/* General */
body.collapsed {
background-color: $bg_color;
#container{
margin: auto;
margin-top:51px;
width: $app_width;
border-top: 0;
background-color: $bg_color;
}
}
a {
color: $link_color;
}
@import "style.scss";
@import "projects.css.scss";
@import "commits.css.scss";
@import "notes.css.scss";
@import "merge_requests.css.scss";
@import "highlight.css.scss";
@import "highlight.black.css.scss";
@import "issues.css.scss";
@import "commits.css.scss";
@import "top_panel.scss";
@import "dashboard.scss";
@import "tree.scss";

View file

@ -0,0 +1,30 @@
body.dashboard-page h2.icon span{ background-position: 9px -69px; }
body.dashboard-page header{margin-bottom: 0}
body.dashboard-page .news-feed{margin-left: 285px; min-height: 600px; margin-top: 20px; margin-right:2px; padding:20px;}
body.dashboard-page .dashboard-content{ position: relative; float: left; width: 100%; height: 100%; }
body.dashboard-page .news-feed h2{float: left;}
body.dashboard-page aside{
min-height: 820px; position: relative; top: 0; bottom: 0; right: 0; width: 260px; float: left; border-right: 1px solid $border_color; padding:20px; padding-right:0;
h4{margin: 0; border-bottom: 1px solid #ccc; padding: 20px 20px 20px 0px; font-size: 11px; font-weight: bold; text-transform: uppercase;}
h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 2%; margin-top: -4px; display: block;}
.project-list {list-style: none; margin: 0; padding: 0;}
.project-list li a {background: white; color: #{$blue_link}; display: block; border-bottom: 1px solid $lite_border_color; padding: 14px 6% 14px 0px;}
.project-list li a span.project-name{font-size: 14px; display: block; margin-bottom: 8px}
.project-list li a span.time{color: #666; font-weight: normal; font-size: 11px}
.project-list li a span.arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999}
}
body.dashboard-page .news-feed .project-updates {
margin-bottom: 20px; display: block; width: 100%;
.data{ padding: 0}
a.project-update {padding: 10px; overflow: hidden; display: block;}
a.project-update:last-child{border-bottom: 0}
a.project-update img{float: left; margin-right: 10px;}
a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
a.project-update span.update-title{margin-bottom: 10px}
a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;}
a.project-update span.update-author strong{font-weight: bold; font-style: normal;}
}
/* eo Dashboard Page */

View file

@ -11,8 +11,8 @@
}
.issues_filter {
margin-top:10px;
.left {
margin:10px 0;
.left {
margin-right:15px;
}
}
@ -72,3 +72,13 @@ body.project-page .edit_snippet table td
}
}
#issues-table {
tr {
border-top: 1px solid $lite_border_color;
&:first-child {
border:none;
}
}
}

View file

@ -42,3 +42,11 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f
.note .note-title { margin-left:55px; }
p.notify_controls input{
margin: 5px;
}
p.notify_controls span{
font-weight: 700;
}

View file

@ -1,30 +1,77 @@
/** MIXINS **/
@mixin round-borders-bottom($radius) {
border-top: 1px solid #eaeaea;
-moz-border-radius-bottomright: $radius;
-moz-border-radius-bottomleft: $radius;
border-bottom-right-radius: $radius;
border-bottom-left-radius: $radius;
-webkit-border-bottom-left-radius: $radius;
-webkit-border-bottom-right-radius: $radius;
body.project-page h2.icon .project-name, body.project-page h2.icon d{border: 1px solid #eee; padding: 5px 30px 5px 10px; border-radius: 5px; position: relative;}
body.project-page h2.icon .project-name i.arrow{float: right;
position: absolute;
right: 10px;
top: 13px;
display: block;
background: url('images.png') no-repeat -97px -29px;
width: 4px;
height: 5px;
}
@mixin round-borders-top($radius) {
border-top: 1px solid #eaeaea;
-moz-border-radius-topright: $radius;
-moz-border-radius-topleft: $radius;
border-top-right-radius: $radius;
border-top-left-radius: $radius;
-webkit-border-top-left-radius: $radius;
-webkit-border-top-right-radius: $radius;
body.project-page h2.icon span{ background-position: -78px -68px; }
body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; padding-bottom: 10px;}
body.project-page .page-title{margin-bottom: 0}
body.project-page .project-sidebar {
width: 110px;
left: 0;
top: 0;
height: 100%;
bottom: 0;
position: absolute;
float: left;
display: inline-block;
background: #FFF;
padding: $app_padding;
padding-right:0px;
margin: 0;
border-right: 1px solid $border_color;
}
@mixin round-borders-all($radius) {
border: 1px solid #eaeaea;
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
border-radius: $radius;
body.projects-page input.text.git-url { font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 0 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px; width: 136px}
body.projects-page input.text.git-url {margin:10px 0 0 }
.git_url_wrapper { margin-right:50px }
.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); box-shadow:0px 0px 10px rgba(0, 0, 0, .1); }
/* New project Page */
.new-project-page .container table{background: white}
body.project-page .project-sidebar aside{width: 109px}
body.project-page .project-sidebar aside a{
display: block;
position: relative;
padding: 15px 10px;
margin: 10px 0 0 0;
}
body.project-page .project-sidebar aside a span.number{float: right; border-radius: 5px; text-shadow: none; background: rgba(0,0,0,.12); text-align: center; padding: 5px 8px; position: absolute; top: 10px; right: 10px}
body.project-page .project-sidebar aside a.current {
color: white;
background: $active_bg_color;
border: 1px solid $active_bd_color;
border-radius:5px;
-webkit-border-top-right-radius: 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-topright: 0px;
-moz-border-radius-bottomright: 0px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
margin-right: -1px;
}
body.project-page .project-content{ padding: $app_padding; display: block; margin-left: 130px; min-height: 600px}
body.project-page .project-content h2{ margin-top: 6px}
body.project-page .project-content .button.right{margin-left: 20px}
body.project-page table .commit a{color: #{$blue_link}}
body.project-page table th, body.project-page table td{ border-bottom: 1px solid #DEE2E3;}
body.project-page .fixed{position: fixed; }
/** File stat **/
.file_stats {
@ -48,90 +95,7 @@ table.round-borders {
text-align: left;
}
a {
color: #111;
}
/** FILE CONTENT VIEW **/
.view_file_content{
.old_line, .new_line {
background:#ECECEC;
color:#777;
width:15px;
float:left;
padding: 0px 10px;
border-right: 1px solid #ccc;
}
.old_line{
display:none;
}
}
.view_file .view_file_header,
.diff_file .diff_file_header {
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
border-bottom: 1px solid #DEE2E3;
padding: 7px 10px;
}
.view_file {
border:1px solid #CCC;
margin-bottom:1em;
.view_file_content {
background:#fff;
color:#514721;
font-size: 11px;
}
.view_file_content_image {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
}
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
border-left: 1px solid #DEE2E3;
background: white;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
background: #F7F7F7;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
}
/** PROJECTS **/
input.ssh_project_url {
@ -157,61 +121,6 @@ input.ssh_project_url {
clear: both;
}
/** FORM INPUTS **/
.new_merge_request,
.edit_merge_request,
.user_new,
.new_key,
.new_issue,
.new_note,
.edit_user,
.edit_issue,
.new_project,
.new_snippet,
.edit_snippet,
.edit_project {
input[type='text'],
input[type='email'],
input[type='password'],
textarea {
width:400px;
padding:8px;
font-size:14px;
@include round-borders-all(4px);
}
}
.input_button {
padding:8px;
font-size:14px;
cursor:pointer;
background-color: #F5F5F5;
border-color: #EEEEEE #DEDEDE #DEDEDE #EEEEEE;
border-right: 1px solid #DEDEDE;
border-style: solid;
border-width: 1px;
}
/** FLASH **/
#flash_container {
height:45px;
position:fixed;
z-index:10001;
top:0px;
width:100%;
margin-bottom:15px;
overflow:hidden;
background:white;
cursor:pointer;
border-bottom:1px solid #777;
h4 {
color:#444;
font-size:22px;
padding-top:5px;
margin:2px;
}
}
/** Buttons **/
.lbutton,
@ -270,7 +179,7 @@ input.ssh_project_url {
body.project-page table .commit {
a.tree-commit-link {
color:gray;
color:#444;
&:hover {
text-decoration:underline;
}
@ -331,7 +240,7 @@ body.project-page table .commit {
border:none;
text-shadow:none;
&.inline {
&.inline {
display:inline;
}
@ -358,8 +267,12 @@ body.project-page table .commit {
color:white;
}
&.note {
background: #2c5c66;
color:white;
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
color: #777;
border: 1px solid #DEDFE1;
}
&.issue {
background: #D12F19;
@ -376,7 +289,8 @@ body.project-page table .commit {
}
#holder {
border: solid 1px #999;
background:#FAFAFA;
border: 1px solid #EEE;
cursor: move;
height: 70%;
overflow: hidden;
@ -428,55 +342,35 @@ body.project-page .team_member_new .span-6, .team_member_edit .span-6{ padding:1
body.projects-page input.text.git-url.project_list_url { width:165px; }
body.project-page table.no-borders th {
background:none;
border-bottom:1px solid #CCC;
color:#333;
}
body.project-page table.no-borders tr,
body.project-page table.no-borders td{
body.project-page table.no-borders td{
border:none;
}
#gitlab-tabs {
.ui-tabs-nav {
border-bottom: 1px solid #DEDFE1;
li {
background: none;
border:none;
font-size: 16px;
margin: 0;
padding: 0;
a {
margin: 0;
padding: 10px 16px;
width:150px;
}
&.ui-tabs-selected {
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
font-weight: bold;
border:1px solid #DEDFE1;
border-bottom: 1px solid #DEDFE1;
-webkit-border-top-left-radius: 5px;
-webkit-border-top-right-radius: 5px;
-moz-border-radius-topleft: 5px;
-moz-border-radius-topright: 5px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
}
}
}
.ajax-tab-loading {
.ajax-tab-loading {
padding:40px;
display:none;
}
#tree-content-holder { float:left; width:100%; }
#tree-readme-holder {
float:left;
width:100%;
.readme {
@include round-borders-all(4px);
padding: 4px 15px;
background:#F7F7F7;
}
}
/* Commit Page */
@ -506,3 +400,173 @@ body.project-page table.no-borders td{
top: 6px;
right: 5px;
}
.box-arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999; margin: 1.5em 0;}
h4.dash-tabs {
margin: 0;
border-bottom: 1px solid #ccc;
padding: 10px 10px;
font-size: 11px;
padding-left:20px;
font-weight: bold; text-transform: uppercase;
background: #F7F7F7;
margin-bottom:20px;
height:13px;
}
.dash-button {
border-right: 1px solid #ddd;
background:none;
padding: 10px 15px;
float:left;
position:relative;
top:-10px;
left:0px;
height:13px;
&:first-child {
border-left: 1px solid #ddd;
}
&.active {
background: #eaeaea;
}
}
.dashboard-loader {
float:right;
margin-right:30px;
display:none;
}
.merge-tabs {
margin: 0;
border: 1px solid #ccc;
padding: 5px;
font-size: 12px;
background: #F7F7F7;
margin-bottom:20px;
height:26px;
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
.tab {
font-weight: bold;
border-right: 1px solid #ddd;
background:none;
padding: 10px;
min-width:60px;
float:left;
position:relative;
top:-5px;
left:-5px;
height:16px;
padding-left:34px;
span {
width: 20px;
height: 20px;
display: inline-block;
position: absolute;
left: 8px;
top: 8px;
}
&.active {
background: #eaeaea;
}
}
}
.merge-tabs.repository .tab span{ background: url("images.png") no-repeat -38px -77px; }
.activities-tab span { background: url("images.png") no-repeat -161px -1px; }
.stat-tab span,
.team-tab span,
.snippets-tab span { background: url("images.png") no-repeat -38px -77px; }
.files-tab span { background: url("images.png") no-repeat -112px -23px; }
.merge-notes-tab span { background: url("images.png") no-repeat -161px -1px; }
.merge-commits-tab span { background: url("images.png") no-repeat -86px 1px; }
.merge-diffs-tab span { background: url("images.png") no-repeat -118px 1px; }
.merge-tabs .dashboard-loader { padding:8px; }
.user-mention {
color: #2FA0BB;
font-weight: bold;
}
.author {
color: #999;
}
.red-button{
border-radius: 5px;
font-size: 12px;
font-weight: bold;
padding: 5px 17px;
border: 1px solid #999;
color: #666;
display: inline-block;
box-shadow: 0 1px 2px rgba(0,0,0,.3);
background: #D12F19;
color: white;
}
.positive-button{
border-radius: 5px;
font-size: 12px;
font-weight: bold;
padding: 5px 17px;
border: 1px solid #999;
color: #666;
display: inline-block;
box-shadow: 0 1px 2px rgba(0,0,0,.3);
background: #4A2;
color: white;
}
.dark_scheme_box {
padding:20px 0;
label {
float:left;
box-shadow: 0 0px 5px rgba(0,0,0,.3);
img {
}
}
}
a.project-update.titled {
position: relative;
padding-left: 235px !important;
.title-block {
padding: 10px;
width: 205px;
position: absolute;
left: 0;
top: 0;
}
}
.add_new {
float: right;
background: #A6B807;
color: white;
padding: 4px 10px;
@include round-borders-all(4px);
font-size:11px;
margin: 10px 0;
}
.new-project-hodler {
padding:20px;
}

View file

@ -9,7 +9,9 @@ audio:not([controls]) { display: none; }
html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
body { margin: 0; font-size: 13px; line-height: 1.231; }
body, button, input, select, textarea { font-family: sans-serif; color: #222; }
body, button, input, select, textarea {
font-family: "helvetica", "arial", "freesans", "clean", sans-serif;
color: #222; }
::-moz-selection { background: #79c3e0; color: #fff; text-shadow: none; }
::selection { background: #79c3e0; color: #fff; text-shadow: none; }
@ -74,9 +76,12 @@ $blue_link: "#2fa0bb";
/* eo Vars */
html{ -webkit-font-smoothing:antialiased; }
body{font-size: 12px; background-color: #eee;}
a{text-decoration: none; font-weight: bold; color: #666}
a:hover{color: #333}
body {
font-size: 12px;
background-color: #FFFFFF;
}
a{text-decoration: none; font-weight: bold; color: #444}
a:hover{color: #555}
/* Typography */
h1,h2,h3,h4,h5{font-weight: normal; color: #666}
h2{margin: 1.5em 0}
@ -122,7 +127,7 @@ table thead th{
td, th{ padding: .9em 1em; vertical-align: middle; }
table thead .image{width:100px}
table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF}
.listed_items tr.odd:hover{background-color:#FFFFCF}
/* eo Tables */
/* Buttons */
@ -130,7 +135,7 @@ table tr:hover, .listed_items tr.odd:hover{background-color:#FFFFCF}
border-radius: 5px;
font-size: 12px;
font-weight: bold;
padding: 6px 20px;
padding: 5px 17px;
border: 1px solid #999;
color: #666;
display: inline-block;
@ -187,12 +192,14 @@ input.button{margin-bottom: 1.5em}
/* eo Buttons */
/* UI Box */
.ui-box{border: 1px solid #DEDFE1; float: left; border-radius: 5px}
//.ui-box{border: 1px solid #DEDFE1; float: left; border-radius: 5px}
.ui-box{float: left;}
.ui-box h3{
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
background:none;
margin: 0;
padding: 1em;
font-size: 12px;
@ -215,13 +222,9 @@ input.button{margin-bottom: 1.5em}
.ui-box .data{padding: .5em 1em}
.ui-box .buttons{background-color: #f7f8f9; padding: 1em;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomright: 5px;
-moz-border-radius-bottomleft: 5px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
.ui-box .buttons{
padding: 1em;
border-top:1px solid $lite_border_color;
}
.ui-box .buttons .button{padding: 8px 9px; font-size: 11px}
@ -309,8 +312,7 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%}
input[type="password"],
textarea
{
border: 1px solid #FFBBBB;
background: #fff4f6;
border: 1px solid #D30 !important;
}
}
/* eo Errors */
@ -328,13 +330,13 @@ body.login-page{background-color: #f1f1f1; padding-top: 10%}
}
/* eo InfoBlock */
/* General */
#container{background-color: white; overflow: hidden; }
body.collapsed #container{margin: auto; width: 980px; border: 1px solid rgba(0,0,0,.22); border-top: 0; box-shadow: 0 0 0px 4px rgba(0,0,0,.04)}
/* Header */
header{background: #474D57 url('bg-header.png') repeat-x bottom; z-index: 10000; height: 44px; padding: 10px 2% 6px 2%; position: relative}
header a{color: white; text-shadow: 0 -1px 0 black}
header{
background: #474D57 url('bg-header.png') repeat-x bottom;
z-index: 10000;
height: 44px;
padding: 10px 2% 6px 2%;
}
header a:hover{color: #f1f1f1}
header h1{
width: 65px;
@ -359,6 +361,9 @@ header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin
margin-top: 2px;
height:30px
}
header nav.shorter_nav{
width: 207px;
}
header nav a{padding: 8px 12px 8px 34px; display: inline-block; color: #D6DADF; border-right: 1px solid #31363E; position: relative; box-shadow: 1px 0 0 rgba(255,255,255,.1); margin: 0}
header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;}
header nav a:last-child {border: 0; box-shadow: none}
@ -382,7 +387,7 @@ header nav a.dashboard {
border-bottom-left-radius: 4px;
}
header nav a.admin{
header nav a.last_elem{
-webkit-border-top-right-radius: 4px;
-webkit-border-bottom-right-radius: 4px;
-moz-border-radius-topright: 4px;
@ -391,13 +396,14 @@ header nav a.admin{
border-bottom-right-radius: 4px;
}
header .search{ display: inline-block; float: right; margin-right: 46px}
header .search{ display: inline-block; float: right; margin-right: 90px}
header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;}
header nav a.dashboard span{background: url('images.png') no-repeat -161px 0;}
header nav a.admin span{background: url('images.png') no-repeat -184px 0;}
header nav a.project span{background: url('images.png') no-repeat -209px -1px; top: 7px}
header nav a.issues span{background: url('images.png') no-repeat -209px -1px; top: 7px}
header .login-top{float: right; width: 180px;
background-image: -webkit-gradient(linear, 0 0, 0 62, color-stop(0.032, #464c56), to(#363c45));
@ -413,7 +419,7 @@ header .login-top a.pic{float: left; margin-right: 10px;
}
header .login-top a.username{margin-bottom: 5px}
header .login-top a.logout{color: #ccc}
header{margin-bottom: 0; clear: both; }
header{margin-bottom: 0; clear: both; position:relative;}
.page-title{background-color: #f1f1f1;display: block; float: left; clear: both; width: 98%; padding: 1% 1%; border-bottom: 1px solid #ccc; box-shadow: 0 -1px 0 white inset; margin-bottom: 1.5em}
.page-title h1{font-size: 20px; width: 400px; margin: 0; padding-top: 8px }
@ -421,8 +427,22 @@ header{margin-bottom: 0; clear: both; }
.right{float: right;}
/* Account box */
header .account-box{position: absolute; right: 0; top: 8px; z-index: 10000; width: 128px; font-size: 11px; float: right; display: block; cursor: pointer;}
header .account-box img{ border-radius: 4px; right: 20px; position: absolute; width: 38px; height: 38px; display: block; box-shadow: 0 1px 2px black}
header .account-box{
position: absolute;
right: 0;
top: 8px;
z-index: 10000;
width: 128px;
font-size: 11px;
float: right;
display: block;
cursor: pointer;}
header .account-box img{
border-radius: 4px;
right: 20px;
position: absolute;
width: 33px; height: 33px;
display: block; top:0;}
header .account-box img:after{
content: " ";
display: block;
@ -446,7 +466,8 @@ float: right;
.account-box.hover{height: 138px;}
.account-box:hover > .account-links{display: block;}
header .account-links{background: white; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); position:relative;}
header .account-links{
background: #79C3E0; display: none; border-radius: 5px; width: 100px; margin-top: 0; float: right; box-shadow: 0 1px 1px rgba(0,0,0,.2); position:relative;}
header .account-links:before {
content: ".";
width:0;
@ -545,8 +566,22 @@ header .account-links a:last-child{
}
/* eo Account Box */
input.search-input{float: left; text-shadow: none; width: 116px; background-image: url('icon-search.png') ; background-repeat: no-repeat; background-position: 10px; border-radius: 100px; border: 1px solid rgba(0,0,0,.7); box-shadow: 0 1px 0 rgba(255,255,255,.2), 0 2px 2px rgba(0,0,0,.4) inset ; background-color: #D2D5DA; background-color: rgba(255,255,255,.5); padding: 5px; padding-left: 26px; margin-top: 4px; margin-right: 10px }
input.search-input:focus{ background-color: white; width: 216px;}
input.search-input{
float: left;
text-shadow: none;
width: 116px;
background-image: url('icon-search.png') ;
background-repeat: no-repeat;
background-position: 10px;
border-radius: 4px;
border: 1px solid #AAA;
background-color: #FFF;
padding: 5px;
padding-left: 26px;
margin-top: 2px;
margin-right: 10px;
}
/*input.search-input:focus{ background-color: white; width: 216px;}*/
input.search-input::-webkit-input-placeholder {color: #666}
/* eo Header */
@ -559,127 +594,12 @@ html, body { height: 100%; }
body.dashboard-page h2.icon span{ background-position: 9px -69px; }
body.dashboard-page header{margin-bottom: 0}
body.dashboard-page .news-feed{padding-left: 1em; margin-right: 450px; min-height: 600px; margin-left: 1%}
body.dashboard-page .dashboard-content{ position: relative; float: left; width: 100%; height: 100%; }
body.dashboard-page .news-feed h2{float: left;}
body.dashboard-page aside{ min-height: 820px; position: relative; top: 0; bottom: 0; right: 0; width: 420px; float: right; background-color: #f7f7f7; border-left: 1px solid #ccc }
body.dashboard-page aside h4{margin: 0; border-bottom: 1px solid #ccc; padding: 10px 10px; font-size: 11px; font-weight: bold; text-transform: uppercase;}
body.dashboard-page aside h4 a.button-small{float: right; text-transform: none; border-radius: 4px; margin-right: 2%; margin-top: -4px; display: block;}
body.dashboard-page aside .project-list {list-style: none; margin: 0; padding: 0;}
body.dashboard-page aside .project-list li a {background: white; color: #{$blue_link}; display: block; border-bottom: 1px solid #eee; padding: 14px 6% 14px 14px;}
body.dashboard-page aside .project-list li a:hover {background: #f1f1f1}
body.dashboard-page aside .project-list li a:hover span.arrow{background-color: #E3E5EA;}
body.dashboard-page aside .project-list li a span.project-name{font-size: 14px; display: block; margin-bottom: 8px}
body.dashboard-page aside .project-list li a span.time{color: #666; font-weight: normal; font-size: 11px}
body.dashboard-page aside .project-list li a span.arrow{float: right; background: #E3E5EA; padding: 10px; border-radius: 5px; margin-top: 2px; text-shadow: none; color: #999}
body.dashboard-page .news-feed .project-updates {margin-bottom: 20px; display: block; width: 100%;}
body.dashboard-page .news-feed .project-updates .data{ padding: 0}
body.dashboard-page .news-feed .project-updates a.project-update {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.dashboard-page .news-feed .project-updates a.project-update:last-child{border-bottom: 0}
body.dashboard-page .news-feed .project-updates a.project-update img{float: left; margin-right: 10px;}
body.dashboard-page .news-feed .project-updates a.project-update span.update-title, .dashboard-page .news-feed .project-updates li a span.update-author{display: block;}
body.dashboard-page .news-feed .project-updates a.project-update span.update-title{margin-bottom: 10px}
body.dashboard-page .news-feed .project-updates a.project-update span.update-author{color: #999; font-weight: normal; font-style: italic;}
body.dashboard-page .news-feed .project-updates a.project-update span.update-author strong{font-weight: bold; font-style: normal;}
/* eo Dashboard Page */
.grey-button.right{margin-top: 20px}
/* Project Page */
body.project-page h2.icon .project-name, body.project-page h2.icon d{border: 1px solid #eee; padding: 5px 30px 5px 10px; border-radius: 5px; position: relative;}
body.project-page h2.icon .project-name i.arrow{float: right;
position: absolute;
right: 10px;
top: 13px;
display: block;
background: url('images.png') no-repeat -97px -29px;
width: 4px;
height: 5px;
}
body.project-page h2.icon span{ background-position: -78px -68px; }
body.project-page .project-container{ position: relative; float: left; width: 100%; height: 100%; padding-bottom: 10px;}
body.project-page .page-title{margin-bottom: 0}
body.project-page .project-sidebar {width: 180px; left: 0; top: 0; height: 100%; bottom: 0; position: absolute; background-color: #f7f7f7; float: left; display: inline-block; background: #f7f7f7; padding: 20px 0 20px 2%; margin: 0; }
body.project-page input.text.git-url,
body.projects-page input.text.git-url { font-size: 12px; border-radius: 5px; color: #666; box-shadow: 0 1px 2px rgba(0,0,0,.2) inset; padding: 8px 0 8px 30px; margin-bottom: 20px; background: white url('images.png') no-repeat 8px -40px; width: 136px}
body.projects-page input.text.git-url {margin:10px 0 0 }
.git_url_wrapper { margin-right:50px }
.projects_selector:hover > .project-box{ -moz-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); -webkit-box-shadow:0px 0px 10px rgba(0, 0, 0, .1); box-shadow:0px 0px 10px rgba(0, 0, 0, .1); }
body.project-page .project-sidebar aside{width: 179px}
body.project-page .project-sidebar aside a{display: block; position: relative; background: white; padding: 15px 10px; border-bottom: 1px solid #eee}
body.project-page .project-sidebar aside a:first-child{
-webkit-border-top-left-radius: 5px;
-moz-border-radius-topleft: 5px;
border-top-left-radius: 5px;
}
.project-page .project-sidebar aside a:last-child{
-webkit-border-bottom-left-radius: 5px;
-moz-border-radius-bottomleft: 5px;
border-bottom-left-radius: 5px;
}
body.project-page .project-sidebar aside a:hover{background-color: #eee;}
body.project-page .project-sidebar aside a span.number{float: right; border-radius: 5px; text-shadow: none; background: rgba(0,0,0,.12); text-align: center; padding: 5px 8px; position: absolute; top: 10px; right: 10px}
body.project-page .project-sidebar aside a.current{background-color: #79c3e0; color: white; text-shadow: none; border-color: transparent}
body.project-page .project-content{ padding: 20px; display: block; margin-left: 205px; min-height: 600px}
body.project-page .project-content h2{ margin-top: 6px}
body.project-page .project-content .button.right{margin-left: 20px}
body.project-page table .commit a{color: #{$blue_link}}
body.project-page table th, body.project-page table td{ border-bottom: 1px solid #DEE2E3;}
body.project-page .fixed{position: fixed; }
/* New project Page */
.new-project-page .container{width: 600px; background-color: rgba(0,0,0,.02); margin: auto; border: 1px solid #eee; padding: 0 20px; margin: 30px auto 60px auto; border-radius: 5px}
.new-project-page .container table{background: white}
/* eo New Project Page */
/* Commit Page */
body.project-page.commits-page .commit-info{float: right;}
body.project-page.commits-page .commit-info data{
padding: 4px 10px;
font-size: 11px;
}
body.project-page.commits-page .commit-info data.commit-button{
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.192, #fff), to(#f4f4f4));
background-image: -webkit-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -moz-linear-gradient(#fff 19.2%, #f4f4f4);
background-image: -o-linear-gradient(#fff 19.2%, #f4f4f4);
box-shadow: 0 -1px 0 white inset;
display: block;
border: 1px solid #eee;
border-radius: 5px;
margin-bottom: 2px;
position: relative;
padding-right: 20px;
}
body.project-page.commits-page .commit-button i{
background: url('images.png') no-repeat -138px -27px;
width: 6px;
height: 9px;
float: right;
position: absolute;
top: 6px;
right: 5px;
}
body.project-page.commits-page .commits-date {display: block; width: 100%; margin-bottom: 20px}
body.project-page.commits-page .commits-date .data {padding: 0}
body.project-page.commits-page a.commit{padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.project-page.commits-page .commits-date a.commit {padding: 10px; border-bottom: 1px solid #eee; overflow: hidden; display: block;}
body.project-page.commits-page .commits-date a.commit:last-child{border-bottom: 0}
body.project-page.commits-page .commits-date a.commit img{float: left; margin-right: 10px;}
body.project-page.commits-page .commits-date a.commit span.commit-title{display: block;}
body.project-page.commits-page .commits-date a.commit span.commit-title{margin-bottom: 10px}
body.project-page.commits-page .commits-date a.commit span.commit-author{color: #999; font-weight: normal; font-style: italic;}
body.project-page.commits-page .commits-date a.commit span.commit-author strong{font-weight: bold; font-style: normal;}
/* eo Commit Page */
/* eo Project Page */
@ -729,12 +649,154 @@ body.projects-page .browse-code{margin-right: 10px}
h2, h3 { page-break-after: avoid; }
}
/**
* author:DZ
* date: Nov 09
* fix different fonts for firefox & webkit
*/
body, button, input, select, textarea {
font-family: "Helvetica", sans-serif;
font-family: "helvetica", "arial", "freesans", "clean", sans-serif;
}
/** FORM INPUTS **/
.new_merge_request,
.edit_merge_request,
.user_new,
.new_key,
.new_issue,
.new_note,
.edit_user,
.edit_issue,
.new_project,
.new_snippet,
.edit_snippet,
.edit_project {
input[type='text'],
input[type='email'],
input[type='password'],
textarea {
width:400px;
padding:8px;
font-size:14px;
@include round-borders-all(4px);
}
}
.text_field {
width:400px;
padding:8px;
font-size:14px;
@include round-borders-all(4px);
}
.input_button {
padding:8px;
font-size:14px;
cursor:pointer;
background-color: #F5F5F5;
border-color: #EEEEEE #DEDEDE #DEDEDE #EEEEEE;
border-right: 1px solid #DEDEDE;
border-style: solid;
border-width: 1px;
}
/** FLASH **/
#flash_container {
height:45px;
position:fixed;
z-index:10001;
top:0px;
width:100%;
margin-bottom:15px;
overflow:hidden;
background:white;
cursor:pointer;
border-bottom:1px solid #777;
h4 {
color:#444;
font-size:22px;
padding-top:5px;
margin:2px;
}
}
.errors_holder {
background:#D30;
color:#fff;
@include round-borders-all(4px);
border:1px solid #a30;
padding:5px;
list-style:none;
font-weight: bold;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
li {
padding:10px;
}
}
.notice_holder {
background:#DDF4FB;
color:#444;
border:1px solid #C6EDF9;
@include round-borders-all(4px);
padding:5px;
list-style:none;
font-weight: bold;
text-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25);
li {
padding:10px;
}
}
.alert_holder {
background:#FDF5D9;
color:#444;
border:1px solid #FCEEC1;
@include round-borders-all(4px);
padding:5px;
list-style:none;
font-weight: bold;
text-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25);
li {
padding:10px;
}
}
.help_content {
margin:20px;
margin-top:71px;
h2 {
margin:0;
padding:0;
}
.menu {
float:left;
width:20%;
.active {
color: $active_bd_color;
}
}
.content {
float:right;
width:78%;
}
.bash {
@include round-borders-all(4px);
background:#eee;
padding:5px;
//overflow-x:scroll;
pre{
padding:0;
line-height:2.0;
margin:0;
font-family: 'Courier New', 'andale mono','lucida console',monospace;
color: #333;
text-align:left;
}
}
}

View file

@ -0,0 +1,146 @@
.main_links {
width:130px;
float:left;
a {
float:left;
}
}
.dashboard_links {
padding:7px;
float:left;
a {
margin: 0 14px;
float: left;
font-size: 14px;
&.active {
color:$active_link_color;
}
&:hover {
color:$active_link_color;
}
}
}
.top-tabs {
margin: 0;
padding: 5px;
font-size: 14px;
padding-bottom:10px;
margin-bottom:20px;
height:26px;
border-bottom:1px solid #ccc;
.tab {
font-weight: bold;
background:none;
padding: 10px;
float:left;
padding-left:0px;
padding-right:40px;
&.active {
color: $active_link_color;
}
}
}
body header {
position:absolute;
width:100%;
padding:0;
margin:0;
top:0;
left:0;
background: #999; /* for non-css3 browsers */
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#EAEAEA'); /* for IE */
background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#EAEAEA)); /* for webkit browsers */
background: -moz-linear-gradient(top, #FFFFFF, #EAEAEA); /* for firefox 3.6+ */
background: -o-linear-gradient(top, #FFFFFF, #EAEAEA); /* for firefox 3.6+ */
border-bottom: 1px solid #ccc;
height:50px;
.wrapper {
margin:auto;
width:$app_width;
position:relative;
.top_panel_content {
padding:10px $app_padding;
}
}
.project_name {
float:left;
width:235px;
margin-right:30px;
font-size:16px;
font-weight:bold;
padding:8px;
color:#333;
}
.git_url_wrapper {
padding:0px;
margin:0px;
float:left;
.git-url {
padding:0px;
margin:0px;
font-size: 12px;
margin-right:10px;
border-radius: 4px;
-moz-border-radius: 4px;
color: #666;
border: 1px solid #AAA;
padding: 0 10px 0 30px;
background: transparent url('images.png') no-repeat 8px -42px;
width: 160px;
height:26px;
}
}
}
.top_panel_holder .chzn-container {
position:relative;
.chzn-drop {
margin:7px 0;
border: 1px solid #CCC;
min-width: 300px;
.chzn-results {
max-height:300px;
}
}
.chzn-single {
background:transparent;
-moz-border-radius: 4px;
border-radius: 4px;
div {
background:transparent;
border-left:none;
}
span {
font-weight: normal;
}
}
}
.rss-icon {
margin:0 15px;
padding:3px;
border:1px solid #AAA;
border-radius:3px;
float:left;
}

View file

@ -0,0 +1,121 @@
#tree-breadcrumbs {
div {
margin:0;
margin-bottom:20px;
float:left;
font-size:14px;
}
}
.tree_progress {
float:left;
width:16px;
height:16px;
margin:2px 6px;
&.loading {
background-position: 0px 0px;
background: url("ajax-loader-facebook.gif") no-repeat;
}
}
/** FILE CONTENT VIEW **/
.view_file_content{
.old_line, .new_line {
background:#ECECEC;
color:#777;
width:15px;
float:left;
padding: 0px 10px;
border-right: 1px solid #ccc;
}
.old_line{
display:none;
}
}
.view_file .view_file_header,
.diff_file .diff_file_header {
background-image: -webkit-gradient(linear, 0 0, 0 26, color-stop(0.076, #fefefe), to(#F6F7F8));
background-image: -webkit-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -moz-linear-gradient(#fefefe 7.6%, #F6F7F8);
background-image: -o-linear-gradient(#fefefe 7.6%, #F6F7F8);
margin: 0;
font-weight: normal;
font-weight: bold;
text-align: left;
color: #666;
border-bottom: 1px solid #DEE2E3;
padding: 7px 10px;
.mode_text,
.file_icon {
margin-right:15px;
padding-right:15px;
border-right:1px solid $lite_border_color;
float:left;
color:#aaa;
}
.file_icon {
padding-left:15px;
}
}
.view_file {
border:1px solid #CCC;
margin-bottom:1em;
.view_file_content {
background:#fff;
color:#514721;
font-size: 11px;
}
.view_file_content_image {
background:#eee;
text-align:center;
img {
padding:100px;
max-width:300px;
}
}
}
td.code {
width: 100%;
.highlight {
margin-left: 55px;
overflow:auto;
overflow-y:hidden;
border-left: 1px solid #DEE2E3;
background: white;
}
}
.highlight pre {
white-space: pre;
word-wrap:normal;
}
table.highlighttable {
border: none;
background: #F7F7F7;
}
body.project-page table.highlighttable td { border: none }
table.highlighttable tr:hover { background:none;}
table.highlighttable pre{
line-height:16px !important;
font-size:12px !important;
}
table.highlighttable .linenodiv pre {
text-align: right;
padding-right: 4px;
color:#888;
}
.tree-item {
&:hover {
background: #FFFFCF;
}
}

View file

@ -9,6 +9,12 @@ class Admin::ProjectsController < ApplicationController
def show
@admin_project = Project.find_by_code(params[:id])
@users = if @admin_project.users.empty?
User
else
User.not_in_project(@admin_project)
end.all
end
def new
@ -19,6 +25,19 @@ class Admin::ProjectsController < ApplicationController
@admin_project = Project.find_by_code(params[:id])
end
def team_update
@admin_project = Project.find_by_code(params[:id])
UsersProject.bulk_import(
@admin_project,
params[:user_ids],
params[:project_access],
params[:repo_access]
)
redirect_to [:admin, @admin_project], notice: 'Project was successfully updated.'
end
def create
@admin_project = Project.new(params[:project])
@admin_project.owner = current_user

View file

@ -27,7 +27,6 @@ class Admin::UsersController < ApplicationController
respond_to do |format|
if @admin_user.save
Notify.new_user_email(@admin_user, params[:user][:password]).deliver
format.html { redirect_to [:admin, @admin_user], notice: 'User was successfully created.' }
format.json { render json: @admin_user, status: :created, location: @admin_user }
else
@ -39,7 +38,7 @@ class Admin::UsersController < ApplicationController
def update
admin = params[:user].delete("admin")
if params[:user][:password].empty?
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end

View file

@ -1,5 +1,6 @@
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :set_current_user_for_mailer
protect_from_forgery
helper_method :abilities, :can?
@ -19,6 +20,10 @@ class ApplicationController < ActionController::Base
end
end
def set_current_user_for_mailer
MailerObserver.current_user = current_user
end
def abilities
@abilities ||= Six.new
end

View file

@ -27,6 +27,8 @@ class CommitsController < ApplicationController
@notes = project.commit_notes(@commit).fresh.limit(20)
@note = @project.build_commit_note(@commit)
@line_notes = project.commit_line_notes(@commit)
respond_to do |format|
format.html
format.js { respond_with_notes }

View file

@ -1,6 +1,28 @@
class DashboardController < ApplicationController
respond_to :html
def index
@projects = current_user.projects.all
@active_projects = @projects.select(&:last_activity_date).sort_by(&:last_activity_date).reverse
@active_projects = @projects.select(&:repo_exists?).select(&:last_activity_date_cached).sort_by(&:last_activity_date_cached).reverse
end
# Get authored or assigned open merge requests
def merge_requests
@projects = current_user.projects.all
@merge_requests = MergeRequest.where("author_id = :id or assignee_id = :id", :id => current_user.id).opened.order("created_at DESC").limit(40)
end
# Get only assigned issues
def issues
@projects = current_user.projects.all
@user = current_user
@issues = current_user.assigned_issues.opened.order("created_at DESC").limit(40)
@issues = @issues.includes(:author, :project)
respond_to do |format|
format.html
format.atom { render :layout => false }
end
end
end

View file

@ -0,0 +1,46 @@
class DeployKeysController < ApplicationController
respond_to :html
layout "project"
before_filter :project
# Authorize
before_filter :add_project_abilities
before_filter :authorize_admin_project!
def project
@project ||= Project.find_by_code(params[:project_id])
end
def index
@keys = @project.deploy_keys.all
end
def show
@key = @project.deploy_keys.find(params[:id])
end
def new
@key = @project.deploy_keys.new
respond_with(@key)
end
def create
@key = @project.deploy_keys.new(params[:key])
if @key.save
redirect_to project_deploy_keys_path(@project)
else
render "new"
end
end
def destroy
@key = @project.deploy_keys.find(params[:id])
@key.destroy
respond_to do |format|
format.html { redirect_to project_deploy_keys_url }
format.js { render :nothing => true }
end
end
end

View file

@ -0,0 +1,4 @@
class HelpController < ApplicationController
def index
end
end

View file

@ -0,0 +1,51 @@
class HooksController < ApplicationController
before_filter :authenticate_user!
before_filter :project
layout "project"
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, :only => [:new, :create, :destroy]
respond_to :html
def index
@hooks = @project.web_hooks
end
def new
@hook = @project.web_hooks.new
end
def create
@hook = @project.web_hooks.new(params[:hook])
@hook.save
if @hook.valid?
redirect_to project_hook_path(@project, @hook)
else
render :new
end
end
def test
@hook = @project.web_hooks.find(params[:id])
commits = @project.commits(@project.default_branch, nil, 3)
data = @project.web_hook_data(commits.last.id, commits.first.id, "refs/heads/#{@project.default_branch}")
@hook.execute(data)
redirect_to :back
end
def show
@hook = @project.web_hooks.find(params[:id])
end
def destroy
@hook = @project.web_hooks.find(params[:id])
@hook.destroy
redirect_to project_hooks_path(@project)
end
end

View file

@ -6,8 +6,18 @@ class IssuesController < ApplicationController
# Authorize
before_filter :add_project_abilities
# Allow read any issue
before_filter :authorize_read_issue!
before_filter :authorize_write_issue!, :only => [:new, :create, :close, :edit, :update, :sort]
# Allow write(create) issue
before_filter :authorize_write_issue!, :only => [:new, :create]
# Allow modify issue
before_filter :authorize_modify_issue!, :only => [:close, :edit, :update, :sort]
# Allow destroy issue
before_filter :authorize_admin_issue!, :only => [:destroy]
respond_to :js, :html
@ -57,10 +67,7 @@ class IssuesController < ApplicationController
def create
@issue = @project.issues.new(params[:issue])
@issue.author = current_user
if @issue.save && @issue.assignee != current_user
Notify.new_issue_email(@issue).deliver
end
@issue.save
respond_with(@issue)
end
@ -80,6 +87,7 @@ class IssuesController < ApplicationController
@issue.destroy
respond_to do |format|
format.html { redirect_to project_issues_path }
format.js { render :nothing => true }
end
end
@ -115,4 +123,13 @@ class IssuesController < ApplicationController
def issue
@issue ||= @project.issues.find(params[:id])
end
def authorize_modify_issue!
can?(current_user, :modify_issue, @issue) ||
@issue.assignee == current_user
end
def authorize_admin_issue!
can?(current_user, :admin_issue, @issue)
end
end

View file

@ -6,6 +6,10 @@ class KeysController < ApplicationController
@keys = current_user.keys.all
end
def show
@key = current_user.keys.find(params[:id])
end
def new
@key = current_user.keys.new

View file

@ -6,11 +6,28 @@ class MergeRequestsController < ApplicationController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_write_project!, :only => [:new, :create, :edit, :update]
# Allow read any merge_request
before_filter :authorize_read_merge_request!
# Allow write(create) merge_request
before_filter :authorize_write_merge_request!, :only => [:new, :create]
# Allow modify merge_request
before_filter :authorize_modify_merge_request!, :only => [:close, :edit, :update, :sort]
# Allow destroy merge_request
before_filter :authorize_admin_merge_request!, :only => [:destroy]
def index
@merge_requests = @project.merge_requests
@merge_requests = case params[:f].to_i
when 2 then @merge_requests.closed
else @merge_requests.opened
end
@merge_requests = @merge_requests.includes(:author, :project)
end
def show
@ -30,14 +47,12 @@ class MergeRequestsController < ApplicationController
def commits
@commits = @project.repo.commits_between(@merge_request.target_branch, @merge_request.source_branch).map {|c| Commit.new(c)}
render :template => "merge_requests/_commits", :layout => false
end
def diffs
@diffs = @merge_request.diffs
@commit = @merge_request.last_commit
render :template => "merge_requests/_diffs", :layout => false
@line_notes = []
end
def new
@ -88,4 +103,13 @@ class MergeRequestsController < ApplicationController
def merge_request
@merge_request ||= @project.merge_requests.find(params[:id])
end
def authorize_modify_merge_request!
can?(current_user, :modify_merge_request, @merge_request) ||
@merge_request.assignee == current_user
end
def authorize_admin_merge_request!
can?(current_user, :admin_merge_request, @merge_request)
end
end

View file

@ -3,6 +3,8 @@ class NotesController < ApplicationController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_note!
before_filter :authorize_write_note!, :only => [:create]
respond_to :js
@ -10,10 +12,9 @@ class NotesController < ApplicationController
def create
@note = @project.notes.new(params[:note])
@note.author = current_user
if @note.save
notify if params[:notify] == '1'
end
@note.notify = true if params[:notify] == '1'
@note.notify_author = true if params[:notify_author] == '1'
@note.save
respond_to do |format|
format.html {redirect_to :back}
@ -33,22 +34,4 @@ class NotesController < ApplicationController
end
end
protected
def notify
@project.users.reject { |u| u.id == current_user.id } .each do |u|
case @note.noteable_type
when "Commit" then
Notify.note_commit_email(u, @note).deliver
when "Issue" then
Notify.note_issue_email(u, @note).deliver
when "MergeRequest"
true # someone should write email notification
when "Snippet"
true
else
Notify.note_wall_email(u, @note).deliver
end
end
end
end

View file

@ -4,10 +4,14 @@ class ProfileController < ApplicationController
@user = current_user
end
def social_update
def design
@user = current_user
end
def update
@user = current_user
@user.update_attributes(params[:user])
redirect_to [:profile]
redirect_to :back
end
def password

View file

@ -9,12 +9,10 @@ class ProjectsController < ApplicationController
before_filter :authorize_read_project!, :except => [:index, :new, :create]
before_filter :authorize_admin_project!, :only => [:edit, :update, :destroy]
before_filter :require_non_empty_project, :only => [:blob, :tree, :graph]
before_filter :load_refs, :only => :tree # load @branch, @tag & @ref
def index
source = current_user.projects
source = source.tagged_with(params[:tag]) unless params[:tag].blank?
@projects = source.all
@limit, @offset = (params[:limit] || 16), (params[:offset] || 0)
@projects = current_user.projects.limit(@limit).offset(@offset)
end
def new
@ -59,7 +57,7 @@ class ProjectsController < ApplicationController
def update
respond_to do |format|
if project.update_attributes(params[:project])
format.html { redirect_to project, :notice => 'Project was successfully updated.' }
format.html { redirect_to info_project_path(project), :notice => 'Project was successfully updated.' }
format.js
else
format.html { render action: "edit" }
@ -71,7 +69,14 @@ class ProjectsController < ApplicationController
def show
return render "projects/empty" unless @project.repo_exists? && @project.has_commits?
limit = (params[:limit] || 20).to_i
@activities = @project.cached_updates(limit)
@activities = @project.activities(limit)#updates_wo_repo(limit)
end
def files
@notes = @project.notes.where("attachment != 'NULL'").order("created_at DESC").limit(100)
end
def info
end
#
@ -94,7 +99,11 @@ class ProjectsController < ApplicationController
end
def destroy
# Disable the UsersProject update_repository call, otherwise it will be
# called once for every person removed from the project
UsersProject.skip_callback(:destroy, :after, :update_repository)
project.destroy
UsersProject.set_callback(:destroy, :after, :update_repository)
respond_to do |format|
format.html { redirect_to projects_url }

View file

@ -0,0 +1,22 @@
class RepositoriesController < ApplicationController
before_filter :project
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :require_non_empty_project
layout "project"
def show
@activities = @project.commits_with_refs(20)
end
def branches
@branches = @project.repo.heads.sort_by(&:name)
end
def tags
@tags = @project.repo.tags.sort_by(&:name).reverse
end
end

View file

@ -5,8 +5,18 @@ class SnippetsController < ApplicationController
# Authorize
before_filter :add_project_abilities
# Allow read any snippet
before_filter :authorize_read_snippet!
before_filter :authorize_write_snippet!, :only => [:new, :create, :close, :edit, :update, :sort]
# Allow write(create) snippet
before_filter :authorize_write_snippet!, :only => [:new, :create]
# Allow modify snippet
before_filter :authorize_modify_snippet!, :only => [:edit, :update]
# Allow destroy snippet
before_filter :authorize_admin_snippet!, :only => [:destroy]
respond_to :html
@ -60,4 +70,14 @@ class SnippetsController < ApplicationController
redirect_to project_snippets_path(@project)
end
protected
def authorize_modify_snippet!
can?(current_user, :modify_snippet, @snippet)
end
def authorize_admin_snippet!
can?(current_user, :admin_snippet, @snippet)
end
end

View file

@ -5,7 +5,7 @@ class TeamMembersController < ApplicationController
# Authorize
before_filter :add_project_abilities
before_filter :authorize_read_project!
before_filter :authorize_admin_project!, :only => [:new, :create, :destroy, :update]
before_filter :authorize_admin_project!, :except => [:show]
def show
@team_member = project.users_projects.find(params[:id])
@ -18,7 +18,11 @@ class TeamMembersController < ApplicationController
def create
@team_member = UsersProject.new(params[:team_member])
@team_member.project = project
@team_member.save
if @team_member.save
redirect_to team_project_path(@project)
else
render "new"
end
end
def update

View file

@ -6,7 +6,7 @@ class TreeDecorator < ApplicationDecorator
part_path = ""
parts = path.split("\/")
parts = parts[0...-1] if is_blob?
#parts = parts[0...-1] if is_blob?
yield(h.link_to("..", "#", :remote => :true)) if parts.count > max_links
@ -32,4 +32,13 @@ class TreeDecorator < ApplicationDecorator
def history_path
h.project_commits_path(project, :path => path, :ref => ref)
end
def mb_size
size = (tree.size / 1024)
if size < 1024
"#{size} KB"
else
"#{size/1024} MB"
end
end
end

View file

@ -1,9 +1,9 @@
require 'digest/md5'
module ApplicationHelper
def gravatar_icon(user_email)
def gravatar_icon(user_email, size = 40)
gravatar_host = request.ssl? ? "https://secure.gravatar.com" : "http://www.gravatar.com"
"#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=40&d=identicon"
"#{gravatar_host}/avatar/#{Digest::MD5.hexdigest(user_email)}?s=#{size}&d=identicon"
end
def fixed_mode?
@ -48,11 +48,11 @@ module ApplicationHelper
def grouped_options_refs(destination = :tree)
options = [
["Branch", @repo.heads.map(&:name) ],
["Branch", @project.repo.heads.map(&:name) ],
[ "Tag", @project.tags ]
]
grouped_options_for_select(options, @ref)
grouped_options_for_select(options, @ref || @project.default_branch)
end
def markdown(text)
@ -82,4 +82,15 @@ module ApplicationHelper
[projects, default_nav, project_nav].flatten.to_json
end
def project_layout
@project && !@project.new_record?
end
def profile_layout
controller.controller_name == "dashboard" || current_page?(projects_path) || controller.controller_name == "profile" || controller.controller_name == "keys"
end
def help_layout
controller.controller_name == "help"
end
end

View file

@ -1,6 +1,4 @@
module CommitsHelper
include Utils::CharEncode
def old_line_number(line, i)
end
@ -25,4 +23,30 @@ module CommitsHelper
link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit),
:remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link"
end
def commit_msg_with_link_to_issues(project, message)
return '' unless message
out = ''
message.split(/(#[0-9]+)/m).each do |m|
if m =~ /(#([0-9]+))/m
begin
issue = project.issues.find($2)
out += link_to($1, project_issue_path(project, $2))
rescue
out += $1
end
else
out += m
end
end
preserve out
end
def build_line_code(line, index, line_new, line_old)
if diff_line_class(line) == "new"
"NEW_#{index}_#{line_new}"
else
"OLD_#{index}_#{line_old}"
end
end
end

View file

@ -10,6 +10,7 @@ module DashboardHelper
when "Issue" then project_issue_path(project, note.noteable_id)
when "Snippet" then project_snippet_path(project, note.noteable_id)
when "Commit" then project_commit_path(project, :id => note.noteable_id)
when "MergeRequest" then project_merge_request_path(project, note.noteable_id)
else wall_project_path(project)
end
else wall_project_path(project)

View file

@ -16,12 +16,26 @@ module ProjectsHelper
nil
end
# expires in 360 days
def switch_colorscheme_link(opts)
if cookies[:colorschema].blank?
link_to_function "paint it black!", "$.cookie('colorschema','black', {expires:360}); window.location.reload()", opts
else
link_to_function "paint it white!", "$.cookie('colorschema','', {expires:360}); window.location.reload()", opts
def project_tab_class
[:show, :files, :team, :edit, :update, :info].each do |action|
return "current" if current_page?(:controller => "projects", :action => action, :id => @project)
end
if controller.controller_name == "snippets" ||
controller.controller_name == "team_members"
"current"
end
end
def tree_tab_class
controller.controller_name == "refs" ?
"current" : nil
end
def repository_tab_class
if controller.controller_name == "repositories" ||
controller.controller_name == "hooks"
"current"
end
end
end

View file

@ -0,0 +1,2 @@
module UserIssuesHelper
end

View file

@ -0,0 +1,3 @@
module UserMergeRequestsHelper
end

View file

@ -28,7 +28,16 @@ class Notify < ActionMailer::Base
@note = note
@project = note.project
@commit = @project.repo.commits(note.noteable_id).first
mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ")
return unless ( note.notify or ( note.notify_author and @commit.author.email == @user.email ) )
mail(:to => @user.email, :subject => "gitlab | note for commit | #{@note.project.name} ")
end
def note_merge_request_email(user, note)
@user = user
@note = note
@project = note.project
@merge_request = note.noteable
mail(:to => @user.email, :subject => "gitlab | note for merge request | #{@note.project.name} ")
end
def note_issue_email(user, note)
@ -36,6 +45,29 @@ class Notify < ActionMailer::Base
@note = note
@project = note.project
@issue = note.noteable
mail(:to => @user.email, :subject => "gitlab | #{@note.project.name} ")
mail(:to => @user.email, :subject => "gitlab | note for issue #{@issue.id} | #{@note.project.name} ")
end
def new_merge_request_email(merge_request)
@user = merge_request.assignee
@merge_request = merge_request
@project = merge_request.project
mail(:to => @user.email, :subject => "gitlab | new merge request | #{@merge_request.title} ")
end
def changed_merge_request_email(user, merge_request)
@user = user
@assignee_was ||= User.find(merge_request.assignee_id_was)
@merge_request = merge_request
@project = merge_request.project
mail(:to => @user.email, :subject => "gitlab | merge request changed | #{@merge_request.title} ")
end
def changed_issue_email(user, issue)
@user = user
@assignee_was ||= User.find(issue.assignee_id_was)
@issue = issue
@project = issue.project
mail(:to => @user.email, :subject => "gitlab | changed issue | #{@issue.title} ")
end
end

View file

@ -19,7 +19,7 @@ class Ability
:read_team_member,
:read_merge_request,
:read_note
] if project.readers.include?(user)
] if project.allow_read_for?(user)
rules << [
:write_project,
@ -27,16 +27,18 @@ class Ability
:write_snippet,
:write_merge_request,
:write_note
] if project.writers.include?(user)
] if project.allow_write_for?(user)
rules << [
:modify_issue,
:modify_snippet,
:admin_project,
:admin_issue,
:admin_snippet,
:admin_team_member,
:admin_merge_request,
:admin_note
] if project.admins.include?(user)
] if project.allow_admin_for?(user)
rules.flatten
end
@ -48,6 +50,7 @@ class Ability
[
:"read_#{name}",
:"write_#{name}",
:"modify_#{name}",
:"admin_#{name}"
]
else

View file

@ -1,8 +1,8 @@
class Commit
include Utils::CharEncode
attr_accessor :commit
attr_accessor :head
attr_accessor :refs
delegate :message,
:committed_date,
@ -22,7 +22,7 @@ class Commit
end
def safe_message
encode(message)
message
end
def created_at
@ -30,11 +30,11 @@ class Commit
end
def author_email
encode(author.email)
author.email
end
def author_name
encode(author.name)
author.name
end
def prev_commit

View file

@ -2,7 +2,7 @@ class Issue < ActiveRecord::Base
belongs_to :project
belongs_to :author, :class_name => "User"
belongs_to :assignee, :class_name => "User"
has_many :notes, :as => :noteable
has_many :notes, :as => :noteable, :dependent => :destroy
attr_protected :author, :author_id, :project, :project_id
@ -59,5 +59,6 @@ end
# closed :boolean default(FALSE), not null
# position :integer default(0)
# critical :boolean default(FALSE), not null
# branch_name :string(255)
#

View file

@ -1,5 +1,6 @@
class Key < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :title,
:presence => true,
@ -15,32 +16,38 @@ class Key < ActiveRecord::Base
after_destroy :repository_delete_key
def set_identifier
self.identifier = "#{user.identifier}_#{Time.now.to_i}"
if is_deploy_key
self.identifier = "deploy_#{project.code}_#{Time.now.to_i}"
else
self.identifier = "#{user.identifier}_#{Time.now.to_i}"
end
end
def update_repository
Gitlabhq::GitHost.system.new.configure do |c|
c.update_keys(identifier, key)
projects.each do |project|
c.update_project(project.path, project)
end
c.update_projects(projects)
end
end
def repository_delete_key
Gitlabhq::GitHost.system.new.configure do |c|
c.delete_key(identifier)
projects.each do |project|
c.update_project(project.path, project)
end
c.update_projects(projects)
end
end
def is_deploy_key
true if project_id
end
#projects that has this key
def projects
user.projects
if is_deploy_key
[project]
else
user.projects
end
end
end
# == Schema Information
@ -48,11 +55,12 @@ end
# Table name: keys
#
# id :integer not null, primary key
# user_id :integer not null
# user_id :integer
# created_at :datetime
# updated_at :datetime
# key :text
# title :string(255)
# identifier :string(255)
# project_id :integer
#

View file

@ -0,0 +1,88 @@
class MailerObserver < ActiveRecord::Observer
observe :issue, :user, :note, :merge_request
cattr_accessor :current_user
def after_create(model)
new_issue(model) if model.kind_of?(Issue)
new_user(model) if model.kind_of?(User)
new_note(model) if model.kind_of?(Note)
new_merge_request(model) if model.kind_of?(MergeRequest)
end
def after_update(model)
changed_merge_request(model) if model.kind_of?(MergeRequest)
changed_issue(model) if model.kind_of?(Issue)
end
protected
def new_issue(issue)
if issue.assignee != current_user
Notify.new_issue_email(issue).deliver
end
end
def new_user(user)
Notify.new_user_email(user, user.password).deliver
end
def new_note(note)
return unless note.notify or note.notify_author
note.project.users.reject { |u| u.id == current_user.id } .each do |u|
case note.noteable_type
when "Commit" then
Notify.note_commit_email(u, note).deliver
when "Issue" then
Notify.note_issue_email(u, note).deliver
when "MergeRequest" then
Notify.note_merge_request_email(u, note).deliver
when "Snippet"
true
else
Notify.note_wall_email(u, note).deliver
end
end
end
def new_merge_request(merge_request)
if merge_request.assignee != current_user
Notify.new_merge_request_email(merge_request).deliver
end
end
def changed_merge_request(merge_request)
if merge_request.assignee_id_changed?
recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
recipients_ids.delete current_user.id
User.find(recipients_ids).each do |user|
Notify.changed_merge_request_email(user, merge_request).deliver
end
end
if merge_request.closed_changed?
note = Note.new(:noteable => merge_request, :project => merge_request.project)
note.author = current_user
note.note = "_Status changed to #{merge_request.closed ? 'closed' : 'reopened'}_"
note.save()
end
end
def changed_issue(issue)
if issue.assignee_id_changed?
recipients_ids = issue.assignee_id_was, issue.assignee_id
recipients_ids.delete current_user.id
User.find(recipients_ids).each do |user|
Notify.changed_issue_email(user, issue).deliver
end
end
if issue.closed_changed?
note = Note.new(:noteable => issue, :project => issue.project)
note.author = current_user
note.note = "_Status changed to #{issue.closed ? 'closed' : 'reopened'}_"
note.save()
end
end
end

View file

@ -2,7 +2,7 @@ class MergeRequest < ActiveRecord::Base
belongs_to :project
belongs_to :author, :class_name => "User"
belongs_to :assignee, :class_name => "User"
has_many :notes, :as => :noteable
has_many :notes, :as => :noteable, :dependent => :destroy
attr_protected :author, :author_id, :project, :project_id
@ -35,12 +35,27 @@ class MergeRequest < ActiveRecord::Base
end
def diffs
commit = project.commit(source_branch)
commits = project.repo.commits_between(target_branch, source_branch).map {|c| Commit.new(c)}
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id)
diffs = project.repo.diff(commits.first.prev_commit.id, commits.last.id) rescue []
end
def last_commit
project.commit(source_branch)
end
end
# == Schema Information
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# closed :boolean default(FALSE), not null
# created_at :datetime
# updated_at :datetime
#

View file

@ -13,6 +13,8 @@ class Note < ActiveRecord::Base
:prefix => true
attr_protected :author, :author_id
attr_accessor :notify
attr_accessor :notify_author
validates_presence_of :project
@ -35,6 +37,43 @@ class Note < ActiveRecord::Base
scope :inc_author, includes(:author)
mount_uploader :attachment, AttachmentUploader
def notify
@notify ||= false
end
def notify_author
@notify_author ||= false
end
def target
if noteable_type == "Commit"
project.commit(noteable_id)
else
noteable
end
# Temp fix to prevent app crash
# if note commit id doesnt exist
rescue
nil
end
def line_file_id
@line_file_id ||= line_code.split("_")[1].to_i if line_code
end
def line_type_id
@line_type_id ||= line_code.split("_").first if line_code
end
def line_number
@line_number ||= line_code.split("_").last.to_i if line_code
end
def for_line?(file_id, old_line, new_line)
line_file_id == file_id &&
((line_type_id == "NEW" && line_number == new_line) || (line_type_id == "OLD" && line_number == old_line ))
end
end
# == Schema Information
#
@ -49,5 +88,6 @@ end
# updated_at :datetime
# project_id :integer
# attachment :string(255)
# line_code :string(255)
#

View file

@ -14,6 +14,8 @@ class Project < ActiveRecord::Base
has_many :users, :through => :users_projects
has_many :notes, :dependent => :destroy
has_many :snippets, :dependent => :destroy
has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key"
has_many :web_hooks, :dependent => :destroy
acts_as_taggable
@ -25,8 +27,8 @@ class Project < ActiveRecord::Base
validates :path,
:uniqueness => true,
:presence => true,
:format => { :with => /^[a-zA-Z0-9_\-]*$/,
:message => "only letters, digits & '_' '-' allowed" },
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
:message => "only letters, digits & '_' '-' '.' allowed" },
:length => { :within => 0..255 }
validates :description,
@ -35,8 +37,8 @@ class Project < ActiveRecord::Base
validates :code,
:presence => true,
:uniqueness => true,
:format => { :with => /^[a-zA-Z0-9_\-]*$/,
:message => "only letters, digits & '_' '-' allowed" },
:format => { :with => /^[a-zA-Z0-9_\-\.]*$/,
:message => "only letters, digits & '_' '-' '.' allowed" },
:length => { :within => 3..255 }
validates :owner,
@ -52,6 +54,9 @@ class Project < ActiveRecord::Base
scope :public_only, where(:private_flag => false)
def self.active
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end
def self.access_options
{
@ -75,21 +80,76 @@ class Project < ActiveRecord::Base
:repo_exists?,
:commit,
:commits,
:commits_with_refs,
:tree,
:heads,
:commits_since,
:fresh_commits,
:commits_between,
:to => :repository, :prefix => nil
def to_param
code
end
def web_url
[GIT_HOST['host'], code].join("/")
end
def execute_web_hooks(oldrev, newrev, ref)
ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits)
return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000"
data = web_hook_data(oldrev, newrev, ref)
web_hooks.each { |web_hook| web_hook.execute(data) }
end
def web_hook_data(oldrev, newrev, ref)
data = {
before: oldrev,
after: newrev,
ref: ref,
repository: {
name: name,
url: web_url,
description: description,
homepage: web_url,
private: private?
},
commits: []
}
commits_between(oldrev, newrev).each do |commit|
data[:commits] << {
id: commit.id,
message: commit.safe_message,
timestamp: commit.date.xmlschema,
url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}",
author: {
name: commit.author_name,
email: commit.author_email
}
}
end
data
end
def team_member_by_name_or_email(email = nil, name = nil)
user = users.where("email like ? or name like ?", email, name).first
users_projects.find_by_user_id(user.id) if user
end
def team_member_by_id(user_id)
users_projects.find_by_user_id(user_id)
end
def fresh_merge_requests(n)
merge_requests.includes(:project, :author).order("created_at desc").first(n)
end
def fresh_issues(n)
issues.includes(:project, :author).order("created_at desc").first(n)
end
@ -107,7 +167,11 @@ class Project < ActiveRecord::Base
end
def commit_notes(commit)
notes.where(:noteable_id => commit.id, :noteable_type => "Commit")
notes.where(:noteable_id => commit.id, :noteable_type => "Commit", :line_code => nil)
end
def commit_line_notes(commit)
notes.where(:noteable_id => commit.id, :noteable_type => "Commit").where("line_code is not null")
end
def has_commits?
@ -136,7 +200,7 @@ class Project < ActiveRecord::Base
def repository_readers
keys = Key.joins({:user => :users_projects}).
where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_R)
keys.map(&:identifier)
keys.map(&:identifier) + deploy_keys.map(&:identifier)
end
def repository_writers
@ -157,6 +221,18 @@ class Project < ActiveRecord::Base
@admins ||= users_projects.includes(:user).where(:project_access => PROJECT_RWA).map(&:user)
end
def allow_read_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_R, PROJECT_RW, PROJECT_RWA]).empty?
end
def allow_write_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_RW, PROJECT_RWA]).empty?
end
def allow_admin_for?(user)
!users_projects.where(:user_id => user.id, :project_access => [PROJECT_RWA]).empty? || owner_id == user.id
end
def root_ref
default_branch || "master"
end
@ -179,6 +255,24 @@ class Project < ActiveRecord::Base
last_activity.try(:created_at)
end
def last_activity_date_cached(expire = 1.hour)
activity_date_key = "project_#{id}_activity_date"
cached_activities = Rails.cache.read(activity_date_key)
if cached_activities
activity_date = if cached_activities == "Never"
nil
else
cached_activities
end
else
activity_date = last_activity_date
Rails.cache.write(activity_date_key, activity_date || "Never", :expires_in => expire)
end
activity_date
end
# Get project updates from cache
# or calculate.
def cached_updates(limit, expire = 2.minutes)
@ -188,7 +282,7 @@ class Project < ActiveRecord::Base
activities = cached_activities
else
activities = updates(limit)
Rails.cache.write(activities_key, activities, :expires_in => 60.seconds)
Rails.cache.write(activities_key, activities, :expires_in => expire)
end
activities
@ -206,6 +300,16 @@ class Project < ActiveRecord::Base
end[0...n]
end
def activities(n=3)
[
fresh_issues(n),
fresh_merge_requests(n),
notes.inc_author_project.where("noteable_type is not null").order("created_at desc").first(n)
].compact.flatten.sort do |x, y|
y.created_at <=> x.created_at
end[0...n]
end
def check_limit
unless owner.can_create_project?
errors[:base] << ("Your own projects limit is #{owner.projects_limit}! Please contact administrator to increase it")
@ -231,14 +335,15 @@ end
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime
# updated_at :datetime
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
# created_at :datetime
# updated_at :datetime
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# default_branch :string(255) default("master"), not null
#

View file

@ -31,6 +31,22 @@ class Repository
project.id
end
def write_hooks
%w(post-receive).each do |hook|
write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook")))
end
end
def write_hook(name, content)
hook_file = File.join(project.path_to_repo, 'hooks', name)
File.open(hook_file, 'w') do |f|
f.write(content)
end
File.chmod(0775, hook_file)
end
def repo
@repo ||= Grit::Repo.new(project.path_to_repo)
end
@ -47,6 +63,8 @@ class Repository
Gitlabhq::GitHost.system.new.configure do |c|
c.update_project(path, project)
end
write_hooks if File.exists?(project.path_to_repo)
end
def destroy_repository
@ -56,7 +74,9 @@ class Repository
end
def repo_exists?
repo rescue false
@repo_exists ||= (repo && !repo.branches.empty?)
rescue
@repo_exists = false
end
def tags
@ -94,6 +114,16 @@ class Repository
commits[0...n]
end
def commits_with_refs(n = 20)
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
commits.sort! do |x, y|
y.committed_date <=> x.committed_date
end
commits[0..n]
end
def commits_since(date)
commits = heads.map do |h|
repo.log(h.name, nil, :since => date).each { |c| Commit.new(c, h) }
@ -115,4 +145,8 @@ class Repository
repo.commits(ref)
end.map{ |c| Commit.new(c) }
end
def commits_between(from, to)
repo.commits_between(from, to).map { |c| Commit.new(c) }
end
end

View file

@ -3,7 +3,7 @@ class Snippet < ActiveRecord::Base
belongs_to :project
belongs_to :author, :class_name => "User"
has_many :notes, :as => :noteable
has_many :notes, :as => :noteable, :dependent => :destroy
delegate :name,
:email,
@ -28,6 +28,7 @@ class Snippet < ActiveRecord::Base
scope :fresh, order("created_at DESC")
scope :non_expired, where(["expires_at IS NULL OR expires_at > ?", Time.current])
scope :expired, where(["expires_at IS NOT NULL AND expires_at < ?", Time.current])
def self.content_types
[

View file

@ -7,6 +7,8 @@ class Tree
:name,
:data,
:mime_type,
:mode,
:size,
:text?,
:colorize,
:to => :tree

View file

@ -6,7 +6,7 @@ class User < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:name, :projects_limit, :skype, :linkedin, :twitter
:name, :projects_limit, :skype, :linkedin, :twitter, :dark_scheme
has_many :users_projects, :dependent => :destroy
has_many :projects, :through => :users_projects
@ -25,6 +25,20 @@ class User < ActiveRecord::Base
:foreign_key => :assignee_id,
:dependent => :destroy
has_many :merge_requests,
:foreign_key => :author_id,
:dependent => :destroy
has_many :assigned_merge_requests,
:class_name => "MergeRequest",
:foreign_key => :assignee_id,
:dependent => :destroy
validates :projects_limit,
:presence => true,
:numericality => {:greater_than_or_equal_to => 0}
before_create :ensure_authentication_token
alias_attribute :private_token, :authentication_token
scope :not_in_project, lambda { |project| where("id not in (:ids)", :ids => project.users.map(&:id) ) }
@ -37,8 +51,12 @@ class User < ActiveRecord::Base
admin
end
def require_ssh_key?
keys.count == 0
end
def can_create_project?
projects_limit >= my_own_projects.count
projects_limit > my_own_projects.count
end
def last_activity_project
@ -69,5 +87,6 @@ end
# linkedin :string(255) default(""), not null
# twitter :string(255) default(""), not null
# authentication_token :string(255)
# dark_scheme :boolean default(FALSE), not null
#

View file

@ -13,6 +13,20 @@ class UsersProject < ActiveRecord::Base
delegate :name, :email, :to => :user, :prefix => true
def self.bulk_import(project, user_ids, project_access, repo_access)
UsersProject.transaction do
user_ids.each do |user_id|
users_project = UsersProject.new(
:repo_access => repo_access,
:project_access => project_access,
:user_id => user_id
)
users_project.project = project
users_project.save
end
end
end
def update_repository
Gitlabhq::GitHost.system.new.configure do |c|
c.update_project(project.path, project)
@ -23,13 +37,12 @@ end
#
# Table name: users_projects
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# read :boolean default(FALSE)
# write :boolean default(FALSE)
# admin :boolean default(FALSE)
# created_at :datetime
# updated_at :datetime
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# repo_access :integer default(0), not null
# project_access :integer default(0), not null
#

31
app/models/web_hook.rb Normal file
View file

@ -0,0 +1,31 @@
class WebHook < ActiveRecord::Base
include HTTParty
# HTTParty timeout
default_timeout 10
belongs_to :project
validates :url,
presence: true,
format: {
with: URI::regexp(%w(http https)),
message: "should be a valid url" }
def execute(data)
WebHook.post(url, body: data.to_json)
rescue
# There was a problem calling this web hook, let's forget about it.
end
end
# == Schema Information
#
# Table name: web_hooks
#
# id :integer not null, primary key
# url :string(255)
# project_id :integer
# created_at :datetime
# updated_at :datetime
#

View file

@ -38,6 +38,23 @@
%h2 Team
= form_tag team_update_admin_project_path(@admin_project), :class => "bulk_import", :method => :put do
%table
%thead
%tr
%th Users
%th Project Access:
%th Repo Access:
%tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), :multiple => true
%td= select_tag :project_access, options_for_select(Project.access_options), :class => "project-access-select"
%td= select_tag :repo_access, options_for_select(Repository.access_options), :class => "repo-access-select"
%tr
%td{ :colspan => 3 }
= submit_tag 'Add', :class => "positive-button"
%table.round-borders
%thead
%tr
@ -52,8 +69,22 @@
%td
= link_to tm.user_name, admin_team_member_path(tm)
%td= time_ago_in_words(tm.updated_at) + " ago"
%td= select_tag :project_access, options_for_select(Project.access_options, tm.project_access), :class => "project-access-select", :disabled => :disabled
%td= select_tag :repo_access, options_for_select(Repository.access_options, tm.repo_access), :class => "repo-access-select", :disabled => :disabled
%td= select_tag :tm_project_access, options_for_select(Project.access_options, tm.project_access), :class => "project-access-select", :disabled => :disabled
%td= select_tag :tm_repo_access, options_for_select(Repository.access_options, tm.repo_access), :class => "repo-access-select", :disabled => :disabled
%td= link_to 'Destroy', admin_team_member_path(tm), :confirm => 'Are you sure?', :method => :delete
= link_to 'New Team Member', new_admin_team_member_path(:team_member => {:project_id => @admin_project.id}), :class => "grey-button"
:css
form select {
width:150px;
}
#user_ids {
width:300px;
}
:javascript
$('select#user_ids').chosen();
$('select#repo_access').chosen();
$('select#project_access').chosen();

View file

View file

@ -17,7 +17,7 @@
= image_tag "no_avatar.png", :class => "left", :width => 40, :style => "padding-right:5px;"
%span.commit-title
%strong
= truncate(commit.safe_message, :length => 60)
= truncate(commit.safe_message, :length => 70)
%span.commit-author
%strong= commit.author_name
= time_ago_in_words(commit.committed_date)

View file

@ -1,13 +1,19 @@
%table
- line_old = 0
- line_new = 0
- diff_str = encode(diff.diff)
- diff_str = diff.diff
- lines_arr = diff_str.lines.to_a
- lines_arr.each do |line|
- next if line.match(/^--- \/dev\/null/)
- next if line.match(/^--- a/)
- next if line.match(/^\+\+\+ b/)
- if line.match(/^@@ -/)
- unless line_old.zero? && line_new.zero?
%tr.line_holder
%td.old_line= "..."
%td.new_line= "..."
%td.line_content &nbsp;
- line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
- line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
- next
@ -18,7 +24,11 @@
= link_to raw(diff_line_class(line) == "new" ? "&nbsp;" : line_old), "#OLD#{index}-#{line_old}", :id => "OLD#{index}-#{line_old}"
%td.new_line
= link_to raw(diff_line_class(line) == "old" ? "&nbsp;" : line_new) , "#NEW#{index}-#{line_new}", :id => "NEW#{index}-#{line_new}"
%td.line_content{:class => diff_line_class(full_line)}= raw "#{full_line} &nbsp;"
%td.line_content{:class => "#{diff_line_class(full_line)} #{build_line_code(line, index, line_new, line_old)}", "line_code" => build_line_code(line, index, line_new, line_old)}= raw "#{full_line} &nbsp;"
- comments = @line_notes.select { |n| n.for_line?(index, line_old, line_new) }.sort_by(&:created_at).reverse
- unless comments.empty?
- comments.each do |note|
= render "notes/per_line_show", :note => note
- if line[0] == "+"
- line_new += 1
- elsif line[0] == "-"

View file

@ -1,18 +1,16 @@
- content_for(:body_class, "project-page commits-page")
- if current_user.private_token
= content_for :rss_icon do
.rss-icon
= link_to project_commits_path(@project, :atom, { :private_token => current_user.private_token, :ref => @ref }) do
= image_tag "Rss-UI.PNG", :width => 22, :title => "feed"
-#%a.right.button{:href => "#"} Download
-#-if can? current_user, :admin_project, @project
%a.right.button.blue{:href => "#"} EDIT
%h2.icon
%span
%d
- if params[:path]
%h2
= link_to project_commits_path(@project) do
= @project.name
- if params[:path]
\/
%a{:href => "#"}= params[:path].split("/").join(" / ")
.right= render :partial => "projects/refs", :locals => { :destination => :commits }
= @project.code
\/
%a{:href => "#"}= params[:path].split("/").join(" / ")
%div{:id => dom_id(@project)}
#commits_list= render "commits"

View file

@ -18,10 +18,21 @@
%hr
%pre.commit_message
= preserve @commit.safe_message
= commit_msg_with_link_to_issues(@project, @commit.safe_message)
.clear
%br
= render "commits/diff"
= render "notes/notes"
= render "notes/per_line_form"
:javascript
$(document).ready(function(){
$(".line_content").live("dblclick", function(e) {
var form = $(".per_line_form");
$(this).parent().after(form);
form.find("#note_line_code").val($(this).attr("line_code"));
form.show();
});
});

View file

@ -0,0 +1,26 @@
#feeds_content_holder
- unless @issues.empty?
.project-box.project-updates.ui-box.ui-box-small.ui-box-big
.data
- @issues.each do |update|
%a.project-update{:href => dashboard_feed_path(update.project, update)}
%strong.issue-number= "##{update.id}"
%span.update-title
= truncate update.title, :length => 35
.right= truncate update.project.name
%span.update-author
%strong= update.author_name
authored
= time_ago_in_words(update.created_at)
ago
.right
- if update.critical
%span.tag.high critical
- if update.today?
%span.tag.today today
- else
%h2
No assigned
%span.tag.open open
issues

View file

@ -0,0 +1,21 @@
-#%h4.dash-tabs
= link_to "Activities", dashboard_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_path) || current_page?(root_path) }", :id => "activities_slide"
= link_to "Issues", dashboard_issues_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_issues_path)}", :id => "issues_slide"
= link_to "Merge Requests", dashboard_merge_requests_path, :remote => true, :class => "dash-button #{"active" if current_page?(dashboard_merge_requests_path)}", :id => "merge_requests_slide"
= image_tag "ajax-loader-facebook.gif", :class => "dashboard-loader"
:javascript
$(function(){
$(".dash-button").live("click", function() {
$(".dash-button").removeClass("active");
$(this).addClass("active");
});
$(".dash-button").live("ajax:before", function() {
$(".dashboard-loader").show();
});
$(".dash-button").live("ajax:complete", function() {
$(".dashboard-loader").hide();
});
});

View file

@ -0,0 +1,24 @@
#feeds_content_holder
- unless @merge_requests.empty?
.project-box.project-updates.ui-box.ui-box-small.ui-box-big
.data
- @merge_requests.each do |update|
%a.project-update{:href => project_merge_request_path(update.project, update)}
= image_tag gravatar_icon(update.author_email), :class => "left", :width => 40
%span.update-title
= truncate update.title, :length => 35
.right= truncate update.project.name
%span.update-author
%strong= update.author_name
authored
= time_ago_in_words(update.created_at)
ago
.right
%span.tag.commit= update.source_branch
&rarr;
%span.tag.commit= update.target_branch
- else
%h2
No authored or assigned
%span.tag.open open
merge requests

View file

@ -0,0 +1,20 @@
#feeds_content_holder
- @active_projects.first(3).each do |project|
.project-box.project-updates.ui-box.ui-box-small.ui-box-big
= link_to project do
%h3= project.name
.data
- project.updates(3).each do |update|
%a.project-update{:href => dashboard_feed_path(project, update)}
= image_tag gravatar_icon(update.author_email), :class => "left", :width => 40
%span.update-title
= dashboard_feed_title(update)
%span.update-author
%strong= update.author_name
authored
= time_ago_in_words(update.created_at)
ago
.right
- klass = update.class.to_s.split("::").last.downcase
%span.tag{ :class => klass }= klass

Some files were not shown because too many files have changed in this diff Show more