[issue #193] Merge branch '3.2-master' into 193-new_design_for_wiki

Conflicts:
	app/assets/stylesheets/custom.scss
This commit is contained in:
George Vinogradov 2012-03-06 17:40:44 +04:00
commit f070a9d729
622 changed files with 43588 additions and 11085 deletions

23
Gemfile
View File

@ -1,19 +1,19 @@
source 'http://rubygems.org'
gem 'rails', '3.2.1' #, :git => 'git://github.com/rails/rails.git'
gem 'rails', '3.2.2' #, :git => 'git://github.com/rails/rails.git'
gem 'pg', '~> 0.13.1'
gem 'pg', '~> 0.13.2'
# gem 'silent-postgres', :git => 'git://github.com/dolzenko/silent-postgres.git' #'~> 0.1.1'
gem 'redhillonrails_core', :git => 'git://github.com/chipiga/redhillonrails_core.git', :branch => 'rails31' # '~> 2.0.0.pre' # deprecated
# gem 'schema_plus', '~> 0.2.1' # buggy shit!
gem 'devise', '~> 2.0.1'
gem 'devise', '~> 2.0.4'
gem 'omniauth', '~> 1.0.2'
gem 'omniauth-openid', '~> 1.0.1'
gem 'cancan', '~> 1.6.7'
gem 'ancestry', '~> 1.2.4'
gem 'paperclip', '~> 2.5.2'
gem 'paperclip', '~> 2.7.0'
gem 'delayed_job_active_record', '~> 0.3.2'
gem 'russian', '~> 0.6.0'
gem 'highline', '~> 1.6.11'
@ -32,26 +32,26 @@ gem 'RedCloth'
gem 'wikicloth'
gem 'unicorn', '~> 4.2.0'
gem 'newrelic_rpm', '~> 3.3.1'
gem 'newrelic_rpm', '~> 3.3.2'
gem 'rails3-jquery-autocomplete', '~> 1.0.6'
gem 'will_paginate', '~> 3.0.3'
gem 'meta-tags', '~> 1.2.4', :require => 'meta_tags'
gem "haml-rails", '~> 0.3.4'
gem 'jquery-rails', '~> 2.0.0'
gem 'jquery-rails', '~> 2.0.1'
group :assets do
gem 'sass-rails', '~> 3.2.4'
gem 'coffee-rails', '~> 3.2.2'
gem 'compass-rails', '~> 1.0.0.rc.3'
gem 'uglifier', '~> 1.2.1'
gem 'compass', '~> 0.12.rc.1' # :git => 'git://github.com/chriseppstein/compass.git', :branch => 'rails31'
gem 'therubyracer', '~> 0.9.9'
gem 'therubyracer', '~> 0.9.10'
end
group :production do
gem "airbrake", '~> 3.0.9'
gem 'bluepill', '~> 0.0.55', :require => false
gem 'daemons', '~> 1.1.8' # for DJ
gem 'bluepill', '~> 0.0.59', :require => false
gem 'daemons', '1.1.6' # for DJ
end
group :development do
@ -62,13 +62,12 @@ group :development do
# deploy
gem 'whenever', :require => false
gem 'capistrano', :require => false
gem 'capistrano-ext', :require => false
gem 'cape', :require => false
gem 'capistrano_colors', :require => false
end
group :test do
gem 'rspec-rails', '~> 2.8.1'
gem 'factory_girl_rails', '~> 1.6.0'
gem 'factory_girl_rails', '~> 1.7.0'
gem 'rr', '~> 1.0.4'
end

View File

@ -26,12 +26,12 @@ GEM
remote: http://rubygems.org/
specs:
RedCloth (4.2.9)
actionmailer (3.2.1)
actionpack (= 3.2.1)
actionmailer (3.2.2)
actionpack (= 3.2.2)
mail (~> 2.4.0)
actionpack (3.2.1)
activemodel (= 3.2.1)
activesupport (= 3.2.1)
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
@ -39,18 +39,18 @@ GEM
rack-cache (~> 1.1)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.1)
activesupport (= 3.2.1)
activemodel (3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
activerecord (3.2.1)
activemodel (= 3.2.1)
activesupport (= 3.2.1)
arel (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.1)
activemodel (= 3.2.1)
activesupport (= 3.2.1)
activesupport (3.2.1)
activeresource (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
airbrake (3.0.9)
@ -60,24 +60,22 @@ GEM
posix-spawn (>= 0.3.6)
ancestry (1.2.4)
activerecord (>= 2.2.2)
arel (3.0.0)
arel (3.0.2)
bcrypt-ruby (3.0.1)
bluepill (0.0.55)
bluepill (0.0.59)
activesupport (>= 3.0.0)
daemons (~> 1.1.4)
daemons (~> 1.1.4, <= 1.1.6)
i18n (>= 0.5.0)
state_machine (~> 1.1.0)
builder (3.0.0)
cancan (1.6.7)
cape (1.4.0)
capistrano (2.9.0)
capistrano (2.11.2)
highline
net-scp (>= 1.0.0)
net-sftp (>= 2.0.0)
net-ssh (>= 2.0.14)
net-ssh-gateway (>= 1.1.0)
capistrano-ext (1.2.1)
capistrano (>= 1.0.0)
capistrano_colors (0.5.5)
chronic (0.6.7)
chunky_png (1.2.5)
@ -93,28 +91,30 @@ GEM
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
compass-rails (1.0.0.rc.3)
compass (~> 0.12.rc.0)
creole (0.4.2)
daemons (1.1.8)
daemons (1.1.6)
delayed_job (3.0.1)
activesupport (~> 3.0)
delayed_job_active_record (0.3.2)
activerecord (> 2.1.0)
delayed_job (~> 3.0.0)
devise (2.0.1)
devise (2.0.4)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.0.3)
railties (~> 3.1)
warden (~> 1.1)
warden (~> 1.1.1)
diff-lcs (1.1.3)
erubis (2.7.0)
eventmachine (0.12.10)
execjs (1.3.0)
multi_json (~> 1.0)
expression_parser (0.9.0)
factory_girl (2.5.2)
factory_girl (2.6.1)
activesupport (>= 2.3.9)
factory_girl_rails (1.6.0)
factory_girl (~> 2.5.0)
factory_girl_rails (1.7.0)
factory_girl (~> 2.6.0)
railties (>= 3.0.0)
fssm (0.2.8.1)
github-markup (0.7.1)
@ -138,9 +138,9 @@ GEM
hike (1.2.1)
hirb (0.6.0)
i18n (0.6.0)
journey (1.0.1)
jquery-rails (2.0.0)
railties (>= 3.2.0.beta, < 5.0)
journey (1.0.3)
jquery-rails (2.0.1)
railties (>= 3.2.0, < 5.0)
thor (~> 0.14)
json (1.6.5)
kgio (2.7.2)
@ -161,7 +161,7 @@ GEM
meta-tags (1.2.4)
actionpack
mime-types (1.17.2)
multi_json (1.0.4)
multi_json (1.1.0)
mustache (0.99.4)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
@ -170,7 +170,7 @@ GEM
net-ssh (2.3.0)
net-ssh-gateway (1.1.0)
net-ssh (>= 1.99.1)
newrelic_rpm (3.3.1)
newrelic_rpm (3.3.2)
nokogiri (1.5.0)
omniauth (1.0.2)
hashie (~> 1.2)
@ -179,12 +179,12 @@ GEM
omniauth (~> 1.0)
rack-openid (~> 1.3.1)
orm_adapter (0.0.6)
paperclip (2.5.2)
paperclip (2.7.0)
activerecord (>= 2.3.0)
activesupport (>= 2.3.2)
cocaine (>= 0.0.2)
mime-types
pg (0.13.1)
pg (0.13.2)
polyglot (0.3.3)
posix-spawn (0.3.6)
rack (1.4.1)
@ -199,22 +199,22 @@ GEM
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.2.1)
actionmailer (= 3.2.1)
actionpack (= 3.2.1)
activerecord (= 3.2.1)
activeresource (= 3.2.1)
activesupport (= 3.2.1)
rails (3.2.2)
actionmailer (= 3.2.2)
actionpack (= 3.2.2)
activerecord (= 3.2.2)
activeresource (= 3.2.2)
activesupport (= 3.2.2)
bundler (~> 1.0)
railties (= 3.2.1)
railties (= 3.2.2)
rails-xmlrpc (0.3.6)
rails3-generators (0.17.4)
railties (>= 3.0.0)
rails3-jquery-autocomplete (1.0.6)
rails (~> 3.0)
railties (3.2.1)
actionpack (= 3.2.1)
activesupport (= 3.2.1)
railties (3.2.2)
actionpack (= 3.2.2)
activesupport (= 3.2.2)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
@ -264,7 +264,7 @@ GEM
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.5)
state_machine (1.1.2)
therubyracer (0.9.9)
therubyracer (0.9.10)
libv8 (~> 3.3.10)
thin (1.3.1)
daemons (>= 1.0.9)
@ -283,9 +283,9 @@ GEM
kgio (~> 2.6)
rack
raindrops (~> 0.7)
warden (1.1.0)
warden (1.1.1)
rack (>= 1.0)
whenever (0.7.2)
whenever (0.7.3)
activesupport (>= 2.3.4)
chronic (~> 0.6.3)
wikicloth (0.7.1)
@ -300,34 +300,33 @@ DEPENDENCIES
RedCloth
airbrake (~> 3.0.9)
ancestry (~> 1.2.4)
bluepill (~> 0.0.55)
bluepill (~> 0.0.59)
cancan (~> 1.6.7)
cape
capistrano
capistrano-ext
capistrano_colors
coffee-rails (~> 3.2.2)
compass (~> 0.12.rc.1)
compass-rails (~> 1.0.0.rc.3)
creole
daemons (~> 1.1.8)
daemons (= 1.1.6)
delayed_job_active_record (~> 0.3.2)
devise (~> 2.0.1)
factory_girl_rails (~> 1.6.0)
devise (~> 2.0.4)
factory_girl_rails (~> 1.7.0)
gollum (= 1.3.1)
grack!
grit!
haml-rails (~> 0.3.4)
highline (~> 1.6.11)
hirb
jquery-rails (~> 2.0.0)
jquery-rails (~> 2.0.1)
mailcatcher
meta-tags (~> 1.2.4)
newrelic_rpm (~> 3.3.1)
newrelic_rpm (~> 3.3.2)
omniauth (~> 1.0.2)
omniauth-openid (~> 1.0.1)
paperclip (~> 2.5.2)
pg (~> 0.13.1)
rails (= 3.2.1)
paperclip (~> 2.7.0)
pg (~> 0.13.2)
rails (= 3.2.2)
rails-xmlrpc (~> 0.3.6)
rails3-generators
rails3-jquery-autocomplete (~> 1.0.6)
@ -339,7 +338,7 @@ DEPENDENCIES
russian (~> 0.6.0)
sass-rails (~> 3.2.4)
shotgun
therubyracer (~> 0.9.9)
therubyracer (~> 0.9.10)
uglifier (~> 1.2.1)
unicorn (~> 4.2.0)
whenever

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,44 +0,0 @@
var droplist = function() {
$user_menu.slideToggle("slow");
}
function loadMessages() {
$("#messages-new").fadeOut("slow");
$("#new-messages").delay(700).fadeIn("slow");
}
function loadOldMessages() {
$("#old-messages").fadeIn("slow");
}
$(document).ready(function(){
$user_menu = $('#droplist');
$user_menu.die('click');
$('div.information > div.user').live('click', function() {
if ($user_menu.is(":hidden")) {
droplist();
}
});
$('div.information > div.profile > a').live('click', function(e) {
e.preventDefault();
});
});
$(document).click(function(e) {
if (!$user_menu.is(":hidden") && ($(e.target).parent().attr('id') != $user_menu.attr('id'))) {
droplist();
}
});
function showActivity(elem) {
$("#activity-bottom"+elem).slideToggle("slow");
var img = $("#expand" + elem).attr("src");
if (img == "assets/expand-gray.png") {
$("#expand" + elem).attr("src","assets/expand-gray2.png");
} else {
$("#expand" + elem).attr("src","assets/expand-gray.png");
}
}

View File

@ -3,8 +3,9 @@
//= require jquery-ui
//= require autocomplete-rails
//= require vendor
//= require_tree .
// require_self
//= require_tree ./design
//= require_tree ./extra
//= require_self
// function disableNotifierCbx(global_cbx) {
// if ($(global_cbx).attr('checked')) {
@ -17,47 +18,24 @@
// }
//
// $(document).ready(function() {
// $('select#build_list_pl_id').change(function() {
// var platform_id = $(this).val();
// var base_platforms = $('.base_platforms input[type=checkbox]');
//
// $('#include_repos').html($('.preloaded_include_repos .include_repos_' + platform_id).html());
//
// base_platforms.each(function(){
// if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) >= 0) {
// if ($(this).val() == platform_id) {
// $(this).attr('checked', 'checked');
// $(this).removeAttr('disabled');
// } else {
// $(this).removeAttr('checked');
// $(this).attr('disabled', 'disabled');
// }
// } else {
// $(this).removeAttr('disabled');
// }
// });
// });
// $('select#build_list_pl_id').trigger('change');
//
// $('input.user_role_chbx').click(function() {
// var current = $(this);
// current.parent().find('input.user_role_chbx').each(function(i,el) {
// if ($(el).attr('id') != current.attr('id')) {
// $(el).removeAttr('checked');
// }
// });
// });
//
// $('#settings_notifier_can_notify').click(function() {
// disableNotifierCbx($(this));
// });
//
// $('div.information > div.user').live('click', function() {
// droplist();
// });
//
// $('div.information > div.profile > a').live('click', function(e) {
// e.preventDefault();
// });
//
// $('input.user_role_chbx').click(function() {
// var current = $(this);
// current.parent().find('input.user_role_chbx').each(function(i,el) {
// if ($(el).attr('id') != current.attr('id')) {
// $(el).removeAttr('checked');
// }
// });
// });
//
// $('#settings_notifier_can_notify').click(function() {
// disableNotifierCbx($(this));
// });
//
// $('div.information > div.user').live('click', function() {
// droplist();
// });
//
// $('div.information > div.profile > a').live('click', function(e) {
// e.preventDefault();
// });
// });

View File

@ -1,38 +0,0 @@
function changeCheck(el)
{
var el = el,
input = el.getElementsByTagName("input")[0];
if(input.checked)
{
el.style.backgroundPosition="0 0";
input.checked=false;
}
else
{
el.style.backgroundPosition="0 -18px";
input.checked=true;
}
return true;
}
function startChangeCheck(el)
{
var el = el,
input = el.getElementsByTagName("input")[0];
if(input.checked)
{
el.style.backgroundPosition="0 -18px";
}
return true;
}
function startCheck()
{
startChangeCheck(document.getElementById("niceCheckbox1"));
startChangeCheck(document.getElementById("niceCheckbox2"));
startChangeCheck(document.getElementById("niceCheckbox3"));
startChangeCheck(document.getElementById("niceCheckbox4"));
}

View File

@ -1,35 +0,0 @@
function addPeople(num) {
$("#people"+num).fadeOut(0);
$("#people-sections"+num).fadeIn("slow");
$("#people-sections-list"+num).fadeIn("slow");
if ($("#people-span").css("display") != "none") {
$("#people-span").fadeOut(0);
}
}
function remPeople(num) {
$("#people"+num).fadeIn("slow");
$("#people-sections"+num).fadeOut(0);
$("#people-sections-list"+num).fadeOut(0);
if (($("#people-sections-list1").css("display") == "none") && ($("#people-sections-list2").css("display") == "none") && ($("#people-sections-list3").css("display") == "none") && ($("#people-sections-list4").css("display") == "none")) {
$("#people-span").fadeIn("slow");
}
}
function addFlag(num) {
$("#flag"+num).fadeOut(0);
$("#flag-list"+num).fadeIn("slow");
$("#flag-list-sections"+num).fadeIn("slow");
if ($("#flag-span").css("display") != "none") {
$("#flag-span").fadeOut(0);
}
}
function remFlag(num) {
$("#flag"+num).fadeIn("slow");
$("#flag-list"+num).fadeOut(0);
$("#flag-list-sections"+num).fadeOut(0);
if (($("#flag-list-sections1").css("display") == "none") && ($("#flag-list-sections2").css("display") == "none") && ($("#flag-list-sections3").css("display") == "none") && ($("#flag-list-sections4").css("display") == "none")) {
$("#flag-span").fadeIn("slow");
}
}

View File

@ -1,11 +0,0 @@
jQuery(document).ready(function(){
var params = {
changedEl: ".lineForm select",
visRows: 999999,
scrollArrows: false
}
cuSel(params);
});

View File

@ -0,0 +1,44 @@
$(document).ready(function() {
var dropbox = $("#droplist");
function loadMessages() {
$("#messages-new").fadeOut("slow");
$("#new-messages").delay(700).fadeIn("slow");
}
function loadOldMessages() {
$("#old-messages").fadeIn("slow");
}
$(document).click(function() {
var dl = dropbox.css("height");
var dl2 = dropbox.css("display");
if ((dl2 == "block")&&(dl == "91px")) {
dropbox.slideUp("slow");
}
});
$('.data-expander').live('click', function(e) {
var $button = $(e.target);
var id = "#content-" + $button.attr('id');
var $slider = $(id);
$slider.slideToggle("slow", function(){
$button.toggleClass('expanded collapsed');
});
});
function showActivity(elem) {
$("#activity-bottom"+elem).slideToggle("slow");
var img = document.getElementById("expand" + elem).className;
if (img == "expand-gray-down") {
document.getElementById("expand" + elem).className = "expand-gray-up";
} else {
document.getElementById("expand" + elem).className = "expand-gray-down";
}
}
$('div.information > div.user').live('click', function() {
dropbox.slideToggle("slow");
});
});

View File

@ -0,0 +1,32 @@
function changeCheck(el) {
var el = el, input = el.find('input[type="checkbox"]');
if(input.attr("checked")) {
el.css('backgroundPosition', '0 0');
input.removeAttr('checked');
} else {
el.css('backgroundPosition', '0 -18px');
input.attr('checked', true);
}
return true;
}
function startChangeCheck(el) {
var el = el, input = el.find('input[type="checkbox"]');
if(input.attr('checked')) {
el.css('backgroundPosition', '0 -18px');
}
return true;
}
$(document).ready(function(){
$('.niceCheck-main').each(function(i,el) {
startChangeCheck($(el));
});
$('.niceCheck-main').click(function() {
changeCheck($(this));
});
});

View File

@ -1,4 +1,6 @@
function changeCheck(el)
/*function changeCheck(el)
{
var el = el,
@ -32,4 +34,4 @@ function startCheck()
{
startChangeCheck(document.getElementById("niceCheckbox1"));
}
}*/

View File

@ -0,0 +1,11 @@
jQuery(document).ready(function(){
//var params = {
// changedEl: ".lineForm select",
// visRows: 999999,
// scrollArrows: false
// }
//
// cuSel(params);
});

View File

@ -0,0 +1,16 @@
$(document).ready(function() {
// $("#myTable").tablesorter({
// headers: {
// 1: {
// sorter: false
// },
// 3: {
// sorter: false
// }
// }
// });
});
function deleteRow(num) {
// $("#Row"+num).fadeOut("slow");
}

View File

@ -0,0 +1,20 @@
$(document).ready(function() {
// $("#myTable").tablesorter({
// headers: {
// 2: {
// sorter: false
// }
// }
//
// });
});
$(document).ready(function() {
// $("a.files-see").click(function() {
// $("#file1").fadeOut(0);
// $("#file2").fadeIn("slow");
// $("#file-name1").fadeOut(0);
// $("#file-name2").fadeIn("slow");
// $("#fork-and-edit").fadeIn("slow");
// });
});

View File

@ -0,0 +1,97 @@
$(document).ready(function(){
$(".niceRadio").each(function() {
changeRadioStart($(this));
});
});
function changeRadio(el) {
var el = el, input = el.find("input").eq(0);
var nm = input.attr("name");
$(".niceRadio input").each(
function() {
if($(this).attr("name")==nm) {
$(this).parent().removeClass("radioChecked");
}
});
if(el.attr("class").indexOf("niceRadioDisabled")==-1) {
el.addClass("radioChecked");
input.attr("checked", true);
}
return true;
}
function changeVisualRadio(input) {
var wrapInput = input.parent();
var nm=input.attr("name");
$(".niceRadio input").each(function() {
if($(this).attr("name")==nm)
{
$(this).parent().removeClass("radioChecked");
}
});
if(input.attr("checked"))
{
wrapInput.addClass("radioChecked");
}
}
function changeRadioStart(el) {
try {
var el = el,
radioName = el.attr("name"),
radioId = el.attr("id"),
radioChecked = el.attr("checked"),
radioDisabled = el.attr("disabled"),
radioTab = el.attr("tabindex"),
radioValue = el.attr("value");
if(radioChecked) {
el.after("<span class='niceRadio radioChecked'>"+
"<input type='radio'"+
"name='"+radioName+"'"+
"id='"+radioId+"'"+
"checked='"+radioChecked+"'"+
"tabindex='"+radioTab+"'"+
"value='"+radioValue+"' /></span>");
} else {
el.after("<span class='niceRadio'>"+
"<input type='radio'"+
"name='"+radioName+"'"+
"id='"+radioId+"'"+
"tabindex='"+radioTab+"'"+
"value='"+radioValue+"' /></span>");
}
if(radioDisabled) {
el.next().addClass("niceRadioDisabled");
el.next().find("input").eq(0).attr("disabled","disabled");
}
el.next().bind("mousedown", function(e) {
changeRadio($(this));
$(this).find("input:radio").change();
});
if($.browser.msie) {
el.next().find("input").eq(0).bind("click", function(e) {
changeVisualRadio($(this))
});
} else {
el.next().find("input").eq(0).bind("change", function(e) {
changeVisualRadio($(this))
});
}
el.remove();
}
catch(e) { }
return true;
}

View File

@ -0,0 +1,26 @@
/*function deleteAdminMember() {
if (document.getElementById("niceCheckbox1-1").checked == true) {
$("#admin-table-members-row1").fadeOut("slow");
}
if (document.getElementById("niceCheckbox2-1").checked == true) {
$("#admin-table-members-row2").fadeOut("slow");
}
if (document.getElementById("niceCheckbox3-1").checked == true) {
$("#admin-table-members-row3").fadeOut("slow");
}
if (document.getElementById("niceCheckbox4-1").checked == true) {
$("#admin-table-members-row4").fadeOut("slow");
}
}*/
function saveAdminMember() {
$('#_method').attr('value', 'post');
$('form#members_form').submit();
}
function deleteAdminMember() {
$('#_method').attr('value', 'delete');
var delete_url = $('form#members_form').attr('delete_url');
$('form#members_form').attr('action', delete_url);
$('form#members_form').submit();
}

View File

@ -0,0 +1,23 @@
$(document).ready(function() {
$('select#build_list_pl_id').change(function() {
var platform_id = $(this).val();
var base_platforms = $('.base_platforms input[type=checkbox]');
$('#include_repos').html($('.preloaded_include_repos .include_repos_' + platform_id).html());
base_platforms.each(function(){
if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) >= 0) {
if ($(this).val() == platform_id) {
$(this).attr('checked', 'checked');
$(this).removeAttr('disabled');
} else {
$(this).removeAttr('checked');
$(this).attr('disabled', 'disabled');
}
} else {
$(this).removeAttr('disabled');
}
});
});
$('select#build_list_pl_id').trigger('change');
});

View File

@ -0,0 +1,237 @@
$(document).ready(function() {
$("#closed-switcher").live('click', function() {
if ($("#blue-switch-select").css("margin-left") != "130px") {
$("#blue-switch-select").animate({"margin-left": "+=130px"}, "fast");
$("#table1").fadeOut(0);
$("#table2").fadeIn("slow");
$('#issues_status').val('closed');
}
else {
$("#blue-switch-select").animate({"margin-left": "-=130px"}, "fast");
$("#table2").fadeOut(0);
$("#table1").fadeIn("slow");
$('#issues_status').val('open');
}
return send_index_tracker_request('GET');
});
$("#manage-labels").live('click', function () {
var toggled = $(this).data('toggled');
$(this).data('toggled', !toggled);
if (!toggled) {
$("#labels-stock").fadeOut(0);
$("#labels-edit").fadeIn("slow");
}
else {
$("#labels-edit").fadeOut(0);
$("#labels-stock").fadeIn("slow");
}
});
$("div.div-tracker-labels").live('click', function() {
var flag = this.id;
flag = flag.replace("label-","flag-");
var bg = $("#"+flag).css("background-color");
var checkbox = $(this).find(':checkbox');
if ($(this).css("background-color") != bg) {
$(this).css("background-color",bg);
$(this).css("color","#FFFFFF");
checkbox.attr('checked', 'checked');
} else {
$(this).css("background-color","rgb(247, 247, 247)");
$(this).css("color","#565657");
checkbox.removeAttr('checked');
}
return send_index_tracker_request('GET');
});
$("#filter_issues #myradio1").live('change', function(event) {
return send_index_tracker_request('GET');
});
$('#search_issue').live('submit', function() {
return send_index_tracker_request('GET', $(this).attr("action"), $(this).serialize());
});
$('#add_label').live('click', function() {
return send_index_tracker_request('POST', $(this).attr("href"), $('#new_label').serialize());
});
$('.righter #update_label').live('click', function() {
return send_index_tracker_request('POST', $(this).attr("href"), $(this).parents('#update_label').serialize());
});
$('.colors .choose').live('click', function() {
var parent = $(this).parents('.colors');
parent.find('.choose.selected').removeClass('selected');
$(this).addClass('selected');
parent.siblings('.lefter').find('#label_color').val($(this).attr('value'));
return false;
});
$('.custom_color').live('click', function() {
$(this).siblings('#label_color').toggle();
return false;
});
$('article a.edit_label').live('click', function() {
$(this).parents('.label.edit').siblings('.label.edit').find('.edit_label_form').hide();
$(this).parents('.label.edit').find('.edit_label_form').toggle();
return false;
});
$('.delete_label').live('click', function() {
return send_index_tracker_request('POST', $(this).attr('href'));
});
function send_index_tracker_request(type_request, url, data) {
data = data || '';
var filter_form = $('#filter_issues');
url = url || filter_form.attr("action");
var label_form = $('#filter_labels');
var status = 'status=' + $('#issues_status').attr('value');
$.ajax({
type: type_request,
url: url,
data: filter_form.serialize() + '&' + label_form.serialize() + '&' + status + '&' + data,
success: function(data){
$('article').html(data);
$(".niceRadio").each(function() { changeRadioStart(jQuery(this)) });
},
error: function(data){
alert('error') // TODO remove
}
});
return false;
};
$('#search_user, #search_labels').live('submit', function() {
var id = $(this).attr('id');
if(id.indexOf('user') != -1) { // FIXME
var which = 'users';
}
else if (id.indexOf('labels') != -1) {
var which = 'labels';
}
$.ajax({
type: 'GET',
url: $(this).attr("action"),
data: $(this).serialize(),
success: function(data){
var tmp = $('#create_issue_'+ which +'_list');
$('#create_issue_'+ which +'_list').html(data);
},
error: function(data){
alert('error') // TODO remove
}
});
return false;
});
function remExecutor(form) {
var el = form.find('.people.selected.remove_executor');
var id = el.attr('id');
$('#'+id+'.add_executor.people.selected').removeClass('select');
el.remove();
}
$('.add_executor.people.selected').live('click', function() {
var form = $('.form.issue');
form.find('#people-span').fadeOut(0);
remExecutor(form);
form.find('#issue_executor').html($(this).clone().removeClass('add_executor').addClass('remove_executor'));
$(this).addClass('select');
});
$('.remove_executor.people.selected').live('click', function() {
var form = $('.form.issue');
form.find('#people-span').fadeIn(0);
remExecutor(form);
});
function remLabel(form, id) {
var el = form.find('.label.remove_label'+'#'+id);
var label = $('#'+id+'.remove_label.label.selected');
label.find('.flag').fadeIn(0);
label.find('.labeltext.selected').removeClass('selected').attr('style', '');
label.fadeIn('slow');
el.fadeOut('slow').remove();
}
$('.add_label.label').live('click', function() {
$(this).addClass('selected').removeClass('add_label').addClass('remove_label');
$(this).find('.labeltext').addClass('selected');
var style = $(this).find('.flag').attr('style');
$(this).find('.flag').fadeOut(0);
$(this).find('.labeltext.selected').attr('style', style);
var form = $('.form.issue');
form.find('#flag-span').fadeOut(0);
form.find('#issue_labels').append($(this).clone());
});
$('.remove_label.label.selected').live('click', function() {
var form = $('.form.issue');
if(form.find('.remove_label.label.selected').length == 1) {
form.find('#flag-span').fadeIn(0);
}
var str = '.label.remove_label'+'#'+$(this).attr('id');
form.find(str).remove();
var label = $(str);
label.removeClass('selected').addClass('add_label').removeClass('remove_label');
label.find('.labeltext.selected').attr('style', '').removeClass('selected');
label.find('.flag').fadeIn(0);
});
$('.issue_status.switch_issue_status').live('click', function () {
var button = $(this);
var status = (button.hasClass('switcher')) ? 'closed' : 'open';
var form = $('#update_issue_status');
form.find('#issue_status').attr('value', status);
$.ajax({
type: 'POST',
url: form.attr("action"),
data: form.serialize(),
success: function(data){
if (status == "open") { button.addClass('switcher').removeClass("switcher-off"); }
else { button.removeClass('switcher').addClass("switcher-off"); }
$('#closed_issue_text').html(data);
},
error: function(data){
alert('error') // TODO remove
}
});
return false;
});
$('#edit_issue_content').live('click', function() {
$('.edit_form.issue').fadeIn('fast');
$(this).fadeOut('fast');
});
$('#cancel_edit_issue_content').live('click', function() {
$('.edit_form.issue').fadeOut('fast');
$('#edit_issue_content').fadeIn('fast');
});
$('.edit_form.issue').live('submit', function() {
var form = $(this);
$.ajax({
type: 'POST',
url: form.attr("action"),
data: form.serialize(),
success: function(data){
form.fadeOut('slow');
$('#edit_issue_content').fadeIn('slow');
$('h3.issue_title').html(form.find('#issue_title').attr('value'));
$('.fulltext.view.issue_body').html(form.find('#issue_body').attr('value'));
},
error: function(data){
alert('error') // TODO remove
}
});
return false;
});
});

View File

@ -0,0 +1,35 @@
//= require jquery
$(document).ready(function() {
var login_default = $('#login_default').val();
var pass_default = $('#password_default').val();
$('.registartion-input').live('keydown', function() {
var id = $(this).attr('id');
if(id == 'user_login' || id == 'user_password') { id = 'login_error'}
$('.error.'+id).fadeOut('slow');
}).live('focus', function() {
var id = $(this).attr('id');
if(id == 'user_login' && $(this).val() == login_default) { $(this).val('')}
else if(id == 'user_password' && $(this).val() == pass_default) { $(this).val('')}
$(this).addClass('registartion-input-focus').removeClass('registartion-input-error');
}).live('blur', function() {
var id = $(this).attr('id');
if(id == 'user_login' && $(this).val() == '') { $(this).val(login_default)}
else if(id == 'user_password' && $(this).val() == '') { $(this).val(pass_default)}
$(this).addClass('registartion-input-no-focus').removeClass('registartion-input-focus');
});
$('#niceCheckbox1').click(function() {
var el = $(this),
input = el.find('input[type="checkbox"]');
if(input.attr("checked")) {
el.css('backgroundPosition', '0 0');
input.removeAttr('checked');
} else {
el.css('backgroundPosition', '0 -18px');
input.attr('checked', true);
}
return true;
});
});

View File

@ -1,20 +0,0 @@
$(document).ready(function() {
$("#myTable").tablesorter({
headers: {
2: {
sorter: false
}
}
});
});
$(document).ready(function() {
$("a.files-see").click(function() {
$("#file1").fadeOut(0);
$("#file2").fadeIn("slow");
$("#file-name1").fadeOut(0);
$("#file-name2").fadeIn("slow");
$("#fork-and-edit").fadeIn("slow");
});
});

View File

@ -1,116 +0,0 @@
jQuery(document).ready(function(){
jQuery(".niceRadio").each(
function() {
changeRadioStart(jQuery(this));
});
});
function changeRadio(el)
{
var el = el,
input = el.find("input").eq(0);
var nm=input.attr("name");
jQuery(".niceRadio input").each(
function() {
if(jQuery(this).attr("name")==nm)
{
jQuery(this).parent().removeClass("radioChecked");
}
});
if(el.attr("class").indexOf("niceRadioDisabled")==-1)
{
el.addClass("radioChecked");
input.attr("checked", true);
}
return true;
}
function changeVisualRadio(input)
{
var wrapInput = input.parent();
var nm=input.attr("name");
jQuery(".niceRadio input").each(
function() {
if(jQuery(this).attr("name")==nm)
{
jQuery(this).parent().removeClass("radioChecked");
}
});
if(input.attr("checked"))
{
wrapInput.addClass("radioChecked");
}
}
function changeRadioStart(el)
{
try
{
var el = el,
radioName = el.attr("name"),
radioId = el.attr("id"),
radioChecked = el.attr("checked"),
radioDisabled = el.attr("disabled"),
radioTab = el.attr("tabindex"),
radioValue = el.attr("value");
if(radioChecked)
el.after("<span class='niceRadio radioChecked'>"+
"<input type='radio'"+
"name='"+radioName+"'"+
"id='"+radioId+"'"+
"checked='"+radioChecked+"'"+
"tabindex='"+radioTab+"'"+
"value='"+radioValue+"' /></span>");
else
el.after("<span class='niceRadio'>"+
"<input type='radio'"+
"name='"+radioName+"'"+
"id='"+radioId+"'"+
"tabindex='"+radioTab+"'"+
"value='"+radioValue+"' /></span>");
if(radioDisabled)
{
el.next().addClass("niceRadioDisabled");
el.next().find("input").eq(0).attr("disabled","disabled");
}
el.next().bind("mousedown", function(e) { changeRadio(jQuery(this)) });
if(jQuery.browser.msie) el.next().find("input").eq(0).bind("click", function(e) { changeVisualRadio(jQuery(this)) });
else el.next().find("input").eq(0).bind("change", function(e) { changeVisualRadio(jQuery(this)) });
el.remove();
}
catch(e)
{
}
return true;
}

View File

@ -1,54 +0,0 @@
function switchThis() {
var doc = document.getElementById("switcher");
if (doc.className == "switcher") {
doc.className = "switcher-off";
$("#open-comment").fadeOut(0);
$("#closed-comment").fadeIn("slow");
} else {
doc.className = "switcher";
$("#closed-comment").fadeOut(0);
$("#open-comment").fadeIn("slow");
}
}
function preload() {
if (document.images) {
var imgsrc = preload.arguments;
arr=new Array(imgsrc.length);
for (var j=0; j<imgsrc.length; j++) {
arr[j] = new Image;
arr[j].src = imgsrc[j];
}
}
}
function manage(elem) {
if (elem == "people") {
var doc = document.getElementById("people-manage");
if (doc.className == "view") {
doc.className = "non-view";
$("#people-manage").fadeOut(0);
$("#people-manage-list").fadeIn("slow");
}
else {
$("#people-manage-list").fadeOut(0);
$("#people-manage").fadeIn("slow");
doc.className = "view";
}
}
if (elem == "labels") {
var doc = document.getElementById("labels-manage");
if (doc.className == "view") {
doc.className = "non-view";
$("#labels-manage").fadeOut(0);
$("#labels-manage-list").fadeIn("slow");
}
else {
$("#labels-manage-list").fadeOut(0);
$("#labels-manage").fadeIn("slow");
doc.className = "view";
}
}
}

View File

@ -1,138 +0,0 @@
$(document).ready(function() {
$("#closed-switcher").click(function() {
if ($("#blue-switch-select").css("margin-left") != "130px") {
$("#blue-switch-select").animate({"margin-left": "+=130px"}, "fast");
$("#table1").fadeOut(0);
$("#table2").fadeIn("slow");
}
else {
$("#blue-switch-select").animate({"margin-left": "-=130px"}, "fast");
$("#table2").fadeOut(0);
$("#table1").fadeIn("slow");
}
});
});
$(document).ready(function() {
$("#myTable").tablesorter({
headers: {
1: {
sorter: false
}
}
});
});
$(document).ready(function() {
$("#myTable2").tablesorter({
headers: {
1: {
sorter: false
}
}
});
});
$(document).ready(function() {
$("#manage-labels").click(function() {
$("#labels-stock").fadeOut(0);
$("#labels-edit").fadeIn("slow");
});
});
$(document).ready(function() {
$("div.delete").click(function() {
var div = "#label-"+this.id;
$(div).fadeOut("slow");
});
});
$(document).ready(function() {
$("div.div-tracker-lables").click(function() {
var flag = this.id;
flag = flag.replace("label-","flag-");
var bg = $("#"+flag).css("background-color");
if ($(this).css("background-color") != bg) {
$(this).css("background-color",bg);
$(this).css("color","#FFFFFF");
var labels = document.getElementsByName("label");
var rows = document.getElementsByName("row");
var arrayLabels;
var rowState = 0;
for (var r in rows) {
for (var l in labels) {
var ro = document.getElementById(rows[r].id);
var cls = ro.className;
var clsLabel = labels[l].id.split("label-")[1];
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
if (cls.indexOf(clsLabel) != -1) {
rowState = 1;
}
}
}
if (rowState == 1) {
showRow(rows[r].id);
rowState = 0;
}
else {
hideRow(rows[r].id);
}
}
} else {
$(this).css("background-color","rgb(247, 247, 247)");
$(this).css("color","#565657");
var labels = document.getElementsByName("label");
var rows = document.getElementsByName("row");
var rowState = 0;
var labelState = 0;
for (var l in labels) {
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
labelState = 1;
}
}
if (labelState == 1) {
for (var r in rows) {
for (var l in labels) {
var ro = document.getElementById(rows[r].id);
var cls = ro.className;
var clsLabel = labels[l].id.split("label-")[1];
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
if (cls.indexOf(clsLabel) != -1) {
rowState = 1;
}
}
}
if (rowState == 1) {
showRow(rows[r].id);
rowState = 0;
}
else {
hideRow(rows[r].id);
}
}
} else {
for (var r in rows) {
showRow(rows[r].id);
}
}
}
});
});
function showRow(elem) {
if ($("#"+elem).css("display") == "none") {
$("#"+elem).fadeIn("slow");
} else {
//$("#"+elem).fadeOut(0);
}
}
function hideRow(elem) {
if ($("#"+elem).css("display") != "none") {
$("#"+elem).fadeOut("fast");
} else {
//$("#"+elem).fadeOut(0);
}
}

View File

@ -1,3 +1,5 @@
@import "vendor";
@import "main";
@import "custom";
@import "design/main";
@import "design/git";
@import "design/common";
@import "design/custom";

View File

@ -1,71 +0,0 @@
// PUT custom styles here ONLY
header div.information div.user {
float: left;
margin-left: 5px;
}
header div.user div.avatar {
padding: 6px 10px 10px 10px;
}
header menu ul li a {
padding: 15px 8px 15px 8px;
}
div#gollum-searchbar-fauxtext {
padding-left: 10px;
}
div#gollum-searchbar-fauxtext input#search-query {
display: inline-block;
float: left;
width: 145px;
border: 1px solid #D3D3D3;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
font-size: 12px;
height: 24px;
margin: 0;
padding: 0 4px;
vertical-align: middle;
white-space: nowrap;
}
a#search-submit {
-moz-box-sizing: content-box;
border-left: medium none;
border-radius: 0 3px 3px 0;
height: 24px;
margin-left: 0;
padding: 0;
position: relative;
vertical-align: middle;
display: inline-block;
float: left;
}
a#search-submit span {
background: image-url("gollum/icon-sprite.png") no-repeat scroll 50% 4px transparent;
background-position: -430px -2px;
height: 24px;
text-indent: -9999px;
width: 16px;
display: block;
height: 21px;
line-height: 21px;
padding: 0 9px 0 7px;
}
a#search-submit:hover span {
background-position: -430px -29px;
}
table.wiki .history .td2 .name span.username {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 164px;
}

View File

@ -0,0 +1,27 @@
@import 'compass/utilities/tables';
table.info {
width: 100%;
border-spacing: 0;
td {padding: 5px;}
}
table.columns2 {
@include inner-table-borders;
th.first, td.first {width: 30%;}
}
table.columns3 {
@include alternating-rows-and-columns(#eee, #ccc, #000);
th.first, td.first, th.last, td.last {width: 30%;}
}
@import 'blueprint/interaction';
.flash {
.notice {
@include success;
}
.warning {
@include notice;
}
.error, .alert {
@include error;
}
}

View File

@ -0,0 +1,431 @@
// PUT custom styles here ONLY
a#manage-labels {
margin-bottom: 10px;
}
article a.edit_label {
color: #FFF;
}
header div.information div.user {
float: left;
margin-left: 5px;
}
header div.user div.avatar {
padding: 6px 10px 10px 10px;
}
header menu ul li a {
padding: 15px 8px 15px 8px;
}
div.description-top input.name {
width: 350px;
padding: 0;
}
div.description-top div.name input {
width: 100%;
height: 100%;
}
article div.activity {
border: 1px solid #D6D6D6;
border-radius: 5px 5px 5px 5px;
color: #333333;
margin-top: 15px;
padding: 6px;
}
article div.messages div.activity {
margin-top: 0;
margin-bottom: 10px;
}
article div.activity .top div.image {
//position: absolute; // TODO broken issue page
float: left;
width: 40px;
height: 40px;
margin-left: 2px;
margin-top: 2px;
}
article div.activity .top .buttons {
display: block;
position: relative;
float: right;
}
article div.activity .top div.text {
float: left;
font-size: 12px;
padding-left: 10px;
}
article div.activity .top .imaged {
margin-left: 44px;
}
article div.activity .top div.text .name {
font-weight: 700;
display: block;
float: left;
}
article div.activity .top div.text .date {
font-size: 11px;
float: left;
}
article div.activity div.fulltext {
font-size: 12px;
padding-top: 10px;
display: block;
}
article div.activity div.fulltext.alone {
padding-top: 0px;
}
article div.activity .fulltext.hidden {
display: none;
}
div.activity .data-expander {
margin-left: 10px;
display: inline-block;
width: 12px;
}
div.activity .data-expander.collapsed {
background: #FFF image-url('expand-gray.png') no-repeat;
background-position: 0 2px;
}
div.activity .data-expander.expanded {
background: #FFF image-url('expand-gray2.png') no-repeat;
background-position: 0 2px;
}
div.activity .fulltext p {
margin: 5px;
line-height: 1.3em;
}
table.blob td.lines pre {
background-color: #ECECEC;
border-right: 1px solid #DDDDDD;
color: #AAAAAA;
padding: 10px;
text-align: right;
}
textarea#code {
width: 845px;
resize: none;
height: 30em;
}
article div.date-block {
margin-bottom: 30px;
}
article div.year {
margin-top: 0;
}
article h3 {
margin-bottom: 10px;
}
article div.date-block div.date {
height: 52px;
}
.date_select {
select {margin:3px 0px; padding:0px;}
}
article div#repo-wrapper div.hr {
width: 100%;
}
article table.commit_stats {
line-height: 1.4em;
margin-top: 12px;
width: 100%;
border-spacing: 0;
border-top: 1px solid #DDDDDD;
margin: 10px 0;
padding: 0;
font-size: 90%;
border-collapse: collapse;
}
article table.commit_stats td {
border-bottom: 1px solid #DDDDDD;
padding: 0.4em 5px;
}
article table.commit_stats .diffstat {
text-align: right;
white-space: nowrap;
padding-right: 0;
width: 1%;
}
article div.file div.diff_data {
overflow-x: auto;
}
article div.file table {
border-spacing: 0;
border-collapse: collapse;
}
#repo-wrapper table.diff td.line_numbers {
-moz-user-select: none;
font-size: 12px;
padding: 0 0.5em;
background-color: #ECECEC;
border-right: 1px solid #DDDDDD;
color: #999999;
text-align: right;
}
#repo-wrapper table.diff td.line_numbers,
#repo-wrapper table.diff td.header,
#repo-wrapper table.diff td.code {
font-family: 'Bitstream Vera Sans Mono','Courier',monospace;
}
#repo-wrapper table.diff td.header {
background-color: #ECECEC;
color: #999999;
width: 100%;
line-height: 1.4em;
font-size: 90%;
}
#repo-wrapper table.diff td.code, #repo-wrapper table.diff td.header {
padding-left: 10px;
}
#repo-wrapper table.diff pre {
padding: 0;
margin: 0;
}
#repo-wrapper table.diff .diff-content {
padding: 0;
margin: 0;
}
#repo-wrapper table.diff tr td.code.del {
background-color: #FFDDDD;
}
#repo-wrapper table.diff tr td.code.del .idiff {
background-color: #F2ACAD;
}
#repo-wrapper table.diff tr td.code.ins {
background-color: #DDFFDD;
}
#repo-wrapper table.diff tr td.code.ins .idiff {
background-color: #BAFBAD;
}
#repo-wrapper div.comment {
background: none repeat scroll 0 0 #DCECFA;
border: 1px solid #B3CCE0;
border-radius: 4px 4px 4px 4px;
margin-bottom: 10px;
margin-top: 15px;
padding: 6px;
text-align: left;
}
#repo-wrapper div.view {
display: block;
}
#repo-wrapper div.comment div.wrapper {
border: 1px solid #DEDEDE;
border-radius: 4px 4px 4px 4px;
color: #575756;
background: #FFFFFF;
font-size: 12px;
height: 110px;
margin: 10px 0;
padding: 5px;
}
#repo-wrapper div.comment div.wrapper textarea {
border: none;
color: #575756;
font-family: Tahoma,Geneva,Helvetica,sans-serif;
width: 100%;
height: 100%;
resize: none;
}
div.comment div.comment-left {
float: left;
font-size: 12px;
margin-top: 4px;
}
div.comment div.comment-right {
float: right;
}
div.comment textarea { resize: none }
#repo-wrapper div.file div.data .formatted {
overflow-x: auto;
overflow-y: none;
}
table.tablesorter tbody td a .issue_title {
color: #58595B;
}
.syntaxhighlighter {
padding: 11px;
}
#output.formatted {
width: auto;
font-family: "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
padding: 10px 5px 0px;
margin-left: 45px;
}
#output.formatted pre {
font-family: "Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;
padding: 0;
margin: 0;
}
div.gutter {
border-right: 1px solid #CCCCCC !important;
}
table.tablesorter tr td.centered {
text-align: center;
}
/* TODO change this */
header div.user div.avatar img {
border: 1px solid #DDDDDD;
}
div.blame_data {
font-size: 80%;
}
div.blame_data tr.firstrow {
border-top: 1px solid #DDDDDD;
}
div.blame_data tr td.commit_info {
padding: 0.5em !important;
vertical-align: top;
width: 210px;
border-right: 1px solid #DDDDDD;
}
div.blame_data tr td.commit_info .date {
display: block;
float: left;
color: #666666;
}
div.blame_data tr td.commit_info .message {
width: 210px;
display: block;
float: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
}
div.blame_data tr td.lines {
padding: 0 0.5em !important;
width: 1%;
font-size: 12px;
line-height: 1.4em;
}
div.blame_data tr td.code {
padding: 0 10px !important;
font-size: 12px;
}
div.blame_data tr td.code pre {
padding: 0;
margin: 0;
}
div#gollum-searchbar-fauxtext {
padding-left: 10px;
}
div#gollum-searchbar-fauxtext input#search-query {
display: inline-block;
float: left;
width: 145px;
border: 1px solid #D3D3D3;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
font-size: 12px;
height: 24px;
margin: 0;
padding: 0 4px;
vertical-align: middle;
white-space: nowrap;
}
a#search-submit {
-moz-box-sizing: content-box;
border-left: medium none;
border-radius: 0 3px 3px 0;
height: 24px;
margin-left: 0;
padding: 0;
position: relative;
vertical-align: middle;
display: inline-block;
float: left;
}
a#search-submit span {
background: image-url("gollum/icon-sprite.png") no-repeat scroll 50% 4px transparent;
background-position: -430px -2px;
height: 24px;
text-indent: -9999px;
width: 16px;
display: block;
height: 21px;
line-height: 21px;
padding: 0 9px 0 7px;
}
a#search-submit:hover span {
background-position: -430px -29px;
}
table.wiki .history .td2 .name span.username {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 164px;
}

View File

@ -0,0 +1,326 @@
html, body {
margin: 0;
padding: 0;
font-family: Tahoma, Geneva, Helvetica, sans-serif;
color: #565667;
background: #1f60a1 image-url("bg.png") repeat-x;
min-width: 940px;
min-height: 300px;
text-align: center;
height: 100%;
}
header, section, footer, aside, nav, article, menu {
display: block;
}
input[type="text"]:focus { outline: none; }
input[type="password"]:focus { outline: none; }
input:focus { outline: none; }
select:focus { outline: none; }
a img { border: none; }
.wrap {
width: 940px;
margin: 0 auto;
text-align: center;
min-height: 95%;
}
.both {
clear: both;
}
/* Header */
header div.logo {
background: image-url("logo.png") no-repeat 50% 100%;
height: 89px;
width: 233px;
margin: 0 auto;
text-align: center;
padding-top: 12%;
}
header div.text {
color: #FFF;
font-size: 28px;
width: 800px;
text-align: left;
margin-left: 90px;
margin-top: 50px;
font-family: Arial;
text-shadow: 0px 1px 1px #000000;
filter: dropshadow(color=#000000, offx=0, offy=1);
padding-left: 0px;
}
.niceCheck {
width: 17px;
height: 17px;
display: inline-block;
cursor: pointer;
background: image-url("checkbox.png");
}
.niceCheck input {
display: none;
}
/* Content */
article {
width: 257px;
height: 227px;
background: #1c394c image-url("bg-signup.png") repeat-x;
border-radius: 5px;
border: 1px solid #38658c;
margin: 0 auto;
text-align: center;
margin-top: 75px;
-webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
-moz-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
}
a.button, input.button {
background: #125687;
background: image-url("button-green-normal.png");
border-radius: 3px;
color: #FFF;
font-family: Tahoma;
font-size: 12px;
-webkit-font-smoothing: antialiased;
font-weight: normal;
padding: 5px 25px;
text-align: center;
border: none;
height: 25px;
width: 106px;
text-decoration: none;
}
input.button {
padding: 3px 27px 8px 27px;
height: 25px;
width: auto;
}
a.button:hover, input.button:hover {
background: #1874b6;
background: image-url("button-green-hover.png");
cursor: pointer;
}
a.button:active, input.button:active{
background: image-url("button-green-press.png");
}
a.button:disabled, a.button.disabled, input.button:disabled, input.button.disabled {
background: #125687;
background: image-url("button-green-disabled.png");
cursor: default;
}
article h1 {
color: #FFF;
font-size: 18px;
font-weight: normal;
font-family: Tahoma;
margin-bottom: 5px;
}
.registartion-input, .registartion-input-focus, .registartion-input-error, .registartion-input-no-focus {
height: 24px;
width: 217px;
border: 1px solid #8199a9;
border-radius: 2px;
color: #cfcfcf;
font-family: Tahoma;
font-size: 12px;
padding-left: 10px;
margin-top: 10px;
}
.registartion-input-focus, .registartion-input-focus-signup {
color: #575756;
-webkit-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
-moz-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
}
.registartion-input-no-focus, .registartion-input-no-focus-signup {
color: #575756;
}
.registartion-input-error, .registartion-input-error-signup {
border: 1px solid #bd4d40;
outline:1px solid #bd4d40;
outline-offset:-2px;
}
div.registration div.remember{
color: #FFF;
font-size: 12px;
float: left;
padding-left: 15px;
padding-top: 5px;
}
div.registration div.remember div.check {
float: left;
margin-top: 15px;
margin-right: 4px;
}
div.registration div.remember div.text {
float: left;
margin-top: 15px;
}
div.registration div.in {
float: right;
padding-right: 15px;
padding-top: 15px;
}
div.hr {
margin-top: 10px;
height: 1px;
width: 100%;
border-bottom: 1px solid #264862;
border-top: 1px solid #264862;
}
article div.other div.left{
float: left;
font-size: 12px;
color: #FFFFFF;
margin-top: 6px;
margin-left: 15px;
}
article div.other div.right {
float: right;
margin-right: 15px;
margin-top: 13px;
}
div.forgot {
width: 257px;
text-align: center;
margin: 0 auto;
}
div.password {
float: right;
padding-top: 4px;
}
div.password p {
margin: 0;
padding: 0;
}
div.password a {
color: #FFF;
font-size: 12px;
text-decoration: none;
}
div.password a:hover {
text-decoration: underline;
}
nav {
width: 96px;
height: 47px;
background: image-url("registration.png") no-repeat;
float: right;
cursor: pointer;
}
nav p {
font-size: 14px;
color: #FFF;
margin: 0;
padding: 0;
padding-top: 10px;
}
div.error {
width: auto;
height: auto;
font-size: 12px;
position: absolute;
margin-top: -159px;
margin-left: 582px;
display: none;
}
div.error div.img {
background: image-url("error-arrow.png") 0% 5px no-repeat;
width: 19px;
height: 35px;
float: left;
}
div.error div.msg {
background: #ededed;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
width: auto;
height: auto;
float: left;
-webkit-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
padding: 5px 20px 5px;
}
div.error p{
margin:0;
padding: 0;
text-align: center;
}
/* Footer */
footer {
height: 32px;
padding-left: 15px;
width: 900px;
margin: 0 auto;
text-align: center;
}
footer ul {
margin: 0;
padding: 0;
list-style: none;
font-size: 12px;
color: #FFF;
padding-top: 10px;
text-align: center;
}
footer ul li {
display: inline;
}
footer ul li a {
font-size: 12px;
color: #FFF;
text-decoration: none;
}
footer ul li a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,285 @@
html, body {
margin: 0;
padding: 0;
font-family: Tahoma, Geneva, Helvetica, sans-serif;
color: #565667;
background: #1f60a1 image-url("bg.png") repeat-x;
min-width: 940px;
min-height: 300px;
text-align: center;
height: 100%;
}
header, section, footer, aside, nav, article, menu {
display: block;
}
input[type="text"]:focus { outline: none; }
input[type="password"]:focus { outline: none; }
input:focus { outline: none; }
select:focus { outline: none; }
a img { border: none; }
.wrap {
width: 940px;
margin: 0 auto;
text-align: center;
min-height: 95%;
}
.both {
clear: both;
}
/* Header */
header div.logo {
background: image-url("logo.png") no-repeat 50% 100%;
height: 89px;
width: 233px;
margin: 0 auto;
text-align: center;
padding-top: 12%;
}
header div.text {
color: #FFF;
font-size: 28px;
width: 800px;
text-align: left;
margin-left: 90px;
margin-top: 50px;
font-family: Arial;
text-shadow: 0px 1px 1px #000000;
filter: dropshadow(color=#000000, offx=0, offy=1);
padding-left: 0px;
}
.niceCheck {
width: 17px;
height: 17px;
display: inline-block;
cursor: pointer;
background: image-url("checkbox.png");
}
.niceCheck input {
display: none;
}
/* Content */
article {
width: 380px;
height: 254px;
background: #1c394c image-url("bg-signup.png") repeat-x;
border-radius: 5px;
border: 1px solid #38658c;
margin: 0 auto;
text-align: center;
margin-top: 75px;
-webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
-moz-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.5);
}
a.button, input.button {
background: #125687;
background: image-url("button-green-normal.png");
border-radius: 3px;
color: #FFF;
font-family: Tahoma;
font-size: 12px;
-webkit-font-smoothing: antialiased;
font-weight: normal;
padding: 5px 25px;
text-align: center;
border: none;
height: 25px;
width: 106px;
text-decoration: none;
}
article div.left {
float: left;
font-size: 12px;
color: #FFF;
margin-left: 15px;
margin-top: 16px;
}
article div.right {
float: right;
margin-right: 15px;
padding-bottom: 2px;
}
.first {
padding-top: 5px;
}
input.button {
padding: 3px 27px 8px 27px;
height: 25px;
width: auto;
}
a.button:hover, input.button:hover {
background: #1874b6;
background: image-url("button-green-hover.png");
cursor: pointer;
}
a.button:active, input.button:active{
background: image-url("button-green-press.png");
}
a.button:disabled, a.button.disabled, input.button:disabled, input.button.disabled{
background: #125687;
background: image-url("button-green-disabled.png");
cursor: default;
}
article h1 {
color: #FFF;
font-size: 18px;
font-weight: normal;
font-family: Tahoma;
margin-bottom: 5px;
}
.registartion-input, .registartion-input-focus, .registartion-input-error, .registartion-input-no-focus {
height: 24px;
width: 217px;
border: 1px solid #8199a9;
border-radius: 2px;
color: #cfcfcf;
font-family: Tahoma;
font-size: 12px;
padding-left: 10px;
margin-top: 10px;
}
.registartion-input-focus, .registartion-input-focus-signup {
color: #575756;
-webkit-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
-moz-box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
box-shadow: 0px 0px 7px 2px rgba(126, 183, 237, 0.75);
}
.registartion-input-no-focus, .registartion-input-no-focus-signup {
color: #575756;
}
.registartion-input-error, .registartion-input-error-signup {
border: 1px solid #bd4d40;
outline:1px solid #bd4d40;
outline-offset:-2px;
}
div.in {
float: right;
margin-right: 15px;
margin-top: 12px;
}
/* Footer */
footer {
height: 32px;
padding-left: 15px;
width: 900px;
margin: 0 auto;
text-align: center;
}
footer ul {
margin: 0;
padding: 0;
list-style: none;
font-size: 12px;
color: #FFF;
padding-top: 10px;
text-align: center;
}
footer ul li {
display: inline;
}
footer ul li a {
font-size: 12px;
color: #FFF;
text-decoration: none;
}
footer ul li a:hover {
text-decoration: underline;
}
div.error {
width: auto;
height: auto;
font-size: 12px;
position: absolute;
margin-top: -159px;
margin-left: 582px;
display: none;
text-align: left;
}
div.error div.img {
background: image-url("error-arrow.png") 0% 5px no-repeat;
width: 19px;
height: 35px;
float: left;
}
div.error div.msg {
background: #ededed;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
width: auto;
height: auto;
float: left;
-webkit-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
box-shadow: 0px 6px 3px -3px rgba(0, 0, 0, 0.2);
padding: 5px 20px 5px;
}
div.error p{
margin:0;
padding: 0;
text-align: center;
}
div.error.login {
margin-top: -242px;
margin-left: 650px;
}
div.error.name {
margin-top: -202px;
margin-left: 650px;
}
div.error.email {
margin-top: -161px;
margin-left: 650px;
}
div.error.password {
margin-top: -101px;
margin-left: 650px;
}

View File

@ -0,0 +1,3 @@
@import 'devise/login';
nav a { text-decoration: none;}

View File

@ -0,0 +1,23 @@
@import "devise/registration";
nav {
width: 96px;
height: 47px;
background: image-url("registration.png") no-repeat;
float: right;
cursor: pointer;
a {
font-size: 14px;
color: #FFF;
margin: 0;
padding: 0;
padding-top: 10px;
text-decoration: none;} }
div.error.forgot {
margin-top: 8px;
margin-left: 308px; }
div.error.reset {
margin-top: -141px;
margin-left: 645px; }

View File

@ -0,0 +1,7 @@
class ActivityFeedsController < ApplicationController
before_filter :authenticate_user!
def index
@activity_feeds = current_user.activity_feeds.order('created_at DESC')
end
end

View File

@ -1,7 +1,7 @@
# -*- encoding : utf-8 -*-
class BuildListsController < ApplicationController
CALLBACK_ACTIONS = [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt]
NESTED_ACTIONS = [:index, :new, :create]
NESTED_ACTIONS = [:search, :index, :new, :create]
before_filter :authenticate_user!, :except => CALLBACK_ACTIONS
before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS
@ -13,30 +13,23 @@ class BuildListsController < ApplicationController
load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true
load_and_authorize_resource :except => CALLBACK_ACTIONS.concat(NESTED_ACTIONS)
def search
new_params = {:filter => {}}
params[:filter].each do |k,v|
new_params[:filter][k] = v unless v.empty?
end
redirect_to @project ? project_build_lists_path(@project, new_params) : build_lists_path(new_params)
end
def index
if request.post?
new_params = {:filter => {}}
params[:filter].each do |k,v|
new_params[:filter][k] = v unless v.empty?
end
@action_url = @project ? search_project_build_lists_path(@project) : search_build_lists_path
@filter = BuildList::Filter.new(@project, current_user, params[:filter] || {})
@build_lists = @filter.find.recent.paginate :page => params[:page]
redirect_to build_lists_path(new_params)
else
filter_params = params[:filter] || {}
if @project
@action_url = project_build_lists_path(@project)
else
@action_url = build_lists_path
end
@filter = BuildList::Filter.new(@project, filter_params)
@build_lists = @filter.find.accessible_by(current_ability).recent.paginate :page => params[:page]
@build_server_status = begin
BuildServer.get_status
rescue Exception # Timeout::Error
{}
end
@build_server_status = begin
BuildServer.get_status
rescue Exception # Timeout::Error
{}
end
end

View File

@ -19,10 +19,10 @@ class CategoriesController < ApplicationController
if @platform
@categories = Category.select('categories.id, categories.name, categories.ancestry, count(projects.id) projects_count').
joins(:projects => :repositories).where('repositories.platform_id = ?', @platform.id).
having('count(projects.id) > 0').group('categories.id, categories.name, categories.ancestry, projects_count').default_order
having('count(projects.id) > 0').group('categories.id, categories.name, categories.ancestry, projects_count')
render 'index2'
else
@categories = Category.default_order.paginate(:page => params[:page])
@categories = Category.paginate(:page => params[:page])
end
end

View File

@ -30,57 +30,57 @@ class CollaboratorsController < ApplicationController
end
def update
all_user_ids = []
all_groups_ids = []
Relation::ROLES.each { |r|
all_user_ids = all_user_ids | params['user'][r.to_sym].keys if params['user'] && params['user'][r.to_sym]
all_groups_ids = all_groups_ids | params['group'][r.to_sym].keys if params['group'] && params['group'][r.to_sym]
}
params['user'].keys.each { |user_id|
role = params['user'][user_id]
# Remove relations
users_for_removing = @project.collaborators.select do |u|
!all_user_ids.map{|k| k.to_i}.include? u.id and @project.owner != u
end
users_for_removing.each do |u|
Relation.by_object(u).by_target(@project).each {|r| r.destroy}
end
groups_for_removing = @project.groups.select do |u|
!all_groups_ids.map{|k| k.to_i}.include? u.id and @project.owner != u
end
groups_for_removing.each do |u|
Relation.by_object(u).by_target(@project).each {|r| r.destroy}
end
if relation = @project.relations.find_by_object_id_and_object_type(user_id, 'User')
relation.update_attribute(:role, role)
else
relation = @project.relations.build(:object_id => user_id, :object_type => 'User', :role => role)
relation.save!
end
} if params['user']
# Create relations
Relation::ROLES.each { |r|
#users_for_creating = users_for_creating params[:user].keys.map{|p| p.to_i} - @project.collaborators.map(&:id)
params['user'][r.to_sym].keys.each { |u|
if relation = @project.relations.find_by_object_id_and_object_type(u, 'User')
relation.update_attribute(:role, r)
else
relation = @project.relations.build(:object_id => u, :object_type => 'User', :role => r)
relation.save!
end
} if params['user'] && params['user'][r.to_sym]
params['group'][r.to_sym].keys.each { |u|
if relation = @project.relations.find_by_object_id_and_object_type(u, 'Group')
relation.update_attribute(:role, r)
else
relation = @project.relations.build(:object_id => u, :object_type => 'Group', :role => r)
relation.save!
end
} if params['group'] && params['group'][r.to_sym]
}
params['group'].keys.each { |group_id|
role = params['group'][group_id]
if relation = @project.relations.find_by_object_id_and_object_type(group_id, 'Group')
relation.update_attribute(:role, role)
else
relation = @project.relations.build(:object_id => user_id, :object_type => 'Group', :role => role)
relation.save!
end
} if params['group']
if @project.save
flash[:notice] = t("flash.collaborators.successfully_changed")
else
flash[:error] = t("flash.collaborators.error_in_changing")
end
redirect_to project_path(@project)
redirect_to edit_project_collaborators_path(@project)
end
def destroy
def remove
all_user_ids = []
all_groups_ids = []
params['user_remove'].keys.each { |user_id|
all_user_ids << user_id if params['user_remove'][user_id] == ["1"]
} if params['user_remove']
params['group_remove'].keys.each { |group_id|
all_group_ids << group_id if params['group_remove'][group_id] == ["1"]
} if params['group_remove']
all_user_ids.each do |user_id|
u = User.find(user_id)
Relation.by_object(u).by_target(@project).each {|r| r.destroy}
end
all_groups_ids.each do |group_id|
g = Group.find(group_id)
Relation.by_object(g).by_target(@project).each {|r| r.destroy}
end
redirect_to edit_project_collaborators_path(@project)
end
def add
@ -96,7 +96,7 @@ class CollaboratorsController < ApplicationController
flash[:warning] << [t('flash.collaborators.member_already_added'), mem.uname]
end
unless @project.relations.exists?(:object_id => mem.id, :object_type => mem.class.to_s)
rel = @project.relations.build(:role => 'reader')
rel = @project.relations.build(:role => params[:role])
rel.object = mem
if rel.save
flash[:notice] << [t('flash.collaborators.successfully_added'), mem.uname]

View File

@ -75,7 +75,7 @@ class CommentsController < ApplicationController
def find_comment
@comment = Comment.find(params[:id])
if @comment.commentable_type == 'Grit::Commit'
if @comment.commit_comment?
@comment.project = @project
@comment.helper
end

View File

@ -14,7 +14,7 @@ class Git::BaseController < ApplicationController
protected
def find_project
@project = Project.find(params[:project_id])
@project = Project.find(params[:project_id] || params[:id])
end
def find_git_repository
@ -30,7 +30,7 @@ class Git::BaseController < ApplicationController
end
def set_treeish
@treeish = params[:treeish].present? ? params[:treeish] : "master"
@treeish = params[:treeish].present? ? params[:treeish] : @project.default_branch
end
def set_current_tag

View File

@ -1,11 +1,12 @@
# -*- encoding : utf-8 -*-
class Git::BlobsController < Git::BaseController
before_filter :find_tree
before_filter :set_path_blob
before_filter :find_branch
before_filter :set_commit_hash
before_filter :set_path_blob
def show
redirect_to project_repo_path(@project) and return unless @blob.present?
redirect_to project_path(@project) and return unless @blob.present?
if params[:raw]
image_url = Rails.root.to_s + "/" + @path
@ -29,8 +30,8 @@ class Git::BlobsController < Git::BaseController
# @git_repository.after_update_file do |repo, sha|
# end
res = @git_repository.update_file(params[:path], params[:content],
:message => params[:message], :actor => current_user, :head => @treeish)
res = @git_repository.update_file(params[:path], params[:content].gsub("\r", ''),
:message => params[:message].gsub("\r", ''), :actor => current_user, :head => @treeish)
if res
flash[:notice] = t("flash.blob.successfully_updated", :name => params[:path].encode_to_default)
else
@ -50,10 +51,17 @@ class Git::BlobsController < Git::BaseController
end
protected
def find_branch
@branch = @project.branch(@treeish)
end
def set_path_blob
@path = params[:path]
@unenc_path = @path.dup
@path.force_encoding(Encoding::ASCII_8BIT)
puts @path.inspect
@blob = @tree / @path
puts @blob.inspect
end
def set_commit_hash
@ -68,9 +76,9 @@ class Git::BlobsController < Git::BaseController
else
puts "2"
@tree = @git_repository.tree(@treeish)
puts @tree.name.inspect
@commit = @git_repository.log(@treeish, @path).first # TODO WTF nil ?
@commit = @git_repository.log(@treeish, @path, :max_count => 1).first # TODO WTF nil ?
end
puts @tree.inspect
puts @commit.inspect
end
end

View File

@ -1,8 +1,11 @@
# -*- encoding : utf-8 -*-
class Git::CommitsController < Git::BaseController
helper_method :split_commits_by_date
def index
@branch_name = params[:treeish] || "master"
@branch_name = params[:treeish] || @project.default_branch
@branch = @project.branch(@branch_name)
@path = params[:path]
if @path.present?
@ -24,4 +27,18 @@ class Git::CommitsController < Git::BaseController
end
end
protected
def split_commits_by_date(commits)
res = commits.sort{|x, y| y.authored_date <=> x.authored_date}.inject({}) do |h, commit|
dt = commit.authored_date
h[dt.year] ||= {}
h[dt.year][dt.month] ||= {}
h[dt.year][dt.month][dt.day] ||= []
h[dt.year][dt.month][dt.day] << commit
h
end
return res
end
end

View File

@ -2,16 +2,24 @@
class Git::TreesController < Git::BaseController
def show
if params[:treeish].present? and @treeish.dup.encode_to_default == @project.default_branch
redirect_to project_path(@project) and return
end
@path = params[:path]
@path.force_encoding(Encoding::ASCII_8BIT) if @path
@tree = @git_repository.tree(@treeish)
@branch = @project.branch(@treeish)
# @commit = @git_repository.commits(@treeish, 1).first
# Raises Grit::Git::GitTimeout
@commit = @git_repository.log(@treeish, @path).first
@commit = @branch.present? ? @branch.commit() : @git_repository.log(@treeish, @path, :max_count => 1).first
render :template => "git/repositories/empty" and return unless @commit
@tree = @tree / @path if @path
if @path
@path.force_encoding(Encoding::ASCII_8BIT)
@tree = @tree / @path
end
render :template => "git/repositories/show"
end

View File

@ -11,12 +11,7 @@ class GroupsController < ApplicationController
autocomplete :group, :uname
def index
puts parent.inspect
@groups = if parent? and !parent.nil?
parent.groups
else
Group
end.accessible_by(current_ability)
@groups = current_user.groups#accessible_by(current_ability)
@groups = if params[:query]
@groups.where(["name LIKE ?", "%#{params[:query]}%"])
@ -40,18 +35,14 @@ class GroupsController < ApplicationController
def create
@group = Group.new params[:group]
@group.owner = if parent? and parent.is_a? User
parent
else
current_user
end
@group.owner = current_user
if @group.save
flash[:notice] = t('flash.group.saved')
redirect_to group_path(@group)
else
flash[:error] = t('flash.group.save_error')
flash[:warning] = @group.errors[:base]
flash[:warning] = @group.errors.full_messages.join('. ')
render :action => :new
end
end

View File

@ -1,36 +1,49 @@
# -*- encoding : utf-8 -*-
class IssuesController < ApplicationController
NON_RESTFUL_ACTION = [:create_label, :update_label, :destroy_label, :search_collaborators]
before_filter :authenticate_user!
before_filter :find_project
before_filter :find_issue_by_serial_id, :only => [:show, :edit, :update, :destroy]
load_and_authorize_resource :project
load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id
load_resource :project
load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id, :only => [:show, :edit, :update, :destroy, :new, :create]
before_filter :load_and_authorize_label, :only => NON_RESTFUL_ACTION
autocomplete :user, :uname
layout 'application'
def index
def index(status = 200)
@is_assigned_to_me = params[:filter] == 'to_me'
@status = params[:status] == 'closed' ? 'closed' : 'open'
@labels = params[:labels] || []
@issues = @project.issues
case params[:status]
when 'open'
@issues = @issues.where(:status => 'open')
when 'closed'
@issues = @issues.where(:status => 'closed')
@issues = @issues.where(:user_id => current_user.id) if @is_assigned_to_me
@issues = @issues.joins(:labels).where(:labels => {:name => @labels}) unless @labels == []
if params[:search_issue]
@issues = @issues.where('issues.title ILIKE ?', "%#{params[:search_issue].mb_chars.downcase}%")
end
@opened_issues = @issues.opened.count
@closed_issues = @issues.closed.count
@issues = @issues.where(:status => @status)
@issues = @issues.includes(:creator, :user).order('serial_id desc').uniq.paginate :per_page => 10, :page => params[:page]
if status == 200
render 'index', :layout => request.format == '*/*' ? 'issues' : 'application' # maybe FIXME '*/*'?
else
render :status => status, :nothing => true
end
@issues = @issues.paginate :per_page => 10, :page => params[:page]
end
def new
@issue = Issue.new(:project => @project)
@issue = @project.issues.new
end
def create
@user_id = params[:user_id]
@user_uname = params[:user_uname]
@issue = Issue.new(params[:issue])
@issue = @project.issues.new(params[:issue])
@issue.creator_id = current_user.id
@issue.user_id = @user_id
@issue.project_id = @project.id
if @issue.save
@issue.subscribe_creator(current_user.id)
@ -43,21 +56,16 @@ class IssuesController < ApplicationController
end
end
def edit
@user_id = @issue.user_id
@user_uname = @issue.assign_uname
end
def update
@user_id = params[:user_id].blank? ? @issue.user_id : params[:user_id]
@user_uname = params[:user_uname].blank? ? @issue.assign_uname : params[:user_uname]
if @issue.update_attributes( params[:issue].merge({:user_id => @user_id}) )
flash[:notice] = I18n.t("flash.issue.saved")
redirect_to [@project, @issue]
if status = params[:issue][:status]
action = 'issues/_status'
@issue.set_close(current_user) if status == 'closed'
@issue.set_open if status == 'open'
status = 200 if @issue.save
render action, :status => (status || 500), :layout => false
else
flash[:error] = I18n.t("flash.issue.save_error")
render :action => :new
status = 200 if @issue.update_attributes(params[:issue])
render :nothing => true, :status => (status || 500), :layout => false
end
end
@ -68,13 +76,38 @@ class IssuesController < ApplicationController
redirect_to root_path
end
def create_label
status = @project.labels.create(:name => params[:name], :color => params[:color]) ? 200 : 500
index(status)
end
def update_label
status = @label.update_attributes( :name => params[:name], :color => params[:color]) ? 200 : 500
index(status)
end
def destroy_label
status = (@label && @label_destroy) ? 200 : 500
index(status)
end
def search_collaborators
search = "%#{params[:search_user]}%"
users = User.joins(:groups => :projects).where(:projects => {:id => @project.id}).where("users.uname ILIKE ?", search)
users2 = @project.collaborators.where("users.uname ILIKE ?", search)
@users = (users + users2).uniq.sort {|x,y| x.uname <=> y.uname}.first(10)
render 'issues/_search_collaborators', :layout => false
end
def search_labels
@labels = @project.labels.where("labels.name ILIKE ?", "%#{params[:search_labels]}%").order('labels.name').limit(10)
render 'issues/_search_labels', :layout => false
end
private
def find_project
@project = Project.find(params[:project_id])
end
def find_issue_by_serial_id
@issue = @project.issues.find_by_serial_id!(params[:id])
def load_and_authorize_label
@label = Label.find(params[:label_id]) if params[:label_id]
authorize! :write, @project
end
end

View File

@ -23,56 +23,60 @@ class MembersController < ApplicationController
@user = User.find params[:id]
render :edit_rights and return
end
@group = parent
end
def create
end
def update
all_user_ids = []
Relation::ROLES.each { |r|
all_user_ids = all_user_ids | params[r.to_sym].keys if params[r.to_sym]
}
params['user'].keys.each { |user_id|
role = params['user'][user_id]
# Remove relations
users_for_removing = parent.members.select do |u|
!all_user_ids.map{|k| k.to_i}.include? u.id and parent.owner != u
end
users_for_removing.each do |u|
Relation.by_object(u).by_target(parent).each {|r| r.destroy}
end
# Create relations
Relation::ROLES.each { |r|
#users_for_creating = users_for_creating params[:user].keys.map{|p| p.to_i} - @project.collaborators.map(&:id)
params[r.to_sym].keys.each { |u|
if relation = parent.objects.find_by_object_id_and_object_type(u, 'User')
relation.update_attribute(:role, r)
else
relation = parent.objects.build(:object_id => u, :object_type => 'User', :role => r)
puts relation.inspect
puts r
relation.save!
end
} if params[r.to_sym]
}
if relation = parent.objects.find_by_object_id_and_object_type(user_id, 'User')
relation.update_attribute(:role, role)
else
relation = parent.objects.build(:object_id => user_id, :object_type => 'User', :role => role)
relation.save!
end
} if params['user']
if parent.save
flash[:notice] = t("flash.members.successfully_changed")
else
flash[:error] = t("flash.members.error_in_changing")
end
redirect_to parent_path
redirect_to edit_group_members_path(parent)
end
def destroy
def remove
if params[:id]
u = User.find(params[:id])
Relation.by_object(u).by_target(parent)[0].destroy
redirect_to groups_path
else
all_user_ids = []
params['user_remove'].keys.each { |user_id|
all_user_ids << user_id if params['user_remove'][user_id] == ["1"]
} if params['user_remove']
all_user_ids.each do |user_id|
u = User.find(user_id)
Relation.by_object(u).by_target(parent).each {|r| r.destroy}
end
redirect_to edit_group_members_path(parent)
end
end
def add
if params['user_id'] and !params['user_id'].empty?
@user = User.find_by_uname(params['user_id'])
unless parent.objects.exists? :object_id => @user.id, :object_type => 'User'
relation = parent.objects.build(:object_id => @user.id, :object_type => 'User', :role => 'reader')
relation = parent.objects.build(:object_id => @user.id, :object_type => 'User', :role => params[:role])
if relation.save
flash[:notice] = t("flash.members.successfully_added")
else

View File

@ -12,7 +12,7 @@ class PersonalRepositoriesController < ApplicationController
else
@projects = @repository.projects.recent.paginate :page => params[:project_page], :per_page => 30
end
@user = @repository.owner
@user = @repository.platform.owner
@urpmi_commands = @repository.platform.urpmi_list(request.host)
end

View File

@ -8,11 +8,7 @@ class PlatformsController < ApplicationController
autocomplete :user, :uname
def build_all
@platform.repositories.each do |repository|
repository.projects.each do |project|
project.delay.build_for(@platform, current_user)
end
end
@platform.delay.build_all(current_user)
redirect_to(platform_path(@platform), :notice => t("flash.platform.build_all_success"))
end
@ -109,24 +105,24 @@ class PlatformsController < ApplicationController
end
def clone
if request.post?
@cloned = @platform.make_clone(:name => params[:platform]['name'], :description => params[:platform]['description'],
:owner_id => current_user.id, :owner_type => current_user.class.to_s)
if @cloned.persisted?
flash[:notice] = I18n.t("flash.platform.clone_success")
redirect_to @cloned
else
flash[:error] = @cloned.errors.full_messages.join('. ')
end
@cloned = Platform.new
@cloned.name = @platform.name + "_clone"
@cloned.description = @platform.description + "_clone"
end
def make_clone
@cloned = @platform.full_clone params[:platform].merge(:owner => current_user)
if @cloned.persisted?
flash[:notice] = I18n.t("flash.platform.clone_success")
redirect_to @cloned
else
@cloned = Platform.new
@cloned.name = @platform.name + "_clone"
@cloned.description = @platform.description + "_clone"
flash[:error] = @cloned.errors.full_messages.join('. ')
render 'clone'
end
end
def destroy
@platform.destroy if @platform
@platform.delay.destroy if @platform
flash[:notice] = t("flash.platform.destroyed")
redirect_to root_path

View File

@ -1,9 +1,9 @@
# -*- encoding : utf-8 -*-
class ProductBuildListsController < ApplicationController
before_filter :authenticate_user!, :except => [:status_build]
load_and_authorize_resource :platform, :only => [:create]
load_and_authorize_resource :product, :through => :platform, :only => [:create]
load_and_authorize_resource :product_build_list, :through => :product, :only => [:create]
load_and_authorize_resource :platform, :only => [:create, :destroy]
load_and_authorize_resource :product, :through => :platform, :only => [:create, :destroy]
load_and_authorize_resource :product_build_list, :through => :product, :only => [:create, :destroy]
before_filter :authenticate_product_builder!, :only => [:status_build]
before_filter :find_product_build_list, :only => [:status_build]
@ -20,6 +20,12 @@ class ProductBuildListsController < ApplicationController
@product_build_list.save!
render :nothing => true
end
def destroy
@product_build_list.destroy
flash[:notice] = t('flash.product.build_list_delete')
redirect_to [@platform, @product]
end
protected

View File

@ -5,7 +5,7 @@ class ProjectsController < ApplicationController
belongs_to :user, :group, :polymorphic => true, :optional => true
before_filter :authenticate_user!, :except => :auto_build
before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork]
before_filter :find_project, :only => [:show, :edit, :update, :destroy, :fork, :sections]
before_filter :get_paths, :only => [:new, :create, :edit, :update]
load_and_authorize_resource
@ -29,10 +29,13 @@ class ProjectsController < ApplicationController
def show
@current_build_lists = @project.build_lists.current.recent.paginate :page => params[:page]
@branch = @project.branch(params[:treeish])
@commit = @branch.present? ? @branch.commit : @git_repository.log(@treeish).first
end
def new
@project = Project.new
@who_owns = :me
end
def edit
@ -40,15 +43,16 @@ class ProjectsController < ApplicationController
def create
@project = Project.new params[:project]
@project.owner = get_owner
# puts @project.owner.inspect
#@project.owner = get_owner
@project.owner = choose_owner
@who_owns = (@project.owner_type == 'User' ? :me : :group)
if @project.save
flash[:notice] = t('flash.project.saved')
redirect_to @project
else
flash[:error] = t('flash.project.save_error')
flash[:warning] = @project.errors[:base]
flash[:warning] = @project.errors.full_messages.join('. ')
render :action => :new
end
end
@ -94,17 +98,29 @@ class ProjectsController < ApplicationController
render :nothing => true
end
def sections
if request.post?
if @project.update_attributes(params[:project])
flash[:notice] = t('flash.project.saved')
else
@project.save
flash[:error] = t('flash.project.save_error')
end
render :action => :sections
end
end
protected
def get_paths
if params[:user_id]
@user = User.find params[:user_id]
@projects_path = user_path(@user) # user_projects_path @user
@new_project_path = new_user_project_path @user
@new_project_path = new_project_path
elsif params[:group_id]
@group = Group.find params[:group_id]
@projects_path = group_path(@group) # group_projects_path @group
@new_projects_path = new_group_project_path @group
@new_projects_path = new_project_path
else
@projects_path = projects_path
@new_projects_path = new_project_path
@ -114,4 +130,12 @@ class ProjectsController < ApplicationController
def find_project
@project = Project.find params[:id]
end
def choose_owner
if params[:who_owns] == 'group'
Group.find(params[:owner_id])
else
current_user
end
end
end

View File

@ -5,7 +5,7 @@ class RegisterRequestsController < ApplicationController
before_filter :find_register_request, :only => [:approve, :reject]
def index
@register_requests = @register_requests.unprocessed.paginate(:page => params[:page])
@register_requests = @register_requests.send((params[:scope] || 'unprocessed').to_sym).paginate(:page => params[:page])
end
def new

View File

@ -42,7 +42,6 @@ class RepositoriesController < ApplicationController
def create
@repository = Repository.new(params[:repository])
@repository.platform_id = params[:platform_id]
@repository.owner = get_owner
if @repository.save
flash[:notice] = t('flash.repository.saved')
redirect_to @repositories_path

View File

@ -117,19 +117,25 @@ class WikiController < ApplicationController
def compare_wiki
if request.post?
@versions = params[:versions] || []
if @versions.size < 2
redirect_to history_project_wiki_index_path(@project)
else
redirect_to compare_versions_project_wiki_index_path(@project,
sprintf('%s...%s', @versions.last, @versions.first))
versions_string = case @versions.size
when 1 then @versions.first
when 2 then sprintf('%s...%s', @versions.last, @versions.first)
else begin
redirect_to history_project_wiki_index_path(@project)
return
end
end
redirect_to compare_versions_project_wiki_index_path(@project, versions_string)
elsif request.get?
@versions = params[:versions].split(/\.{2,3}/)
if @versions.size < 2
redirect_to history_project_wiki_index_path(@project)
return
@versions = params[:versions].split(/\.{2,3}/) || []
@diffs = case @versions.size
when 1 then @wiki.repo.commit_diff(@versions.first)
when 2 then @wiki.repo.diff(@versions.first, @versions.last)
else begin
redirect_to history_project_wiki_index_path(@project)
return
end
end
@diffs = @wiki.repo.diff(@versions.first, @versions.last)
render :compare
else
redirect_to project_wiki_path(@project, CGI.escape(@name))
@ -141,8 +147,9 @@ class WikiController < ApplicationController
@page = @wiki.page(@name)
sha1 = params[:sha1]
sha2 = params[:sha2]
sha2 = nil if params[:sha2] == 'prev'
if @wiki.revert_page(@page, sha1, sha2, {:committer => committer}).commit
if c = @wiki.revert_page(@page, sha1, sha2, {:committer => committer}) and c.commit
flash[:notice] = t("flash.wiki.revert_success")
redirect_to project_wiki_path(@project, CGI.escape(@name))
else
@ -162,14 +169,14 @@ class WikiController < ApplicationController
def revert_wiki
sha1 = params[:sha1]
sha2 = params[:sha2]
if @wiki.revert_commit(sha1, sha2, {:committer => committer}).commit
sha2 = nil if sha2 == 'prev'
if c = @wiki.revert_commit(sha1, sha2, {:committer => committer}) and c.commit
flash[:notice] = t("flash.wiki.revert_success")
redirect_to project_wiki_index_path(@project)
else
sha2, sha1 = sha1, "#{sha1}^" if !sha2
@versions = [sha1, sha2]
diffs = @wiki.repo.diff(@versions.first, @versions.last)
@diffs = [diffs.first]
@diffs = @wiki.repo.diff(@versions.first, @versions.last)
flash[:error] = t("flash.wiki.patch_does_not_apply")
render :compare
end
@ -249,6 +256,7 @@ class WikiController < ApplicationController
# @committer.after_commit do |committer, sha1|
# here goes callback for notification
# end
ActivityFeedObserver.instance.after_create(@committer).delay
@committer
end

View File

@ -0,0 +1,5 @@
module ActivityFeedsHelper
def render_activity_feed(activity_feed)
render :partial => activity_feed.partial, :locals => activity_feed.data
end
end

View File

@ -9,4 +9,17 @@ module ApplicationHelper
return title
end
def layout_class
case
when params[:controller] == 'issues' && params[:action] == 'new'
'right nopadding'
when params[:controller] == 'build_lists' && params[:action] == 'index'
'right slim'
when params[:controller] == 'build_lists' && ['new', 'create'].include?(params[:action])
nil
else
content_for?(:sidebar) ? 'right' : 'all'
end
end
end

View File

@ -5,11 +5,15 @@ module CommitHelper
res = ["<table class='commit_stats'>"]
stats.files.each do |filename, adds, deletes, total|
res << "<tr>"
res << "<td><a href='##{h(filename)}'>#{h(filename)}</a></td>"
res << "<td>#{total}</td>"
res << "<td><small class='deletions'>#{(0...deletes).map{|i| "-" }.join}</small>"
res << "<small class='insertions'>#{(0...adds).map{|i| "+" }.join}</small></td>"
res << "</tr>"
res << "<td><a href='##{h(filename)}'>#{h(filename)}</a></td>".encode_to_default
res << "<td class='diffstat'>"
res << I18n.t("layout.projects.inline_changes_count", :count => total).strip +
" (" +
I18n.t("layout.projects.inline_additions_count", :count => adds).strip +
", " +
I18n.t("layout.projects.inline_deletions_count", :count => deletes).strip +
")"
res << "</td>"
end
res << "</table>"
@ -37,4 +41,10 @@ module CommitHelper
truncate(message, :length => 42, :omission => "...").encode_to_default
end
def commit_author_link(author)
name = author.name.encode_to_default
email = author.email
u = User.where(:email => email).first
u.present? ? link_to(name, user_path(u)) : mail_to(email, name)
end
end

View File

@ -0,0 +1,21 @@
# -*- encoding : utf-8 -*-
module DeviseHelper
def getDeviseErrors(*name)
res = Array.new(name.count)
resource.errors.each do |attr, message|
if index = name.index(attr)
res[index] = message
end
end
res
end
def showDeviseHintError(name, error, additional_class = '')
if error
"<div id='hint' class='error #{name.to_s} #{additional_class}' style='display: block;'> \
<div class='img'></div> \
<div class='msg'> #{error}</div> \
</div>".html_safe
end
end
end

View File

@ -4,11 +4,11 @@ module DiffHelper
def render_diff(diff)
diff_display ||= Diff::Display::Unified.new(diff.diff)
res = "<a name='#{h(diff.a_path)}'></a>"
#res = "<a name='#{h(diff.a_path)}'></a>"
res += "<table class='diff inline' cellspacing='0' cellpadding='0'>"
res = "<table class='diff inline' cellspacing='0' cellpadding='0'>"
res += "<tbody>"
res += diff_display.render(Git::Diff::InlineCallback.new)
res += diff_display.render(Git::Diff::InlineCallback.new).encode_to_default
res += "</tbody>"
res += "</table>"

View File

@ -39,9 +39,9 @@ module GitHelper
def render_line_numbers(n)
res = ""
1.upto(n) {|i| res += "<span>#{i}</span>\n" }
1.upto(n) {|i| res += "<span>#{i}</span><br/>" }
res
res.html_safe
end
def render_blob(blob)
@ -61,4 +61,34 @@ module GitHelper
string.dup.encode_to_default
end
def iterate_path(path, &block)
path.split(File::SEPARATOR).inject('') do |a, e|
if e != '.' and e != '..'
a = File.join(a, e)
a = a[1..-1] if a[0] == File::SEPARATOR
block.call(a, e) if a.length > 1
end
a
end
end
# TODO This is very dirty hack. Maybe need to be changed.
def branch_selector_options(project)
tmp = params.dup
unless tmp['treeish'].present?
tmp.merge!('project_id' => project.id, 'treeish' => project.default_branch).delete('id')
end
tmp.delete('treeish') if tmp['commit_hash'].present?
res = {}
current = url_for(tmp).split('?', 2).first
tmp['commit_hash'] = truncate(tmp['commit_hash'], :length => 20) if tmp['commit_hash']
res = project.branches.inject(res) do |h, branch|
h[truncate(branch.name, :length => 20)] = url_for(tmp.merge('treeish' => branch.name)).split('?', 2).first
h
end
res.merge!(tmp['commit_hash'] || tmp['treeish'] => current)
options_for_select(res.sort, current).html_safe
end
end

View File

@ -1,3 +1,9 @@
# -*- encoding : utf-8 -*-
module IssuesHelper
def tracker_search_field(name, txt)
str = "<input name='#{name}' id='#{name}' type='text' value='#{txt}'"
str << "onblur=\"if(this.value==''){this.value='#{txt}';this.className='gray';}\""
str << "onclick=\"if(this.value=='#{txt}'){this.value='';this.className='black';}\" class=\"gray\">"
str.html_safe
end
end

View File

@ -2,9 +2,17 @@
module ProjectsHelper
def git_repo_url(name)
if current_user
"http://#{current_user.uname}@#{request.host_with_port}/#{name}.git"
"https://#{current_user.uname}@#{request.host_with_port}/#{name}.git"
else
"http://#{request.host_with_port}/#{name}.git"
"https://#{request.host_with_port}/#{name}.git"
end
end
def options_for_collaborators_roles_select
options_for_select(
Relation::ROLES.collect { |role|
[t("layout.collaborators.role_names.#{ role }"), role]
}
)
end
end

View File

@ -17,8 +17,8 @@ module WikiHelper
end
end
def gravatar_url(email)
"http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(email.downcase)}?s=16&r=pg"
def gravatar_url(email, size = 16)
"https://secure.gravatar.com/avatar/#{Digest::MD5.hexdigest(email.downcase)}?s=#{size}&r=pg"
end
def escaped_name

View File

@ -13,7 +13,7 @@ class UserMailer < ActionMailer::Base
def new_comment_notification(comment, user)
@user = user
@comment = comment
mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_#{comment.commentable.class == Grit::Commit ? 'commit_' : ''}comment_notification")) do |format|
mail(:to => user.email, :subject => I18n.t("notifications.subjects.new_#{comment.commit_comment? ? 'commit_' : ''}comment_notification")) do |format|
format.html
end
end

View File

@ -12,23 +12,27 @@ class Ability
user ||= User.new # guest user (not logged in)
@user = user
if user.admin?
can :manage, :all
cannot :destroy, Subscribe
cannot :create, Subscribe
cannot :create, RegisterRequest
else
# Shared rights between guests and registered users
can :forbidden, Platform
# Shared rights between guests and registered users
can :forbidden, Platform
# TODO remove because auth callbacks skipped
can :auto_build, Project
can [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList
# TODO remove because auth callbacks skipped
can :auto_build, Project
can [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList
if user.guest? # Guest rights
can :create, User
can [:create, :show_message], RegisterRequest
else # Registered user rights
if user.admin?
can :manage, :all
cannot :destroy, Subscribe
cannot :create, Subscribe
cannot :create, RegisterRequest
cannot :approve, RegisterRequest, :approved => true
cannot :reject, RegisterRequest, :rejected => true
cannot [:owned, :related], BuildList
end
if user.guest? # Guest rights
can :create, User
can [:create, :show_message], RegisterRequest
else # Registered user rights
if user.user?
can [:show, :autocomplete_user_uname], User
can [:show, :update], Settings::Notifier, :user_id => user.id
@ -45,7 +49,7 @@ class Ability
can :read, Project, :owner_type => 'Group', :owner_id => user.group_ids
can(:read, Project, read_relations_for('projects')) {|project| local_reader? project}
can(:write, Project) {|project| local_writer? project} # for grack
can([:update, :manage_collaborators], Project) {|project| local_admin? project}
can([:update, :sections, :manage_collaborators], Project) {|project| local_admin? project}
can(:fork, Project) {|project| can? :read, project}
can(:destroy, Project) {|project| owner? project}
@ -53,34 +57,36 @@ class Ability
#can :create, AutoBuildList
#can [:index, :destroy], AutoBuildList, :project_id => user.own_project_ids
can [:read, :owned], BuildList, :user_id => user.id
can :read, BuildList, :project => {:visibility => 'open'}
can :read, BuildList, :project => {:owner_type => 'User', :owner_id => user.id}
can :read, BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids}
can [:read, :related], BuildList, :project => {:owner_type => 'User', :owner_id => user.id}
can [:read, :related], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids}
can(:read, BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project}
can(:create, BuildList) {|build_list| can? :write, build_list.project}
can(:create, BuildList) {|build_list| build_list.project.is_rpm && can?(:write, build_list.project)}
can(:publish, BuildList) {|build_list| build_list.can_publish? && can?(:write, build_list.project)}
can(:cancel, BuildList) {|build_list| build_list.can_cancel? && can?(:write, build_list.project)}
can :read, Platform, :visibility => 'open'
can :read, Platform, :owner_type => 'User', :owner_id => user.id
can :read, Platform, :owner_type => 'Group', :owner_id => user.group_ids
can(:read, Platform, read_relations_for('platforms')) {|platform| local_reader? platform}
can(:update, Platform) {|platform| local_admin? platform}
can([:update, :build_all], Platform) {|platform| local_admin? platform}
can([:freeze, :unfreeze, :destroy], Platform) {|platform| owner? platform}
can :autocomplete_user_uname, Platform
# TODO delegate to platform?
can :read, Repository, :platform => {:visibility => 'open'}
can :read, Repository, :owner_type => 'User', :owner_id => user.id
can :read, Repository, :owner_type => 'Group', :owner_id => user.group_ids
can(:read, Repository, read_relations_for('repositories')) {|repository| local_reader? repository}
can(:create, Repository) {|repository| local_admin? repository.platform}
can([:update, :add_project, :remove_project], Repository) {|repository| local_admin? repository}
can([:change_visibility, :settings, :destroy], Repository) {|repository| owner? repository}
can :read, Repository, :platform => {:owner_type => 'User', :owner_id => user.id}
can :read, Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids}
can(:read, Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform}
can([:create, :update, :projects_list, :add_project, :remove_project], Repository) {|repository| local_admin? repository.platform}
can([:change_visibility, :settings, :destroy], Repository) {|repository| owner? repository.platform}
can :read, Product, :platform => {:owner_type => 'User', :owner_id => user.id}
can :read, Product, :platform => {:owner_type => 'Group', :owner_id => user.group_ids}
can(:manage, Product, read_relations_for('products', 'platforms')) {|product| local_admin? product.platform}
can(:create, ProductBuildList) {|pbl| pbl.product.can_build? and can?(:update, pbl.product)}
can(:destroy, ProductBuildList) {|pbl| can?(:destroy, pbl.product)}
can [:read, :platforms], Category
can [:read, :create], PrivateUser, :platform => {:owner_type => 'User', :owner_id => user.id}
@ -101,19 +107,19 @@ class Ability
cannot(:manage, Comment) {|comment| comment.commentable_type == 'Issue' && !comment.commentable.project.has_issues} # switch off issues
cannot :manage, RegisterRequest
end
end
# Shared cannot rights for all users (guests, registered, admin)
cannot :destroy, Platform, :platform_type => 'personal'
cannot :destroy, Repository, :platform => {:platform_type => 'personal'}
cannot :fork, Project, :owner_id => user.id, :owner_type => user.class.to_s
cannot :destroy, Issue
# Shared cannot rights for all users (registered, admin)
cannot :destroy, Platform, :platform_type => 'personal'
cannot :destroy, Repository, :platform => {:platform_type => 'personal'}
cannot :fork, Project, :owner_id => user.id, :owner_type => user.class.to_s
cannot :destroy, Issue
can :create, Subscribe do |subscribe|
!subscribe.subscribeable.subscribes.exists?(:user_id => user.id)
end
can :destroy, Subscribe do |subscribe|
subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id
can :create, Subscribe do |subscribe|
!subscribe.subscribeable.subscribes.exists?(:user_id => user.id)
end
can :destroy, Subscribe do |subscribe|
subscribe.subscribeable.subscribes.exists?(:user_id => user.id) && user.id == subscribe.user_id
end
end
end

View File

@ -0,0 +1,9 @@
class ActivityFeed < ActiveRecord::Base
belongs_to :user
serialize :data
def partial
'activity_feeds/partials/' + self.kind
end
end

View File

@ -0,0 +1,121 @@
class ActivityFeedObserver < ActiveRecord::Observer
observe :issue, :comment, :user
def after_create(record)
case record.class.to_s
when 'User'
ActivityFeed.create(
:user => record,
:kind => 'new_user_notification',
:data => {:user_name => record.name, :user_email => record.email}
)
when 'Issue'
recipients = record.collect_recipient_ids
recipients.each do |recipient_id|
recipient = User.find(recipient_id)
UserMailer.delay.new_issue_notification(record, recipient) if User.find(recipient).notifier.can_notify && User.find(recipient).notifier.new_issue
ActivityFeed.create(
:user => recipient,
:kind => 'new_issue_notification',
:data => {:user_name => recipient.name, :issue_serial_id => record.serial_id, :issue_title => record.title, :project_id => record.project.id, :project_name => record.project.name}
)
end
if record.user_id_was != record.user_id
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:kind => 'issue_assign_notification',
:data => {:user_name => record.user.name, :issue_serial_id => record.serial_id, :project_id => record.project.id, :issue_title => record.title}
)
end
when 'Comment'
if record.commentable.class == Issue
subscribes = record.commentable.subscribes.finder_hack
subscribes.each do |subscribe|
if record.user_id != subscribe.user_id
UserMailer.delay.new_comment_notification(record, subscribe.user) if record.can_notify_on_new_comment?(subscribe)
ActivityFeed.create(
:user => subscribe.user,
:kind => 'new_comment_notification',
:data => {:user_name => subscribe.user.name, :comment_body => record.body, :issue_title => record.commentable.title,
:issue_serial_id => record.commentable.serial_id, :project_id => record.commentable.project.id}
)
end
end
elsif record.commit_comment?
subscribes = Subscribe.comment_subscribes(record).where(:status => true)
subscribes.each do |subscribe|
next if record.own_comment?(subscribe.user)
UserMailer.delay.new_comment_notification(record, subscribe.user) if subscribe.user.notifier.can_notify
ActivityFeed.create(
:user => subscribe.user,
:kind => 'new_comment_commit_notification',
:data => {:user_name => subscribe.user.name, :comment_body => record.body, :commit_message => record.commentable.message.encode_to_default,
:commit_id => record.commentable.id, :project_id => record.project.id}
)
end
end
when 'GitHook'
change_type = record.change_type
branch_name = record.refname.match(/\/([\w\d]+)$/)[1]
#user_name = record.
#owner = record.owner
project = Project.find_by_name(record.repo)
last_commits = project.git_repository.repo.log(branch_name, nil).first(3).collect do |commit| #:author => 'author'
[commit.sha, commit.message]
end
if change_type == 'delete'
kind = 'git_delete_branch_notification'
options = {:project_id => project.id, :project_name => project.name, :branch_name => branch_name, :change_type => change_type}
else
kind = 'git_new_push_notification'
options = {:project_id => project.id, :project_name => project.name, :last_commits => last_commits, :branch_name => branch_name, :change_type => change_type}
end
project.owner_and_admin_ids.each do |recipient|
ActivityFeed.create(
:user => User.find(recipient),
:kind => kind,
:data => options
)
end
when 'Gollum::Committer'
actor = User.find_by_uname(record.actor.name)
project_name = record.wiki.path.match(/\/(\w+)\.wiki\.git$/)[1]
project = Project.find_by_name(project_name)
commit_sha = record.commit
#wiki_name = record.wiki.name
project.owner_and_admin_ids.each do |recipient|
ActivityFeed.create(
:user => User.find(recipient),#record.user,
:kind => 'wiki_new_commit_notification',
:data => {:user_id => actor.id, :user_name => actor.name, :project_id => project.id, :project_name => project_name, :commit_sha => commit_sha}
)
end
end
end
def after_update(record)
case record.class.to_s
when 'Issue'
if record.user_id_was != record.user_id
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:kind => 'issue_assign_notification',
:data => {:user_name => record.user.name, :issue_serial_id => record.serial_id, :project_id => record.project.id, :issue_title => record.title}
)
end
end
end
end

View File

@ -35,8 +35,9 @@ class BuildList < ActiveRecord::Base
BuildServer::PLATFORM_PENDING,
BuildServer::PROJECT_NOT_FOUND,
BuildServer::PROJECT_VERSION_NOT_FOUND,
BuildServer::BINARY_TEST_FAILED,
BuildServer::DEPENDENCY_TEST_FAILED ]
# BuildServer::BINARY_TEST_FAILED,
# BuildServer::DEPENDENCY_TEST_FAILED
]
HUMAN_STATUSES = { WAITING_FOR_RESPONSE => :waiting_for_response,
BUILD_CANCELED => :build_canceled,
@ -51,8 +52,8 @@ class BuildList < ActiveRecord::Base
BuildServer::PLATFORM_PENDING => :platform_pending,
BuildServer::PROJECT_NOT_FOUND => :project_not_found,
BuildServer::PROJECT_VERSION_NOT_FOUND => :project_version_not_found,
BuildServer::DEPENDENCY_TEST_FAILED => :dependency_test_failed,
BuildServer::BINARY_TEST_FAILED => :binary_test_failed
# BuildServer::DEPENDENCY_TEST_FAILED => :dependency_test_failed,
# BuildServer::BINARY_TEST_FAILED => :binary_test_failed
}
scope :recent, order("#{table_name}.updated_at DESC")

View File

@ -1,7 +1,8 @@
# -*- encoding : utf-8 -*-
class BuildList::Filter
def initialize(project, options = {})
def initialize(project, user, options = {})
@project = project
@user = user
set_options(options)
end
@ -11,6 +12,7 @@ class BuildList::Filter
if @options[:bs_id]
build_lists = build_lists.where(:bs_id => @options[:bs_id])
else
build_lists = build_lists.accessible_by(::Ability.new(@user), @options[:ownership].to_sym) if @options[:ownership]
build_lists = build_lists.for_status(@options[:status]) if @options[:status]
build_lists = build_lists.scoped_to_arch(@options[:arch_id]) if @options[:arch_id]
build_lists = build_lists.scoped_to_project_version(@options[:project_version]) if @options[:project_version]
@ -25,7 +27,7 @@ class BuildList::Filter
end
end
build_lists.recent
build_lists
end
def respond_to?(name)
@ -41,6 +43,7 @@ class BuildList::Filter
def set_options(options)
@options = HashWithIndifferentAccess.new(options.reverse_merge({
:ownership => nil,
:status => nil,
:created_at_start => nil,
:created_at_end => nil,
@ -53,6 +56,7 @@ class BuildList::Filter
:project_name => nil
}))
@options[:ownership] = @options[:ownership].presence || 'owned'
@options[:status] = @options[:status].present? ? @options[:status].to_i : nil
@options[:created_at_start] = build_date_from_params(:created_at_start, @options)
@options[:created_at_end] = build_date_from_params(:created_at_end, @options)

View File

@ -4,7 +4,7 @@ class Category < ActiveRecord::Base
validates :name, :presence => true
scope :default_order, order('categories.name')
default_scope order('categories.name')
has_ancestry
end

View File

@ -6,9 +6,12 @@ class Comment < ActiveRecord::Base
validates :body, :user_id, :commentable_id, :commentable_type, :presence => true
after_create :invoke_helper, :if => "commentable_type == 'Grit::Commit'"
default_scope order('created_at')
# FIXME
after_create :subscribe_on_reply, :unless => lambda {|c| c.commit_comment?}
after_create :invoke_helper, :if => lambda {|c| c.commit_comment?}
after_create :subscribe_users
after_create {|c| Subscribe.new_comment_notification(c)}
def helper
class_eval { def commentable; project.git_repository.commit(commentable_id.to_s(16)); end } if commit_comment?
@ -22,8 +25,16 @@ class Comment < ActiveRecord::Base
commentable_type == 'Grit::Commit'
end
def can_notify_on_new_comment?(subscribe)
User.find(subscribe.user).notifier.new_comment && User.find(subscribe.user).notifier.can_notify
end
protected
def subscribe_on_reply
self.commentable.subscribes.create(:user_id => self.user_id) if !self.commentable.subscribes.exists?(:user_id => self.user_id)
end
def invoke_helper
self.helper
end
@ -31,7 +42,7 @@ class Comment < ActiveRecord::Base
def subscribe_users
if self.commentable.class == Issue
self.commentable.subscribes.create(:user => self.user) if !self.commentable.subscribes.exists?(:user_id => self.user.id)
elsif self.commentable.class == Grit::Commit
elsif self.commit_comment?
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map &:object # admins
recipients << self.user << User.where(:email => self.commentable.committer.email).first # commentor and committer
recipients << self.project.owner if self.project.owner_type == 'User' # project owner

View File

@ -1,3 +1,4 @@
# -*- encoding : utf-8 -*-
class GitHook
attr_reader :repo, :newrev, :oldrev, :newrev_type, :oldrev_type, :refname,
:change_type, :rev, :rev_type, :refname_type, :owner, :project
@ -53,4 +54,4 @@ class GitHook
@refname_type= "*** Unknown type of update to $refname (#{rev_type})"
end
end
end
end

View File

@ -2,33 +2,45 @@
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :own_projects, :as => :owner, :class_name => 'Project'
has_many :relations, :as => :object, :dependent => :destroy
has_many :objects, :as => :target, :class_name => 'Relation'
has_many :targets, :as => :object, :class_name => 'Relation'
has_many :members, :through => :objects, :source => :object, :source_type => 'User', :autosave => true
has_many :projects, :through => :targets, :source => :target, :source_type => 'Project', :autosave => true
has_many :platforms, :through => :targets, :source => :target, :source_type => 'Platform', :autosave => true, :dependent => :destroy
has_many :repositories, :through => :targets, :source => :target, :source_type => 'Repository', :autosave => true
has_many :relations, :as => :object, :dependent => :destroy
has_many :platforms, :through => :targets, :source => :target, :source_type => 'Platform', :autosave => true
validates :name, :owner, :presence => true
has_many :own_projects, :as => :owner, :class_name => 'Project', :dependent => :destroy
has_many :own_platforms, :as => :owner, :class_name => 'Platform', :dependent => :destroy
validates :owner, :presence => true
validates :uname, :presence => true, :uniqueness => {:case_sensitive => false}, :format => { :with => /^[a-z0-9_]+$/ }
validate { errors.add(:uname, :taken) if User.where('uname LIKE ?', uname).present? }
attr_readonly :uname, :own_projects_count
scope :by_owner, lambda { |owner| where(:owner_id => owner.id) }
scope :by_admin, lambda { |admin| joins(:relations).where(:'relations.role' => 'admin', :'relations.target_id' => admin.id, :'relations.target_type' => 'User') }
attr_readonly :own_projects_count
delegate :ssh_key, :email, :to => :owner
after_create :add_owner_to_members
include Modules::Models::PersonalRepository
# include Modules::Models::Owner
# include Modules::Models::Owner
def self.can_own_project(user)
(by_owner(user) | by_admin(user)).collect { |el| [el.name, el.id] }
end
def name
uname
end
protected
def add_owner_to_members
Relation.create_with_role(self.owner, self, 'admin')
# members << self.owner if !members.exists?(:id => self.owner.id)
end
def add_owner_to_members
Relation.create_with_role(self.owner, self, 'admin')
# members << self.owner if !members.exists?(:id => self.owner.id)
end
end

View File

@ -4,9 +4,13 @@ class Issue < ActiveRecord::Base
belongs_to :project
belongs_to :user
belongs_to :creator, :class_name => 'User', :foreign_key => 'creator_id'
belongs_to :closer, :class_name => 'User', :foreign_key => 'closed_by'
has_many :comments, :as => :commentable, :dependent => :destroy #, :finder_sql => proc { "comments.commentable_id = '#{self.id}' AND comments.commentable_type = '#{self.class.name}'"}
has_many :subscribes, :as => :subscribeable, :dependent => :destroy #, :finder_sql => proc { "subscribes.subscribeable_id = '#{self.id}' AND subscribes.subscribeable_type = '#{self.class.name}'"}
has_many :labels, :through => :labelings
has_many :labelings
validates :title, :body, :project_id, :presence => true
@ -14,11 +18,14 @@ class Issue < ActiveRecord::Base
after_create :set_serial_id
after_create :subscribe_users
after_create :deliver_new_issue_notification
after_create :deliver_issue_assign_notification
after_update :deliver_issue_assign_notification
after_update :subscribe_issue_assigned_user
attr_accessible :labelings_attributes, :title, :body
accepts_nested_attributes_for :labelings, :allow_destroy => true
scope :opened, where(:status => 'open', :closed_by => nil, :closed_at => nil)
scope :closed, where(:status => 'closed').where("closed_by is not null and closed_at is not null")
def assign_uname
user.uname if user
end
@ -33,6 +40,29 @@ class Issue < ActiveRecord::Base
end
end
def closed?
closed_by && closed_at && status == 'closed'
end
def set_close(closed_by)
self.closed_at = Time.now
self.closer = closed_by
self.status = 'closed'
end
def set_open
self.closed_at = self.closed_by = nil
self.status = 'open'
end
def collect_recipient_ids
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.user_id] if self.user_id
recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User'
recipients
end
protected
def set_serial_id
@ -40,18 +70,6 @@ class Issue < ActiveRecord::Base
self.save!
end
def deliver_new_issue_notification
recipients = collect_recipient_ids
recipients.each do |recipient_id|
recipient = User.find(recipient_id)
UserMailer.delay.new_issue_notification(self, recipient) if User.find(recipient).notifier.can_notify && User.find(recipient).notifier.new_issue
end
end
def deliver_issue_assign_notification
UserMailer.delay.issue_assign_notification(self, self.user) if self.user_id_was != self.user_id && self.user.notifier.issue_assign && self.user.notifier.can_notify
end
def subscribe_users
recipients = collect_recipient_ids
recipients.each do |recipient_id|
@ -60,19 +78,6 @@ class Issue < ActiveRecord::Base
end
end
def collect_recipient_ids
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.user_id] if self.user_id
recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User'
# filter by notification settings
recipients = recipients.select do |recipient|
User.find(recipient).notifier.new_issue && User.find(recipient).notifier.can_notify
end
recipients
end
def subscribe_issue_assigned_user
if self.user_id_was != self.user_id
self.subscribes.where(:user_id => self.user_id_was).first.destroy unless self.user_id_was.blank?
@ -81,4 +86,5 @@ class Issue < ActiveRecord::Base
end
end
end
end

9
app/models/label.rb Normal file
View File

@ -0,0 +1,9 @@
class Label < ActiveRecord::Base
has_many :labelings, :dependent => :destroy
has_many :issues, :through => :labelings
belongs_to :project
validates :name, :uniqueness => { :scope => :project_id}
validates :name, :color, :presence => true
validates :color, :format => { :with => /\A([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/, :message => I18n.t('layout.issues.invalid_labels')}
end

5
app/models/labeling.rb Normal file
View File

@ -0,0 +1,5 @@
class Labeling < ActiveRecord::Base
belongs_to :issue
belongs_to :label
end

View File

@ -14,10 +14,11 @@ class Platform < ActiveRecord::Base
has_many :members, :through => :objects, :source => :object, :source_type => 'User'
has_many :groups, :through => :objects, :source => :object, :source_type => 'Group'
validates :description, :presence => true, :uniqueness => true
validates :description, :presence => true
validates :name, :uniqueness => {:case_sensitive => false}, :presence => true, :format => { :with => /^[a-zA-Z0-9_\-]+$/ }
validates :distrib_type, :presence => true, :inclusion => {:in => APP_CONFIG['distr_types']}
before_create :create_directory, :if => lambda {Thread.current[:skip]} # TODO remove this when core will be ready
before_create :xml_rpc_create, :unless => lambda {Thread.current[:skip]}
before_destroy :xml_rpc_destroy
# before_update :check_freezing
@ -98,27 +99,23 @@ class Platform < ActiveRecord::Base
platform_type == 'personal'
end
def full_clone(attrs) # :description, :name, :owner
def base_clone(attrs = {}) # :description, :name, :owner
clone.tap do |c|
c.attributes = attrs
c.attributes = attrs # attrs.each {|k,v| c.send("#{k}=", v)}
c.updated_at = nil; c.created_at = nil # :id = nil
c.parent = self
new_attrs = {:platform_id => nil}
c.repositories = repositories.map{|r| r.full_clone(new_attrs.merge(:owner_id => attrs[:owner_id], :owner_type => attrs[:owner_type]))}
c.products = products.map{|p| p.full_clone(new_attrs)}
end
end
# TODO * make it Delayed Job *
def make_clone(attrs)
p = full_clone(attrs)
begin
Thread.current[:skip] = true
p.save and xml_rpc_clone(attrs[:name])
ensure
Thread.current[:skip] = false
def clone_relations(from = parent)
self.repositories = from.repositories.map{|r| r.full_clone(:platform_id => id)}
self.products = from.products.map(&:full_clone)
end
def full_clone(attrs = {})
base_clone(attrs).tap do |c|
with_skip {c.save} and c.clone_relations(self) and c.delay.xml_rpc_clone
end
p
end
def name
@ -135,9 +132,13 @@ class Platform < ActiveRecord::Base
end
end
def create_directory
system("sudo mkdir -p -m 0777 #{path}")
end
def mount_directory_for_rsync
# umount_directory_for_rsync # TODO ignore errors
system("sudo mkdir -p #{mount_path}")
system("sudo mkdir -p -m 0777 #{mount_path}")
system("sudo mount --bind #{path} #{mount_path}")
Arch.all.each do |arch|
str = "country=Russian Federation,city=Moscow,latitude=52.18,longitude=48.88,bw=1GB,version=2011,arch=#{arch.name},type=distrib,url=#{public_downloads_url}\n"
@ -157,6 +158,23 @@ class Platform < ActiveRecord::Base
end
end
def build_all(user)
repositories.find_by_name('main').projects.find_in_batches(:batch_size => 5) do |group|
sleep 1
group.each do |p|
begin
p.build_for(self, user)
rescue RuntimeError, Exception
p.delay.build_for(self, user)
end
end
end
end
def destroy
with_skip {super} # avoid cascade XML RPC requests
end
protected
def build_path(dir)
@ -181,12 +199,12 @@ class Platform < ActiveRecord::Base
end
end
def xml_rpc_clone(new_name)
result = BuildServer.clone_platform new_name, self.name, APP_CONFIG['root_path'] + '/platforms'
def xml_rpc_clone(old_name = parent.name, new_name = name)
result = BuildServer.clone_platform new_name, old_name, APP_CONFIG['root_path'] + '/platforms'
if result == BuildServer::SUCCESS
return true
else
raise "Failed to clone platform #{name} with code #{result}. Path: #{build_path(name)} to platform #{new_name}"
raise "Failed to clone platform #{old_name} with code #{result}. Path: #{build_path(old_name)} to platform #{new_name}"
end
end

View File

@ -10,7 +10,7 @@ class Product < ActiveRecord::Base
has_attached_file :tar
validates_attachment_content_type :tar, :content_type => ["application/gnutar", "application/x-compressed", "application/x-gzip", "application/x-bzip", "application/x-bzip2", "application/x-tar"], :message => I18n.t('layout.invalid_content_type')
validates_attachment_content_type :tar, :content_type => ["application/gnutar", "application/x-compressed", "application/x-gzip", "application/x-bzip", "application/x-bzip2", "application/x-tar", "application/octet-stream"], :message => I18n.t('layout.invalid_content_type')
validates :name, :presence => true, :uniqueness => {:scope => :platform_id}
scope :recent, order("name ASC")
@ -59,9 +59,10 @@ class Product < ActiveRecord::Base
EOF
end
def full_clone(attrs) # owner
def full_clone(attrs = {})
clone.tap do |c| # dup
c.attributes = attrs
c.platform_id = nil
attrs.each {|k,v| c.send("#{k}=", v)}
c.updated_at = nil; c.created_at = nil # :id = nil
end
end

View File

@ -14,6 +14,11 @@ class ProductBuildList < ActiveRecord::Base
attr_accessor :base_url
after_create :xml_rpc_create
after_destroy :xml_delete_iso_container
def container_path
"/downloads/#{product.platform.name}/product/#{id}/"
end
def human_status
I18n.t("layout.product_build_lists.statuses.#{status}")
@ -30,8 +35,17 @@ class ProductBuildList < ActiveRecord::Base
if result == ProductBuilder::SUCCESS
return true
else
# return false
raise "Failed to create product_build_list #{id} inside platform #{product.platform.name} tar url #{tar_url} with code #{result}."
raise "Failed to create product_build_list #{id} inside platform #{platform.name} tar url #{tar_url} with code #{result}."
end
end
def xml_delete_iso_container
result = ProductBuilder.delete_iso_container self
if result == ProductBuilder::SUCCESS
return true
else
raise "Failed to destroy product_build_list #{id} inside platform #{platform.name} with code #{result}."
end
end
end

View File

@ -17,8 +17,9 @@ class Project < ActiveRecord::Base
has_many :relations, :as => :target, :dependent => :destroy
has_many :collaborators, :through => :relations, :source => :object, :source_type => 'User'
has_many :groups, :through => :relations, :source => :object, :source_type => 'Group'
has_many :labels
validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, :format => { :with => /^[a-zA-Z0-9_\-\+\.]+$/ }
validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, :format => {:with => /^[a-zA-Z0-9_\-\+\.]+$/}
validates :owner, :presence => true
validate { errors.add(:base, :can_have_less_or_equal, :count => MAX_OWN_PROJECTS) if owner.projects.size >= MAX_OWN_PROJECTS }
# validate {errors.add(:base, I18n.t('flash.project.save_warning_ssh_key')) if owner.ssh_key.blank?}
@ -61,14 +62,13 @@ class Project < ActiveRecord::Base
end
end
def build_for(platform, user)
def build_for(platform, user)
build_lists.create do |bl|
bl.pl = platform
bl.bpl = platform
bl.update_type = 'recommended'
bl.update_type = 'newpackage'
bl.arch = Arch.find_by_name('x86_64') # Return i586 after mass rebuild
# FIXME: Need to set "latest_#{platform.name}"
bl.project_version = "latest_import_mandriva2011"
bl.project_version = "latest_#{platform.name}" # "latest_import_mandriva2011"
bl.build_requires = false # already set as db default
bl.user = user
bl.auto_publish = true # already set as db default
@ -84,6 +84,46 @@ class Project < ActiveRecord::Base
self.git_repository.branches
end
def last_active_branch
@last_active_branch ||= branches.inject do |r, c|
r_last = r.commit.committed_date || r.commit.authored_date unless r.nil?
c_last = c.commit.committed_date || c.commit.authored_date
if r.nil? or r_last < c_last
r = c
end
r
end
@last_active_branch
end
def branch(name = nil)
name = default_branch if name.blank?
branches.select{|b| b.name == name}.first
end
def tree_info(tree, treeish = nil, path = nil)
treeish = tree.id unless treeish.present?
# initialize result as hash of <tree_entry> => nil
res = (tree.trees.sort + tree.blobs.sort).inject({}){|h, e| h.merge!({e => nil})}
# fills result vith commits that describes this file
res = res.inject(res) do |h, (entry, commit)|
# only if commit == nil ...
if commit.nil? and entry.respond_to? :name
# ... find last commit corresponds to this file ...
c = git_repository.log(treeish, File.join([path, entry.name].compact), :max_count => 1).first
# ... and add it to result.
h[entry] = c
# find another files, that linked to this commit and set them their commit
c.diffs.map{|diff| diff.b_path.split(File::SEPARATOR, 2).first}.each do |name|
h.each_pair do |k, v|
h[k] = c if k.name == name and v.nil?
end
end
end
h
end
end
def versions
tags.map(&:name) + branches.map{|b| "latest_#{b.name}"}
end
@ -153,7 +193,7 @@ class Project < ActiveRecord::Base
class << self
def commit_comments(commit, project)
comments = Comment.where(:commentable_id => commit.id.hex, :commentable_type => 'Grit::Commit').order(:created_at)
comments = Comment.where(:commentable_id => commit.id.hex, :commentable_type => 'Grit::Commit')
comments.each {|x| x.project = project; x.helper}
end
end
@ -164,7 +204,13 @@ class Project < ActiveRecord::Base
def self.process_hook(owner_uname, repo, newrev, oldrev, ref, newrev_type, oldrev_type)
rec = GitHook.new(owner_uname, repo, newrev, oldrev, ref, newrev_type, oldrev_type)
#ActivityFeedObserver.instance.after_create rec # for example
ActivityFeedObserver.instance.after_create rec
end
def owner_and_admin_ids
recipients = self.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.owner_id] if self.owner_type == 'User'
recipients
end
protected
@ -212,9 +258,20 @@ class Project < ActiveRecord::Base
end
def write_hook
is_production = ENV['RAILS_ENV'] == 'production'
hook = File.join(::Rails.root.to_s, 'tmp', "post-receive-hook")
FileUtils.cp(File.join(::Rails.root.to_s, 'bin', "post-receive-hook.partial"), hook)
File.open(hook, 'a') do |f|
s = "\n /bin/bash -l -c \"cd #{is_production ? '/srv/rosa_build/current' : Rails.root.to_s} && #{is_production ? 'RAILS_ENV=production' : ''} bundle exec rails runner 'Project.delay.process_hook(\"$owner\", \"$reponame\", \"$newrev\", \"$oldrev\", \"$ref\", \"$newrev_type\", \"$oldrev_type\")'\""
s << " > /dev/null 2>&1" if is_production
s << "\ndone\n"
f.write(s)
end
hook_file = File.join(path, 'hooks', 'post-receive')
FileUtils.cp(File.join(::Rails.root.to_s, 'lib', 'post-receive-hook'), hook_file)
#File.chmod(0775, hook_file) # need?
FileUtils.cp(hook, hook_file)
FileUtils.rm_rf(hook)
rescue Exception # FIXME
end
end

View File

@ -6,7 +6,7 @@ class ProjectToRepository < ActiveRecord::Base
delegate :path, :to => :project
after_create lambda { project.xml_rpc_create(repository) }, :unless => lambda {Thread.current[:skip]}
after_destroy lambda { project.xml_rpc_destroy(repository) }
after_destroy lambda { project.xml_rpc_destroy(repository) }, :unless => lambda {Thread.current[:skip]}
# after_rollback lambda { project.xml_rpc_destroy(repository) rescue true if new_record? }
validate :one_project_in_platform_repositories

View File

@ -1,3 +1,4 @@
# -*- encoding : utf-8 -*-
class RegisterRequest < ActiveRecord::Base
default_scope order('created_at ASC')

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