diff --git a/.gitignore b/.gitignore index ef7fd18e7..0511a9ffc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ .bundle .rvmrc .DS_Store diff --git a/Gemfile b/Gemfile index 60869b4bb..891323817 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'http://rubygems.org' -gem 'rails', '3.2.7' #, :git => 'git://github.com/rails/rails.git' +gem 'rails', '3.2.8' #, :git => 'git://github.com/rails/rails.git' gem 'pg', '~> 0.14.0' # gem 'silent-postgres', :git => 'git://github.com/dolzenko/silent-postgres.git' #'~> 0.1.1' @@ -25,14 +25,14 @@ gem 'state_machine' # gem 'rugged', '~> 0.16.0' gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' gem "grit", :git => 'git://github.com/warpc/grit.git' #, :path => '~/Sites/code/grit' -gem 'charlock_holmes', '~> 0.6.8' #, :git => 'git://github.com/brianmario/charlock_holmes.git', :branch => 'bundle-icu' +gem 'charlock_holmes', '~> 0.6.9' #, :git => 'git://github.com/brianmario/charlock_holmes.git', :branch => 'bundle-icu' # gem 'ruby-filemagic', '~> 0.4.2', :require => 'filemagic/ext' -gem 'github-linguist', '~> 2.1.2', :require => 'linguist' +gem 'github-linguist', '~> 2.2.1', :require => 'linguist' gem 'diff-display', '~> 0.0.1' # Wiki -gem "gollum", :git => 'git://github.com/github/gollum.git' -gem "redcarpet", "1.17.2" +gem "gollum", '~> 2.1.3' +gem "redcarpet", "~> 2.1.1" gem 'creole' gem 'rdiscount' # gem 'org-ruby' @@ -58,7 +58,7 @@ group :assets do gem 'coffee-rails', '~> 3.2.2' gem 'compass-rails', '~> 1.0.3' gem 'uglifier', '~> 1.2.4' - gem 'therubyracer', '~> 0.10.1', :platforms => [:mri, :rbx] + gem 'therubyracer', '~> 0.10.2', :platforms => [:mri, :rbx] gem 'therubyrhino', '~> 1.73.1', :platforms => :jruby end @@ -81,7 +81,7 @@ end group :test do gem 'rspec-rails', '~> 2.11.0', :group => 'development' - gem 'factory_girl_rails', '~> 3.6.0' + gem 'factory_girl_rails', '~> 4.0.0' gem 'rr', '~> 1.0.4' gem 'shoulda' end diff --git a/Gemfile.lock b/Gemfile.lock index 896f7a8ab..b207f5084 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,23 +6,6 @@ GIT redhillonrails_core (2.0.0.pre) activerecord (>= 3.1.0.rc) -GIT - remote: git://github.com/github/gollum.git - revision: 8422b712048656c8ea391c2d7ef27fb29f66746b - specs: - gollum (2.1.0) - github-markdown - github-markup (>= 0.7.0, < 1.0.0) - grit (~> 2.5.0) - mustache (>= 0.11.2, < 1.0.0) - nokogiri (~> 1.4) - posix-spawn (~> 0.3.0) - pygments.rb (~> 0.2.0) - sanitize (~> 2.0.0) - sinatra (~> 1.0) - stringex (~> 1.4.0) - useragent (~> 0.4.9) - GIT remote: git://github.com/rdblue/grack.git revision: 020be3fef3fb308b9d214252522aa5945bf6584a @@ -42,12 +25,12 @@ GEM remote: http://rubygems.org/ specs: RedCloth (4.2.9) - actionmailer (3.2.7) - actionpack (= 3.2.7) + actionmailer (3.2.8) + actionpack (= 3.2.8) mail (~> 2.4.4) - actionpack (3.2.7) - activemodel (= 3.2.7) - activesupport (= 3.2.7) + actionpack (3.2.8) + activemodel (= 3.2.8) + activesupport (= 3.2.8) builder (~> 3.0.0) erubis (~> 2.7.0) journey (~> 1.0.4) @@ -55,18 +38,18 @@ GEM rack-cache (~> 1.2) rack-test (~> 0.6.1) sprockets (~> 2.1.3) - activemodel (3.2.7) - activesupport (= 3.2.7) + activemodel (3.2.8) + activesupport (= 3.2.8) builder (~> 3.0.0) - activerecord (3.2.7) - activemodel (= 3.2.7) - activesupport (= 3.2.7) + activerecord (3.2.8) + activemodel (= 3.2.8) + activesupport (= 3.2.8) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activeresource (3.2.7) - activemodel (= 3.2.7) - activesupport (= 3.2.7) - activesupport (3.2.7) + activeresource (3.2.8) + activemodel (= 3.2.8) + activesupport (= 3.2.8) + activesupport (3.2.8) i18n (~> 0.6) multi_json (~> 1.0) airbrake (3.1.2) @@ -92,9 +75,9 @@ GEM net-ssh (>= 2.0.14) net-ssh-gateway (>= 1.1.0) capistrano_colors (0.5.5) - charlock_holmes (0.6.8) + charlock_holmes (0.6.9) chronic (0.6.7) - chunky_png (1.2.5) + chunky_png (1.2.6) cocaine (0.2.1) coffee-rails (3.2.2) coffee-script (>= 2.2.0) @@ -125,21 +108,33 @@ GEM execjs (1.4.0) multi_json (~> 1.0) expression_parser (0.9.0) - factory_girl (3.6.0) + factory_girl (4.0.0) activesupport (>= 3.0.0) - factory_girl_rails (3.6.0) - factory_girl (~> 3.6.0) + factory_girl_rails (4.0.0) + factory_girl (~> 4.0.0) railties (>= 3.0.0) ffi (1.0.11) fssm (0.2.9) - github-linguist (2.1.2) + github-linguist (2.2.1) charlock_holmes (~> 0.6.6) escape_utils (~> 0.2.3) mime-types (~> 1.18) pygments.rb (>= 0.2.13) github-markdown (0.5.0) github-markup (0.7.4) - haml (3.1.6) + gollum (2.1.3) + github-markdown + github-markup (>= 0.7.0, < 1.0.0) + grit (~> 2.5.0) + mustache (>= 0.11.2, < 1.0.0) + nokogiri (~> 1.4) + posix-spawn (~> 0.3.0) + pygments.rb (~> 0.2.0) + sanitize (~> 2.0.0) + sinatra (~> 1.0) + stringex (~> 1.4.0) + useragent (~> 0.4.9) + haml (3.1.7) haml-rails (0.3.4) actionpack (~> 3.0) activesupport (~> 3.0) @@ -150,7 +145,7 @@ GEM hike (1.2.1) hirb (0.7.0) i18n (0.6.0) - jbuilder (0.4.0) + jbuilder (0.4.3) activesupport (>= 3.0.0) blankslate (>= 2.1.2.4) journey (1.0.4) @@ -223,14 +218,14 @@ GEM rack rack-test (0.6.1) rack (>= 1.0) - rails (3.2.7) - actionmailer (= 3.2.7) - actionpack (= 3.2.7) - activerecord (= 3.2.7) - activeresource (= 3.2.7) - activesupport (= 3.2.7) + rails (3.2.8) + actionmailer (= 3.2.8) + actionpack (= 3.2.8) + activerecord (= 3.2.8) + activeresource (= 3.2.8) + activesupport (= 3.2.8) bundler (~> 1.0) - railties (= 3.2.7) + railties (= 3.2.8) rails-backbone (0.7.2) coffee-script (~> 2.2.0) ejs (~> 1.0.0) @@ -239,9 +234,9 @@ GEM railties (>= 3.0.0) rails3-jquery-autocomplete (1.0.7) rails (~> 3.0) - railties (3.2.7) - actionpack (= 3.2.7) - activesupport (= 3.2.7) + railties (3.2.8) + actionpack (= 3.2.8) + activesupport (= 3.2.8) rack-ssl (~> 1.3.2) rake (>= 0.8.7) rdoc (~> 3.4) @@ -251,9 +246,9 @@ GEM rdiscount (1.6.8) rdoc (3.12) json (~> 1.4) - redcarpet (1.17.2) + redcarpet (2.1.1) redis (3.0.1) - redis-namespace (1.2.0) + redis-namespace (1.2.1) redis (~> 3.0.0) redisk (0.2.2) redis (>= 0.1.1) @@ -277,7 +272,7 @@ GEM rspec-core (2.11.1) rspec-expectations (2.11.2) diff-lcs (~> 1.1.3) - rspec-mocks (2.11.1) + rspec-mocks (2.11.2) rspec-rails (2.11.0) actionpack (>= 3.0) activesupport (>= 3.0) @@ -296,7 +291,7 @@ GEM capistrano (>= 2.0.0) sanitize (2.0.3) nokogiri (>= 1.4.4, < 1.6) - sass (3.1.20) + sass (3.2.0) sass-rails (3.2.5) railties (~> 3.2.0) sass (>= 3.1.10) @@ -324,19 +319,19 @@ GEM state_machine (1.1.2) stringex (1.4.0) systemu (2.5.2) - therubyracer (0.10.1) + therubyracer (0.10.2) libv8 (~> 3.3.10) thin (1.4.1) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) - thor (0.15.4) + thor (0.16.0) tilt (1.3.3) treetop (1.4.10) polyglot polyglot (>= 0.3.1) tzinfo (0.3.33) - uglifier (1.2.6) + uglifier (1.2.7) execjs (>= 0.3.0) multi_json (~> 1.3) unicorn (4.3.1) @@ -370,15 +365,15 @@ DEPENDENCIES cape capistrano capistrano_colors - charlock_holmes (~> 0.6.8) + charlock_holmes (~> 0.6.9) coffee-rails (~> 3.2.2) compass-rails (~> 1.0.3) creole devise (~> 2.1.2) diff-display (~> 0.0.1) - factory_girl_rails (~> 3.6.0) - github-linguist (~> 2.1.2) - gollum! + factory_girl_rails (~> 4.0.0) + github-linguist (~> 2.2.1) + gollum (~> 2.1.3) grack! grit! haml-rails (~> 0.3.4) @@ -394,12 +389,12 @@ DEPENDENCIES paperclip (~> 3.1.4) perform_later (~> 1.3.0) pg (~> 0.14.0) - rails (= 3.2.7) + rails (= 3.2.8) rails-backbone (~> 0.7.2) rails3-generators rails3-jquery-autocomplete (~> 1.0.7) rdiscount - redcarpet (= 1.17.2) + redcarpet (~> 2.1.1) redhillonrails_core! resque (~> 1.21.0) resque-status (~> 0.3.3) @@ -413,7 +408,7 @@ DEPENDENCIES shotgun shoulda state_machine - therubyracer (~> 0.10.1) + therubyracer (~> 0.10.2) therubyrhino (~> 1.73.1) trinidad (~> 1.0.2) uglifier (~> 1.2.4) diff --git a/app/assets/images/trigger.gif b/app/assets/images/trigger.gif new file mode 100644 index 000000000..4be7710be Binary files /dev/null and b/app/assets/images/trigger.gif differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 6b311c6f5..69d7c1b0c 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -34,7 +34,7 @@ $(document).ready(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'); + $(el).removeAttr('checked'); } }); }); @@ -81,4 +81,10 @@ $(document).ready(function() { } return false; }); + + window.CodeMirrorRun = function(code) { + CodeMirror.runMode(code.innerHTML.replace(/&/gi, '&').replace(/</gi, '<').replace(/>/gi, '>'), code.className, code); + } + + $('.md_and_cm code').each(function (code) { CodeMirrorRun(this); }); }); diff --git a/app/assets/javascripts/backbone/views/project_modify_view.js b/app/assets/javascripts/backbone/views/project_modify_view.js new file mode 100644 index 000000000..348b9a50e --- /dev/null +++ b/app/assets/javascripts/backbone/views/project_modify_view.js @@ -0,0 +1,23 @@ +Rosa.Views.ProjectModifyView = Backbone.View.extend({ + initialize: function() { + _.bindAll(this, 'checkboxClick'); + + this.$checkbox_wrapper = $('#niceCheckbox1'); + this._$checkbox = this.$checkbox_wrapper.children('#project_is_package').first(); + this.$maintainer_form = $('#maintainer_form'); + + this.$checkbox_wrapper.on('click', this.checkboxClick); + }, + + checkboxClick: function() { + if (this._$checkbox.is(':checked')) { + this.$maintainer_form.slideDown(); + } else { + this.$maintainer_form.slideUp(); + } + }, + + render: function() { + this.checkboxClick(); + } +}); diff --git a/app/assets/javascripts/design/checkbox-main.js b/app/assets/javascripts/design/checkbox-main.js index 225051b87..b8b219cfb 100644 --- a/app/assets/javascripts/design/checkbox-main.js +++ b/app/assets/javascripts/design/checkbox-main.js @@ -1,32 +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)); - }); -}); +function changeCheck(el) { + var 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 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)); + }); +}); diff --git a/app/assets/javascripts/extra/build_list.js b/app/assets/javascripts/extra/build_list.js index 6402c9e5d..99241e9cf 100644 --- a/app/assets/javascripts/extra/build_list.js +++ b/app/assets/javascripts/extra/build_list.js @@ -1,98 +1,47 @@ $(document).ready(function() { // TODO: Refactor this handler!! It's too complicated. - $('#build_list_save_to_platform_id').change(function() { - var platform_id = $(this).val(); - var base_platforms = $('.all_platforms input[type=checkbox].build_bpl_ids'); + $('#build_list_save_to_repository_id').change(function() { + var selected_option = $(this).find("option:selected"); - base_platforms.each(function(){ - if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) >= 0) { - if ($(this).val() == platform_id) { - if ($(this).attr('data-released') === '1') { - $('#build_list_auto_publish').removeAttr('checked').attr('disabled', 'disabled'); - //disableUpdateTypes(); - } else { - $('#build_list_auto_publish').removeAttr('disabled').attr('checked', 'checked'); - //enableUpdateTypes(); - } + var platform_id = selected_option.attr('platform_id'); + var rep_name = selected_option.text().match(/[\w-]+\/([\w-]+)/)[1]; - $(this).attr('checked', 'checked').removeAttr('disabled').trigger('change'); - $(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled'); - - var rep_name = $('#build_list_save_to_platform_id option[value="' + $(this).val() + '"]').text().match(/[\w-]+\/([\w-]+)/)[1]; - if (rep_name != 'main') { - $(this).parent().find('.offset25 input[type="checkbox"][rep_name="' + rep_name + '"]').attr('checked', 'checked'); - } - $(this).parent().find('.offset25 input[type="checkbox"][rep_name="main"]').attr('checked', 'checked'); - } else { - $(this).removeAttr('checked').attr('disabled', 'disabled').trigger('change'); - $(this).parent().find('.offset25 input[type="checkbox"]').attr('disabled', 'disabled').removeAttr('checked'); - } - } else { - $(this).removeAttr('disabled').removeAttr('checked').trigger('change'); - $(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled').removeAttr('checked'); - $('#build_list_auto_publish').removeAttr('disabled').attr('checked', 'checked'); + var build_platform = $('#build_for_pl_' + platform_id); + var all_repositories = $('.all_platforms input'); + all_repositories.removeAttr('checked'); + if (build_platform.size() == 0) { + all_repositories.removeAttr('disabled'); + } else { + all_repositories.attr('disabled', 'disabled'); + var parent = build_platform.parent(); + parent.find('input').removeAttr('disabled'); + parent.find('input[rep_name="main"]').attr('checked', 'checked'); + if (rep_name != 'main') { + parent.find('input[rep_name="' + rep_name + '"]').attr('checked', 'checked'); } - }); - - if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) == -1) { - // For personal platforms update types always enebaled: - enableUpdateTypes(); + setBranchSelected(selected_option); + } + var build_list_auto_publish = $('#build_list_auto_publish'); + if (selected_option.attr('publish_without_qa') == '1') { + build_list_auto_publish.removeAttr('disabled').attr('checked', 'checked'); + } else { + build_list_auto_publish.removeAttr('checked').attr('disabled', 'disabled'); } - - - setBranchSelected(); - }); - $('#build_list_save_to_platform_id').trigger('change'); - - $('.offset25 label').click(function() { - setPlChecked($(this).prev()[0], !$(this).prev().attr('checked')); - }); - $('.offset25 input[type="checkbox"]').click(function() { - setPlChecked(this, $(this).attr('checked')); - }); - - $('.build_bpl_ids').click(function() { - return false; }); + $('#build_list_save_to_repository_id').trigger('change'); }); -function setPlChecked(pointer, checked) { - var pl_cbx = $(pointer).parent().parent().parent().find('input[type="checkbox"].build_bpl_ids'); - var pl_id = pl_cbx.val(); - if (checked && !$(pointer).attr('disabled')) { - pl_cbx.attr('checked', 'checked').trigger('change'); - } else if ($('input[pl_id=' + pl_id + '][checked="checked"]').size() == 0) { - pl_cbx.removeAttr('checked').trigger('change'); +function setBranchSelected(selected_option) { + var pl_name = selected_option.text().match(/([\w-.]+)\/[\w-.]+/)[1]; + var bl_version_sel = $('#build_list_project_version'); + var branch_pl_opt = bl_version_sel.find('option[value="latest_' + pl_name + '"]'); + // If there is branch we need - set it selected: + if (branch_pl_opt.size() > 0) { + bl_version_sel.find('option[selected]').removeAttr('selected'); + branch_pl_opt.attr('selected', 'selected'); + bl_version_sel.val(branch_pl_opt); + // hack for FF to force render of select box. + bl_version_sel[0].innerHTML = bl_version_sel[0].innerHTML; } } - -function setBranchSelected() { - var pl_id = $('#build_list_save_to_platform_id').val(); - // Checks if selected platform is main or not: - if ( $('.all_platforms').find('input[type="checkbox"][value=' + pl_id + '].build_bpl_ids').size() > 0 ) { - var pl_name = $('#build_list_save_to_platform_id option[value="' + pl_id + '"]').text().match(/([\w-.]+)\/[\w-.]+/)[1]; - var branch_pl_opt = $('#build_list_project_version option[value="latest_' + pl_name + '"]'); - // If there is branch we need - set it selected: - if ( branch_pl_opt.size() > 0 ) { - $('#build_list_project_version option[selected]').removeAttr('selected'); - branch_pl_opt.attr('selected', 'selected'); - } - } -} - -function disableUpdateTypes() { - $("select#build_list_update_type option").each(function(i,el) { - if ( $.inArray($(el).attr("value"), ["security", "bugfix"]) == -1 ) { - $(el).attr("disabled", "disabled"); - // If disabled option is selected - select 'bugfix': - if ( $(el).attr("selected") ) { - $( $('select#build_list_update_type option[value="bugfix"]') ).attr("selected", "selected"); - } - } - }); -} - -function enableUpdateTypes() { - $("select#build_list_update_type option").removeAttr("disabled"); -} diff --git a/app/assets/javascripts/extra/comment.js b/app/assets/javascripts/extra/comment.js new file mode 100644 index 000000000..5bcf3aa20 --- /dev/null +++ b/app/assets/javascripts/extra/comment.js @@ -0,0 +1,34 @@ +$(document).ready(function() { + $('.buttons a.edit_comment').live('click', function() { + $(this).parent().parent().parent().hide(); + $('#open-comment'+'.comment.'+$(this).attr('id')).show(); + return false; + }); + + $('.cancel_edit_comment.button').live('click', function() { + $(this).parent().parent().parent().hide(); + $('.buttons a.edit_comment#'+$(this).attr('id')).parent().parent().parent().show(); + return false; + }); + + $('form.edit_comment').live('submit', function() { + var form = $(this); + form.parent().find('.flash').remove(); + $.ajax({ + type: 'POST', + url: form.attr("action"), + data: form.serialize(), + success: function(data){ + var cancel_button = form.find('.cancel_edit_comment.button'); + cancel_button.click(); + $('.buttons a.edit_comment#'+cancel_button.attr('id')).parent().parent().find('.cm-s-default.md_and_cm').html(data).find('code').each(function (code) { CodeMirrorRun(this); }) + }, + error: function(data){ + form.before(data.responseText); + } + }); + return false; + }); + + +}); diff --git a/app/assets/javascripts/extra/preview.js b/app/assets/javascripts/extra/preview.js new file mode 100644 index 000000000..5f9002ea0 --- /dev/null +++ b/app/assets/javascripts/extra/preview.js @@ -0,0 +1,23 @@ +$(document).ready(function() { + var preview_url = $('#preview_url').val(); + $('#md_tabs.nav.nav-tabs').each(function(i) { $(this).find('a:first').tab('show') }); + + $('#md_tabs a[data-toggle="tab"]').on('shown', function (e) { + if(e.relatedTarget) { var hash = e.relatedTarget.hash; } + else { var hash = e.currentTarget.hash; } + var el = $(hash+'_input'); + var el_dup = $(hash+'_input_dup'); + var preview = $(e.target.hash+' > .formatted.cm-s-default'); + if(el.val() != el_dup.val() || preview.val() === '') { + el_dup.val(el.val()); + $.ajax({ + type: 'POST', + url: preview_url, + data: el_dup.serialize(), + success: function(data){ + preview.html(data).find('code').each(function (code) { CodeMirrorRun(this); }) + } + }); + }; + }); +}); diff --git a/app/assets/javascripts/extra/tracker.js b/app/assets/javascripts/extra/tracker.js index 25dc641b6..24aaf3f2f 100644 --- a/app/assets/javascripts/extra/tracker.js +++ b/app/assets/javascripts/extra/tracker.js @@ -224,6 +224,7 @@ $(document).ready(function() { $('.edit_form.issue').live('submit', function() { var form = $(this); + form.parent().find('.flash').remove(); $.ajax({ type: 'POST', url: form.attr("action"), @@ -232,10 +233,10 @@ $(document).ready(function() { 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')); + $('.fulltext.view.issue_body').html(data).find('code').each(function (code) { CodeMirrorRun(this); }) }, error: function(data){ - alert('error'); // TODO remove + form.before(data.responseText); } }); return false; diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index f374857d0..32d816e38 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -760,7 +760,7 @@ div.tos ol li { } div.tos_sidebar ul { - list-style: none outside none; + list-style: none outside none; margin: 20px 0 0; padding: 0; text-align: left; @@ -1160,19 +1160,19 @@ form.mass_build section.right { } /* remove this lines after change to backbone */ -table.tablesorter.advisories thead tr.search th { +table.tablesorter.static-search thead tr.search th { padding: 0 5px; } -table.tablesorter.advisories thead tr.search th input[type='text'] { +table.tablesorter.static-search thead tr.search th input[type='text'] { width: 640px; } -table.tablesorter.advisories thead tr.search th form { +table.tablesorter.static-search thead tr.search th form { float: left; } -table.tablesorter.advisories thead tr.search th form.button_to { +table.tablesorter.static-search thead tr.search th form.button_to { padding: 3px 0 0 7px; } @@ -1401,10 +1401,238 @@ hr.bootstrap { } } -time.js-relative-date { - display: block; -} - div.reloader { float: right; } + +div.log-wrapper { + width: 565px; + + div.log-header { + margin-bottom: 10px; + + div.text-wrap, span { + height: 16px; + margin: 0; + padding: 0; + } + div.text-wrap { + width: 545px; + float: left; + + h3 { + margin: 0; + } + } + + span { + background: image-url("trigger.gif") no-repeat scroll left bottom transparent; + margin: 1px; + cursor: pointer; + display: block; + font-size: 0; + height: 14px; + width: 14px; + float: right; + } + span.closed { + background-position: left top; + } + } + + div.log-body { + + div.reloader, .log { + border: 1px solid #ccc; + } + + div.reloader { + padding: 5px 10px; + border-bottom: none; + border-radius: 5px 5px 0 0; + + table.options { + border-spacing: 5px 0; + + tr { + + td a { + display: inline-block; + height: 13px; + padding-left: 16px; + background: image-url('code.png') no-repeat; + padding-bottom: 2px; + margin: 2px 0 0 1px; + } + td.first { + padding-right: 5px; + } + td.last { + border-left: 1px solid #ccc; + } + } + + tr.bottom { + td { + padding-left: 5px; + } + td.first { + border-top: 1px solid #ccc; + } + } + } + } + + .log { + background: #f8f8f8; + height: 500px; + width: 543px; + min-width: 543px; + border-radius: 5px 0 5px 5px; + overflow: auto; + padding: 10px; + margin: 0; + font-family: monospace; + font-size: 13px; + } + } +} + +table.tablesorter.platform-maintainers.static-search thead tr.search th input[type="text"] { + width: 430px; +} + +.all_platforms { + > .both { margin: 0 0 5px; } + .build_for_pl { font-weight: bold; } + .offset25 { padding-left: 10px } +} + +.md_and_cm { + overflow: auto; + margin: 10px 0 10px; + padding: 5px; + border-radius: 4px 4px 4px 4px; + background: #FFF; + + pre { + background-color: #F8F8F8; + border: 1px solid #D6D6D6; + border-radius: 5px 5px 5px 5px; + color: #333333; + padding: 6px 10px; + overflow: auto; + + code { + background-color: transparent; + border: none; + } + } + + code { + background-color: #F8F8F8; + border: 1px solid #D6D6D6; + border-radius: 5px 5px 5px 5px; + margin: 0 2px; + padding: 0px 5px; + } + + blockquote { + border-left: 4px solid #EDEDED; + padding: 0 15px; + } + + p { + margin-top: 10px; + + } +} + +.md_and_cm > :first-child { + margin-top: 0 !important; +} + +#md_tabs { + margin: 0; +} + +#md_tabs.nav-tabs > li > a { + line-height: 5px; +} + +#open-comment { + textarea { + background: none repeat scroll 0 0 #FFFFFF; + border: 1px solid #DEDEDE; + border-radius: 4px 4px 4px 4px; + color: #575756; + font-size: 12px; + height: 110px; + padding: 5px; + width: 98%; + max-width: 98%; + margin: 10px 0 10px; + resize: vertical; + } + + textarea#issue_title { + height: 16px; + margin: 0 0 10px; + } + + .tab-pane textarea { + min-height: 112px; + } + + .edit_comment .comment-left { + margin-top: 0; + } +} + +#open-comment > h3.tmargin0 { + margin-bottom: 0; +} + +#md_help { + width: 800px; + top: 30%; + left: 42%; + + .modal-body { + max-height: 560px; + padding-top: 0; + } + + .modal-header { + padding-bottom: 0; + } + + .col { + width: 245px; + float: left; + padding: 0; + margin-right: 10px; + + h3 { + margin: 6px 0 0 0; + } + + pre { + background: none repeat scroll 0 0 #EDEDED; + box-shadow: 0 3px 3px -1px rgba(18, 86, 135, 0.2); + border: 1px solid #EDEDED; + padding: 5px; + margin-top: 3px; + } + } +} + +#repo-wrapper .edit_form.issue div.comment div.wrapper { + background: 0; + border: 0; + border-radius: 0; + height: 30px; + margin: 0; + padding: 0; +} + diff --git a/app/assets/stylesheets/design/main.scss b/app/assets/stylesheets/design/main.scss index ba02e8d26..63a3fc533 100644 --- a/app/assets/stylesheets/design/main.scss +++ b/app/assets/stylesheets/design/main.scss @@ -828,11 +828,11 @@ article div.activity div.fulltext.view { margin: 0px 0px 10px 0px; } -.right div.rightlist div.check { +div.rightlist div.check { float: left; } -.right div.rightlist div.forcheck { +div.rightlist div.forcheck { float: left; margin: 1px 0px 0px 5px; } diff --git a/app/controllers/groups/profile_controller.rb b/app/controllers/groups/profile_controller.rb index 1fd4ebab5..ff35cac4a 100644 --- a/app/controllers/groups/profile_controller.rb +++ b/app/controllers/groups/profile_controller.rb @@ -10,7 +10,7 @@ class Groups::ProfileController < Groups::BaseController end def show - @projects = @group.projects #.paginate(:page => params[:project_page], :per_page => 10) + @projects = @group.projects.by_visibilities(['open']) end def new diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index ebf5ed17c..2c8046360 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -9,11 +9,11 @@ class PagesController < ApplicationController def tour_inside @entries = case params[:id] - when 'projects' + when 'builds' %w(repo builds monitoring) when 'sources' %w(source history annotation edit) - when 'builds' + when 'projects' %w(control git tracker) end render "pages/tour/tour-inside", :layout => 'tour' diff --git a/app/controllers/platforms/maintainers_controller.rb b/app/controllers/platforms/maintainers_controller.rb new file mode 100644 index 000000000..386e48ae4 --- /dev/null +++ b/app/controllers/platforms/maintainers_controller.rb @@ -0,0 +1,15 @@ +# -*- encoding : utf-8 -*- +class Platforms::MaintainersController < ApplicationController + before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => [:index] if APP_CONFIG['anonymous_access'] + load_and_authorize_resource :platform + + def index + @maintainers = BuildList::Package.actual.by_platform(@platform) + .order('lower(name) ASC, length(name) ASC') + .includes(:project) + @maintainers = @maintainers.where('name ILIKE ?', "%#{params[:q]}%") if params[:q].present? + @maintainers = @maintainers.paginate(:page => params[:page]) + end + +end diff --git a/app/controllers/platforms/platforms_controller.rb b/app/controllers/platforms/platforms_controller.rb index 5a948d06e..a35f64b5c 100644 --- a/app/controllers/platforms/platforms_controller.rb +++ b/app/controllers/platforms/platforms_controller.rb @@ -2,7 +2,7 @@ class Platforms::PlatformsController < Platforms::BaseController before_filter :authenticate_user! - skip_before_filter :authenticate_user!, :only => [:advisories] if APP_CONFIG['anonymous_access'] + skip_before_filter :authenticate_user!, :only => [:advisories, :members, :show] if APP_CONFIG['anonymous_access'] load_and_authorize_resource autocomplete :user, :uname @@ -86,33 +86,23 @@ class Platforms::PlatformsController < Platforms::BaseController end def remove_members - all_user_ids = params['user_remove'].inject([]) {|a, (k, v)| a << k if v.first == '1'; a} - all_user_ids.each do |uid| - Relation.by_target(@platform).where(:actor_id => uid, :actor_type => 'User').each{|r| r.destroy} - end + user_ids = params[:user_remove] ? + params[:user_remove].map{ |k, v| k if v.first == '1' }.compact : [] + User.where(:id => user_ids).each{ |user| @platform.remove_member(user) } redirect_to members_platform_path(@platform) end def remove_member - u = User.find(params[:member_id]) - Relation.by_actor(u).by_target(@platform).each{|r| r.destroy} - + User.where(:id => params[:member_id]).each{ |user| @platform.remove_member(user) } redirect_to members_platform_path(@platform) end def add_member - if params[:member_id].present? - member = User.find(params[:member_id]) - if @platform.relations.exists?(:actor_id => member.id, :actor_type => member.class.to_s) or @platform.owner == member - flash[:warning] = t('flash.platform.members.already_added', :name => member.uname) + if member = User.where(:id => params[:member_id]).first + if @platform.add_member(member) + flash[:notice] = t('flash.platform.members.successfully_added', :name => member.uname) else - rel = @platform.relations.build(:role => 'admin') - rel.actor = member - if rel.save - flash[:notice] = t('flash.platform.members.successfully_added', :name => member.uname) - else - flash[:error] = t('flash.platform.members.error_in_adding', :name => member.uname) - end + flash[:error] = t('flash.platform.members.error_in_adding', :name => member.uname) end end redirect_to members_platform_url(@platform) diff --git a/app/controllers/platforms/product_build_lists_controller.rb b/app/controllers/platforms/product_build_lists_controller.rb index bed67a54b..64bf45541 100644 --- a/app/controllers/platforms/product_build_lists_controller.rb +++ b/app/controllers/platforms/product_build_lists_controller.rb @@ -23,7 +23,7 @@ class Platforms::ProductBuildListsController < Platforms::BaseController end def destroy - if @product_build_list.destroy + if @product_build_list.destroy flash[:notice] = t('flash.product_build_list.delete') else flash[:error] = t('flash.product_build_list.delete_error') diff --git a/app/controllers/platforms/products_controller.rb b/app/controllers/platforms/products_controller.rb index 020866a1c..7d97915ba 100644 --- a/app/controllers/platforms/products_controller.rb +++ b/app/controllers/platforms/products_controller.rb @@ -1,7 +1,8 @@ # -*- encoding : utf-8 -*- class Platforms::ProductsController < Platforms::BaseController before_filter :authenticate_user! - + skip_before_filter :authenticate_user!, :only => [:index, :show] if APP_CONFIG['anonymous_access'] + load_and_authorize_resource :platform load_and_authorize_resource :product, :through => :platform diff --git a/app/controllers/platforms/repositories_controller.rb b/app/controllers/platforms/repositories_controller.rb index 7e617e7d5..7afc34a21 100644 --- a/app/controllers/platforms/repositories_controller.rb +++ b/app/controllers/platforms/repositories_controller.rb @@ -1,9 +1,11 @@ # -*- encoding : utf-8 -*- class Platforms::RepositoriesController < Platforms::BaseController before_filter :authenticate_user! + skip_before_filter :authenticate_user!, :only => [:index, :show, :projects_list] if APP_CONFIG['anonymous_access'] load_and_authorize_resource :platform load_and_authorize_resource :repository, :through => :platform, :shallow => true + before_filter :set_members, :only => [:edit, :update] def index @repositories = @repositories.paginate(:page => params[:page]) @@ -14,6 +16,46 @@ class Platforms::RepositoriesController < Platforms::BaseController @projects = @projects.search(params[:query]).search_order if params[:query].present? end + def edit + end + + def update + if @repository.update_attributes( + :description => params[:repository][:description], + :publish_without_qa => (params[:repository][:publish_without_qa] || @repository.publish_without_qa) + ) + flash[:notice] = I18n.t("flash.repository.updated") + redirect_to platform_repository_path(@platform, @repository) + else + flash[:error] = I18n.t("flash.repository.update_error") + flash[:warning] = @repository.errors.full_messages.join('. ') + render :action => :edit + end + end + + def remove_members + user_ids = params[:user_remove] ? + params[:user_remove].map{ |k, v| k if v.first == '1' }.compact : [] + User.where(:id => user_ids).each{ |user| @repository.remove_member(user) } + redirect_to edit_platform_repository_path(@platform, @repository) + end + + def remove_member + User.where(:id => params[:member_id]).each{ |user| @repository.remove_member(user) } + redirect_to edit_platform_repository_path(@platform, @repository) + end + + def add_member + if member = User.where(:id => params[:member_id]).first + if @repository.add_member(member) + flash[:notice] = t('flash.repository.members.successfully_added', :name => member.uname) + else + flash[:error] = t('flash.repository.members.error_in_adding', :name => member.uname) + end + end + redirect_to edit_platform_repository_path(@platform, @repository) + end + def new @repository = Repository.new @platform_id = params[:platform_id] @@ -94,4 +136,10 @@ class Platforms::RepositoriesController < Platforms::BaseController redirect_to platform_repository_path(@platform, @repository), :notice => t('flash.repository.project_removed') end + protected + + def set_members + @members = @repository.members.order('name') + end + end diff --git a/app/controllers/projects/build_lists_controller.rb b/app/controllers/projects/build_lists_controller.rb index 9045a49ed..742530992 100644 --- a/app/controllers/projects/build_lists_controller.rb +++ b/app/controllers/projects/build_lists_controller.rb @@ -5,9 +5,9 @@ class Projects::BuildListsController < Projects::BaseController before_filter :authenticate_user!, :except => CALLBACK_ACTIONS before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS - skip_before_filter :authenticate_user!, :only => [:show, :index, :search] if APP_CONFIG['anonymous_access'] + skip_before_filter :authenticate_user!, :only => [:show, :index, :search, :log] if APP_CONFIG['anonymous_access'] - before_filter :find_build_list, :only => [:show, :publish, :cancel, :update] + before_filter :find_build_list, :only => [:show, :publish, :cancel, :update, :log] before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build, :circle_build] load_and_authorize_resource :project, :only => NESTED_ACTIONS @@ -44,15 +44,18 @@ class Projects::BuildListsController < Projects::BaseController def create notices, errors = [], [] - @platform = Platform.includes(:repositories).find params[:build_list][:save_to_platform_id] + @repository = Repository.find params[:build_list][:save_to_repository_id] + @platform = @repository.platform - @repository = @project.repositories.where(:id => @platform.repository_ids).first + params[:build_list][:save_to_platform_id] = @platform.id + params[:build_list][:auto_publish] = false unless @repository.publish_without_qa? - params[:build_list][:save_to_repository_id] = @repository.id - params[:build_list][:auto_publish] = false if @platform.released + + build_for_platforms = Repository.select(:platform_id). + where(:id => params[:build_list][:include_repos]).group(:platform_id).map(&:platform_id) Arch.where(:id => params[:arches]).each do |arch| - Platform.main.where(:id => params[:build_for_platforms]).each do |build_for_platform| + Platform.main.where(:id => build_for_platforms).each do |build_for_platform| @build_list = @project.build_lists.build(params[:build_list]) @build_list.commit_hash = @project.repo.commits(@build_list.project_version.match(/^latest_(.+)/).to_a.last || @build_list.project_version).first.id if @build_list.project_version @@ -101,6 +104,15 @@ class Projects::BuildListsController < Projects::BaseController end end + def log + @log = `tail -n #{params[:load_lines].to_i} #{Rails.root + 'public' + @build_list.fs_log_path}` + @log = t("layout.build_lists.log.not_available") unless $?.success? + + respond_to do |format| + format.json { render :json => { :log => @log, :building => @build_list.build_started? } } + end + end + def publish_build if params[:status].to_i == 0 # ok @build_list.published diff --git a/app/controllers/projects/comments_controller.rb b/app/controllers/projects/comments_controller.rb index 3b36c3456..003ed166e 100644 --- a/app/controllers/projects/comments_controller.rb +++ b/app/controllers/projects/comments_controller.rb @@ -4,7 +4,7 @@ class Projects::CommentsController < Projects::BaseController load_and_authorize_resource :project before_filter :find_commentable before_filter :find_or_build_comment - load_and_authorize_resource + load_and_authorize_resource #:through => :commentable include CommentsHelper @@ -14,7 +14,8 @@ class Projects::CommentsController < Projects::BaseController redirect_to project_commentable_path(@project, @commentable) else flash[:error] = I18n.t("flash.comment.save_error") - render :action => 'new' + flash[:warning] = @comment.errors.full_messages.join('. ') + redirect_to project_commentable_path(@project, @commentable) end end @@ -22,13 +23,12 @@ class Projects::CommentsController < Projects::BaseController end def update - if @comment.update_attributes(params[:comment]) - flash[:notice] = I18n.t("flash.comment.saved") - redirect_to project_commentable_path(@project, @commentable) + status, message = if @comment.update_attributes(params[:comment]) + [200, view_context.markdown(@comment.body)] else - flash[:error] = I18n.t("flash.comment.save_error") - render :action => 'new' + [400, view_context.local_alert(@comment.errors.full_messages.join('. '))] end + render :inline => message, :status => status end def destroy diff --git a/app/controllers/projects/git/trees_controller.rb b/app/controllers/projects/git/trees_controller.rb index 45dae2aa1..d19e4fe31 100644 --- a/app/controllers/projects/git/trees_controller.rb +++ b/app/controllers/projects/git/trees_controller.rb @@ -1,6 +1,7 @@ # -*- encoding : utf-8 -*- class Projects::Git::TreesController < Projects::Git::BaseController - before_filter lambda{redirect_to @project if params[:treeish] == @project.default_branch and params[:path].blank?}, :only => 'show' + before_filter lambda{redirect_to @project if params[:treeish] == @project.default_branch and params[:path].blank?}, :only => :show + skip_before_filter :set_branch_and_tree, :only => :archive def show @tree = @tree / @path if @path.present? @@ -9,13 +10,18 @@ class Projects::Git::TreesController < Projects::Git::BaseController end def archive - @commit = @project.repo.log(@treeish, nil, :max_count => 1).first + format = params[:format] + if (@treeish =~ /^#{@project.owner.uname}-#{@project.name}-/) && !(@treeish =~ /[\s]+/) && (format =~ /^(zip|tar\.gz)$/) + @treeish = @treeish.gsub(/^#{@project.owner.uname}-#{@project.name}-/, '') + @commit = @project.repo.commits(@treeish, 1).first + end raise Grit::NoSuchPathError unless @commit - name = "#{@project.owner.uname}-#{@project.name}#{@project.repo.tags.include?(@treeish) ? "-#{@treeish}" : ''}-#{@commit.id[0..19]}" - fullname = "#{name}.#{params[:format] == 'tar' ? 'tar.gz' : 'zip'}" + name = "#{@project.owner.uname}-#{@project.name}-#{@treeish}" + fullname = "#{name}.#{format == 'zip' ? 'zip' : 'tar.gz'}" file = Tempfile.new fullname, 'tmp' - system("cd #{@project.path}; git archive --format=#{params[:format]} --prefix=#{name}/ #{@treeish} #{params[:format] == 'tar' ? ' | gzip -9' : ''} > #{file.path}") + system("cd #{@project.path}; git archive --format=#{format} --prefix=#{name}/ #{@treeish} #{format == 'zip' ? '' : ' | gzip -9'} > #{file.path}") file.close - send_file file.path, :disposition => 'attachment', :type => "application/#{params[:format] == 'tar' ? 'x-tar-gz' : 'zip'}", :filename => fullname + send_file file.path, :disposition => 'attachment', :type => "application/#{format == 'zip' ? 'zip' : 'x-tar-gz'}", :filename => fullname end + end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index bc1d28d77..dbf9e5841 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -1,33 +1,31 @@ # -*- encoding : utf-8 -*- class Projects::IssuesController < Projects::BaseController - NON_RESTFUL_ACTION = [:create_label, :update_label, :destroy_label, :search_collaborators] + NON_RESTFUL_ACTION = [:create_label, :update_label, :destroy_label] before_filter :authenticate_user! skip_before_filter :authenticate_user!, :only => [:index, :show] if APP_CONFIG['anonymous_access'] load_resource :project load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id, :only => [:show, :edit, :update, :destroy, :new, :create, :index] before_filter :load_and_authorize_label, :only => NON_RESTFUL_ACTION - layout 'application' + layout false, :only => [:update, :search_collaborators] def index(status = 200) @labels = params[:labels] || [] @issues = @project.issues.includes(:pull_request).includes(:assignee, :user) @issues = @issues.where(:assignee_id => current_user.id) if @is_assigned_to_me = params[:filter] == 'to_me' @issues = @issues.joins(:labels).where(:labels => {:name => @labels}) unless @labels == [] - + # Using mb_chars for correct transform to lowercase ('Русский Текст'.downcase => "Русский Текст") @issues = @issues.where('issues.title ILIKE ?', "%#{params[:search_issue].mb_chars.downcase}%") if params[:search_issue] - @opened_issues = @issues.not_closed_or_merged.count - @closed_issues = @issues.closed_or_merged.count - @issues = @issues + + @opened_issues, @closed_issues = @issues.not_closed_or_merged.count, @issues.closed_or_merged.count if params[:status] == 'closed' - @issues = @issues.closed_or_merged - @status = params[:status] + @issues, @status = @issues.closed_or_merged, params[:status] else - @issues = @issues.not_closed_or_merged - @status = 'open' + @issues, @status = @issues.not_closed_or_merged, 'open' end - @issues = @issues.order('issues.serial_id desc').uniq.paginate :per_page => 10, :page => params[:page] + @issues = @issues.includes(:assignee, :user).order('issues.serial_id desc').uniq + .paginate :per_page => 10, :page => params[:page] if status == 200 render 'index', :layout => request.xhr? ? 'issues' : 'application' else @@ -36,17 +34,14 @@ class Projects::IssuesController < Projects::BaseController end def new - @issue = @project.issues.new end def create @assignee_uname = params[:assignee_uname] - @issue = @project.issues.new(params[:issue]) @issue.user_id = current_user.id if @issue.save @issue.subscribe_creator(current_user.id) - flash[:notice] = I18n.t("flash.issue.saved") redirect_to project_issues_path(@project) else @@ -60,41 +55,39 @@ class Projects::IssuesController < Projects::BaseController end def update + @issue.labelings.destroy_all if params[:update_labels] if params[:issue] && status = params[:issue][:status] - action = 'status' @issue.set_close(current_user) if status == 'closed' @issue.set_open if status == 'open' - status = 200 if @issue.save - render :partial => action, :status => (status || 500), :layout => false + render :partial => 'status', :status => (@issue.save ? 200 : 400) elsif params[:issue] - @issue.labelings.destroy_all if params[:issue][:labelings_attributes] # FIXME - status = 200 if @issue.update_attributes(params[:issue]) - render :nothing => true, :status => (status || 500), :layout => false + status, message = if @issue.update_attributes(params[:issue]) + [200, view_context.markdown(@issue.body)] + else + [400, view_context.local_alert(@issue.errors.full_messages.join('. '))] + end + render :inline => message, :status => status else - render :nothing => true, :status => 200, :layout => false + render :nothing => true, :status => 200 end end def destroy @issue.destroy - flash[:notice] = t("flash.issue.destroyed") redirect_to root_path end def create_label - status = @project.labels.create!(:name => params[:name], :color => params[:color]) ? 200 : 500 - index(status) + index(@project.labels.create!(:name => params[:name], :color => params[:color]) ? 200 : 500) end def update_label - status = @label.update_attributes(:name => params[:name], :color => params[:color]) ? 200 : 500 - index(status) + index(@label.update_attributes(:name => params[:name], :color => params[:color]) ? 200 : 500) end def destroy_label - status = (@label && @label_destroy) ? 200 : 500 - index(status) + index((@label && @label_destroy) ? 200 : 500) end def search_collaborators @@ -102,7 +95,7 @@ class Projects::IssuesController < Projects::BaseController 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 :partial => 'search_collaborators', :layout => false + render :partial => 'search_collaborators' end private diff --git a/app/controllers/projects/projects_controller.rb b/app/controllers/projects/projects_controller.rb index 30b452dc4..b3f0906e2 100644 --- a/app/controllers/projects/projects_controller.rb +++ b/app/controllers/projects/projects_controller.rb @@ -5,9 +5,7 @@ class Projects::ProjectsController < Projects::BaseController def index @projects = Project.accessible_by(current_ability, :membered) - # @projects = @projects.search(params[:query]).search_order if params[:query].present? - #puts prepare_list(@projects).inspect respond_to do |format| format.html { @projects = @projects.recent.paginate(:page => params[:page], :per_page => 25) } format.json { @projects = prepare_list(@projects) } @@ -39,6 +37,7 @@ class Projects::ProjectsController < Projects::BaseController end def update + params[:project].delete(:maintainer_id) if params[:project][:maintainer_id].blank? if @project.update_attributes(params[:project]) flash[:notice] = t('flash.project.saved') redirect_to @project @@ -86,6 +85,18 @@ class Projects::ProjectsController < Projects::BaseController redirect_to projects_path end + def autocomplete_maintainers + term, limit = params[:term], params[:limit] || 10 + items = User.member_of_project(@project) + .where("users.name ILIKE ? OR users.uname ILIKE ?", "%#{term}%", "%#{term}%") + .limit(limit).map { |u| {:value => u.fullname, :label => u.fullname, :id => u.id} } + render :json => items + end + + def preview + render :inline => view_context.markdown(params[:text]), :layout => false + end + protected def prepare_list(projects) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index bea77c80a..f4bf67e81 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -5,7 +5,6 @@ class SearchController < ApplicationController def index params[:type] ||= 'all' - params[:type] = 'projects' unless current_user case params[:type] when 'all' find_collection('projects') diff --git a/app/controllers/users/profile_controller.rb b/app/controllers/users/profile_controller.rb index 1e7c18869..25aa15e31 100644 --- a/app/controllers/users/profile_controller.rb +++ b/app/controllers/users/profile_controller.rb @@ -3,8 +3,6 @@ class Users::ProfileController < Users::BaseController autocomplete :user, :uname def show - @groups = @user.groups.uniq - @platforms = @user.platforms.paginate(:page => params[:platform_page], :per_page => 10) - @projects = @user.projects.paginate(:page => params[:project_page], :per_page => 10) + @projects = @user.projects.by_visibilities(['open']) end end diff --git a/app/controllers/users/register_requests_controller.rb b/app/controllers/users/register_requests_controller.rb index 234ef39ab..a21249cdb 100644 --- a/app/controllers/users/register_requests_controller.rb +++ b/app/controllers/users/register_requests_controller.rb @@ -8,6 +8,7 @@ class Users::RegisterRequestsController < ApplicationController end def create + params[:register_request][:language] = I18n.locale if params[:register_request] RegisterRequest.create(params[:register_request]) render :thanks end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 26ed84332..970715327 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -39,4 +39,19 @@ module ApplicationHelper else object.class.name end end + + def markdown(text) + unless @redcarpet + html_options = {filter_html: true, hard_wrap: true, with_toc_data: true} + options = {no_intraemphasis: true, tables: true, fenced_code_blocks: true, autolink: true, strikethrough: true, lax_html_blocks: true} + @redcarpet = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new(html_options), options) + end + @redcarpet.render(text).html_safe + end + + def local_alert(text, type = 'error') + html = "
#{text}" + html << link_to('×', '#', :class => 'close close-alert', 'data-dismiss' => 'alert') + html << '
' + end end diff --git a/app/helpers/build_lists_helper.rb b/app/helpers/build_lists_helper.rb index 6509b3cb5..367ba78e7 100644 --- a/app/helpers/build_lists_helper.rb +++ b/app/helpers/build_lists_helper.rb @@ -43,4 +43,22 @@ module BuildListsHelper build_list.project_version end end + + def container_url + "http://#{request.host_with_port}/downloads#{@build_list.container_path}".html_safe + end + + def build_list_log_url(log_type) + "http://#{request.host_with_port}/#{@build_list.fs_log_path(log_type)}".html_safe + end + + def log_reload_time_options + t = I18n.t("layout.build_lists.log.reload_times").map { |i| i.reverse } + + options_for_select(t, t.first).html_safe + end + + def log_reload_lines_options + options_for_select([100, 200, 500, 1000, 1500, 2000], 1000).html_safe + end end diff --git a/app/mailers/feedback_mailer.rb b/app/mailers/feedback_mailer.rb index 1d38f4e4d..272849100 100644 --- a/app/mailers/feedback_mailer.rb +++ b/app/mailers/feedback_mailer.rb @@ -4,6 +4,7 @@ class FeedbackMailer < ActionMailer::Base default :to => FBM_CONFIG['email'], :cc => FBM_CONFIG['cc'], :bcc => FBM_CONFIG['bcc'] + default_url_options.merge!(:protocol => 'https') if APP_CONFIG['mailer_https_url'] include Resque::Mailer # send email async diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index e19cb799f..057c735cc 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -2,6 +2,7 @@ class UserMailer < ActionMailer::Base default :from => APP_CONFIG['do-not-reply-email'] + default_url_options.merge!(:protocol => 'https') if APP_CONFIG['mailer_https_url'] include Resque::Mailer # send email async @@ -36,8 +37,24 @@ class UserMailer < ActionMailer::Base end end + def build_list_notification(build_list, user) + I18n.locale = user.language if user.language + @user, @build_list = user, build_list + + subject = "[№ #{build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")}] " + subject << (build_list.project ? build_list.project.name_with_owner : t("layout.projects.unexisted_project")) + subject << " - #{build_list.human_status} " + subject << I18n.t("notifications.subjects.for_arch", :arch => @build_list.arch.name) + mail(:to => user.email, :subject => subject) do |format| + format.html + end + end + def invite_approve_notification(register_request) + I18n.locale = register_request.language if register_request.language @register_request = register_request - mail :to => register_request.email, :subject => I18n.t("notifications.subjects.invite_approve_notification") + mail :to => register_request.email, :subject => I18n.t("notifications.subjects.invite_approve_notification") do |format| + format.html + end end end diff --git a/app/models/ability.rb b/app/models/ability.rb index ad2c9205c..bbeff275b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -13,18 +13,22 @@ class Ability @user = user # Shared rights between guests and registered users - can :show, Project, :visibility => 'open' - can :archive, Project, :visibility => 'open' + can [:show, :archive], Project, :visibility => 'open' can :read, Issue, :project => {:visibility => 'open'} can :read, PullRequest, :project => {:visibility => 'open'} can :search, BuildList - can [:read, :everything], BuildList, :project => {:visibility => 'open'} + can [:read, :log, :everything], BuildList, :project => {:visibility => 'open'} can :read, ProductBuildList#, :product => {:platform => {:visibility => 'open'}} # double nested hash don't work can :read, Advisory - can(:advisories, Platform) {APP_CONFIG['anonymous_access']} + # Core callbacks can [:publish_build, :status_build, :pre_build, :post_build, :circle_build, :new_bbdt], BuildList + # Platforms block + can [:show, :members, :advisories], Platform, :visibility == 'open' + can [:read, :projects_list], Repository, :platform => {:visibility => 'open'} + can :read, Product, :platform => {:visibility => 'open'} + if user.guest? # Guest rights # can [:new, :create], RegisterRequest else # Registered user rights @@ -56,52 +60,49 @@ class Ability can [:read, :archive], Project, :owner_type => 'Group', :owner_id => user.group_ids can([:read, :membered], Project, read_relations_for('projects')) {|project| local_reader? project} can(:write, Project) {|project| local_writer? project} # for grack - can([:update, :sections, :manage_collaborators], Project) {|project| local_admin? project} + can([:update, :sections, :manage_collaborators, :autocomplete_maintainers], Project) {|project| local_admin? project} can(:fork, Project) {|project| can? :read, project} can(:fork, Project) {|project| project.owner_type == 'Group' and can? :update, project.owner} can(:destroy, Project) {|project| owner? project} can(:destroy, Project) {|project| project.owner_type == 'Group' and project.owner.actors.exists?(:actor_type => 'User', :actor_id => user.id, :role => 'admin')} can :remove_user, Project - can [:autocomplete_base_project_name, :autocomplete_head_project_name], Project + can [:preview, :autocomplete_base_project_name, :autocomplete_head_project_name], Project - can [:read, :owned, :everything], BuildList, :user_id => user.id - can [:read, :related, :everything], BuildList, :project => {:owner_type => 'User', :owner_id => user.id} - can [:read, :related, :everything], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids} - can([:read, :everything], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project} + can [:read, :log, :owned, :everything], BuildList, :user_id => user.id + can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'User', :owner_id => user.id} + can [:read, :log, :related, :everything], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids} + can([:read, :log, :everything], BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project} can([:create, :update], BuildList) {|build_list| build_list.project.is_package && can?(:write, build_list.project)} can(:publish, BuildList) do |build_list| - build_list.can_publish? and build_list.save_to_platform.released ? local_admin?(build_list.save_to_platform) : can?(:write, build_list.project) + build_list.can_publish? and build_list.save_to_repository.publish_without_qa ? can?(:write, build_list.project) : local_admin?(build_list.save_to_platform) end can(:reject_publish, BuildList) do |build_list| - build_list.can_reject_publish? and build_list.save_to_platform.released and local_admin?(build_list.save_to_platform) + build_list.can_reject_publish? and not build_list.save_to_repository.publish_without_qa and local_admin?(build_list.save_to_platform) end can(:cancel, BuildList) {|build_list| build_list.can_cancel? && can?(:write, build_list.project)} - can [:read], Advisory - - can [:read, :members], Platform, :visibility => 'open' can [:read, :owned, :related, :members], Platform, :owner_type => 'User', :owner_id => user.id can [:read, :related, :members], Platform, :owner_type => 'Group', :owner_id => user.group_ids can([:read, :related, :members], Platform, read_relations_for('platforms')) {|platform| local_reader? platform} can([:update, :members], Platform) {|platform| local_admin? platform} can([:destroy, :members, :add_member, :remove_member, :remove_members] , Platform) {|platform| owner?(platform) || local_admin?(platform) } - can [:autocomplete_user_uname, :read_advisories, :advisories], Platform + can [:autocomplete_user_uname], Platform can([:failed_builds_list, :create], MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && mass_build.platform.main? } can(:cancel, MassBuild) {|mass_build| (owner?(mass_build.platform) || local_admin?(mass_build.platform)) && !mass_build.stop_build && mass_build.platform.main?} - can [:read, :projects_list], Repository, :platform => {:visibility => 'open'} can [:read, :projects_list], Repository, :platform => {:owner_type => 'User', :owner_id => user.id} can [:read, :projects_list], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} can([:read, :projects_list], 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([:create, :edit, :update, :destroy, :projects_list, :add_project, :remove_project], Repository) {|repository| local_admin? repository.platform} + can([:remove_members, :remove_member, :add_member], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)} + can([:add_project, :remove_project], Repository) {|repository| repository.members.exists?(:id => user.id)} can(:clear, Platform) {|platform| local_admin?(platform) && platform.personal?} - can([:change_visibility, :settings, :destroy], Repository) {|repository| owner? repository.platform} + can([:change_visibility, :settings, :destroy, :edit, :update], Repository) {|repository| owner? repository.platform} can([:create, :destroy], KeyPair) {|key_pair| owner?(key_pair.repository.platform) || local_admin?(key_pair.repository.platform)} - can :read, Product, :platform => {:visibility => 'open'} can :read, Product, :platform => {:owner_type => 'User', :owner_id => user.id, :platform_type => 'main'} can :read, Product, :platform => {:owner_type => 'Group', :owner_id => user.group_ids, :platform_type => 'main'} can(:read, Product, read_relations_for('products', 'platforms')) {|product| product.platform.main?} @@ -116,20 +117,21 @@ class Ability can :read, Issue, :project => {:owner_type => 'User', :owner_id => user.id} can :read, Issue, :project => {:owner_type => 'Group', :owner_id => user.group_ids} can(:read, Issue, read_relations_for('issues', 'projects')) {|issue| can? :read, issue.project rescue nil} - can([:merge, :create], Issue) {|issue| can? :write, issue.project} + can(:merge, Issue) {|issue| can? :write, issue.project} + can(:create, Issue) {|issue| can? :read, issue.project} can([:update, :destroy], Issue) {|issue| issue.user_id == user.id or local_admin?(issue.project)} cannot :manage, Issue, :project => {:has_issues => false} # switch off issues can(:autocomplete_base_project, Issue, read_relations_for('issues', 'projects')) {|issue| can? :read, issue.project rescue nil} can(:create, Comment) {|comment| can? :read, comment.project} - can(:update, Comment) {|comment| comment.user_id == user.id or local_admin?(comment.project || comment.commentable.project)} + can(:update, Comment) {|comment| comment.user == user or comment.project.owner == user or local_admin?(comment.project)} cannot :manage, Comment, :commentable_type => 'Issue', :commentable => {:project => {:has_issues => false}} # switch off issues end # Shared cannot rights for all users (registered, admin) cannot :destroy, Platform, :platform_type => 'personal' - cannot [:create, :destroy, :add_project, :remove_project], Repository, :platform => {:platform_type => 'personal'} + cannot [:create, :destroy, :edit, :update, :add_project, :remove_project], Repository, :platform => {:platform_type => 'personal'} cannot :clear, Platform, :platform_type => 'main' cannot :destroy, Issue diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 07cc933b8..2f540422c 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -79,7 +79,7 @@ class BuildList < ActiveRecord::Base scope :recent, order("#{table_name}.updated_at DESC") scope :for_status, lambda {|status| where(:status => status) } scope :for_user, lambda { |user| where(:user_id => user.id) } - scope :for_platform, lambda { |platform| where(:build_for_platform_id => platform.id) } + scope :for_platform, lambda { |platform| where(:build_for_platform_id => platform) } scope :by_mass_build, lambda { |mass_build| where(:mass_build_id => mass_build) } scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) } scope :scoped_to_save_platform, lambda {|pl_id| where(:save_to_platform_id => pl_id) } @@ -125,7 +125,11 @@ class BuildList < ActiveRecord::Base end end - after_transition :on => :published, :do => :set_version_and_tag + after_transition :on => :published, :do => [:set_version_and_tag, :actualize_packages] + + after_transition :on => [:published, :fail_publish, :build_error], :do => :notify_users + after_transition :on => :build_success, :do => :notify_users, + :unless => lambda { |build_list| build_list.auto_publish? } event :place_build do transition :waiting_for_response => :build_pending, :if => lambda { |build_list| @@ -201,6 +205,19 @@ class BuildList < ActiveRecord::Base save end + def actualize_packages + ActiveRecord::Base.transaction do + old_pkgs = self.class.where(:project_id => self.project_id) + .where(:save_to_repository_id => self.save_to_repository_id) + .for_platform(self.build_for_platform_id) + .scoped_to_arch(self.arch_id) + .for_status(BUILD_PUBLISHED) + .recent.limit(2).last.packages # packages from previous build_list + old_pkgs.update_all(:actual => false) + self.packages.update_all(:actual => true) + end + end + #TODO: Share this checking on product owner. def can_cancel? [BUILD_PENDING, BuildServer::PLATFORM_PENDING].include?(status) && bs_id @@ -211,7 +228,7 @@ class BuildList < ActiveRecord::Base end def can_reject_publish? - can_publish? and save_to_platform.released + can_publish? and not save_to_repository.publish_without_qa end @@ -266,6 +283,10 @@ class BuildList < ActiveRecord::Base I18n.t("layout.build_lists.human_duration", {:hours => (duration/3600).to_i, :minutes => (duration%3600/60).to_i}) end + def fs_log_path(log_type = :build) + container_path? ? "downloads/#{container_path}/log/#{project.name}/#{log_type.to_s}.log" : nil + end + def in_work? status == BuildServer::BUILD_STARTED #[WAITING_FOR_RESPONSE, BuildServer::BUILD_PENDING, BuildServer::BUILD_STARTED].include?(status) @@ -273,6 +294,22 @@ class BuildList < ActiveRecord::Base protected + def notify_users + unless mass_build_id + users = [] + if project # find associated users + users = project.all_members. + select{ |user| user.notifier.can_notify? && user.notifier.new_associated_build? } + end + if user.notifier.can_notify? && user.notifier.new_build? + users = users | [user] + end + users.each do |user| + UserMailer.build_list_notification(self, user).deliver + end + end + end # notify_users + def delete_container if can_cancel? BuildServer.delete_build_list bs_id diff --git a/app/models/build_list/package.rb b/app/models/build_list/package.rb index b491c0d13..ce894412b 100644 --- a/app/models/build_list/package.rb +++ b/app/models/build_list/package.rb @@ -7,6 +7,18 @@ class BuildList::Package < ActiveRecord::Base attr_accessible :fullname, :name, :release, :version - validates :build_list_id, :project_id, :platform_id, :fullname, :package_type, :name, :release, :version, :presence => true + validates :build_list_id, :project_id, :platform_id, :fullname, + :package_type, :name, :release, :version, + :presence => true validates :package_type, :inclusion => PACKAGE_TYPES + + # Fetches only actual (last publised) packages. + scope :actual, where(:actual => true) + scope :by_platform, lambda {|platform| where(:platform_id => platform) } + scope :by_name, lambda {|name| where(:name => name) } + scope :by_package_type, lambda {|type| where(:package_type => type) } + + def assignee + project.maintainer + end end diff --git a/app/models/build_list_observer.rb b/app/models/build_list_observer.rb index bc54e088d..2d12b85d0 100644 --- a/app/models/build_list_observer.rb +++ b/app/models/build_list_observer.rb @@ -16,5 +16,6 @@ class BuildListObserver < ActiveRecord::Observer end end end - end + end # before_update + end diff --git a/app/models/mass_build.rb b/app/models/mass_build.rb index 4fa18720e..8383151f7 100644 --- a/app/models/mass_build.rb +++ b/app/models/mass_build.rb @@ -45,8 +45,8 @@ class MassBuild < ActiveRecord::Base end def cancel_all - self.update_attribute(:stop_build, true) - self.build_lists.find_each(:batch_size => 100) do |bl| + update_column(:stop_build, true) + build_lists.find_each(:batch_size => 100) do |bl| bl.cancel end end diff --git a/app/models/platform.rb b/app/models/platform.rb index c0b13246a..49a84e898 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -28,13 +28,12 @@ class Platform < ActiveRecord::Base before_create :xml_rpc_create, :unless => lambda {Thread.current[:skip]} before_destroy :xml_rpc_destroy - after_update :freeze_platform + after_update :freeze_platform_and_update_repos + after_update :update_owner_relation after_create lambda { symlink_directory unless hidden? } after_destroy lambda { remove_symlink_directory unless hidden? } - after_update :update_owner_relation - scope :search_order, order("CHAR_LENGTH(name) ASC") scope :search, lambda {|q| where("name ILIKE ?", "%#{q.to_s.strip}%")} scope :by_visibilities, lambda {|v| where(:visibility => v)} @@ -74,6 +73,14 @@ class Platform < ActiveRecord::Base build_path(name) end + def add_member(member, role = 'admin') + Relation.add_member(member, self, role) + end + + def remove_member(member) + Relation.remove_member(member, self) + end + def symlink_path Rails.root.join("public", "downloads", name) end @@ -131,11 +138,11 @@ class Platform < ActiveRecord::Base end def change_visibility - if !self.hidden? - self.update_attribute(:visibility, 'hidden') + if !hidden? + update_attributes(:visibility => 'hidden') remove_symlink_directory else - self.update_attribute(:visibility, 'open') + update_attributes(:visibility => 'open') symlink_directory end end @@ -230,10 +237,11 @@ class Platform < ActiveRecord::Base end later :xml_rpc_clone, :loner => true, :queue => :clone_build - def freeze_platform + def freeze_platform_and_update_repos if released_changed? && released == true - result = BuildServer.freeze(name) + result = BuildServer.freeze(name) raise "Failed freeze platform #{name} with code #{result}" if result != BuildServer::SUCCESS + repositories.update_all(:publish_without_qa => false) end end end diff --git a/app/models/project.rb b/app/models/project.rb index e772a5808..132e6a6c9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -5,6 +5,7 @@ class Project < ActiveRecord::Base NAME_REGEXP = /[a-zA-Z0-9_\-\+\.]+/ belongs_to :owner, :polymorphic => true, :counter_cache => :own_projects_count + belongs_to :maintainer, :class_name => "User" has_many :issues, :dependent => :destroy has_many :pull_requests, :dependent => :destroy, :foreign_key => 'base_project_id' @@ -22,12 +23,15 @@ class Project < ActiveRecord::Base has_many :packages, :class_name => "BuildList::Package", :dependent => :destroy has_and_belongs_to_many :advisories # should be without :dependent => :destroy - validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, :format => {:with => /^#{NAME_REGEXP}$/, :message => I18n.t("activerecord.errors.project.uname")} + validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, + :presence => true, + :format => {:with => /^#{NAME_REGEXP}$/, :message => I18n.t("activerecord.errors.project.uname")} validates :owner, :presence => true + validates :maintainer_id, :presence => true, :unless => :new_record? validates :visibility, :presence => true, :inclusion => {:in => VISIBILITIES} validate { errors.add(:base, :can_have_less_or_equal, :count => MAX_OWN_PROJECTS) if owner.projects.size >= MAX_OWN_PROJECTS } - attr_accessible :name, :description, :visibility, :srpm, :is_package, :default_branch, :has_issues, :has_wiki + attr_accessible :name, :description, :visibility, :srpm, :is_package, :default_branch, :has_issues, :has_wiki, :maintainer_id attr_readonly :name, :owner_id, :owner_type scope :recent, order("name ASC") @@ -36,9 +40,19 @@ class Project < ActiveRecord::Base scope :by_name, lambda {|name| where('projects.name ILIKE ?', name)} scope :by_visibilities, lambda {|v| where(:visibility => v)} scope :opened, where(:visibility => 'open') - scope :addable_to_repository, lambda { |repository_id| where("projects.id NOT IN (SELECT project_to_repositories.project_id FROM project_to_repositories WHERE (project_to_repositories.repository_id = #{ repository_id }))") } + scope :package, where(:is_package => true) + scope :addable_to_repository, lambda { |repository_id| where %Q( + projects.id NOT IN ( + SELECT + ptr.project_id + FROM + project_to_repositories AS ptr + WHERE (ptr.repository_id = #{ repository_id }) + ) + ) } - after_create :attach_to_personal_repository + before_create :set_maintainer + after_save :attach_to_personal_repository has_ancestry :orphan_strategy => :rootify #:adopt not available yet @@ -63,8 +77,12 @@ class Project < ActiveRecord::Base name end + def all_members + members | (owner_type == 'User' ? [owner] : owner.members) + end + def members - collaborators + groups.map(&:members).flatten + collaborators | groups.map(&:members).flatten end def platforms @@ -121,6 +139,8 @@ class Project < ActiveRecord::Base c.parent_id = id c.owner = new_owner c.updated_at = nil; c.created_at = nil # :id = nil + # Hack to call protected method :) + c.send :set_maintainer c.save end end @@ -129,6 +149,10 @@ class Project < ActiveRecord::Base I18n.t("layout.projects.human_average_build_time", {:hours => (average_build_time/3600).to_i, :minutes => (average_build_time%3600/60).to_i}) end + def formatted_average_build_time + "%02d:%02d" % [average_build_time / 3600, average_build_time % 3600 / 60] + end + def xml_rpc_create(repository) result = BuildServer.create_project name, repository.platform.name, repository.name, path if result == BuildServer::SUCCESS @@ -150,6 +174,16 @@ class Project < ActiveRecord::Base protected def attach_to_personal_repository - repositories << self.owner.personal_repository if !repositories.exists?(:id => self.owner.personal_repository) + owner_rep = self.owner.personal_repository + if is_package + repositories << owner_rep unless repositories.exists?(:id => owner_rep) + else + repositories.delete owner_rep + end end + + def set_maintainer + self.maintainer_id = (owner_type == 'User') ? self.owner_id : self.owner.owner_id + end + end diff --git a/app/models/register_request.rb b/app/models/register_request.rb index 972dc1029..b40e7f4b4 100644 --- a/app/models/register_request.rb +++ b/app/models/register_request.rb @@ -1,16 +1,17 @@ # -*- encoding : utf-8 -*- class RegisterRequest < ActiveRecord::Base + default_scope order('created_at ASC') scope :rejected, where(:rejected => true) scope :approved, where(:approved => true) scope :unprocessed, where(:approved => false, :rejected => false) + validates :email, :presence => true, :uniqueness => {:case_sensitive => false}, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } + # before_create :generate_token before_update :invite_approve_notification - validates :email, :presence => true, :uniqueness => {:case_sensitive => false}, :format => { :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i } - def approve update_attributes(:approved => true, :rejected => false) end @@ -26,7 +27,7 @@ class RegisterRequest < ActiveRecord::Base end def invite_approve_notification - if approved_changed? and approved == true + if approved_changed? && approved? generate_token UserMailer.invite_approve_notification(self).deliver end diff --git a/app/models/relation.rb b/app/models/relation.rb index 5e5e5bb5e..76829a849 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -10,9 +10,9 @@ class Relation < ActiveRecord::Base before_validation :add_default_role scope :by_user_through_groups, lambda {|u| where("actor_type = 'User' AND actor_id = ? OR actor_type = 'Group' AND actor_id IN (?)", u.id, u.group_ids)} - scope :by_actor, lambda {|obj| {:conditions => ['actor_id = ? AND actor_type = ?', obj.id, obj.class.to_s]}} - scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}} - scope :by_role, lambda {|role| {:conditions => ['role = ?', role]}} + scope :by_actor, lambda {|obj| where(:actor_id => obj.id, :actor_type => obj.class.to_s)} + scope :by_target, lambda {|tar| where(:target_id => tar.id, :target_type => tar.class.to_s)} + scope :by_role, lambda {|role| where(:role => role)} def self.create_with_role(actor, target, role) r = self.new @@ -22,6 +22,20 @@ class Relation < ActiveRecord::Base r.save end + def self.add_member(member, target, role) + if target.relations.exists?(:actor_id => member.id, :actor_type => member.class.to_s) || @platform.try(:owner) == member + true + else + rel = target.relations.build(:role => role) + rel.actor = member + rel.save + end + end + + def self.remove_member(member, target) + Relation.by_actor(member).by_target(target).each{|r| r.destroy} + end + protected def add_default_role diff --git a/app/models/repository.rb b/app/models/repository.rb index 491d219e8..cd28fe6ec 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -2,6 +2,11 @@ class Repository < ActiveRecord::Base belongs_to :platform + + has_many :relations, :as => :target, :dependent => :destroy + has_many :actors, :as => :target, :class_name => 'Relation', :dependent => :destroy + has_many :members, :through => :actors, :source => :actor, :source_type => 'User' + has_many :project_to_repositories, :dependent => :destroy, :validate => true has_many :projects, :through => :project_to_repositories has_one :key_pair, :dependent => :destroy @@ -14,7 +19,7 @@ class Repository < ActiveRecord::Base before_create :xml_rpc_create, :unless => lambda {Thread.current[:skip]} before_destroy :xml_rpc_destroy, :unless => lambda {Thread.current[:skip]} - attr_accessible :name, :description + attr_accessible :name, :description, :publish_without_qa attr_readonly :name, :platform_id def base_clone(attrs = {}) @@ -38,6 +43,14 @@ class Repository < ActiveRecord::Base end end + def add_member(member, role = 'admin') + Relation.add_member(member, self, role) + end + + def remove_member(member) + Relation.remove_member(member, self) + end + class << self def build_stub(platform) rep = Repository.new diff --git a/app/models/subscribe.rb b/app/models/subscribe.rb index 8b6907d98..8145286e1 100644 --- a/app/models/subscribe.rb +++ b/app/models/subscribe.rb @@ -34,7 +34,7 @@ class Subscribe < ActiveRecord::Base def self.set_subscribe_to_commit(options, status) if subscribe = Subscribe.where(options).first - subscribe.update_attribute(:status, status) + subscribe.update_attributes(:status => status) else Subscribe.create(options.merge(:status => status)) end diff --git a/app/models/user.rb b/app/models/user.rb index c807cfbac..027175d5c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -52,6 +52,10 @@ class User < ActiveRecord::Base scope :admin, where(:role => 'admin') scope :real, where(:role => ['', nil]) + scope :member_of_project, lambda {|item| + where "#{table_name}.id IN (?)", item.members.map(&:id).uniq + } + after_create lambda { self.create_notifier } before_create :ensure_authentication_token diff --git a/app/presenters/comment_presenter.rb b/app/presenters/comment_presenter.rb index bab0b3343..687316cc5 100644 --- a/app/presenters/comment_presenter.rb +++ b/app/presenters/comment_presenter.rb @@ -9,7 +9,7 @@ class CommentPresenter < ApplicationPresenter @user = comment.user @options = opts - @content = simple_format(@comment.body, {}, :sanitize => true).html_safe + @content = @comment.body end def expandable? @@ -40,7 +40,8 @@ class CommentPresenter < ApplicationPresenter res = [] if controller.can? :update, @comment - res << link_to(t("layout.edit"), ep).html_safe + res << link_to(t('layout.comments.md_cheatsheet_header'), '#md_help', 'data-toggle' => 'modal') + res << link_to(t("layout.edit"), ep, :id => "comment-#{comment.id}", :class => "edit_comment").html_safe end if controller.can? :delete, @comment res << link_to(t("layout.delete"), dp, :method => "delete", diff --git a/app/presenters/maintainer_presenter.rb b/app/presenters/maintainer_presenter.rb new file mode 100644 index 000000000..186266061 --- /dev/null +++ b/app/presenters/maintainer_presenter.rb @@ -0,0 +1,43 @@ +class MaintainerPresenter < ApplicationPresenter + + attr_reader :package, :package_link, :package_name, :package_type, + :package_version, :package_release, :package_version_release, + :package_updated_at + attr_reader :maintainer, :maintainer_fullname, :maintainer_email, + :maintainer_link, :maintainer_mail_link + delegate :package_type, :to => :package + + [:name, :version, :release, :updated_at].each do |meth| + define_method "package_#{meth}" do + @package.send meth + end + end + + [:fullname, :email].each do |meth| + define_method "maintainer_#{meth}" do + @maintainer.send meth + end + end + + def initialize(package, opts = {}) + @package = package + @maintainer = package.try(:assignee) + end + + def package_link + link_to @package.name, @package.project + end + + def package_version_release + "#{@package.version}-#{@package.release}" + end + + def maintainer_link + link_to @maintainer.fullname, @maintainer + end + + def maintainer_email_link + mail_to @maintainer.email, @maintainer.email, :encode => "javascript" + end + +end diff --git a/app/views/advisories/_list.html.haml b/app/views/advisories/_list.html.haml index c19ff66b9..5f3ea4f52 100644 --- a/app/views/advisories/_list.html.haml +++ b/app/views/advisories/_list.html.haml @@ -1,4 +1,4 @@ -%table#myTable.tablesorter.advisories{:cellspacing => "0", :cellpadding => "0"} +%table#myTable.tablesorter.advisories.static-search{:cellspacing => "0", :cellpadding => "0"} %thead %tr %th.th1= t("activerecord.attributes.advisory.advisory_id") diff --git a/app/views/groups/profile/show.html.haml b/app/views/groups/profile/show.html.haml index 8525cffe3..a67abf0e8 100644 --- a/app/views/groups/profile/show.html.haml +++ b/app/views/groups/profile/show.html.haml @@ -3,7 +3,7 @@ %h3= @group.name %h4= t("activerecord.attributes.group.description") + ":" %p= @group.description - %h4= t("layout.groups.projects_list") + ":" + %h4= t("layout.groups.public_projects_list") + ":" %p - @projects.each do |project| = link_to project.name, project diff --git a/app/views/layouts/tour.html.haml b/app/views/layouts/tour.html.haml index 1f0a486bc..c02d56642 100644 --- a/app/views/layouts/tour.html.haml +++ b/app/views/layouts/tour.html.haml @@ -34,6 +34,7 @@ .a= link_to t('layout.logout'), destroy_user_session_path, :method => :delete - else .user + .profile= link_to t("layout.devise.shared_links.sign_up"), new_register_request_path .profile= link_to t("layout.devise.shared_links.sign_in"), new_user_session_path .right .both @@ -55,4 +56,4 @@ bg_alpha : 0.5, piro_scroll : true //pirobox always positioned at the center of the page }); - }); \ No newline at end of file + }); diff --git a/app/views/platforms/base/_sidebar.html.haml b/app/views/platforms/base/_sidebar.html.haml index 35916f520..eda999eeb 100644 --- a/app/views/platforms/base/_sidebar.html.haml +++ b/app/views/platforms/base/_sidebar.html.haml @@ -10,13 +10,16 @@ = link_to t("layout.platforms.about"), platform_path(@platform) %li{:class => (contr == :repositories) ? 'active' : ''} = link_to t("layout.repositories.list_header"), platform_repositories_path(@platform) + - if can? :show, @platform + %li{:class => (act == :index && contr == :maintainers) ? 'active' : nil} + = link_to t("layout.platforms.maintainers"), platform_maintainers_path(@platform) - if can? :edit, @platform %li{:class => (contr == :mass_builds && [:index, :create].include?(act)) ? 'active' : ''} = link_to t("layout.platforms.mass_build"), platform_mass_builds_path(@platform) - if can? :read, @platform.products.build %li{:class => (contr == :products) ? 'active' : ''} = link_to t("layout.products.list_header"), platform_products_path(@platform) - - if can? :read_advisories, @platform + - if can? :advisories, @platform %li{:class => (contr == :platforms and act == :advisories) ? 'active' : ''} = link_to t("layout.advisories.list_header"), advisories_platform_path(@platform) - if can? :update, @platform diff --git a/app/views/platforms/maintainers/_list.html.haml b/app/views/platforms/maintainers/_list.html.haml new file mode 100644 index 000000000..6effeb766 --- /dev/null +++ b/app/views/platforms/maintainers/_list.html.haml @@ -0,0 +1,25 @@ +%table#myTable.tablesorter.platform-maintainers.static-search{:cellspacing => "0", :cellpadding => "0"} + %thead + %tr + %th.centered= t("activerecord.attributes.maintainer.package_name") + %th.centered= t("activerecord.attributes.maintainer.package_type") + %th.centered= t("activerecord.attributes.maintainer.version") + %th.centered{:colspan => 2}= t("activerecord.models.maintainer") + %th.centered= t("activerecord.attributes.maintainer.updated_at") + %tr.search + -# TODO: change filter to Backbone.js + %th{:colspan => 6, :rowspan => 1} + = form_tag platform_maintainers_path(@platform), :method => :get do |f| + = text_field_tag('q', params[:q], :placeholder => t("layout.maintainers.search_by_package"), :class => params[:q].present? ? 'black' : 'gray') + %input{:type => 'submit', :value => t("layout.search.header")} + = button_to t('layout.clear'), {:action => :index} , :method => :get + + %tbody + - MaintainerPresenter.present_collection(@maintainers) do |pr| + %tr{:class => cycle("odd", "even")} + %td= pr.package_link + %td= pr.package_type + %td= pr.package_version_release + %td= pr.maintainer_link + %td= pr.maintainer_email_link.html_safe + %td= pr.package_updated_at diff --git a/app/views/platforms/maintainers/assignee.js.erb b/app/views/platforms/maintainers/assignee.js.erb new file mode 100644 index 000000000..b36cdfcac --- /dev/null +++ b/app/views/platforms/maintainers/assignee.js.erb @@ -0,0 +1 @@ +doHandleAbfAssignee(<%= @ret.to_json.html_safe %>); diff --git a/app/views/platforms/maintainers/index.html.haml b/app/views/platforms/maintainers/index.html.haml new file mode 100644 index 000000000..fd52033d6 --- /dev/null +++ b/app/views/platforms/maintainers/index.html.haml @@ -0,0 +1,7 @@ +-set_meta_tags :title => [title_object(@platform), t('layout.maintainers.list_header')] +-# FIXME: no these 'base' links! += render 'platforms/base/submenu' += render 'platforms/base/sidebar' += render :partial => 'list', :object => @maintainers += will_paginate @maintainers + diff --git a/app/views/platforms/platforms/members.html.haml b/app/views/platforms/platforms/members.html.haml index 712747edf..89bd0bedd 100644 --- a/app/views/platforms/platforms/members.html.haml +++ b/app/views/platforms/platforms/members.html.haml @@ -2,42 +2,9 @@ = render 'submenu' = render 'sidebar' -= form_tag remove_members_platform_path(@platform), :id => 'members_form', :method => :post do - %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} - %thead - %tr - - if can? :remove_members, @platform - %th - \  - %th - = t("layout.collaborators.members") - - if can? :remove_member, @platform - %th.buttons - = t("layout.remove") - %tbody - - @members.select{|u| u != @platform.owner}.each_with_index do |user, num| - %tr{:id => "admin-table-members-row#{num}", :class => cycle(:odd, :even)} - - if can? :remove_members, @platform - %td - %span.niceCheck-main{:id => "niceCheckbox#{num}", :style => "background-position: 0px 0px; "} - = check_box_tag "user_remove[#{user.id}][]" - %td - .img - = image_tag avatar_url(user) - .forimg= link_to user.fullname, user_path(user) - - if can? :remove_member, @platform - %td.buttons - = link_to remove_member_platform_path(@platform, :member_id => user.id), :method => :delete, :confirm => t("layout.confirm") do - %span.delete   - - if can? :remove_memvers, @platforms - = submit_tag t("layout.delete"), :class => 'button' - .both - -- if can? :add_member, @platform - .hr.top - = form_tag add_member_platform_path(@platform) do - .admin-search - = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_users_path, :id_element => '#member_id_field' - = hidden_field_tag 'member_id', nil, :id => 'member_id_field' - = submit_tag t("layout.add"), :class => 'button' - .both += render "shared/members_table", + :remove_members_path => remove_members_platform_path(@platform), + :remove_member_path => remove_member_platform_path(@platform), + :add_member_path => add_member_platform_path(@platform), + :members => @members.select{|u| u != @platform.owner}, + :editable_object => @platform diff --git a/app/views/platforms/repositories/_form.html.haml b/app/views/platforms/repositories/_form.html.haml index 2539464d6..065e99e24 100644 --- a/app/views/platforms/repositories/_form.html.haml +++ b/app/views/platforms/repositories/_form.html.haml @@ -1,9 +1,13 @@ -.leftlist= f.label :name, t("activerecord.attributes.repository.name"), :class => :label -.rightlist= f.text_field :name, :class => 'text_field' +- unless ['edit', 'update'].include? controller.action_name + .leftlist= f.label :name, t("activerecord.attributes.repository.name"), :class => :label + .rightlist= f.text_field :name, :class => 'text_field' .leftlist= f.label :description, t("activerecord.attributes.repository.description"), :class => :label .rightlist= f.text_field :description, :class => 'text_field' +.leftlist= f.label :publish_without_qa, t("activerecord.attributes.repository.publish_without_qa"), :class => :label +.rightlist= f.check_box :publish_without_qa, :class => 'check_box' + .both .button_block diff --git a/app/views/platforms/repositories/_list.html.haml b/app/views/platforms/repositories/_list.html.haml index 69ef431d5..a1a4371f7 100644 --- a/app/views/platforms/repositories/_list.html.haml +++ b/app/views/platforms/repositories/_list.html.haml @@ -3,7 +3,7 @@ %tr %th.th1= t("activerecord.attributes.repository.name") %th.th2= t("layout.repositories.projects") - %th= t("layout.delete") + %th= t("activerecord.attributes.repository.publish_without_qa") %tbody - @repositories.each do |repository| %tr{:class => cycle("odd", "even")} @@ -11,7 +11,5 @@ = link_to repository.name, platform_repository_path(@platform, repository) %td = repository.projects.count - %td.buttons - - if can? :destroy, repository - = link_to platform_repository_path(@platform, repository), :method => :delete, :confirm => t("layout.repositories.confirm_delete") do - %span.delete   \ No newline at end of file + %td + = repository.publish_without_qa \ No newline at end of file diff --git a/app/views/platforms/repositories/edit.html.haml b/app/views/platforms/repositories/edit.html.haml new file mode 100644 index 000000000..5a9f1b232 --- /dev/null +++ b/app/views/platforms/repositories/edit.html.haml @@ -0,0 +1,16 @@ +-set_meta_tags :title => [title_object(@repository), t('layout.repositories.edit')] += render 'submenu' += render 'sidebar' + +%h3.fix= "#{t("layout.repositories.about")}: #{@repository.name}" + += form_for @repository, :url => platform_repository_path(@platform, @repository), :html => { :class => :form } do |f| + = render "form", :f => f +%br + += render "shared/members_table", + :remove_members_path => remove_members_platform_repository_path(@platform, @repository), + :remove_member_path => remove_member_platform_repository_path(@platform, @repository), + :add_member_path => add_member_platform_repository_path(@platform, @repository), + :members => @members, + :editable_object => @repository diff --git a/app/views/platforms/repositories/show.html.haml b/app/views/platforms/repositories/show.html.haml index 5540913de..5bc491bfe 100644 --- a/app/views/platforms/repositories/show.html.haml +++ b/app/views/platforms/repositories/show.html.haml @@ -2,10 +2,15 @@ = render 'submenu' = render 'sidebar' -%h3.fix= "#{t("layout.repositories.about")}: #{@repository.name}" +%h3 #{t("layout.repositories.about")} #{@repository.name} %p= @repository.description +.buttons_block + - if can? :update, @repository + = link_to image_tag("code.png", :alt => t("layout.edit")) + " " + t("layout.edit"), edit_platform_repository_path(@platform, @repository), :class => "button" + - if can? :destroy, @repository + = link_to image_tag("x.png", :alt => t("layout.delete")) + " " + t("layout.delete"), platform_repository_path(@platform, @repository), :method => "delete", :class => "button", :confirm => t("layout.repositories.confirm_delete") %br %br %h3.fix= t("layout.projects.list_header") diff --git a/app/views/projects/base/_repo_block.html.haml b/app/views/projects/base/_repo_block.html.haml index f301d63b3..d8788f420 100644 --- a/app/views/projects/base/_repo_block.html.haml +++ b/app/views/projects/base/_repo_block.html.haml @@ -6,8 +6,9 @@ =image_tag 'zip.png', :alt => 'ZIP' %b.caret %ul.dropdown-menu - %li=link_to "tar.gz", archive_path(project, @treeish, 'tar') - %li=link_to "zip", archive_path(project, @treeish, 'zip') + - file_name = "#{@project.owner.uname}-#{@project.name}-#{@commit.id}" + %li=link_to "tar.gz", archive_path(project, file_name, 'tar.gz') + %li=link_to "zip", archive_path(project, file_name, 'zip') = text_field_tag :url, git_repo_url(project.git_repo_name), :class => 'name', :spellcheck => 'false', :readonly => true .git_help ? diff --git a/app/views/projects/build_lists/_build_list.html.haml b/app/views/projects/build_lists/_build_list.html.haml index 520023631..a5daa5d67 100644 --- a/app/views/projects/build_lists/_build_list.html.haml +++ b/app/views/projects/build_lists/_build_list.html.haml @@ -2,9 +2,14 @@ %td= link_to (build_list.bs_id.present? ? build_list.bs_id : t("layout.build_lists.bs_id_not_set")), build_list %td = build_list.human_status + %br - if BuildList::HUMAN_STATUSES[build_list.status].in? [:build_pending, :build_started, :build_publish] %time.js-relative-date{:datetime => build_list.updated_at.strftime("%FT%T%:z"), :title => build_list.updated_at.strftime("%F %T")} = build_list.updated_at.strftime "%F %T" + - if build_list.build_started? && ((build_list.project.average_build_time || 0) > 0) + \/ + %time + = build_list.project.formatted_average_build_time - if build_list.project.present? %td= link_to build_list.project.name_with_owner, build_list.project %td= build_list_version_link(build_list) diff --git a/app/views/projects/build_lists/_include_repos.html.haml b/app/views/projects/build_lists/_include_repos.html.haml index 11ef89f97..504b73cee 100644 --- a/app/views/projects/build_lists/_include_repos.html.haml +++ b/app/views/projects/build_lists/_include_repos.html.haml @@ -1,4 +1,4 @@ - platform.repositories.each do |repo| .both - = check_box_tag "build_list[include_repos][]", repo.id, repo.name == 'main' || @project.repositories.map(&:id).include?(repo.id), :id => "include_repos_#{repo.id}", :save_to_platform_id => platform.id, :rep_name => repo.name + = check_box_tag "build_list[include_repos][]", repo.id, repo.name == 'main' || @project.repositories.map(&:id).include?(repo.id), :id => "include_repos_#{repo.id}", :rep_name => repo.name = label_tag "include_repos_#{repo.id}", repo.name diff --git a/app/views/projects/build_lists/_log.html.haml b/app/views/projects/build_lists/_log.html.haml new file mode 100644 index 000000000..a2cd38da0 --- /dev/null +++ b/app/views/projects/build_lists/_log.html.haml @@ -0,0 +1,148 @@ +.hr +%a{:name => 'log'} +.log-wrapper + .log-header + .text-wrap + = link_to({:anchor => :log}, {:id => 'log_anchor'}) do + %h3= t("layout.build_lists.log.build_log") + %span + .both + .log-body.hidden + .reloader + %table.options + %tr.top + %td.first + = label_tag :word_wrap do + = check_box_tag :word_wrap + = t("layout.word_wrap") + %td.last{ :class => @build_list.build_started? ? nil : :hidden } + = label_tag :autoreload do + = check_box_tag :autoreload, true, @build_list.build_started? + = t("layout.build_lists.log.autoreload") + = select_tag :reload_interval, log_reload_time_options + %tr.bottom + %td.first + = link_to t("layout.build_lists.log.download"), build_list_log_url(:build), :id => :log_url + %td.last{ :class => @build_list.build_started? ? nil : :hidden } + = label_tag :load_lines do + = raw t("layout.build_lists.log.load_lines", :count => select_tag(:load_lines, log_reload_lines_options)) + .both + %textarea.log{ :readonly => :readonly, :wrap => 'off', + :data => { :url => log_build_list_path(@build_list), :log_type => :build } } + = t("layout.build_lists.log.not_available") + +:javascript + $(function() { + (function() { + var $wrapper = $('div.log-wrapper'); + var $logBody = $wrapper.children('div.log-body').first(); + var $logCont = $logBody.children('.log').first(); + + var logUrl = $logCont.data('url'); + var $logHead = $wrapper.children('div.log-header').first(); + + var $trigger = $logHead.children('span').first(); + var $autoload = $('#autoreload'); + + var state = $logBody.is(':visible'); + var t = null; // timer + var first_open = true; + + if (state) { + $trigger.removeClass('closed'); + $wrapper.removeClass('inactive') + .addClass('active'); + } else { + $trigger.addClass('closed'); + $logBody.addClass('hidden'); + $wrapper.removeClass('active') + .addClass('inactive'); + } + + function getLineHeight(element){ + var temp = document.createElement(element.nodeName); + temp.setAttribute("style","margin:0px;padding:0px;font-family:"+element.style.fontFamily+";font-size:"+element.style.fontSize); + temp.innerHTML = "test"; + temp = element.parentNode.appendChild(temp); + var ret = temp.clientHeight; + temp.parentNode.removeChild(temp); + return ret; + } + + var loadLog = function() { + $.ajax({ + url: logUrl, + type: "GET", + dataType: 'json', + data: $logCont.data(), + beforeSend: function( xhr ) { + var token = $('meta[name="csrf-token"]').attr('content'); + if (token) xhr.setRequestHeader('X-CSRF-Token', token); + }, + success: function(data, textStatus, jqXHR) { + var l = $logCont[0]; + var vScroll = l.scrollTop; + var hScroll = l.scrollLeft; + var onBottom = Math.abs((l.clientHeight + vScroll - l.scrollHeight)) < getLineHeight(l); + + $logCont.text(data.log); + + $logCont.scrollLeft(hScroll); + $logCont.scrollTop((onBottom || first_open) ? l.scrollHeight - l.clientHeight : vScroll); + first_open = false; + if (!data.building) $autoload.attr({'checked': false}).trigger('change'); + } + }); + } + + var reloadChange = function() { + if ($(this).is(':checked')) { + first_open = true; + loadLog(); + $logCont.scrollTop($logCont[0].scrollHeight - $logCont[0].clientHeight); + t = setInterval(function() { + loadLog(); + }, $('#reload_interval').val()); + } else { + clearInterval(t); + } + } + + var toggleHandler = function() { + state = !state; + // if log opened + if (state) { + if ($autoload.is(':checked')) { + $autoload.trigger('change'); + } + } else { + clearInterval(t); + } + $logBody.slideToggle('slow') + .toggleClass('hidden'); + $logHead.toggleClass('active inactive'); + $trigger.toggleClass('closed'); + + window.location.href = $('a#log_anchor').attr('href'); + } + + $wrapper.on('click', '.log-header > span', toggleHandler); + $autoload.on('change', reloadChange); + + $('#word_wrap').on('change', function() { + $logCont.attr({'wrap': ($(this).is(':checked')) ? 'soft' : 'off'}); + }); + + $('#reload_interval').on('change', function() { + clearInterval(t); + if ($autoload.is(':checked')) { + t = setInterval($(this).val()); + } + }); + $('#load_lines').on('change', function() { + $logCont.data('load_lines', $(this).val()); + }).trigger('change'); + loadLog(); + + })(); + }); diff --git a/app/views/projects/build_lists/new.html.haml b/app/views/projects/build_lists/new.html.haml index ccda36113..6a0c3fad6 100644 --- a/app/views/projects/build_lists/new.html.haml +++ b/app/views/projects/build_lists/new.html.haml @@ -6,12 +6,13 @@ - Platform.main.each do |pl| - if pl.repository_ids.size > 0 .both - = check_box_tag "build_for_platforms[]", pl.id, (params[:build_for_platforms]||[]).include?(pl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{pl.id}", :'data-released' => pl.released ? 1 : 0 - = label_tag "bpls_#{pl.id}", pl.name - .offset25{:style => 'padding-left: 25px'}= render 'include_repos', :platform => pl + =# check_box_tag "build_for_platforms[]", pl.id, (params[:build_for_platforms]||[]).include?(pl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{pl.id}" + =# label_tag "bpls_#{pl.id}", pl.name + %div{:id => "build_for_pl_#{pl.id}", :class => 'build_for_pl'}= pl.name + .offset25= render 'include_repos', :platform => pl %section.right - %h3= t("activerecord.attributes.build_list.save_to_platform") - .lineForm= f.select :save_to_platform_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.platform.id]} + %h3= t("activerecord.attributes.build_list.save_to_repository") + .lineForm= f.select :save_to_repository_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.id, {:publish_without_qa => r.publish_without_qa? ? 1 : 0, :platform_id => r.platform.id}]} %h3= t("activerecord.attributes.build_list.project_version") .lineForm= f.select :project_version, versions_for_group_select(@project), :selected => params[:build_list].try(:fetch, :project_version) || "latest_" + @project.default_branch %h3= t("activerecord.attributes.build_list.arch") diff --git a/app/views/projects/build_lists/show.html.haml b/app/views/projects/build_lists/show.html.haml index 5ea60692a..b0f7f7f22 100644 --- a/app/views/projects/build_lists/show.html.haml +++ b/app/views/projects/build_lists/show.html.haml @@ -13,7 +13,6 @@ link_to("#{@build_list.save_to_platform.name}/#{@build_list.save_to_repository.name}", [@build_list.save_to_platform, @build_list.save_to_repository])] - elsif @build_list.container_path.present? - - container_url = "http://#{request.host_with_port}/downloads#{@build_list.container_path}" = link_to container_url, container_url .both @@ -125,8 +124,15 @@ var r = new Rosa.Routers.BuildListsAdvisoriesRouter(); }); - = submit_tag t("layout.publish"), :confirm => t("layout.confirm"), :name => 'publish' if @build_list.can_publish? && can?(:publish, @build_list) - = submit_tag t("layout.reject_publish"), :confirm => t("layout.confirm"), :name => 'reject_publish' if @build_list.can_reject_publish? && can?(:reject_publish, @build_list) + - if BuildList::HUMAN_STATUSES[@build_list.status].in? [:build_started, :build_error, :success] + = render :partial => 'projects/build_lists/log' + + + - if (can_publish = @build_list.can_publish? && can?(:publish, @build_list)) + .hr + = submit_tag t("layout.publish"), :confirm => t("layout.confirm"), :name => 'publish' if can_publish + - if @build_list.can_reject_publish? && can?(:reject_publish, @build_list) + = submit_tag t("layout.reject_publish"), :confirm => t("layout.confirm"), :name => 'reject_publish' .hr %h3= t("layout.build_lists.items_header") diff --git a/app/views/projects/comments/_add.html.haml b/app/views/projects/comments/_add.html.haml index e78fe2ac3..e0ab3eb65 100644 --- a/app/views/projects/comments/_add.html.haml +++ b/app/views/projects/comments/_add.html.haml @@ -1,4 +1,5 @@ #open-comment.comment.view + =link_to t('layout.comments.md_cheatsheet_header'), '#md_help', 'data-toggle' => 'modal', :style => 'float:right;' %h3.tmargin0= t("layout.comments.new_header") - if Comment.issue_comment?(commentable.class) - new_path = project_issue_comments_path(project, commentable) @@ -10,7 +11,7 @@ - subscribe_path = is_subscribed ? unsubscribe_commit_path(project, commentable) : subscribe_commit_path(project, commentable) = form_for :comment, :url => new_path, :method => :post, :html => { :class => :form } do |f| - = render "projects/comments/form", :f => f + = render "projects/comments/form", :f => f, :id => 'new' .comment-left = t("layout.comments.notifications_are") %span.bold diff --git a/app/views/projects/comments/_body.html.haml b/app/views/projects/comments/_body.html.haml new file mode 100644 index 000000000..4b57f37b1 --- /dev/null +++ b/app/views/projects/comments/_body.html.haml @@ -0,0 +1,13 @@ +%ul.nav.nav-tabs#md_tabs + %li + %a{"data-toggle" => "tab", :href => "##{id}_edit"}=t 'layout.edit' + %li + %a{"data-toggle" => "tab", :href => "##{id}_preview"}=t 'layout.preview' + +.tab-content + .tab-pane.active{:id => "#{id}_edit"} + =f.text_area :body, :cols => 80, :id => "#{id}_edit_input" + =hidden_field_tag :body_dup, nil, :name => 'text', :id => "#{id}_edit_input_dup" + .tab-pane{:id => "#{id}_preview"} + .formatted.cm-s-default.md_and_cm + diff --git a/app/views/projects/comments/_form.html.haml b/app/views/projects/comments/_form.html.haml index d7814ddad..f37286e42 100644 --- a/app/views/projects/comments/_form.html.haml +++ b/app/views/projects/comments/_form.html.haml @@ -1,2 +1,2 @@ -.wrapper= f.text_area :body, :cols => 80 -.comment-right= submit_tag t("layout.save") \ No newline at end of file +=render 'projects/comments/body', :f => f, :id => id +.comment-right= submit_tag t("layout.save") diff --git a/app/views/projects/comments/_list.html.haml b/app/views/projects/comments/_list.html.haml index 8fc7adb93..e74842d82 100644 --- a/app/views/projects/comments/_list.html.haml +++ b/app/views/projects/comments/_list.html.haml @@ -3,4 +3,12 @@ %h3#block-list= t("layout.comments.comments_header") - list.each do |comment| - CommentPresenter.present(comment, :project => project, :commentable => commentable) do |presenter| - = render 'shared/feed_message', :presenter => presenter \ No newline at end of file + = render 'shared/feed_message', :presenter => presenter + #open-comment.comment.hidden{:class => "comment-#{comment.id}"} + %h3.tmargin0= t("layout.comments.edit_header") + = form_for comment, :url => project_commentable_comment_path(project, commentable, comment), :html => { :class => 'form edit_comment' } do |f| + = render "projects/comments/form", :f => f, :id => "edit_#{comment.id}" + .comment-left + =link_to t('layout.cancel'), '#', :id => "comment-#{comment.id}", :class => 'cancel_edit_comment button' + .both += render "projects/comments/markdown_help" diff --git a/app/views/projects/comments/_markdown_help.html.haml b/app/views/projects/comments/_markdown_help.html.haml new file mode 100644 index 000000000..28cf08dd8 --- /dev/null +++ b/app/views/projects/comments/_markdown_help.html.haml @@ -0,0 +1,86 @@ +#md_help.modal.hidden + .modal-header + %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} × + %h3#myModalLabel=t('layout.comments.md_cheatsheet_header') + .modal-body + .mod + .col + %h3=t 'layout.comments.md_cheatsheet.format_text' + %p=t 'layout.comments.md_cheatsheet.headers' + %pre + -[1, 3, 6].each do |i| + ="#{'#'*i} This is an tag" + %p=t 'layout.comments.md_cheatsheet.text_styles' + %pre + :preserve + *This text will be italic* + _This will also be italic_ + **This text will be bold** + __This will also be bold__ + .col + %h3=t 'layout.comments.md_cheatsheet.lists' + %p=t 'layout.comments.md_cheatsheet.unordered' + %pre + :preserve + * Item 1 + * Item 2 + * Item 2a + * Item 2b + %p=t 'layout.comments.md_cheatsheet.ordered' + %pre + :preserve + 1. Item 1 + 2. Item 2 + 3. Item 3 + * Item 3a + * Item 3b + .col + %h3=t 'layout.comments.md_cheatsheet.miscellaneous' + %p=t 'layout.comments.md_cheatsheet.images' + %pre + :preserve + ![avatar](/assets/ava.png) + Format: ![Alt Text](url) + %p=t 'layout.comments.md_cheatsheet.links' + %pre + :preserve + https://abf.rosalinux.ru + [ABF](https://abf.rosalinux.ru/) + %p=t 'layout.comments.md_cheatsheet.blockquotes' + %pre + :preserve + As Kanye West said: + + > We're living the future so + > the present is our past. + .both + %hr.bootstrap + %h3=t 'layout.comments.md_cheatsheet.code_examples' + .col + %p + =t 'layout.comments.md_cheatsheet.syntax_highlighting' + %pre + :preserve + ```javascript + function fancyAlert(arg) { + if(arg) { + $.facebox({div:'#foo'}) + } + } + ``` + .col + %p=t 'layout.comments.md_cheatsheet.indent_code' + %pre + :preserve + Here is a Python code example + without syntax highlighting: + + def foo: + if not bar: + return true + .col + %p=t 'layout.comments.md_cheatsheet.inline_code' + %pre + :preserve + I think you should use an + `[addr]` element here instead. diff --git a/app/views/projects/comments/edit.html.haml b/app/views/projects/comments/edit.html.haml index a27158367..0fd4907c1 100644 --- a/app/views/projects/comments/edit.html.haml +++ b/app/views/projects/comments/edit.html.haml @@ -8,4 +8,6 @@ = t("layout.comments.edit_header") .inner = form_for @comment, :url => project_commentable_comment_path(@project, @commentable, @comment), :html => {:class => :form} do |f| - = render "form", :f => f + = render "form", :f => f, :id => "edit_#{@comment.id}" +=hidden_field_tag :preview_url, project_md_preview_path(@project) + diff --git a/app/views/projects/git/commits/show.html.haml b/app/views/projects/git/commits/show.html.haml index 28a008ac9..5cc736823 100644 --- a/app/views/projects/git/commits/show.html.haml +++ b/app/views/projects/git/commits/show.html.haml @@ -14,3 +14,6 @@ = render "projects/comments/list", :list => Comment.for_commit(@commit), :project => @project, :commentable => @commit = render "projects/comments/add", :project => @project, :commentable => @commit if current_user + +=hidden_field_tag :preview_url, project_md_preview_path(@project) + diff --git a/app/views/projects/git/trees/empty.html.haml b/app/views/projects/git/trees/empty.html.haml index 19edecbf5..228170179 100644 --- a/app/views/projects/git/trees/empty.html.haml +++ b/app/views/projects/git/trees/empty.html.haml @@ -30,7 +30,7 @@ %h3= t("layout.projects.git_global_setup") %p %code - = "git config --global user.name #{current_user.fullname}" + = "git config --global user.name '#{current_user.fullname}'" %br/ = "git config --global user.email #{current_user.email}" %br/ diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 72c9f4cc7..01b6a0a1a 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,8 +1,4 @@ -.leftlist= t('activerecord.attributes.issue.title') + ':' -.rightlist= f.text_field :title -.leftlist= t('activerecord.attributes.issue.body') + ':' -.rightlist= f.text_area :body -.both +=render 'title_body', :f => f, :id => 'new' .leftlist= t('activerecord.attributes.issue.assignee') + ':' .rightlist %span#people-span.small-text= t('layout.issues.choose_user_on_left') diff --git a/app/views/projects/issues/_header.html.haml b/app/views/projects/issues/_header.html.haml index 941755b54..3393fb965 100644 --- a/app/views/projects/issues/_header.html.haml +++ b/app/views/projects/issues/_header.html.haml @@ -9,19 +9,13 @@ %span.date=@issue.created_at.to_s(:long) %br/ .both - .fulltext.view.issue_body=@issue.body + .fulltext.view.issue_body.formatted.cm-s-default.md_and_cm=markdown @issue.body .both %br - if can? :update, @issue =link_to t('layout.edit'), '#', :id => 'edit_issue_content', :class => 'button' =form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_form issue', :style => 'display:none;' } do |f| - .leftlist= t('activerecord.attributes.issue.title') + ':' - .rightlist{:style => @issue.pull_request ? 'width: 500px;' : ''} - = f.text_field :title - .leftlist= t('activerecord.attributes.issue.body') + ':' - .rightlist{:style => @issue.pull_request ? 'width: 500px;' : ''} - = f.text_area :body - .both + =render 'projects/issues/title_body', :f => f, :id => 'update' =f.submit t('layout.update'), :id => 'update_issue_content' =link_to t('layout.issues.cancel_button'), '#', :id => 'cancel_edit_issue_content', :class => 'button' -%br \ No newline at end of file +%br diff --git a/app/views/projects/issues/_labels.html.haml b/app/views/projects/issues/_labels.html.haml index 81effb206..ebca53989 100644 --- a/app/views/projects/issues/_labels.html.haml +++ b/app/views/projects/issues/_labels.html.haml @@ -3,10 +3,10 @@ #labels-stock =form_tag project_issues_path(@project), :id => 'filter_labels', :method => :get do - @project.labels.each_with_index do |label, index| - .div-tracker-labels{:id => "label-#{label.name}", :style => @labels.include?(label.name) ? "background-color:##{label.color};color:#FFF" : ''} + .div-tracker-labels{:id => "label-#{label.name.parameterize}", :style => @labels.include?(label.name) ? "background-color:##{label.color};color:#FFF" : ''} .div-label-left .label - .flag{:id => "flag-#{label.name}", :style => "background-color: ##{label.color};"} + .flag{:id => "flag-#{label.name.parameterize}", :style => "background-color: ##{label.color};"} .labeltext=label.name =check_box_tag 'labels[]', label.name, @labels.include?(label.name), :style => 'display:none' .both diff --git a/app/views/projects/issues/_manage_sidebar.html.haml b/app/views/projects/issues/_manage_sidebar.html.haml index 492265b57..9b819e38c 100644 --- a/app/views/projects/issues/_manage_sidebar.html.haml +++ b/app/views/projects/issues/_manage_sidebar.html.haml @@ -27,7 +27,7 @@ .avatar=image_tag avatar_url(@issue.assignee), :alt => 'avatar' .name=@issue.assignee.fullname .both - =link_to(t('layout.issues.label_manage'), '#', :class => "button tmargin10 manage_assignee") if can_manage + =link_to(t('layout.issues.assignee_manage'), '#', :class => "button tmargin10 manage_assignee") if can_manage - if can_manage =form_tag search_collaborators_project_issues_path(@project), :id => 'search_user', :method => :get, :style => @issue.persisted? ? 'display:none' : '' do =tracker_search_field(:search_user, t('layout.issues.search_user')) @@ -47,6 +47,7 @@ .both=hidden_field_tag "flag#{label.id}", label.id, :name => "issue[labelings_attributes][#{label.id}][label_id]" =form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_labels issue'} do |f| .manage_labels + =hidden_field_tag "update_labels", true - @issue.labels.each do |label| =hidden_field_tag "flag#{label.id}", label.id, :name => "issue[labelings_attributes][#{label.id}][label_id]" - else diff --git a/app/views/projects/issues/_title_body.html.haml b/app/views/projects/issues/_title_body.html.haml new file mode 100644 index 000000000..806f9c16a --- /dev/null +++ b/app/views/projects/issues/_title_body.html.haml @@ -0,0 +1,8 @@ +#open-comment.comment.view + %h3.tmargin0= t 'activerecord.attributes.issue.title' + .wrapper= f.text_area :title, :cols => 80, :rows => 1 +#open-comment.comment.view + =link_to t('layout.comments.md_cheatsheet_header'), '#md_help', 'data-toggle' => 'modal', :style => 'float:right;' + %h3.tmargin0= t 'activerecord.attributes.issue.body' + =render 'projects/comments/body', :f => f, :id => id +.both diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index e3c4948e7..a59cc7a10 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -5,3 +5,6 @@ %h3.bpadding10= t("layout.issues.create_header") = form_for :issue, :url => project_issues_path(@project), :html => { :class => 'form issue' } do |f| = render "form", :f => f +=hidden_field_tag :preview_url, project_md_preview_path(@project) += render "projects/comments/markdown_help" + diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 827eded43..6d8b4d06e 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -9,3 +9,6 @@ = render "projects/comments/list", :list => @issue.comments, :project => @project, :commentable => @issue %br = render "projects/comments/add", :project => @project, :commentable => @issue if current_user + +=hidden_field_tag :preview_url, project_md_preview_path(@project) + diff --git a/app/views/projects/projects/_form.html.haml b/app/views/projects/projects/_form.html.haml index 9a7b90ba2..6bf9cb82a 100644 --- a/app/views/projects/projects/_form.html.haml +++ b/app/views/projects/projects/_form.html.haml @@ -6,14 +6,12 @@ .leftlist= f.label :description, t("activerecord.attributes.project.description"), :class => :label .rightlist= f.text_area :description, :class => 'text_field', :cols => 80 .both -- if ['new', 'create'].include? controller.action_name +- if [:new, :create].include? act .leftlist= f.label :owner_id, t("activerecord.attributes.project.owner"), :class => :label .rightlist = label_tag t("activerecord.attributes.project.who_owns.me") - if Group.can_own_project(current_user).count > 0 = radio_button_tag :who_owns, 'me', @who_owns == :me #{}.merge( (@who_owns == :me) ? {:checked => 'checked'} : {} ) - - puts @who_owns.inspect - - puts @who_owns == :group = label_tag t("activerecord.attributes.project.who_owns.group") = radio_button_tag :who_owns, 'group', @who_owns == :group #{}.merge( (@who_owns == :group) ? {:checked => 'checked'} : {} ) -# TODO: Make our own select_box helper with new design, blackjack and bitches! @@ -33,19 +31,33 @@ = image_tag("lock.png") = t("activerecord.attributes.project.visibilities.#{visibility}") .both +.leftlist + \  +.rightlist + .check + %span#niceCheckbox1.niceCheck-main= f.check_box :is_package#, :class => 'niceCheckbox1' + .forcheck= t("activerecord.attributes.project.is_package") + .both +.both - if [:edit, :update].include? act .leftlist= t("activerecord.attributes.project.default_branch") - .rightlist= f.select :default_branch, options_from_collection_for_select(@project.repo.branches, :name, :name, @project.default_branch), :class => 'sel80', :id => 'branch_selector' - .both -- if [:edit, :update].include? act - .leftlist - \  .rightlist - .check - %span#niceCheckbox1.niceCheck-main= f.check_box :is_package#, :class => 'niceCheckbox1' - .forcheck= t("activerecord.attributes.project.is_package") - .both -.both + = f.select :default_branch, + options_from_collection_for_select( @project.repo.branches, + :name, :name, @project.default_branch), + :class => 'sel80', :id => 'branch_selector' + .both + #maintainer_form{:class => @project.is_package ? '' : 'hidden'} + = f.hidden_field :maintainer_id, :value => @project.maintainer_id + .leftlist + = t("activerecord.attributes.project.maintainer") + .rightlist + -# TODO: Maybe use something like Chosen with filter and prepopulated + -# list of potential maintainers? + = autocomplete_field_tag :maintainer_name, @project.maintainer.fullname, + autocomplete_maintainers_path(@project.owner, @project), + :id_element => '#project_maintainer_id', + :placeholder => @project.maintainer.fullname - if [:new, :create].include? act .leftlist= f.label :srpm, t("activerecord.attributes.project.srpm"), :class => :label .rightlist= f.file_field :srpm, :class => 'file_field' @@ -54,3 +66,7 @@ \  .rightlist= submit_tag t("layout.save"), :class => 'button' .both +:javascript + $(function() { + ( new Rosa.Views.ProjectModifyView ).render(); + }); diff --git a/app/views/projects/pull_requests/show.html.haml b/app/views/projects/pull_requests/show.html.haml index 630368568..30b30b592 100644 --- a/app/views/projects/pull_requests/show.html.haml +++ b/app/views/projects/pull_requests/show.html.haml @@ -15,3 +15,5 @@ .pull_status =render 'status' =render 'diff_commits_tabs' if @pull.status != 'already' +=hidden_field_tag :preview_url, project_md_preview_path(@project) + diff --git a/app/views/shared/_feed_message.html.haml b/app/views/shared/_feed_message.html.haml index 42b5d942a..0e7e8e420 100644 --- a/app/views/shared/_feed_message.html.haml +++ b/app/views/shared/_feed_message.html.haml @@ -19,5 +19,5 @@ - if presenter.content? .fulltext{:class => "#{presenter.expandable? ? "hidden" : ''} #{presenter.caption? ? "" : "alone"}", :id => presenter.expandable? ? "content-expand#{item_no}" : ''} - = presenter.content + .cm-s-default.md_and_cm=markdown presenter.content .both diff --git a/app/views/shared/_members_table.html.haml b/app/views/shared/_members_table.html.haml new file mode 100644 index 000000000..2b9ca7177 --- /dev/null +++ b/app/views/shared/_members_table.html.haml @@ -0,0 +1,39 @@ += form_tag remove_members_path, :id => 'members_form', :method => :post do + %table.tablesorter{:cellpadding => "0", :cellspacing => "0"} + %thead + %tr + - if can? :remove_members, editable_object + %th + \  + %th + = t("layout.collaborators.members") + - if can? :remove_member, editable_object + %th.buttons + = t("layout.remove") + %tbody + - members.each_with_index do |user, num| + %tr{:id => "admin-table-members-row#{num}", :class => cycle(:odd, :even)} + - if can? :remove_members, editable_object + %td + %span.niceCheck-main{:id => "niceCheckbox#{num}", :style => "background-position: 0px 0px; "} + = check_box_tag "user_remove[#{user.id}][]" + %td + .img + = image_tag avatar_url(user) + .forimg= link_to user.fullname, user_path(user) + - if can? :remove_member, editable_object + %td.buttons + = link_to "#{remove_member_path}?member_id=#{user.id}", :method => :delete, :confirm => t("layout.confirm") do + %span.delete   + - if can? :remove_memvers, editable_object + = submit_tag t("layout.delete"), :class => 'button' + .both + +- if can? :add_member, editable_object + .hr.top + = form_tag add_member_path do + .admin-search + = autocomplete_field_tag 'member_id', params[:member_id], autocomplete_user_uname_users_path, :id_element => '#member_id_field' + = hidden_field_tag 'member_id', nil, :id => 'member_id_field' + = submit_tag t("layout.add"), :class => 'button' + .both diff --git a/app/views/user_mailer/build_list_notification.en.haml b/app/views/user_mailer/build_list_notification.en.haml new file mode 100644 index 000000000..9f58dafe3 --- /dev/null +++ b/app/views/user_mailer/build_list_notification.en.haml @@ -0,0 +1,15 @@ +%p + Build project + - if @build_list.project + = link_to @build_list.project.name_with_owner, project_url(@build_list.project) + - else + = t("layout.projects.unexisted_project") + completed with status + = @build_list.human_status + for arch + = "#{@build_list.arch.name}." +%p + More detailed information you can get by link: + = link_to "task [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list) + +%p== Support team «ROSA Build System» diff --git a/app/views/user_mailer/build_list_notification.ru.haml b/app/views/user_mailer/build_list_notification.ru.haml new file mode 100644 index 000000000..8a0970053 --- /dev/null +++ b/app/views/user_mailer/build_list_notification.ru.haml @@ -0,0 +1,15 @@ +%p + Сборка проекта + - if @build_list.project + = link_to @build_list.project.name_with_owner, project_url(@build_list.project) + - else + = t("layout.projects.unexisted_project") + завершена со статусом + = @build_list.human_status + для архитектуры + = "#{@build_list.arch.name}." +%p + Более подробную информацию можно получить по ссылке: + = link_to "задание [№ #{@build_list.bs_id ? @build_list.bs_id : t("layout.build_lists.bs_id_not_set")}]", build_list_url(@build_list) + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/invite_approve_notification.en.haml b/app/views/user_mailer/invite_approve_notification.en.haml new file mode 100644 index 000000000..27a4ca11a --- /dev/null +++ b/app/views/user_mailer/invite_approve_notification.en.haml @@ -0,0 +1,7 @@ +%p== Hello, #{@register_request.name || @register_request.email}. + +%p + You have been invited to project ABF. Please click on the following + = link_to 'link for registration', new_user_registration_url(:invitation_token => @register_request.token) + +%p== Support team «ROSA Build System» diff --git a/app/views/user_mailer/invite_approve_notification.html.haml b/app/views/user_mailer/invite_approve_notification.html.haml deleted file mode 100644 index 8d4bcc6bd..000000000 --- a/app/views/user_mailer/invite_approve_notification.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%p== Здравствуйте, #{@register_request.name || @register_request.email}. - -%p - Вы приглашены в проект ABF. Чтобы зарегистрироваться перейдите по - = link_to 'ссылке', new_user_registration_url(:invitation_token => @register_request.token) - -%p== Команда поддержки «ROSA Build System» diff --git a/app/views/user_mailer/invite_approve_notification.ru.haml b/app/views/user_mailer/invite_approve_notification.ru.haml new file mode 100644 index 000000000..e75296fe2 --- /dev/null +++ b/app/views/user_mailer/invite_approve_notification.ru.haml @@ -0,0 +1,7 @@ +%p== Здравствуйте, #{@register_request.name || @register_request.email}. + +%p + Вы приглашены в проект ABF. Пожалуйста, перейдите по + = link_to 'ссылке для регистрации', new_user_registration_url(:invitation_token => @register_request.token) + +%p== Команда поддержки «ROSA Build System» diff --git a/app/views/users/profile/show.html.haml b/app/views/users/profile/show.html.haml index bc6167a0c..3ab79e99f 100644 --- a/app/views/users/profile/show.html.haml +++ b/app/views/users/profile/show.html.haml @@ -6,13 +6,13 @@ %h3= title @user.uname = @user.name %br - = link_to @user.email, "mailto:#{@user.email}" + = mail_to @user.email, @user.email, :encode => "javascript" %br %h4= t("activerecord.attributes.user.professional_experience") + ":" %p= @user.professional_experience %h4= t("layout.users.public_projects_list") + ":" %p - - @user.projects.by_visibilities(['open']).each do |project| + - @projects.each do |project| = link_to project.name, project %br diff --git a/app/views/users/settings/_notifier.html.haml b/app/views/users/settings/_notifier.html.haml new file mode 100644 index 000000000..83946d397 --- /dev/null +++ b/app/views/users/settings/_notifier.html.haml @@ -0,0 +1,3 @@ +.leftside.w25= f.check_box field +.leftside= f.label field, t("activerecord.attributes.settings.notifier.#{field}") +.both diff --git a/app/views/users/settings/notifiers.html.haml b/app/views/users/settings/notifiers.html.haml index 2d206c2aa..d5917480a 100644 --- a/app/views/users/settings/notifiers.html.haml +++ b/app/views/users/settings/notifiers.html.haml @@ -7,32 +7,16 @@ %br = form_for @user.notifier, :url => notifiers_settings_path, :html => {:class => :form} do |f| - .leftside.w25= f.check_box :can_notify - .leftside= f.label :can_notify, t('activerecord.attributes.settings.notifier.can_notify') - .both + = render 'notifier', :f => f, :field => :can_notify %h3= t("layout.settings.notifiers.code_header") - .leftside.w25= f.check_box :new_comment_commit_owner, :class => 'notify_cbx' - .leftside= f.label :new_comment_commit_owner, t('activerecord.attributes.settings.notifier.new_comment_commit_owner') - .both - .leftside.w25= f.check_box :new_comment_commit_repo_owner, :class => 'notify_cbx' - .leftside= f.label :new_comment_commit_repo_owner, t('activerecord.attributes.settings.notifier.new_comment_commit_repo_owner') - .both - .leftside.w25= f.check_box :new_comment_commit_commentor, :class => 'notify_cbx' - .leftside= f.label :new_comment_commit_commentor, t('activerecord.attributes.settings.notifier.new_comment_commit_commentor') - .both + - [:new_comment_commit_owner, :new_comment_commit_repo_owner, :new_comment_commit_commentor].each do |field| + = render 'notifier', :f => f, :field => field %h3= t("layout.settings.notifiers.tracker_header") - .leftside.w25= f.check_box :new_comment, :class => 'notify_cbx' - .leftside= f.label :new_comment, t('activerecord.attributes.settings.notifier.new_comment') - .both - .leftside.w25= f.check_box :new_comment_reply, :class => 'notify_cbx' - .leftside= f.label :new_comment_reply, t('activerecord.attributes.settings.notifier.new_comment_reply') - .both - .leftside.w25= f.check_box :new_issue, :class => 'notify_cbx' - .leftside= f.label :new_issue, t('activerecord.attributes.settings.notifier.new_issue') - .both - .leftside.w25= f.check_box :issue_assign, :class => 'notify_cbx' - .leftside= f.label :issue_assign, t('activerecord.attributes.settings.notifier.issue_assign') - .both + - [:new_comment, :new_comment_reply, :new_issue, :issue_assign].each do |field| + = render 'notifier', :f => f, :field => field + %h3= t("layout.settings.notifiers.build_list_header") + - [:new_build, :new_associated_build].each do |field| + = render 'notifier', :f => f, :field => field %br .leftside.w25 diff --git a/config/deploy.rb b/config/deploy.rb index 41f67ab13..c2581e0a9 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -55,7 +55,7 @@ namespace :deploy do run "ln -nfs #{fetch :shared_path}/config/database.yml #{fetch :release_path}/config/database.yml" # Setup application - run "cp -n #{fetch :release_path}/config/deploy/application.#{fetch :stage}.yml #{fetch :shared_path}/config/application.yml" + run "cp -n #{fetch :release_path}/config/application.yml.sample #{fetch :shared_path}/config/application.yml" run "ln -nfs #{fetch :shared_path}/config/application.yml #{fetch :release_path}/config/application.yml" # It will survive downloads folder between deployments @@ -101,3 +101,33 @@ namespace :rake_tasks do mirror_rake_tasks 'db:seeds' end end + +namespace :update do + desc "Copy remote production shared files to localhost" + task :shared do + run_locally "rsync --recursive --times --rsh=ssh --compress --human-readable --progress #{user}@#{domain}:#{shared_path}/shared_contents/uploads public/uploads" + end + + desc "Dump remote production postgresql database, rsync to localhost" + task :postgresql do + get("#{current_path}/config/database.yml", "tmp/database.yml") + + remote_settings = YAML::load_file("tmp/database.yml")[rails_env] + local_settings = YAML::load_file("config/database.yml")["development"] + + + run "export PGPASSWORD=#{remote_settings["password"]} && pg_dump --host=#{remote_settings["host"]} --port=#{remote_settings["port"]} --username #{remote_settings["username"]} --file #{current_path}/tmp/#{remote_settings["database"]}_dump -Fc #{remote_settings["database"]}" + + run_locally "rsync --recursive --times --rsh=ssh --compress --human-readable --progress #{user}@#{domain}:#{current_path}/tmp/#{remote_settings["database"]}_dump tmp/" + + run_locally "dropdb -U #{local_settings["username"]} --host=#{local_settings["host"]} --port=#{local_settings["port"]} #{local_settings["database"]}" + run_locally "createdb -U #{local_settings["username"]} --host=#{local_settings["host"]} --port=#{local_settings["port"]} -T template0 #{local_settings["database"]}" + run_locally "pg_restore -U #{local_settings["username"]} --host=#{local_settings["host"]} --port=#{local_settings["port"]} -d #{local_settings["database"]} tmp/#{remote_settings["database"]}_dump" + end + + desc "Dump all remote data to localhost" + task :all do + # update.shared + update.postgresql + end +end diff --git a/config/deploy/application.production.yml b/config/deploy/application.production.yml deleted file mode 100644 index b4248563f..000000000 --- a/config/deploy/application.production.yml +++ /dev/null @@ -1,16 +0,0 @@ -production: - action_mailer_host: "abf.rosalinux.ru" - root_path: /share - nginx_log: /srv/rosa_build/shared/log/nginx.access.log - do-not-reply-email: do-not-reply@rosalinux.ru - project_name: ABF - repo_project_name: ABF - build_server_ip: 127.0.0.1 - build_server_port: 12555 - build_server_path: /xmlrpc - product_builder_ip: - mdv: 192.168.122.144 - nau5: 192.168.122.203 - product_builder_port: 12554 - product_builder_path: /xmlrpc - distr_types: ['mdv'] diff --git a/config/deploy/application.school.yml b/config/deploy/application.school.yml deleted file mode 100644 index 891090c97..000000000 --- a/config/deploy/application.school.yml +++ /dev/null @@ -1,23 +0,0 @@ -common: &common - project_name: ABF - repo_project_name: ABF - distr_types: ['mdv', 'nau5', 'suse', 'deb'] - wiki_formats: - markdown: "Markdown" - textile: "Textile" - org: "Org-mode" - -production: - <<: *common - action_mailer_host: "school.rosalab.ru" - root_path: /share - nginx_log: /srv/rosa_build/shared/log/nginx.access.log - do-not-reply-email: do-not-reply@school.rosalab.ru - build_server_ip: 127.0.0.1 - build_server_port: 12555 - build_server_path: /xmlrpc - product_builder_ip: - mdv: 192.168.122.19 - nau5: 192.168.122.203 - product_builder_port: 12554 - product_builder_path: /xmlrpc diff --git a/config/deploy/application.staging.yml b/config/deploy/application.staging.yml deleted file mode 100644 index 5e2c3e8a6..000000000 --- a/config/deploy/application.staging.yml +++ /dev/null @@ -1,16 +0,0 @@ -production: - action_mailer_host: "dev-abf.rosalinux.ru" - root_path: /share - nginx_log: /srv/rosa_build/shared/log/nginx.access.log - do-not-reply-email: do-not-reply@dev-abf.rosalinux.ru - project_name: ABF - repo_project_name: ABF - build_server_ip: 127.0.0.1 - build_server_port: 12555 - build_server_path: /xmlrpc - product_builder_ip: - mdv: 192.168.122.19 - nau5: 192.168.122.203 - product_builder_port: 12554 - product_builder_path: /xmlrpc - distr_types: ['mdv', 'nau5'] diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index e9b053ba5..c421b2c2e 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -12,7 +12,8 @@ Mime::Type.register "text/plain", 'patch' [["text/x-python", ['py'], '8bit'], ["text/x-rpm-spec", ['spec'], '8bit'], ["text/x-csrc", ['h', 'c'], '8bit'], - ["text/x-c++src", ['cpp'], '8bit'] + ["text/x-c++src", ['cpp'], '8bit'], + ["text/x-diff", ['diff'], '8bit'] ].each do |type| MIME::Types.add MIME::Type.from_array(type) end diff --git a/config/locales/en.yml b/config/locales/en.yml index 33cf8933b..6370f6eb9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,6 +47,7 @@ en: processing: working ... invalid_content_type: incorrect type atom_link_tag_title: Private feed for %{nickname} | %{app_name} + preview: Preview settings: label: Settings @@ -57,6 +58,7 @@ en: change_email_link: Change email address code_header: Code tracker_header: Tracker + build_list_header: Build List devise: shared_links: @@ -178,6 +180,8 @@ en: new_comment_commit_owner: Notify about comments to my commit new_comment_commit_repo_owner: Notify about comments to my repository commits new_comment_commit_commentor: Notify about comments after my commit + new_build: Notify about my build tasks + new_associated_build: Notify about associated with me build tasks private_user: login: Login diff --git a/config/locales/layout.en.yml b/config/locales/layout.en.yml index 5268e0cc3..d4fefe67b 100644 --- a/config/locales/layout.en.yml +++ b/config/locales/layout.en.yml @@ -1,6 +1,8 @@ en: layout: autoreload_page: Update page automatically + autoreload_log: Update log every + word_wrap: Word wrap read_more: Read more turned_on: on turned_off: off diff --git a/config/locales/layout.ru.yml b/config/locales/layout.ru.yml index 29247306a..8dae4c389 100644 --- a/config/locales/layout.ru.yml +++ b/config/locales/layout.ru.yml @@ -1,6 +1,8 @@ ru: layout: autoreload_page: Автоматически обновлять страницу + word_wrap: Перенос строк + autoreload_log: Обновлять лог каждые read_more: Читать дальше turned_on: включены turned_off: выключены diff --git a/config/locales/models/activity_feed.en.yml b/config/locales/models/activity_feed.en.yml index f047c89ab..8df4770b3 100644 --- a/config/locales/models/activity_feed.en.yml +++ b/config/locales/models/activity_feed.en.yml @@ -18,6 +18,7 @@ en: new_user_notification: Registered on project «%{ project_name }» issue_assign_notification: New task assigned invite_approve_notification: Invitation to ABF + for_arch: for arch %{arch}. bodies: new_comment_notification: diff --git a/config/locales/models/activity_feed.ru.yml b/config/locales/models/activity_feed.ru.yml index 66466da28..60911a6c8 100644 --- a/config/locales/models/activity_feed.ru.yml +++ b/config/locales/models/activity_feed.ru.yml @@ -18,7 +18,7 @@ ru: new_user_notification: Регистрация на проекте «%{ project_name }» issue_assign_notification: Вам назначили задачу invite_approve_notification: Приглашение в ABF - build_task: Сборочное задание + for_arch: для архитектуры %{arch}. bodies: new_comment_notification: diff --git a/config/locales/models/build_list.en.yml b/config/locales/models/build_list.en.yml index 087269400..f29c7d7cb 100644 --- a/config/locales/models/build_list.en.yml +++ b/config/locales/models/build_list.en.yml @@ -18,8 +18,7 @@ en: additional_repos: Additional repositories include_repos: Included repositories created_at: Created on - save_to_platform: Platform - save_to_repository: Repository + save_to_repository: Save to repository build_for_platform: Build for platform update_type: Update type build_requires: Build with all the required packages @@ -72,7 +71,7 @@ en: main_data: Main data human_current_duration: Build currently takes %{hours} h. %{minutes} min. - human_duration: Builded in %{hours} h. %{minutes} min. + human_duration: Built in %{hours} h. %{minutes} min. attached_advisory: Attached advisory create_advisory: Create new advisory @@ -118,6 +117,21 @@ en: project_not_found: Project not found project_version_not_found: Project version not found + log: + build_log: Build Log + not_available: Log not available yet. + download: Download log + autoreload: Update log every + load_lines: Load last %{count} lines + + reload_times: + 10000: "10 s" + 30000: "30 s" + 60000: "1 m" + 300000: "5 m" + 600000: "10 m" + 900000: "15 m" + flash: build_list: saved: Build list for project version '%{project_version}', platform '%{build_for_platform}' and architecture '%{arch}' has been created successfully diff --git a/config/locales/models/build_list.ru.yml b/config/locales/models/build_list.ru.yml index bb6dfa8ae..2f81ba2d0 100644 --- a/config/locales/models/build_list.ru.yml +++ b/config/locales/models/build_list.ru.yml @@ -18,8 +18,7 @@ ru: additional_repos: Дополнительные репозитории include_repos: Подключаемые репозитории created_at: Создан - save_to_platform: Платформа - save_to_repository: Репозиторий + save_to_repository: Сохранить в репозиторий build_for_platform: Собрано для платформы update_type: Критичность обновления build_requires: Пересборка с зависимостями @@ -117,6 +116,21 @@ ru: project_not_found: проект не найден project_version_not_found: версия не найдена + log: + build_log: Лог сборки + not_available: В настоящий момент лог недоступен. + download: Загрузить лог + autoreload: Обновлять лог каждые + load_lines: Загружать последние %{count} строк + + reload_times: + 10000: "10 сек" + 30000: "30 сек" + 60000: "1 мин" + 300000: "5 мин" + 600000: "10 мин" + 900000: "15 мин" + flash: build_list: saved: Билд лист для версии '%{project_version}', платформы '%{build_for_platform}' и архитектуры '%{arch}' создан успешно diff --git a/config/locales/models/comment.en.yml b/config/locales/models/comment.en.yml index a1bcd69ec..845864a64 100644 --- a/config/locales/models/comment.en.yml +++ b/config/locales/models/comment.en.yml @@ -4,10 +4,26 @@ en: confirm_delete: Are you sure you want to delete the comment? new_header: New comment edit_header: Editing a comment - has_commented: "added a note" - notifications_are: "Notifications for new comments are" - comments_header: "Comments" - back: 'Back' + has_commented: added a note + notifications_are: Notifications for new comments are + comments_header: Comments + back: Back + md_cheatsheet_header: Markdown Cheat Sheet + md_cheatsheet: + format_text: Format Text + headers: Headers + text_styles: Text styles + lists: Lists + unordered: Unordered + ordered: Ordered + miscellaneous: Miscellaneous + images: Images + links: Links + blockquotes: Blockquotes + code_examples: Code Examples in Markdown + syntax_highlighting: Syntax highlighting + indent_code: indent your code 4 spaces + inline_code: Inline code for comments flash: comment: diff --git a/config/locales/models/comment.ru.yml b/config/locales/models/comment.ru.yml index ff9e15dd3..89d87f2b4 100644 --- a/config/locales/models/comment.ru.yml +++ b/config/locales/models/comment.ru.yml @@ -4,17 +4,33 @@ ru: confirm_delete: Вы уверены, что хотите удалить комментарий? new_header: Новый комментарий edit_header: Редактирование комментария - has_commented: "оставил комментарий" - notifications_are: "Уведомления о последующих комментариях" - comments_header: "Комментарии" - back: 'Назад' + has_commented: оставил комментарий + notifications_are: Уведомления о последующих комментариях + comments_header: Комментарии + back: Назад + md_cheatsheet_header: Шпаргалка по Markdown + md_cheatsheet: + format_text: Формат текста + headers: Заголовки + text_styles: Стиль текста + lists: Списки + unordered: Маркированный + ordered: Нумерованный + miscellaneous: Разное + images: Изображения + links: Ссылки + blockquotes: Цитирование + code_examples: Примеры кода в Markdown + syntax_highlighting: Подсветка синтаксиса + indent_code: Отступ кода на 4 пробела + inline_code: Встроенный код в строке flash: comment: saved: Комментарий успешно сохранен save_error: Ошибка сохранения комментария destroyed: Комментарий удален - + activerecord: attributes: comment: diff --git a/config/locales/models/group.en.yml b/config/locales/models/group.en.yml index f8e015de3..1cb6a06dc 100644 --- a/config/locales/models/group.en.yml +++ b/config/locales/models/group.en.yml @@ -16,6 +16,7 @@ en: description: Descripton leave_group: Leave group projects_list: Projects list + public_projects_list: Public projects list public_profile: Public profile delete_warning: Attention! Deleted group can not be restored! diff --git a/config/locales/models/group.ru.yml b/config/locales/models/group.ru.yml index 01c5aaf7a..c43e33e2a 100644 --- a/config/locales/models/group.ru.yml +++ b/config/locales/models/group.ru.yml @@ -16,6 +16,7 @@ ru: description: Описание leave_group: Покинуть группу projects_list: Список проектов + public_projects_list: Список публичных проектов public_profile: Публичный профиль delete_warning: Внимание! Удаленная группа восстановлению не подлежит. diff --git a/config/locales/models/issue.en.yml b/config/locales/models/issue.en.yml index bb6b3111d..d656ab48f 100644 --- a/config/locales/models/issue.en.yml +++ b/config/locales/models/issue.en.yml @@ -41,6 +41,7 @@ en: update_label: Update label label_custom_color: Custom color label_manage: Manage labels + assignee_manage: Manage assignee assignee: Assignee search_user: Find user... search_labels: Find labels... diff --git a/config/locales/models/issue.ru.yml b/config/locales/models/issue.ru.yml index 74745f91c..a4bca52ff 100644 --- a/config/locales/models/issue.ru.yml +++ b/config/locales/models/issue.ru.yml @@ -40,7 +40,8 @@ ru: new_label: Название новой метки update_label: Обновить метку label_custom_color: Свой цвет - label_manage: Управление + label_manage: Управление метками + assignee_manage: Назначить исполнителя assignee: Исполнитель search_user: Найти пользователя... search_labels: Найти метки... diff --git a/config/locales/models/maintainer.en.yml b/config/locales/models/maintainer.en.yml new file mode 100644 index 000000000..92720e4a7 --- /dev/null +++ b/config/locales/models/maintainer.en.yml @@ -0,0 +1,25 @@ +en: + layout: + maintainers: + list_header: Maintainers + search_by_package: Search by package name + + flash: + maintainer: + saved: Maintainer saved + created: Maintainer created + save_error: Maintainer saves error + create_error: Maintainer create error + destroyed: Maintainer deleted + + activerecord: + models: + maintainer: Maintainer + attributes: + maintainer: + package_name: Package + package_type: Type + created_at: First Update + updated_at: Last Update + version: Version-Release + diff --git a/config/locales/models/maintainer.ru.yml b/config/locales/models/maintainer.ru.yml new file mode 100644 index 000000000..04cd6f7b8 --- /dev/null +++ b/config/locales/models/maintainer.ru.yml @@ -0,0 +1,25 @@ +ru: + layout: + maintainers: + list_header: Майнтейнеры + search_by_package: Искать по имени пакета + + flash: + maintainer: + saved: Майнтейнер успешно сохранен + created: Майнтейнер успешно добавлен + save_error: Не удалось сохранить майнтейнера + create_error: Не удалось создать майнтейнера + destroyed: Майнтейнер успешно удален + + activerecord: + models: + maintainer: Майнтейнер + attributes: + maintainer: + package_name: Пакет + package_type: Тип + created_at: Создан + updated_at: Обновлен + version: Версия-Релиз + diff --git a/config/locales/models/platform.en.yml b/config/locales/models/platform.en.yml index cf44b6550..9a828c31c 100644 --- a/config/locales/models/platform.en.yml +++ b/config/locales/models/platform.en.yml @@ -40,6 +40,7 @@ en: target_platform: Target platform target_architecture: Target architecture members: Members + maintainers: Maintainers project: Project arch: Architecture mass_build: Mass build @@ -62,7 +63,6 @@ en: cancel_mass_build: Mass build canceled clone_success: Cloned successfully members: - already_added: "%{name} is already a member of platform" successfully_added: "%{name} successfully added to the platform" error_in_adding: "Unable to add %{name} as member" diff --git a/config/locales/models/platform.ru.yml b/config/locales/models/platform.ru.yml index 6c04bbca8..a71e49bdc 100644 --- a/config/locales/models/platform.ru.yml +++ b/config/locales/models/platform.ru.yml @@ -40,6 +40,7 @@ ru: target_platform: Целевая платформа target_architecture: Целевая архитектура members: Участники + maintainers: Майнтейнеры arch: Архитектура mass_build: Массовая сборка build_task: Сборочное задание @@ -62,7 +63,6 @@ ru: cancel_mass_build: Массовая сборка отменена clone_success: Клонирование успешно members: - already_added: "%{name} уже является участником платформы" successfully_added: "Участник %{name} успешно добавлен к платформе" error_in_adding: "Не удалось добавить участника %{name}" diff --git a/config/locales/models/product_build_list.en.yml b/config/locales/models/product_build_list.en.yml index 0eabdae27..8b1b281e0 100644 --- a/config/locales/models/product_build_list.en.yml +++ b/config/locales/models/product_build_list.en.yml @@ -10,7 +10,7 @@ en: '2': 'build in progress' build_failed: Build failed build_started: Build in progress - build_completed: Builded + build_completed: Built ownership: header: Build list ownership diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml index f284eadc1..a538a25f9 100644 --- a/config/locales/models/project.en.yml +++ b/config/locales/models/project.en.yml @@ -93,6 +93,8 @@ en: group: Group default_branch: Default branch is_package: Project is a package + maintainer: Maintainer of project + maintainer_id: Maintainer of project errors: project: uname: The name can only use lower case Latin letters (a-z), numbers (0-9) and underscore (_) diff --git a/config/locales/models/project.ru.yml b/config/locales/models/project.ru.yml index c05f18619..321fb777d 100644 --- a/config/locales/models/project.ru.yml +++ b/config/locales/models/project.ru.yml @@ -93,6 +93,8 @@ ru: group: Группа default_branch: Ветка по умолчанию is_package: Проект является пакетом + maintainer_id: Майнтейнер проекта + maintainer: Майнтейнер проекта errors: project: uname: В имени можно использовать только строчные символы латинского алфавита (a-z), цифры (0-9) и символ нижнего подчеркивания (_) diff --git a/config/locales/models/repository.en.yml b/config/locales/models/repository.en.yml index 7f8ad732b..5fe13700d 100644 --- a/config/locales/models/repository.en.yml +++ b/config/locales/models/repository.en.yml @@ -2,6 +2,7 @@ en: layout: repositories: add_project_to: Add project to repository + edit: Settings list: List about: About repository list_header: Repositories @@ -29,13 +30,18 @@ en: flash: repository: saved: Repository added + updated: Repository updated save_error: Unable to add repository + update_error: Unable to update repository destroyed: Repository deleted project_added: Project added to repository project_not_added: Project adding error. A project with such name already exists in this repository. Remove the old project first project_removed: Project deleted project_not_removed: Unable to delete project from repository clear: Platform successfully cleared! + members: + successfully_added: "%{name} successfully added to the repository" + error_in_adding: "Unable to add %{name} as member" activerecord: models: @@ -44,6 +50,7 @@ en: repository: name: Name description: Description + publish_without_qa: Publication without QA platform_id: Platform platform: Platform created_at: Created diff --git a/config/locales/models/repository.ru.yml b/config/locales/models/repository.ru.yml index b01307e1c..a35396fd9 100644 --- a/config/locales/models/repository.ru.yml +++ b/config/locales/models/repository.ru.yml @@ -2,6 +2,7 @@ ru: layout: repositories: add_project_to: Добавить проект к репозиторию + edit: Настройки list: Список about: О репозитории list_header: Репозитории @@ -29,13 +30,18 @@ ru: flash: repository: saved: Репозиторий успешно добавлен + updated: Репозиторий успешно обновлен save_error: Не удалось добавить репозиторий + update_error: Не удалось обновить репозиторий destroyed: Репозиторий успешно удален project_added: Проект добавлен к репозиторию project_not_added: Не удалось добавить проект. В этом репозитории уже есть проект с таким именем. Сначала нужно удалить старый проект project_removed: Проект удален из репозитория project_not_removed: Не удалось удалить проект из репозитория clear: Платформа успешно очищена! + members: + successfully_added: "Участник %{name} успешно добавлен к репозиторию" + error_in_adding: "Не удалось добавить участника %{name}" activerecord: models: @@ -44,6 +50,7 @@ ru: repository: name: Название description: Описание + publish_without_qa: Публикация без участия QA platform_id: Платформа platform: Платформа created_at: Создан diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 3ca418329..e26b13393 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -47,6 +47,7 @@ ru: processing: Обрабатывается... invalid_content_type: имеет неверный тип atom_link_tag_title: Приватная лента для %{nickname} | %{app_name} + preview: Предосмотр settings: label: 'Настройки' @@ -57,6 +58,7 @@ ru: change_email_link: Изменить адрес электронной почты code_header: Код tracker_header: Трекер задач + build_list_header: Сборочные задания devise: shared_links: @@ -179,6 +181,8 @@ ru: new_comment_commit_owner: Оповещать о комментариях к моему коммиту new_comment_commit_repo_owner: Оповещать о комментариях к коммитам в моем репозитории new_comment_commit_commentor: Оповещать о комментариях к коммиту после моего + new_build: Оповещать о моих сборочных заданиях + new_associated_build: Оповещать о связанных со мной сборочных заданиях private_user: login: Логин diff --git a/config/routes.rb b/config/routes.rb index a1525eef4..49cb74f1f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,6 +15,7 @@ Rosa::Application.routes.draw do get '/forbidden' => 'pages#forbidden', :as => 'forbidden' get '/terms-of-service' => 'pages#tos', :as => 'tos' get '/tour/:id' => 'pages#tour_inside', :as => 'tour_inside', :id => /projects|sources|builds/ + match '/invite.html' => redirect('/register_requests/new') get '/activity_feeds.:format' => 'activity_feeds#index', :as => 'atom_activity_feeds', :format => /atom/ if APP_CONFIG['anonymous_access'] @@ -55,7 +56,7 @@ Rosa::Application.routes.draw do post :clear get :clone get :members - post :remove_members + post :remove_members # fixme: change post to delete delete :remove_member post :add_member post :make_clone @@ -75,13 +76,16 @@ Rosa::Application.routes.draw do get :add_project delete :remove_project get :projects_list + post :remove_members # fixme: change post to delete + delete :remove_member + post :add_member end end resources :key_pairs, :only => [:create, :index, :destroy] resources :products do resources :product_build_lists, :only => [:create, :destroy] end - + resources :maintainers, :only => [:index] end match '/private/:platform_name/*file_path' => 'privates#show' @@ -135,6 +139,7 @@ Rosa::Application.routes.draw do resources :build_lists, :only => [:index, :show, :update] do member do put :cancel + get :log end collection { post :search } end @@ -185,9 +190,11 @@ Rosa::Application.routes.draw do get :autocomplete_base_project, :on => :collection put :merge, :on => :member end + post '/preview' => 'projects#preview', :as => 'md_preview' end # Resource + get '/autocomplete_maintainers' => 'projects#autocomplete_maintainers', :as => :autocomplete_maintainers get '/modify' => 'projects#edit', :as => :edit_project put '/' => 'projects#update' delete '/' => 'projects#destroy' @@ -222,7 +229,7 @@ Rosa::Application.routes.draw do # Raw get '/raw/:treeish/*path' => "git/blobs#raw", :as => :raw, :format => false # Archive - get '/archive/:treeish.:format' => "git/trees#archive", :as => :archive, :format => /zip|tar/ + get '/archive/:treeish.:format' => "git/trees#archive", :as => :archive, :format => /zip|tar\.gz/ end end end diff --git a/db/migrate/20120306212914_add_project_to_comment.rb b/db/migrate/20120306212914_add_project_to_comment.rb index 94b6f8361..1e8ef5abe 100644 --- a/db/migrate/20120306212914_add_project_to_comment.rb +++ b/db/migrate/20120306212914_add_project_to_comment.rb @@ -5,7 +5,7 @@ class AddProjectToComment < ActiveRecord::Migration Subscribe.reset_column_information Comment.where(:commentable_type => 'Grit::Commit').destroy_all Comment.where(:commentable_type => 'Issue').each do |comment| - comment.update_attribute(:project_id, comment.commentable.project) + comment.update_column(:project_id, comment.commentable.project.id) end end diff --git a/db/migrate/20120601142035_add_name_platform_index_to_build_list_packages.rb b/db/migrate/20120601142035_add_name_platform_index_to_build_list_packages.rb new file mode 100644 index 000000000..8e8cfee7d --- /dev/null +++ b/db/migrate/20120601142035_add_name_platform_index_to_build_list_packages.rb @@ -0,0 +1,5 @@ +class AddNamePlatformIndexToBuildListPackages < ActiveRecord::Migration + def change + add_index :build_list_packages, [:name,:project_id] + end +end diff --git a/db/migrate/20120822130632_add_maintainer_id_to_projects.rb b/db/migrate/20120822130632_add_maintainer_id_to_projects.rb new file mode 100644 index 000000000..94614ef56 --- /dev/null +++ b/db/migrate/20120822130632_add_maintainer_id_to_projects.rb @@ -0,0 +1,9 @@ +class AddMaintainerIdToProjects < ActiveRecord::Migration + def self.up + add_column :projects, :maintainer_id, :integer + end + + def self.down + remove_column :projects, :maintainer_id + end +end diff --git a/db/migrate/20120822210712_add_actual_to_build_list_packages.rb b/db/migrate/20120822210712_add_actual_to_build_list_packages.rb new file mode 100644 index 000000000..847eed97e --- /dev/null +++ b/db/migrate/20120822210712_add_actual_to_build_list_packages.rb @@ -0,0 +1,14 @@ +class AddActualToBuildListPackages < ActiveRecord::Migration + def self.up + add_column :build_list_packages, :actual, :boolean, :default => false + add_index :build_list_packages, [:actual, :platform_id] + add_index :build_lists, [:project_id, :save_to_repository_id, :build_for_platform_id, :arch_id], + :name => :maintainer_search_index + end + + def self.down + remove_index :build_list_packages, [:actual, :platform_id] + remove_column :build_list_packages, :actual + remove_index :build_lists, :name => :maintainer_search_index + end +end diff --git a/db/migrate/20120906115648_add_publish_without_qa_to_repositories.rb b/db/migrate/20120906115648_add_publish_without_qa_to_repositories.rb new file mode 100644 index 000000000..c3f83e01e --- /dev/null +++ b/db/migrate/20120906115648_add_publish_without_qa_to_repositories.rb @@ -0,0 +1,20 @@ +class AddPublishWithoutQaToRepositories < ActiveRecord::Migration + + class Platform < ActiveRecord::Base + has_many :repositories, :dependent => :destroy + end + + class Repository < ActiveRecord::Base + belongs_to :platform + end + + def up + add_column :repositories, :publish_without_qa, :boolean, :default => true + Platform.where(:released => true).each{|p| p.repositories.update_all(:publish_without_qa => false)} + Platform.where(:released => false).each{|p| p.repositories.update_all(:publish_without_qa => true)} + end + + def down + remove_column :repositories, :publish_without_qa + end +end diff --git a/db/migrate/20120910094748_add_language_to_register_request.rb b/db/migrate/20120910094748_add_language_to_register_request.rb new file mode 100644 index 000000000..56b2dcfbd --- /dev/null +++ b/db/migrate/20120910094748_add_language_to_register_request.rb @@ -0,0 +1,5 @@ +class AddLanguageToRegisterRequest < ActiveRecord::Migration + def change + add_column :register_requests, :language, :string + end +end diff --git a/db/migrate/20120914160741_add_build_list_notifications_to_settings_notifier.rb b/db/migrate/20120914160741_add_build_list_notifications_to_settings_notifier.rb new file mode 100644 index 000000000..e94e14128 --- /dev/null +++ b/db/migrate/20120914160741_add_build_list_notifications_to_settings_notifier.rb @@ -0,0 +1,6 @@ +class AddBuildListNotificationsToSettingsNotifier < ActiveRecord::Migration + def change + add_column :settings_notifiers, :new_build, :boolean, :default => true + add_column :settings_notifiers, :new_associated_build, :boolean, :default => true + end +end diff --git a/db/schema.rb b/db/schema.rb index 85a2d95b9..7d4175660 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120730214052) do +ActiveRecord::Schema.define(:version => 20120914160741) do create_table "activity_feeds", :force => true do |t| t.integer "user_id", :null => false @@ -91,11 +91,14 @@ ActiveRecord::Schema.define(:version => 20120730214052) do t.string "version" t.string "release" t.string "package_type" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "actual", :default => false end + add_index "build_list_packages", ["actual", "platform_id"], :name => "index_build_list_packages_on_actual_and_platform_id" add_index "build_list_packages", ["build_list_id"], :name => "index_build_list_packages_on_build_list_id" + add_index "build_list_packages", ["name", "project_id"], :name => "index_build_list_packages_on_name_and_project_id" add_index "build_list_packages", ["platform_id"], :name => "index_build_list_packages_on_platform_id" add_index "build_list_packages", ["project_id"], :name => "index_build_list_packages_on_project_id" @@ -131,6 +134,7 @@ ActiveRecord::Schema.define(:version => 20120730214052) do add_index "build_lists", ["advisory_id"], :name => "index_build_lists_on_advisory_id" add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id" + add_index "build_lists", ["project_id", "save_to_repository_id", "build_for_platform_id", "arch_id"], :name => "maintainer_search_index" add_index "build_lists", ["bs_id"], :name => "index_build_lists_on_bs_id", :unique => true add_index "build_lists", ["project_id"], :name => "index_build_lists_on_project_id" @@ -332,6 +336,7 @@ ActiveRecord::Schema.define(:version => 20120730214052) do t.boolean "is_package", :default => true, :null => false t.integer "average_build_time", :default => 0, :null => false t.integer "build_count", :default => 0, :null => false + t.integer "maintainer_id" end add_index "projects", ["owner_id"], :name => "index_projects_on_name_and_owner_id_and_owner_type", :unique => true, :case_sensitive => false @@ -358,6 +363,7 @@ ActiveRecord::Schema.define(:version => 20120730214052) do t.datetime "updated_at", :null => false t.string "interest" t.text "more" + t.string "language" end add_index "register_requests", ["email"], :name => "index_register_requests_on_email", :unique => true, :case_sensitive => false @@ -374,11 +380,12 @@ ActiveRecord::Schema.define(:version => 20120730214052) do end create_table "repositories", :force => true do |t| - t.string "description", :null => false - t.integer "platform_id", :null => false - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "name", :null => false + t.string "description", :null => false + t.integer "platform_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.string "name", :null => false + t.boolean "publish_without_qa", :default => true end create_table "settings_notifiers", :force => true do |t| @@ -393,6 +400,8 @@ ActiveRecord::Schema.define(:version => 20120730214052) do t.boolean "new_comment_commit_owner", :default => true t.boolean "new_comment_commit_repo_owner", :default => true t.boolean "new_comment_commit_commentor", :default => true + t.boolean "new_build", :default => true + t.boolean "new_associated_build", :default => true end create_table "subscribes", :force => true do |t| diff --git a/lib/plugins/rosa_presenter/base.rb b/lib/plugins/rosa_presenter/base.rb index eff0f3387..531c74205 100644 --- a/lib/plugins/rosa_presenter/base.rb +++ b/lib/plugins/rosa_presenter/base.rb @@ -6,6 +6,7 @@ module RosaPresenter include ActionDispatch::Routing::UrlFor include ActionView::Helpers::UrlHelper include ActionView::Helpers::TextHelper + include ActionView::Helpers::JavaScriptHelper include Rails.application.routes.url_helpers def initialize(item, opts) diff --git a/lib/tasks/add_branch.rake b/lib/tasks/add_branch.rake index 1857cb7c9..47c0bf9ed 100644 --- a/lib/tasks/add_branch.rake +++ b/lib/tasks/add_branch.rake @@ -1,22 +1,55 @@ require 'highline/import' +require 'open-uri' -desc "Add branch for platform projects" -task :add_branch => :environment do - src_branch = ENV['SRC_BRANCH'] || 'import_mandriva2011' - dst_branch = ENV['DST_BRANCH'] || 'rosa2012lts' - - say "START add branch #{dst_branch} from #{src_branch}" - Platform.find_by_name(dst_branch).repositories.each do |r| - say "=== Process #{r.name} repo" - r.projects.find_each do |p| - next if p.repo.branches.map(&:name).include?(dst_branch) - say "===== Process #{p.name} project" - tmp_path = Rails.root.join('tmp', p.name) - system("git clone #{p.path} #{tmp_path}") - system("cd #{tmp_path} && git checkout remotes/origin/#{src_branch} || git checkout master") - system("cd #{tmp_path} && git checkout -b #{dst_branch} && git push origin HEAD") - FileUtils.rm_rf tmp_path - end +namespace :add_branch do + desc 'Fork project branch' + task :fork_branch, :path, :src_branch, :dst_branch do |t, args| + tmp_path = File.join Dir.tmpdir, "#{Time.now.to_i}#{rand(1000)}" + system("git clone #{args[:path]} #{tmp_path}") + system("cd #{tmp_path} && git push origin :#{args[:dst_branch]}") + system("cd #{tmp_path} && git checkout remotes/origin/#{args[:src_branch]} || git checkout master") + system("cd #{tmp_path} && git checkout -b #{args[:dst_branch]} && git push origin HEAD") + FileUtils.rm_rf tmp_path + end + + desc "Add branch for platform projects" + task :platform => :environment do + src_branch = ENV['SRC_BRANCH'] || 'import_mandriva2011' + dst_branch = ENV['DST_BRANCH'] || 'rosa2012lts' + say "START add branch #{dst_branch} from #{src_branch}" + Platform.find_by_name(dst_branch).repositories.each do |r| + say "=== Process #{r.name} repo" + r.projects.find_each do |p| + next if p.repo.branches.map(&:name).include?(dst_branch) + say "===== Process #{p.name} project" + Rake::Task['add_branch:fork_branch'].execute(:path => p.path, :src_branch => src_branch, :dst_branch => dst_branch) + end + end + say 'DONE' + end + + desc "Add branch for owner projects by list" + task :list => :environment do + source = ENV['SOURCE'] || 'https://dl.dropbox.com/u/984976/texlive.txt' + owner = User.find_by_uname(ENV['OWNER']) || Group.find_by_uname!(ENV['OWNER'] || 'import') + platform = Platform.find_by_name!(ENV['PLATFORM'] || 'rosa2012.1') + repo = platform.repositories.find_by_name!(ENV['REPO'] || 'main') + src_branch = ENV['SRC_BRANCH'] || 'import_cooker' + dst_branch = ENV['DST_BRANCH'] || 'rosa2012.1' + say "START fork from #{src_branch} to #{dst_branch} branch using #{source} for #{owner.uname}. Add to repo '#{platform.name}/#{repo.name}'." + open(source).readlines.each do |name| + name.chomp!; name.strip! + print "Fork branch for '#{name}'... " + if p = Project.find_by_owner_and_name(owner.uname, name) + # Rake::Task['add_branch:fork_branch'].execute(:path => p.path, :src_branch => src_branch, :dst_branch => dst_branch) + system "bundle exec rake add_branch:fork_branch[#{p.path},#{src_branch},#{dst_branch}] -s RAILS_ENV=#{Rails.env} > /dev/null 2>&1" + print 'Ok!' + repo.projects << p rescue print ' Add to repo failed!' + else + print 'Not Found!' + end + puts + end + say 'DONE' end - say 'DONE' end diff --git a/lib/tasks/buildlist.rake b/lib/tasks/buildlist.rake index c22cea26d..c56ecce00 100644 --- a/lib/tasks/buildlist.rake +++ b/lib/tasks/buildlist.rake @@ -1,6 +1,6 @@ namespace :buildlist do - + namespace :clear do desc 'Remove outdated BuildLists and MassBuilds' task :outdated => :environment do @@ -17,4 +17,32 @@ namespace :buildlist do say "Outdated BuildLists and MassBuilds was successfully removed" end end + + namespace :packages do + # TODO Maybe do it in migration, because it's just a single query? + desc 'Actualize packages for all platforms' + task :actualize => :environment do + + say "Updating packages" + packages = BuildList::Package.joins( %q{ + JOIN ( + SELECT + name AS j_pn, + package_type AS j_pt, + platform_id AS j_plid, + MAX(created_at) AS j_ca + FROM + build_list_packages + GROUP BY + j_pn, j_pt, j_plid + ) AS lastmaints + ON + j_pn = name + AND j_pt = package_type + AND j_plid = platform_id + AND j_ca = created_at + } ).update_all(:actual => true) + say "'Actual' setted to #{packages} packages" + end + end end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 4c5899cbf..d485d14ad 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -77,13 +77,15 @@ namespace :import do namespace :sync do desc "Sync all repos" - task :all do - system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=main") - system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=contrib") - system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=non-free") + task :all => :environment do + # system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=main") + # system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=contrib") + # system("bundle exec rake import:sync:run RELEASE=official/2011 PLATFORM=mandriva2011 REPOSITORY=non-free") system("bundle exec rake import:sync:run RELEASE=devel/cooker PLATFORM=cooker REPOSITORY=main") system("bundle exec rake import:sync:run RELEASE=devel/cooker PLATFORM=cooker REPOSITORY=contrib") system("bundle exec rake import:sync:run RELEASE=devel/cooker PLATFORM=cooker REPOSITORY=non-free") + system("bundle exec rake import:sync:run SOURCE=rsync://mirror.yandex.ru/fedora-epel/6/SRPMS/ DESTINATION=#{File.join(APP_CONFIG['root_path'], 'mirror.yandex.ru', 'fedora-epel', '6', 'SRPMS')} PLATFORM=server_personal REPOSITORY=main OWNER=server BRANCH=import") + system("bundle exec rake import:sync:run SOURCE=rsync://rh-mirror.redhat.com/redhat/linux/enterprise/6Server/en/os/SRPMS/ DESTINATION=#{File.join(APP_CONFIG['root_path'], 'rh-mirror.redhat.com', 'redhat', 'linux', 'enterprise', '6Server', 'en', 'os', 'SRPMS')} PLATFORM=server_personal REPOSITORY=main OWNER=server BRANCH=import") end task :run => [:rsync, :parse] @@ -92,7 +94,7 @@ namespace :import do task :rsync => :environment do release = ENV['RELEASE'] || 'official/2011' repository = ENV['REPOSITORY'] || 'main' - source = "rsync://mirror.yandex.ru/mandriva/#{release}/SRPMS/#{repository}/" + source = ENV['SOURCE'] || "rsync://mirror.yandex.ru/mandriva/#{release}/SRPMS/#{repository}/" destination = ENV['DESTINATION'] || File.join(APP_CONFIG['root_path'], 'mirror.yandex.ru', 'mandriva', release, 'SRPMS', repository) say "START rsync projects (*.src.rpm) from '#{source}' to '#{destination}' (#{Time.now.utc})" if system "rsync -rtv --delete --exclude='backports/*' --exclude='testing/*' #{source} #{destination}" # --include='*.src.rpm' @@ -108,29 +110,34 @@ namespace :import do release = ENV['RELEASE'] || 'official/2011' platform = Platform.find_by_name(ENV['PLATFORM'] || "mandriva2011") repository = platform.repositories.find_by_name(ENV['REPOSITORY'] || 'main') - source = ENV['SOURCE'] || File.join(APP_CONFIG['root_path'], 'mirror.yandex.ru', 'mandriva', release, 'SRPMS', repository.name) + source = ENV['DESTINATION'] || File.join(APP_CONFIG['root_path'], 'mirror.yandex.ru', 'mandriva', release, 'SRPMS', repository.name, '{release,updates}') owner = Group.find_or_create_by_uname(ENV['OWNER'] || 'import') {|g| g.name = g.uname; g.owner = User.first} - branch = "import_#{platform.name}" + branch = ENV['BRANCH'] || "import_#{platform.name}" say "START (#{Time.now.utc})" - Dir[File.join source, '{release,updates}', '*.src.rpm'].each do |srpm_file| + Dir[File.join source, '*.src.rpm'].each do |srpm_file| print "Processing '#{srpm_file}'... " if name = `rpm -q --qf '[%{Name}]' -p #{srpm_file}` and $?.success? and name.present? and version = `rpm -q --qf '[%{Version}-%{Release}]' -p #{srpm_file}` and $?.success? and version.present? project_import = ProjectImport.find_by_name_and_platform_id(name, platform.id) || ProjectImport.by_name(name).where(:platform_id => platform.id).first || ProjectImport.new(:name => name, :platform_id => platform.id) if version != project_import.version.to_s and File.mtime(srpm_file) > project_import.file_mtime unless project = project_import.project - if project = repository.projects.find_by_name(name) || repository.projects.by_name(name).first # fallback to speedup - print "Found project #{project.name_with_owner} in #{platform.name}/#{repository.name}. " - elsif scoped = Project.where(:owner_id => owner.id, :owner_type => owner.class) and - project = scoped.find_by_name(name) || scoped.by_name(name).first - repository.projects << project - print "Add project #{project.name_with_owner} to #{platform.name}/#{repository.name}. " - else - description = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', `rpm -q --qf '[%{Description}]' -p #{srpm_file}`) - project = Project.create!(:name => name, :description => description) {|p| p.owner = owner} - repository.projects << project - print "Create project #{project.name_with_owner} at #{platform.name}/#{repository.name}. " + if platform.personal? # search project through owner # used for testhat + project = Project.find_or_create_by_name_and_owner_type_and_owner_id(name, owner.class.to_s, owner.id) + print "Use project #{project.name_with_owner}. " + else # search project through repository + if project = repository.projects.find_by_name(name) || repository.projects.by_name(name).first # fallback to speedup + print "Found project #{project.name_with_owner} in #{platform.name}/#{repository.name}. " + elsif scoped = Project.where(:owner_id => owner.id, :owner_type => owner.class) and + project = scoped.find_by_name(name) || scoped.by_name(name).first + repository.projects << project + print "Add project #{project.name_with_owner} to #{platform.name}/#{repository.name}. " + else + description = ::Iconv.conv('UTF-8//IGNORE', 'UTF-8', `rpm -q --qf '[%{Description}]' -p #{srpm_file}`) + project = Project.create!(:name => name, :description => description) {|p| p.owner = owner} + repository.projects << project + print "Create project #{project.name_with_owner} at #{platform.name}/#{repository.name}. " + end end end project.import_srpm(srpm_file, branch) diff --git a/lib/tasks/projects.rake b/lib/tasks/projects.rake new file mode 100644 index 000000000..bfe2d982f --- /dev/null +++ b/lib/tasks/projects.rake @@ -0,0 +1,28 @@ +namespace :project do + + namespace :maintainer do + desc 'Set maintainer to owner (or to owners owner if owner is a group) to projects' + task :set_to_owner => :environment do + projects = Project.scoped + count = projects.count + say "Setting maintainer to all projects (#{count})" + percent_per_batch = 100 * 1000 / count + i = 1 + + projects.find_in_batches do |batch| + ActiveRecord::Base.transaction do + batch.each do |proj| + maintainer_id = (proj.owner_type == 'User') ? proj.owner_id : proj.owner.owner_id + proj.maintainer_id = maintainer_id + proj.save + end + end + say "#{percent_per_batch * i}% done." + i += 1 + end + say "100% done" + end + end + task :maintainer => 'maintainer:set_to_owner' + +end diff --git a/spec/controllers/groups/profile_controller_spec.rb b/spec/controllers/groups/profile_controller_spec.rb index a0e29c31b..8ea65d72b 100644 --- a/spec/controllers/groups/profile_controller_spec.rb +++ b/spec/controllers/groups/profile_controller_spec.rb @@ -129,7 +129,8 @@ describe Groups::ProfileController do before(:each) do @user = FactoryGirl.create(:user) set_session_for(@user) - @group.update_attribute(:owner, @user) + @group.owner = @user + @group.save @group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end diff --git a/spec/controllers/platforms/key_pairs_controller_spec.rb b/spec/controllers/platforms/key_pairs_controller_spec.rb index 5b27e41b0..e9b73fc97 100644 --- a/spec/controllers/platforms/key_pairs_controller_spec.rb +++ b/spec/controllers/platforms/key_pairs_controller_spec.rb @@ -126,7 +126,9 @@ describe Platforms::KeyPairsController do before(:each) do @user = FactoryGirl.create(:user) set_session_for(@user) - @platform.update_attribute(:owner, @user) + + @platform.owner = @user + @platform.save end it_should_behave_like 'key_pair platform owner' diff --git a/spec/controllers/platforms/maintainers_controller_spec.rb b/spec/controllers/platforms/maintainers_controller_spec.rb new file mode 100644 index 000000000..d4c03b245 --- /dev/null +++ b/spec/controllers/platforms/maintainers_controller_spec.rb @@ -0,0 +1,69 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +shared_examples_for 'guest user' do + + it "should be able to view maintainers list(index)" do + get :index, :platform_id => @platform.id + response.should be_success + end +end + +describe Platforms::MaintainersController do + before(:each) do + stub_symlink_methods + + @platform = FactoryGirl.create(:platform) + @user = FactoryGirl.create(:user) + set_session_for(@user) + end + + context 'for guest' do + before {set_session_for(User.new)} + + it "should be able to view maintainers list(index)", :anonymous_access => true do + get :index, :platform_id => @platform.id + response.should be_success + end + + it "should not be able to view maintainers list(index)", :anonymous_access => false do + get :index, :platform_id => @platform.id + response.should redirect_to(new_user_session_path) + end + end + + context 'for global admin' do + before(:each) do + @user.role = "admin" + @user.save + end + + it_should_behave_like 'guest user' + end + + context 'for registrated user' do + + it_should_behave_like 'guest user' + end + + + context 'for platform owner' do + before(:each) do + @user = @platform.owner + set_session_for(@user) + end + + it_should_behave_like 'guest user' + end + + context 'for platform member' do + before(:each) do + @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end + + it_should_behave_like 'guest user' + end + +end + + diff --git a/spec/controllers/platforms/mass_builds_controller_spec.rb b/spec/controllers/platforms/mass_builds_controller_spec.rb index 1cd611d72..8c673fb99 100644 --- a/spec/controllers/platforms/mass_builds_controller_spec.rb +++ b/spec/controllers/platforms/mass_builds_controller_spec.rb @@ -23,7 +23,7 @@ shared_examples_for 'mass_build platform owner' do end it 'should not be able to perform cancel action if stop_build is true' do - @mass_build.update_attribute(:stop_build, true) + @mass_build.stop_build = true; @mass_build.save post :cancel, :platform_id => @platform, :id => @mass_build response.should redirect_to(forbidden_path) end @@ -133,7 +133,9 @@ describe Platforms::MassBuildsController do before(:each) do @user = FactoryGirl.create(:user) set_session_for(@user) - @platform.update_attribute(:owner, @user) + + @platform.owner = @user + @platform.save end it_should_behave_like 'mass_build platform owner' diff --git a/spec/controllers/platforms/platforms_controller_spec.rb b/spec/controllers/platforms/platforms_controller_spec.rb index 242315f10..6ec4157b4 100644 --- a/spec/controllers/platforms/platforms_controller_spec.rb +++ b/spec/controllers/platforms/platforms_controller_spec.rb @@ -2,7 +2,6 @@ require 'spec_helper' shared_examples_for 'platform owner' do - it_should_behave_like 'platform index viewer' it 'should not be able to destroy personal platform' do delete :destroy, :id => @personal_platform.id @@ -19,14 +18,39 @@ shared_examples_for 'platform owner' do end end -shared_examples_for 'platform index viewer' do +shared_examples_for 'system registered user' do it 'should be able to perform index action' do get :index response.should render_template(:index) end -end + it 'should be able to perform show action' do + get :show, :id => @platform.id + response.should render_template(:show) + assigns(:platform).should eq @platform + end + + it 'should be able to perform members action' do + get :members, :id => @platform.id + response.should render_template(:members) + response.should be_success + end + + it 'should be able to perform advisories action' do + get :advisories, :id => @platform.id + response.should render_template(:advisories) + response.should be_success + end + +end + shared_examples_for 'user without create rights' do + + it 'should not be able to perform new action' do + get :new + response.should redirect_to(forbidden_path) + end + it 'should not be able to create platform' do post :create, @create_params response.should redirect_to(forbidden_path) @@ -39,16 +63,22 @@ describe Platforms::PlatformsController do @platform = FactoryGirl.create(:platform) @personal_platform = FactoryGirl.create(:platform, :platform_type => 'personal') + @user = FactoryGirl.create(:user) + set_session_for(@user) + @create_params = {:platform => { :name => 'pl1', :description => 'pl1', :platform_type => 'main', :distrib_type => APP_CONFIG['distr_types'].first }} - end + end context 'for guest' do + before(:each) do + set_session_for(User.new) + end [:index, :create].each do |action| it "should not be able to perform #{ action } action" do @@ -57,21 +87,39 @@ describe Platforms::PlatformsController do end end - [:show, :new, :edit, :clone, :destroy].each do |action| + [:new, :edit, :clone, :destroy].each do |action| it "should not be able to perform #{ action } action" do get action, :id => @platform response.should redirect_to(new_user_session_path) end end + + [:show, :members, :advisories].each do |action| + it "should not be able to perform #{ action } action", :anonymous_access => false do + get action, :id => @platform + response.should redirect_to(new_user_session_path) + end + end + + [:show, :members, :advisories].each do |action| + it "should be able to perform #{ action } action", :anonymous_access => true do + get action, :id => @platform + response.should render_template(action) + response.should be_success + end + end + end context 'for global admin' do before(:each) do - @admin = FactoryGirl.create(:admin) - @user = FactoryGirl.create(:user) - set_session_for(@admin) + @user.role = "admin" + @user.save end + it_should_behave_like 'system registered user' + it_should_behave_like 'platform owner' + it 'should be able to perform new action' do get :new response.should render_template(:new) @@ -86,51 +134,37 @@ describe Platforms::PlatformsController do lambda { post :create, @create_params }.should change{ Platform.count }.by(1) end - it_should_behave_like 'platform owner' - it 'should create platform with mentioned owner if owner id present' do - post :create, @create_params.merge({:admin_id => @user.id, :admin_uname => @user.uname}) - Platform.last.owner.id.should eql(@user.id) + owner = FactoryGirl.create(:user) + post :create, @create_params.merge({:admin_id => owner.id, :admin_uname => owner.uname}) + Platform.last.owner.id.should eql(owner.id) end it 'should create platform with current user as owner if owner id not present' do post :create, @create_params - Platform.last.owner.id.should eql(@admin.id) + Platform.last.owner.id.should eql(@user.id) end end context 'for owner user' do before(:each) do - @user = FactoryGirl.create(:user) + @user = @platform.owner set_session_for(@user) - @platform.update_attribute(:owner, @user) - @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end + it_should_behave_like 'system registered user' it_should_behave_like 'user without create rights' it_should_behave_like 'platform owner' - it 'should be able to perform new action' do - get :new - response.should redirect_to(forbidden_path) - end - - it 'should be able to perform create action' do - post :create, @create_params - response.should redirect_to(forbidden_path) - end - end context 'for reader user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') end - it_should_behave_like 'platform index viewer' + it_should_behave_like 'system registered user' it_should_behave_like 'user without create rights' it 'should not be able to perform destroy action' do diff --git a/spec/controllers/platforms/products_controller_spec.rb b/spec/controllers/platforms/products_controller_spec.rb index e90d48cbc..4c500eb48 100644 --- a/spec/controllers/platforms/products_controller_spec.rb +++ b/spec/controllers/platforms/products_controller_spec.rb @@ -31,9 +31,16 @@ describe Platforms::ProductsController do @product = FactoryGirl.create(:product, :platform => @platform) @create_params = {:product => {:name => 'pro'}, :platform_id => @platform.id} @update_params = {:product => {:name => 'pro2'}, :platform_id => @platform.id} + + @user = FactoryGirl.create(:user) + set_session_for(@user) end - context 'for guest' do + context 'for guest' do + before(:each) do + set_session_for(User.new) + end + [:create].each do |action| it "should not be able to perform #{ action } action" do get action, :platform_id => @platform.id @@ -41,40 +48,56 @@ describe Platforms::ProductsController do end end - [:show, :new, :edit, :update, :destroy].each do |action| + [:new, :edit, :update, :destroy].each do |action| it "should not be able to perform #{ action } action" do get action, :id => @product.id, :platform_id => @platform.id response.should redirect_to(new_user_session_path) end end + + [:show, :index].each do |action| + it "should not be able to perform #{ action } action", :anonymous_access => false do + get action, :id => @product.id, :platform_id => @platform.id + response.should redirect_to(new_user_session_path) + end + end + + [:show, :index].each do |action| + it "should be able to perform #{ action } action", :anonymous_access => true do + get action, :id => @product.id, :platform_id => @platform.id + response.should render_template(action) + response.should be_success + end + end end context 'for global admin' do - before(:each) do - @admin = FactoryGirl.create(:admin) - set_session_for(@admin) - end + before(:each) do + @user.role = "admin" + @user.save + end it_should_behave_like 'admin user' end - + context 'for platform owner' do + before(:each) do + @user = @platform.owner + set_session_for(@user) + end + + it_should_behave_like 'admin user' + end context 'for admin relation user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) + before(:each) do @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') - end + end it_should_behave_like 'admin user' end context 'for no relation user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) - end it 'should not be able to create product' do lambda { post :create, @create_params }.should change{ Product.count }.by(0) diff --git a/spec/controllers/platforms/repositories_controller_spec.rb b/spec/controllers/platforms/repositories_controller_spec.rb index 82c4a5712..c4481c9ea 100644 --- a/spec/controllers/platforms/repositories_controller_spec.rb +++ b/spec/controllers/platforms/repositories_controller_spec.rb @@ -5,29 +5,97 @@ shared_examples_for 'not destroy personal repository' do it 'should not be able to destroy personal repository' do lambda { delete :destroy, :id => @personal_repository.id, :platform_id => @personal_repository.platform.id}.should change{ Repository.count }.by(0) - response.should redirect_to(forbidden_path) + response.should redirect_to(redirect_path) end end shared_examples_for 'user with change projects in repository rights' do - + it 'should be able to see add_project page' do get :add_project, :id => @repository.id, :platform_id => @platform.id response.should render_template(:projects_list) end - + it 'should be able to add project to repository' do get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id response.should redirect_to(platform_repository_path(@repository.platform, @repository)) - @repository.projects.should include (@project) + @repository.projects.should include(@project) end - + it 'should be able to remove project from repository' do get :remove_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id response.should redirect_to(platform_repository_path(@repository.platform, @repository)) - @repository.projects.should_not include (@project) + @repository.projects.should_not include(@project) end - + +end + +shared_examples_for 'user without change projects in repository rights' do + it 'should not be able to add project to repository' do + get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id + response.should redirect_to(redirect_path) + @repository.projects.should_not include(@project) + end + + it 'should not be able to remove project from repository' do + delete :remove_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id + response.should redirect_to(redirect_path) + @repository.projects.should_not include(@project) + end +end + +shared_examples_for 'registered user or guest' do + it 'should not be able to perform new action' do + get :new, :platform_id => @platform.id + response.should redirect_to(redirect_path) + end + + it 'should not be able to perform create action' do + post :create, @create_params + lambda { post :create, @create_params }.should change{ Repository.count }.by(0) + response.should redirect_to(redirect_path) + end + + it 'should not be able to perform edit action' do + get :edit, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(redirect_path) + end + + it 'should not be able to perform update action' do + put :update, :id => @repository.id, :platform_id => @platform.id + response.should redirect_to(redirect_path) + end + + it 'should not be able to add new member to repository' do + post :add_member, :id => @repository.id, :platform_id => @platform.id, :member_id => @another_user.id + response.should redirect_to(redirect_path) + @repository.members.should_not include(@another_user) + end + + it 'should not be able to remove member from repository' do + @repository.relations.create(:role => 'admin', :actor => @another_user) + delete :remove_member, :id => @repository.id, :platform_id => @platform.id, :member_id => @another_user.id + response.should redirect_to(redirect_path) + @repository.members.should include(@another_user) + end + + it 'should not be able to remove members from repository' do + another_user2 = FactoryGirl.create(:user) + @repository.relations.create(:role => 'admin', :actor => @another_user) + @repository.relations.create(:role => 'admin', :actor => another_user2) + post :remove_members, :id => @repository.id, :platform_id => @platform.id, + :user_remove => {@another_user.id => [1], another_user2.id => [1]} + response.should redirect_to(redirect_path) + @repository.members.should include(@another_user, another_user2) + end + + it 'should not be able to destroy repository in main platform' do + delete :destroy, :id => @repository.id + response.should redirect_to(redirect_path) + lambda { delete :destroy, :id => @repository.id }.should_not change{ Repository.count }.by(-1) + end + + it_should_behave_like 'not destroy personal repository' end shared_examples_for 'registered user' do @@ -40,6 +108,12 @@ shared_examples_for 'registered user' do get :show, :id => @repository.id response.should render_template(:show) end + + it 'should be able to perform projects_list action' do + get :projects_list, :id => @repository.id, :platform_id => @platform.id, :format => :json + response.should be_success + end + end shared_examples_for 'platform admin user' do @@ -61,8 +135,38 @@ shared_examples_for 'platform admin user' do response.should redirect_to(platform_repositories_path(@repository.platform)) end + it 'should be able to perform edit action' do + get :edit, :id => @repository.id, :platform_id => @platform.id + response.should render_template(:edit) + end + + it 'should be able to add new member to repository' do + post :add_member, :id => @repository.id, :platform_id => @platform.id, :member_id => @another_user.id + response.should redirect_to(edit_platform_repository_path(@repository.platform, @repository)) + @repository.members.should include(@another_user) + end + + it 'should be able to remove member from repository' do + @repository.relations.create(:role => 'admin', :actor => @another_user) + delete :remove_member, :id => @repository.id, :platform_id => @platform.id, :member_id => @another_user.id + response.should redirect_to(edit_platform_repository_path(@repository.platform, @repository)) + @repository.members.should_not include(@another_user) + end + + it 'should be able to remove members from repository' do + another_user2 = FactoryGirl.create(:user) + @repository.relations.create(:role => 'admin', :actor => @another_user) + @repository.relations.create(:role => 'admin', :actor => another_user2) + post :remove_members, :id => @repository.id, :platform_id => @platform.id, + :user_remove => {@another_user.id => [1], another_user2.id => [1]} + response.should redirect_to(edit_platform_repository_path(@repository.platform, @repository)) + @repository.members.should_not include(@another_user, another_user2) + end + it_should_behave_like 'user with change projects in repository rights' - it_should_behave_like 'not destroy personal repository' + it_should_behave_like 'not destroy personal repository' do + let(:redirect_path) { forbidden_path } + end end describe Platforms::RepositoriesController do @@ -75,83 +179,85 @@ describe Platforms::RepositoriesController do @project = FactoryGirl.create(:project) @another_user = FactoryGirl.create(:user) @create_params = {:repository => {:name => 'pro', :description => 'pro2'}, :platform_id => @platform.id} + + @user = FactoryGirl.create(:user) + set_session_for(@user) end context 'for guest' do - [:index, :create].each do |action| - it "should not be able to perform #{ action } action" do - get action, :platform_id => @platform - response.should redirect_to(new_user_session_path) - end + + before(:each) do + set_session_for(User.new) end - [:show, :new, :add_project, :remove_project, :destroy].each do |action| - it "should not be able to perform #{ action } action" do - get action, :id => @repository.id - response.should redirect_to(new_user_session_path) - end + it_should_behave_like 'registered user' if APP_CONFIG['anonymous_access'] + + let(:redirect_path) { new_user_session_path } + it_should_behave_like 'registered user or guest' + it_should_behave_like 'user without change projects in repository rights' + + it "should not be able to perform show action", :anonymous_access => false do + get :show, :id => @repository + response.should redirect_to(new_user_session_path) end + + it "should not be able to perform index action", :anonymous_access => false do + get :index, :platform_id => @platform + response.should redirect_to(new_user_session_path) + end + + it 'should not be able to perform projects_list action', :anonymous_access => false do + get :projects_list, :id => @repository.id, :platform_id => @platform.id, :format => :json + response.response_code.should == 401 + end + + end + + context 'for user' do + it_should_behave_like 'registered user' + + let(:redirect_path) { forbidden_path } + it_should_behave_like 'registered user or guest' + it_should_behave_like 'user without change projects in repository rights' end context 'for admin' do before(:each) do - @admin = FactoryGirl.create(:admin) - set_session_for(@admin) + @user.role = "admin" + @user.save end it_should_behave_like 'platform admin user' end - + context 'for platform owner user' do before(:each) do - @user = FactoryGirl.create(:user) + @user = @repository.platform.owner set_session_for(@user) - @repository.platform.update_attribute(:owner, @user) - @repository.platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end it_should_behave_like 'platform admin user' end - context 'for user' do + context 'for platform member user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) + @platform.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end - + + it_should_behave_like 'platform admin user' + end + + context 'for repository member user' do + before(:each) do + @repository.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + end + it_should_behave_like 'registered user' - it 'should not be able to perform new action' do - get :new, :platform_id => @platform.id - response.should redirect_to(forbidden_path) - end - - it 'should not be able to perform create action' do - post :create, @create_params - lambda { post :create, @create_params }.should change{ Repository.count }.by(0) - response.should redirect_to(forbidden_path) - end - - it 'should not be able to destroy repository in main platform' do - delete :destroy, :id => @repository.id - response.should redirect_to(forbidden_path) - lambda { delete :destroy, :id => @repository.id }.should_not change{ Repository.count }.by(-1) - end - - it 'should not be able to add project to repository' do - get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id - response.should redirect_to(forbidden_path) - @repository.projects.should_not include (@project) - end - - it 'should not be able to remove project from repository' do - get :remove_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id - response.should redirect_to(forbidden_path) - @repository.projects.should_not include (@project) - end - - it_should_behave_like 'not destroy personal repository' + let(:redirect_path) { forbidden_path } + it_should_behave_like 'registered user or guest' + it_should_behave_like 'user with change projects in repository rights' end end diff --git a/spec/controllers/projects/build_lists_controller_spec.rb b/spec/controllers/projects/build_lists_controller_spec.rb index b2aa0d17d..4e9575f9c 100644 --- a/spec/controllers/projects/build_lists_controller_spec.rb +++ b/spec/controllers/projects/build_lists_controller_spec.rb @@ -109,7 +109,7 @@ describe Projects::BuildListsController do set_session_for(@user) @show_params = {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @build_list.id} end - + context 'for all build lists' do before(:each) do @build_list1 = FactoryGirl.create(:build_list_core) @@ -326,8 +326,8 @@ describe Projects::BuildListsController do describe 'publish_build' do before { test_git_commit(build_list.project) - build_list.update_attribute :commit_hash, build_list.project.repo.commits('master').last.id - build_list.update_attribute(:status, BuildList::BUILD_PUBLISH) + build_list.update_column(:commit_hash, build_list.project.repo.commits('master').last.id) + build_list.update_column(:status, BuildList::BUILD_PUBLISH) build_list_package } @@ -337,7 +337,7 @@ describe Projects::BuildListsController do end it(:passes) { - build_list.update_attribute(:status, BuildServer::BUILD_STARTED) + build_list.update_column(:status, BuildServer::BUILD_STARTED) do_get(BuildServer::SUCCESS) response.should be_ok } @@ -401,7 +401,7 @@ describe Projects::BuildListsController do describe 'pre_build' do before do - build_list.update_attribute :status, BuildList::BUILD_PENDING + build_list.update_column :status, BuildList::BUILD_PENDING end def do_get @@ -428,29 +428,29 @@ describe Projects::BuildListsController do context 'with auto_publish' do it(:passes) { - build_list.update_attribute(:started_at, (Time.now - 1.day)) - build_list.update_attribute(:status, BuildServer::BUILD_STARTED) + build_list.update_column(:started_at, (Time.now - 1.day)) + build_list.update_column(:status, BuildServer::BUILD_STARTED) build_list.reload lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :status).to(BuildList::BUILD_PUBLISH) } it(:passes) { - build_list.update_attribute(:started_at, (Time.now - 1.day)) - build_list.update_attribute(:status, BuildServer::BUILD_STARTED) + build_list.update_column(:started_at, (Time.now - 1.day)) + build_list.update_column(:status, BuildServer::BUILD_STARTED) lambda{ do_get(BuildServer::BUILD_ERROR) }.should change(build_list, :status).to(BuildServer::BUILD_ERROR) } end context 'without auto_publish' do - before { build_list.update_attribute(:auto_publish, false) } + before { build_list.update_column(:auto_publish, false) } it(:passes) { - build_list.update_attribute(:started_at, (Time.now - 1.day)) - build_list.update_attribute(:status, BuildServer::BUILD_STARTED) + build_list.update_column(:started_at, (Time.now - 1.day)) + build_list.update_column(:status, BuildServer::BUILD_STARTED) lambda{ do_get(BuildServer::SUCCESS) }.should change(build_list, :status).to(BuildServer::SUCCESS) } it(:passes) { - build_list.update_attribute(:started_at, (Time.now - 1.day)) - build_list.update_attribute(:status, BuildServer::BUILD_STARTED) + build_list.update_column(:started_at, (Time.now - 1.day)) + build_list.update_column(:status, BuildServer::BUILD_STARTED) lambda{ do_get(BuildServer::BUILD_ERROR) }.should change(build_list, :status).to(BuildServer::BUILD_ERROR) } end diff --git a/spec/controllers/projects/collaborators_controller_spec.rb b/spec/controllers/projects/collaborators_controller_spec.rb index 5c8d320c2..eac133b35 100644 --- a/spec/controllers/projects/collaborators_controller_spec.rb +++ b/spec/controllers/projects/collaborators_controller_spec.rb @@ -1,21 +1,35 @@ # -*- encoding : utf-8 -*- require 'spec_helper' -def create_params - @user_params = { - :actor_id => @another_user.id.to_s, - :actor_type => 'user', - :role => 'reader' - } - @group_params = { - :actor_id => @group.id.to_s, - :actor_type => 'group', - :role => 'reader' - } - @create_params = { - :owner_name => @project.owner.uname, :project_name => @project.name, - :format => :json - } +shared_context "collaborators controller" do + before(:each) do + stub_symlink_methods + @project = FactoryGirl.create(:project) + @another_user = FactoryGirl.create(:user) + @group = FactoryGirl.create(:group) + @member_user = FactoryGirl.create(:user) + # Create relation with 'writer' rights + @collaborator = Collaborator.create(:actor => @member_user, :project => @project, :role => 'writer') + + @user = FactoryGirl.create(:user) + set_session_for(@user) + + @user_params = { + :actor_id => @another_user.id.to_s, + :actor_type => 'user', + :role => 'reader' + } + @group_params = { + :actor_id => @group.id.to_s, + :actor_type => 'group', + :role => 'reader' + } if @group + @create_params = { + :owner_name => @project.owner.uname, :project_name => @project.name, + :format => :json + } + @update_params = @create_params.merge(:collaborator => {:role => 'reader'}) + end end shared_examples_for 'project admin user' do @@ -25,7 +39,7 @@ shared_examples_for 'project admin user' do end it 'should be able to perform update action' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @collaborator.id}.merge(@update_params) + put :update, {:id => @collaborator.id}.merge(@update_params) response.should be_success end @@ -40,7 +54,7 @@ shared_examples_for 'project admin user' do end it 'should be able to set reader role for any user' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @collaborator.id}.merge(@update_params) + put :update, {:id => @collaborator.id}.merge(@update_params) @another_user.relations.exists? :target_id => @project.id, :target_type => 'Project', :role => 'read' end end @@ -52,45 +66,38 @@ shared_examples_for 'user with no rights for this project' do end it 'should not be able to perform update action' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @collaborator.id}.merge(@update_params) + put :update, {:id => @collaborator.id}.merge(@update_params) response.should redirect_to(forbidden_path) end it 'should not be able to set reader role for any user' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @collaborator.id}.merge(@update_params) + put :update, {:id => @collaborator.id}.merge(@update_params) !@another_user.relations.exists? :target_id => @project.id, :target_type => 'Project', :role => 'read' end end describe Projects::CollaboratorsController do - before(:each) do - stub_symlink_methods - @project = FactoryGirl.create(:project) - @another_user = FactoryGirl.create(:user) - @member_user = FactoryGirl.create(:user) - @update_params = {:collaborator => {:role => :reader}, :format => :json} - # Create relation with 'writer' rights - @collaborator = Collaborator.create(:actor => @member_user, :project => @project, :role => 'writer') - end + include_context "collaborators controller" context 'for guest' do + before(:each) do + set_session_for(User.new) + end it 'should not be able to perform index action' do get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should redirect_to(new_user_session_path) end it 'should not be able to perform update action' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name, :id => @collaborator.id}.merge(@update_params) + put :update, {:id => @collaborator.id}.merge(@update_params) response.code.should == '401' end end context 'for global admin' do before(:each) do - @admin = FactoryGirl.create(:admin) - set_session_for(@admin) - @group = FactoryGirl.create(:group) - create_params + @user.role = "admin" + @user.save end it_should_behave_like 'project admin user' @@ -98,28 +105,16 @@ describe Projects::CollaboratorsController do context 'for admin user' do before(:each) do - @user = FactoryGirl.create(:user) -# @user.relations - set_session_for(@user) - @group = FactoryGirl.create(:group) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') - create_params end it_should_behave_like 'project admin user' - end context 'for owner user' do before(:each) do - @user = FactoryGirl.create(:user) + @user = @project.owner # owner should be user set_session_for(@user) - @group = FactoryGirl.create(:group) - - @project.update_attribute(:owner, @user) - @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') - - create_params end it_should_behave_like 'project admin user' @@ -127,8 +122,6 @@ describe Projects::CollaboratorsController do context 'for reader user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') end @@ -137,8 +130,6 @@ describe Projects::CollaboratorsController do context 'for writer user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') end diff --git a/spec/controllers/projects/comments_controller_spec.rb b/spec/controllers/projects/comments_controller_spec.rb index 1e0501495..3fb36a7cd 100644 --- a/spec/controllers/projects/comments_controller_spec.rb +++ b/spec/controllers/projects/comments_controller_spec.rb @@ -1,13 +1,33 @@ # -*- encoding : utf-8 -*- require 'spec_helper' +shared_context "comments controller" do + before(:each) do + stub_symlink_methods + + @project = FactoryGirl.create(:project) + @issue = FactoryGirl.create(:issue, :project_id => @project.id, :user => FactoryGirl.create(:user)) + @comment = FactoryGirl.create(:comment, :commentable => @issue, :project_id => @project.id) + + @user = FactoryGirl.create(:user) + @own_comment = FactoryGirl.create(:comment, :commentable => @issue, :user => @user, :project_id => @project.id) + + set_session_for(@user) + + @address = {:owner_name => @project.owner.uname, :project_name => @project.name, :issue_id => @issue.serial_id} + @create_params = {:comment => {:body => 'I am a comment!'}}.merge(@address) + @update_params = {:comment => {:body => 'updated'}}.merge(@address) + end + +end + shared_examples_for 'user with create comment rights' do it 'should be able to perform create action' do post :create, @create_params response.should redirect_to(project_issue_path(@project, @issue)) end - it 'should create subscribe object into db' do + it 'should create comment in the database' do lambda{ post :create, @create_params }.should change{ Comment.count }.by(1) end end @@ -18,7 +38,7 @@ shared_examples_for 'user with update own comment rights' do response.should redirect_to([@project, @issue]) end - it 'should update subscribe body' do + it 'should update comment body' do put :update, {:id => @own_comment.id}.merge(@update_params) @own_comment.reload.body.should == 'updated' end @@ -30,7 +50,7 @@ shared_examples_for 'user with update stranger comment rights' do response.should redirect_to([@project, @issue]) end - it 'should update issue title' do + it 'should update comment body' do put :update, {:id => @comment.id}.merge(@update_params) @comment.reload.body.should == 'updated' end @@ -42,7 +62,7 @@ shared_examples_for 'user without update stranger comment rights' do response.should redirect_to(forbidden_path) end - it 'should not update issue title' do + it 'should not update comment body' do put :update, {:id => @comment.id}.merge(@update_params) @comment.reload.body.should_not == 'updated' end @@ -50,42 +70,39 @@ end shared_examples_for 'user without destroy comment rights' do it 'should not be able to perform destroy action' do - delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :owner_name => @project.owner.uname, :project_name => @project.name + delete :destroy, {:id => @comment.id}.merge(@address) response.should redirect_to(forbidden_path) end - it 'should not reduce comments count' do - lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.serial_id, :owner_name => @project.owner.uname, :project_name => @project.name }.should change{ Issue.count }.by(0) + it 'should not delete comment from database' do + lambda{ delete :destroy, {:id => @comment.id}.merge(@address)}.should change{ Issue.count }.by(0) end end -#shared_examples_for 'user with destroy rights' do -# it 'should be able to perform destroy action' do -# delete :destroy, :id => @comment.id, :issue_id => @issue.id, :owner_name => @project.owner.uname, :project_name => @project.name -# response.should redirect_to([@project, @issue]) -# end -# -# it 'should reduce comments count' do -# lambda{ delete :destroy, :id => @comment.id, :issue_id => @issue.id, :owner_name => @project.owner.uname, :project_name => @project.name }.should change{ Comment.count }.by(-1) -# end -#end +shared_examples_for 'user with destroy comment rights' do + it 'should be able to perform destroy action' do + delete :destroy, {:id => @comment.id}.merge(@address) + response.should redirect_to([@project, @issue]) + end + + it 'should delete comment from database' do + lambda{ delete :destroy, {:id => @comment.id}.merge(@address)}.should change{ Comment.count }.by(-1) + end +end describe Projects::CommentsController do - before(:each) do - stub_symlink_methods + include_context "comments controller" - @project = FactoryGirl.create(:project) - @issue = FactoryGirl.create(:issue, :project_id => @project.id, :user => FactoryGirl.create(:user)) - @comment = FactoryGirl.create(:comment, :commentable => @issue, :project_id => @project.id) + context 'for global admin user' do + before(:each) do + @user.role = "admin" + @user.save + end - @create_params = {:comment => {:body => 'I am a comment!'}, :owner_name => @project.owner.uname, :project_name => @project.name, :issue_id => @issue.serial_id} - @update_params = {:comment => {:body => 'updated'}, :owner_name => @project.owner.uname, :project_name => @project.name, :issue_id => @issue.serial_id} - - any_instance_of(Project, :versions => ['v1.0', 'v2.0']) - - @user = FactoryGirl.create(:user) - set_session_for(@user) - @own_comment = FactoryGirl.create(:comment, :commentable => @issue, :user => @user, :project_id => @project.id) + it_should_behave_like 'user with create comment rights' + it_should_behave_like 'user with update stranger comment rights' + it_should_behave_like 'user with update own comment rights' + it_should_behave_like 'user with destroy comment rights' end context 'for project admin user' do @@ -101,10 +118,7 @@ describe Projects::CommentsController do context 'for project owner user' do before(:each) do - @project.update_attribute(:owner, @user) - @project.relations.destroy_all - @project.relations.create :actor_id => @project.owner.id, :actor_type => @project.owner.class.to_s, :role => 'admin' - @create_params[:owner_name] = @user.uname; @update_params[:owner_name] = @user.uname + set_session_for(@project.owner) # owner should be user end it_should_behave_like 'user with create comment rights' diff --git a/spec/controllers/projects/git/git_trees_controller_spec.rb b/spec/controllers/projects/git/git_trees_controller_spec.rb index f2baf5020..5bf420e11 100644 --- a/spec/controllers/projects/git/git_trees_controller_spec.rb +++ b/spec/controllers/projects/git/git_trees_controller_spec.rb @@ -12,22 +12,22 @@ describe Projects::Git::TreesController do @project = FactoryGirl.create(:project) @another_user = FactoryGirl.create(:user) - @params = {:owner_name => @project.owner.uname, :project_name => @project.name, :treeish => 'master'} + @params = { :owner_name => @project.owner.uname, + :project_name => @project.name, + :treeish => "#{@project.owner.uname}-#{@project.name}-master"} end context 'for guest' do - if APP_CONFIG['anonymous_access'] - it 'should be able to perform archive action with anonymous acccess' do - fill_project - get :archive, @params.merge(:format => 'tar') - response.should be_success - end - else - it 'should not be able to perform archive action without anonymous acccess' do - fill_project - get :archive, @params.merge(:format => 'tar') - response.code.should == '401' - end + it 'should be able to perform archive action with anonymous acccess', :anonymous_access => true do + fill_project + get :archive, @params.merge(:format => 'tar.gz') + response.should be_success + end + + it 'should not be able to perform archive action without anonymous acccess', :anonymous_access => false do + fill_project + get :archive, @params.merge(:format => 'tar.gz') + response.code.should == '401' end end @@ -35,14 +35,14 @@ describe Projects::Git::TreesController do it 'should not be able to archive empty project' do @user = FactoryGirl.create(:user) set_session_for(@user) - expect { get :archive, @params.merge(:format => 'tar') }.to raise_error(ActionController::RoutingError) + expect { get :archive, @params.merge(:format => 'tar.gz') }.to raise_error(ActionController::RoutingError) end it 'should not be able to injection code with format' do @user = FactoryGirl.create(:user) set_session_for(@user) fill_project - expect { get :archive, @params.merge(:format => "tar master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError) + expect { get :archive, @params.merge(:format => "tar.gz master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError) end it 'should not be able to injection code with treeish' do @@ -56,7 +56,7 @@ describe Projects::Git::TreesController do @user = FactoryGirl.create(:user) set_session_for(@user) fill_project - get :archive, @params.merge(:format => 'tar') + get :archive, @params.merge(:format => 'tar.gz') response.should be_success end end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 49a6e9b07..65873a0a3 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1,7 +1,43 @@ # -*- encoding : utf-8 -*- require 'spec_helper' -shared_examples_for 'issue user with project reader rights' do +shared_context "issues controller" do + before(:each) do + stub_symlink_methods + + @project = FactoryGirl.create(:project) + @issue_user = FactoryGirl.create(:user) + + @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id) + + @project_with_turned_off_issues = FactoryGirl.create(:project, :has_issues => false) + @turned_of_issue = FactoryGirl.create(:issue, :project_id => @project_with_turned_off_issues.id, :assignee_id => @issue_user.id) + + @user = FactoryGirl.create(:user) + set_session_for(@user) + + @create_params = { + :owner_name => @project.owner.uname, :project_name => @project.name, + :issue => { + :title => "issue1", + :body => "issue body" + }, + :assignee_id => @issue_user.id, + :assignee_uname => @issue_user.uname + } + + @update_params = { + :owner_name => @project.owner.uname, :project_name => @project.name, + :issue => { + :title => "issue2" + } + } + + end + +end + +shared_examples_for 'issue user with project guest rights' do it 'should be able to perform index action' do get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should render_template(:index) @@ -11,9 +47,12 @@ shared_examples_for 'issue user with project reader rights' do get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @issue.serial_id response.should render_template(:show) end +end + +shared_examples_for 'issue user with project reader rights' do it 'should be able to perform index action on hidden project' do - @project.update_attribute :visibility, 'hidden' + @project.update_attributes(:visibility => 'hidden') get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should render_template(:index) end @@ -66,9 +105,9 @@ shared_examples_for 'user without issue destroy rights' do end shared_examples_for 'project with issues turned off' do - pending 'should not be able to perform index action' do + it 'should not be able to perform index action' do get :index, :project_id => @project_with_turned_off_issues.id - response.should render_template(:index) + response.should redirect_to(forbidden_path) end it 'should not be able to perform show action' do @@ -78,107 +117,82 @@ shared_examples_for 'project with issues turned off' do end describe Projects::IssuesController do - before(:each) do - stub_symlink_methods - - @project = FactoryGirl.create(:project) - @issue_user = FactoryGirl.create(:user) - - any_instance_of(Project, :versions => ['v1.0', 'v2.0']) - - @issue = FactoryGirl.create(:issue, :project_id => @project.id, :assignee_id => @issue_user.id) - @create_params = { - :owner_name => @project.owner.uname, :project_name => @project.name, - :issue => { - :title => "issue1", - :body => "issue body" - }, - :assignee_id => @issue_user.id, - :assignee_uname => @issue_user.uname - } - @update_params = { - :owner_name => @project.owner.uname, :project_name => @project.name, - :issue => { - :title => "issue2" - } - } - - @project_with_turned_off_issues = FactoryGirl.create(:project, :has_issues => false) - @turned_of_issue = FactoryGirl.create(:issue, :project_id => @project_with_turned_off_issues.id, :assignee_id => @issue_user.id) - end + include_context "issues controller" context 'for global admin user' do before(:each) do - @admin = FactoryGirl.create(:admin) - set_session_for(@admin) + @user.role = "admin" + @user.save end + it_should_behave_like 'issue user with project guest rights' + it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'issue user with project writer rights' + it_should_behave_like 'user with issue update rights' + it_should_behave_like 'project with issues turned off' it_should_behave_like 'user without issue destroy rights' end context 'for project admin user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end + it_should_behave_like 'issue user with project guest rights' it_should_behave_like 'issue user with project reader rights' it_should_behave_like 'issue user with project writer rights' it_should_behave_like 'user with issue update rights' - it_should_behave_like 'user without issue destroy rights' it_should_behave_like 'project with issues turned off' + it_should_behave_like 'user without issue destroy rights' end context 'for project owner user' do before(:each) do - @user = FactoryGirl.create(:user) + @user = @project.owner set_session_for(@user) - @project.update_attribute(:owner, @user); @create_params[:owner_name] = @user.uname; @update_params[:owner_name] = @user.uname - @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end + it_should_behave_like 'issue user with project guest rights' it_should_behave_like 'issue user with project reader rights' it_should_behave_like 'issue user with project writer rights' it_should_behave_like 'user with issue update rights' - it_should_behave_like 'user without issue destroy rights' it_should_behave_like 'project with issues turned off' + it_should_behave_like 'user without issue destroy rights' end context 'for project reader user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') end + it_should_behave_like 'issue user with project guest rights' it_should_behave_like 'issue user with project reader rights' + it_should_behave_like 'issue user with project writer rights' it_should_behave_like 'user without issue update rights' - it_should_behave_like 'user without issue destroy rights' it_should_behave_like 'project with issues turned off' + it_should_behave_like 'user without issue destroy rights' - it 'should not be able to perform create action' do - post :create, @create_params - response.should redirect_to(forbidden_path) - end + # it 'should not be able to perform create action on project' do + # post :create, @create_params + # response.should redirect_to(forbidden_path) + # end - it 'should not create issue object into db' do - lambda{ post :create, @create_params }.should change{ Issue.count }.by(0) - end + # it 'should not create issue object into db' do + # lambda{ post :create, @create_params }.should change{ Issue.count }.by(0) + # end end context 'for project writer user' do before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') end + it_should_behave_like 'issue user with project guest rights' it_should_behave_like 'issue user with project reader rights' it_should_behave_like 'issue user with project writer rights' it_should_behave_like 'user without issue update rights' - it_should_behave_like 'user without issue destroy rights' it_should_behave_like 'project with issues turned off' + it_should_behave_like 'user without issue destroy rights' end context 'for issue assign user' do @@ -187,28 +201,26 @@ describe Projects::IssuesController do end it_should_behave_like 'user without issue update rights' - it_should_behave_like 'user without issue destroy rights' it_should_behave_like 'project with issues turned off' + it_should_behave_like 'user without issue destroy rights' end context 'for guest' do + + before(:each) do + set_session_for(User.new) + end + if APP_CONFIG['anonymous_access'] - # it_should_behave_like 'issue user with project reader rights' - it 'should be able to perform index action' do - get :index, :owner_name => @project.owner.uname, :project_name => @project.name - response.should render_template(:index) - end - - it 'should be able to perform show action' do - get :show, :owner_name => @project.owner.uname, :project_name => @project.name, :id => @issue.serial_id - response.should render_template(:show) - end - + + it_should_behave_like 'issue user with project guest rights' + it 'should not be able to perform index action on hidden project' do - @project.update_attribute :visibility, 'hidden' + @project.update_attributes(:visibility => 'hidden') get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should redirect_to(forbidden_path) end + else it 'should not be able to perform index action' do get :index, :owner_name => @project.owner.uname, :project_name => @project.name @@ -221,7 +233,7 @@ describe Projects::IssuesController do end it 'should not be able to perform index action on hidden project' do - @project.update_attribute :visibility, 'hidden' + @project.update_attributes(:visibility => 'hidden') get :index, :owner_name => @project.owner.uname, :project_name => @project.name response.should redirect_to(new_user_session_path) end diff --git a/spec/controllers/projects/projects_controller_spec.rb b/spec/controllers/projects/projects_controller_spec.rb index a94cced96..dc240686a 100644 --- a/spec/controllers/projects/projects_controller_spec.rb +++ b/spec/controllers/projects/projects_controller_spec.rb @@ -1,217 +1,277 @@ # -*- encoding : utf-8 -*- require 'spec_helper' +shared_examples_for 'projects user with reader rights' do + + it 'should be able to fork project' do + post :fork, :owner_name => @project.owner.uname, :project_name => @project.name + response.should redirect_to(project_path(Project.last)) + end + + it 'should be able to fork project to their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + lambda {post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, + :group => group.id}.should change{ Project.count }.by(1) + end + + it 'should be able to fork project to own group' do + group = FactoryGirl.create(:group, :owner => @user) + lambda {post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, + :group => group.id}.should change{ Project.count }.by(1) + end + + # it 'should be able to view project' do + # get :show, :owner_name => @project.owner.uname, :project_name => @project.name + # assigns(:project).should eq @project + # end + +end + +shared_examples_for 'projects user with project admin rights' do + it 'should be able to perform update action' do + put :update, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@update_params) + response.should redirect_to(project_path(@project)) + end +end + +shared_examples_for 'user with destroy rights' do + it 'should be able to perform destroy action' do + delete :destroy, {:owner_name => @project.owner.uname, :project_name => @project.name} + response.should redirect_to(@project.owner) + end + + it 'should change objects count on destroy' do + lambda { delete :destroy, :owner_name => @project.owner.uname, :project_name => @project.name }.should change{ Project.count }.by(-1) + end +end + +shared_examples_for 'projects user without project admin rights' do + it 'should not be able to edit project' do + description = @project.description + put :update, :project=>{:description =>"hack"}, :owner_name => @project.owner.uname, :project_name => @project.name + @project.reload.description.should == description + response.should redirect_to(forbidden_path) + end + + it 'should not be able to edit project sections' do + has_wiki, has_issues = @project.has_wiki, @project.has_issues + post :sections, :project =>{:has_wiki => !has_wiki, :has_issues => !has_issues}, :owner_name => @project.owner.uname, :project_name => @project.name + @project.reload.has_wiki.should == has_wiki + @project.reload.has_issues.should == has_issues + response.should redirect_to(forbidden_path) + end +end + describe Projects::ProjectsController do before(:each) do stub_symlink_methods @project = FactoryGirl.create(:project) - @another_user = FactoryGirl.create(:user) + @create_params = {:project => {:name => 'pro'}} @update_params = {:project => {:description => 'pro2'}} + + @user = FactoryGirl.create(:user) + set_session_for(@user) end - context 'for guest' do - it 'should not be able to perform index action' do - get :index - response.should redirect_to(new_user_session_path) + context 'for system users' do + + context 'guest' do + + before(:each) do + set_session_for(User.new) + end + + it 'should not be able to perform index action' do + get :index + response.should redirect_to(new_user_session_path) + end + + it 'should not be able to perform update action' do + put :update, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@update_params) + response.should redirect_to(new_user_session_path) + end end - it 'should not be able to perform update action' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@update_params) - response.should redirect_to(new_user_session_path) - end - end + context 'registered user' do + + it 'should be able to perform index action' do + get :index + response.should render_template(:index) + end + + context 'create project for myself' do + + it 'should be able to perform create action' do + post :create, @create_params + response.should redirect_to(project_path( Project.last )) + end + + it 'should create project in the database' do + lambda { post :create, @create_params }.should change{ Project.count }.by(1) + end + end + + context 'create project for group' do + + it 'should not be able to create project for alien group' do + group = FactoryGirl.create(:group) + post :create, @create_params.merge({:who_owns => 'group', :owner_id => group.id}) + response.should redirect_to(forbidden_path) + end + + it 'should be able to create project for their group' do + group = FactoryGirl.create(:group) + group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + lambda { post :create, @create_params.merge({:who_owns => 'group', :owner_id => group.id})}.should change{ Project.count }.by(1) + end + + it 'should be able to create project for own group' do + group = FactoryGirl.create(:group, :owner => @user) + lambda { post :create, @create_params.merge({:who_owns => 'group', :owner_id => group.id})}.should change{ Project.count }.by(1) + end + + end + + end # context 'registered user' + end # context 'for system users' + + context 'for project members' do + + context 'for global admin' do + before(:each) do + @user.role = "admin" + @user.save + set_session_for(@user) + end + + it_should_behave_like 'projects user with project admin rights' + it_should_behave_like 'projects user with reader rights' + it_should_behave_like 'user with destroy rights' - context 'for admin' do - before(:each) do - @admin = FactoryGirl.create(:admin) - set_session_for(@admin) end - it_should_behave_like 'projects user with admin rights' - it_should_behave_like 'projects user with reader rights' + context 'for owner user' do + before(:each) do + @user = @project.owner + set_session_for(@user) # owner should be user + end + + it_should_behave_like 'projects user with project admin rights' + it_should_behave_like 'projects user with reader rights' + it_should_behave_like 'user with destroy rights' + + it 'should not be able to fork own project' do + post :fork, :owner_name => @project.owner.uname, :project_name => @project.name + response.should redirect_to(@project) + end - it 'should be able to perform create action' do - post :create, @create_params - response.should redirect_to(project_path( Project.last )) end - it 'should change objects count on create' do - lambda { post :create, @create_params }.should change{ Project.count }.by(1) - end - end + context 'for reader user' do + before(:each) do + @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') + end - context 'for owner user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) - @project.update_attribute(:owner, @user) - @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') + it_should_behave_like 'projects user with reader rights' + it_should_behave_like 'projects user without project admin rights' end - it_should_behave_like 'projects user with admin rights' - it_should_behave_like 'user with rights to view projects' + context 'for writer user' do + before(:each) do + @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') + end + + it_should_behave_like 'projects user with reader rights' + it_should_behave_like 'projects user without project admin rights' - it 'should be able to perform destroy action' do - delete :destroy, {:owner_name => @project.owner.uname, :project_name => @project.name} - response.should redirect_to(@project.owner) end - it 'should change objects count on destroy' do - lambda { delete :destroy, :owner_name => @project.owner.uname, :project_name => @project.name }.should change{ Project.count }.by(-1) + context 'for other user' do + + it 'should not be able to fork hidden project' do + @project.update_attributes(:visibility => 'hidden') + post :fork, :owner_name => @project.owner.uname, :project_name => @project.name + response.should redirect_to(forbidden_path) + end + + it_should_behave_like 'projects user without project admin rights' + end - it 'should not be able to fork project' do - post :fork, :owner_name => @project.owner.uname, :project_name => @project.name - # @project.errors.count.should == 1 - response.should redirect_to(@project) - end - - end - - context 'for reader user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) - @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'reader') - end - - it_should_behave_like 'projects user with reader rights' - it_should_behave_like 'user without update rights' - end - - context 'for writer user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) - @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'writer') - end - - it_should_behave_like 'projects user with reader rights' - - it 'should not be able to create project to other group' do - group = FactoryGirl.create(:group) - post :create, @create_params.merge({:who_owns => 'group', :owner_id => group.id}) - response.should redirect_to(forbidden_path) - end - - it 'should not be able to fork project to other group' do - group = FactoryGirl.create(:group) - post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, :group => group.id - response.should redirect_to(forbidden_path) - end - - it 'should be able to fork project to group' do - group = FactoryGirl.create(:group) - group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') - post :fork, :owner_name => @project.owner.uname, :project_name => @project.name, :group => group.id - response.should redirect_to(project_path(group.projects.first)) - end - end - - context 'search projects' do - before(:each) do - @admin = FactoryGirl.create(:admin) - @project1 = FactoryGirl.create(:project, :name => 'perl-debug') - @project2 = FactoryGirl.create(:project, :name => 'perl') - set_session_for(@admin) - end - - pending 'should return projects in right order' do - get :index, :query => 'per' - assigns(:projects).should eq([@project2, @project1]) - end - end - - context 'for other user' do - before(:each) do - @user = FactoryGirl.create(:user) - set_session_for(@user) - end - - it 'should not be able to fork hidden project' do - @project.update_attribute(:visibility, 'hidden') - post :fork, :owner_name => @project.owner.uname, :project_name => @project.name - response.should redirect_to(forbidden_path) - end - - it_should_behave_like 'user without update rights' - end + end # context 'for project members' context 'for group' do before(:each) do @group = FactoryGirl.create(:group) - @group_user = FactoryGirl.create(:user) - @project.relations.destroy_all - set_session_for(@group_user) end - context 'owner of the project' do + context 'group is owner of the project' do before(:each) do - @project.update_attribute :owner, @group - @project.relations.create :actor_id => @project.owner.id, :actor_type => @project.owner.class.to_s, :role => 'admin' + @project = FactoryGirl.create(:project, :owner => @group) end - context 'reader user' do + context 'group member user with reader role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') end it_should_behave_like 'projects user with reader rights' - it_should_behave_like 'user without update rights' + it_should_behave_like 'projects user without project admin rights' it 'should has reader role to group project' do - @group_user.best_role(@project).should eql('reader') # Need this? + @user.best_role(@project).should eql('reader') end context 'user should has best role' do before(:each) do - @project.relations.create :actor_id => @group_user.id, :actor_type => @group_user.class.to_s, :role => 'admin' + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'admin' end - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' end end - context 'admin user' do + context 'group member user with admin role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') end - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' it_should_behave_like 'projects user with reader rights' end end - context 'member of the project' do + context 'group is member of the project' do context 'with admin rights' do before(:each) do @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'admin' end - context 'reader user' do + context 'group member user with reader role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') end it_should_behave_like 'projects user with reader rights' - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' context 'user should has best role' do before(:each) do - @project.relations.create :actor_id => @group_user.id, :actor_type => @group_user.class.to_s, :role => 'reader' + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'reader' end - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' end end - context 'admin user' do + context 'group member user with admin role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') end - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' it_should_behave_like 'projects user with reader rights' end end @@ -221,29 +281,29 @@ describe Projects::ProjectsController do @project.relations.create :actor_id => @group.id, :actor_type => @group.class.to_s, :role => 'reader' end - context 'reader user' do + context 'group member user with reader role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'reader') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'reader') end it_should_behave_like 'projects user with reader rights' - it_should_behave_like 'user without update rights' + it_should_behave_like 'projects user without project admin rights' context 'user should has best role' do before(:each) do - @project.relations.create :actor_id => @group_user.id, :actor_type => @group_user.class.to_s, :role => 'admin' + @project.relations.create :actor_id => @user.id, :actor_type => @user.class.to_s, :role => 'admin' end - it_should_behave_like 'projects user with admin rights' + it_should_behave_like 'projects user with project admin rights' end end - context 'admin user' do + context 'group member user with admin role' do before(:each) do - @group.actors.create(:actor_id => @group_user.id, :actor_type => 'User', :role => 'admin') + @group.actors.create(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') end it_should_behave_like 'projects user with reader rights' - it_should_behave_like 'user without update rights' + it_should_behave_like 'projects user without project admin rights' end end end diff --git a/spec/factories/build_lists.rb b/spec/factories/build_lists.rb index 0271f50ea..5f2b0d574 100644 --- a/spec/factories/build_lists.rb +++ b/spec/factories/build_lists.rb @@ -6,6 +6,7 @@ FactoryGirl.define do association :save_to_platform, :factory => :platform_with_repos association :arch build_for_platform {|bl| bl.save_to_platform} + save_to_repository {|bl| bl.save_to_platform.repositories.first} project_version "1.0" build_requires true update_type 'security' diff --git a/spec/factories/product_build_lists.rb b/spec/factories/product_build_lists.rb index ef7642340..9e99cbcc1 100644 --- a/spec/factories/product_build_lists.rb +++ b/spec/factories/product_build_lists.rb @@ -2,5 +2,6 @@ FactoryGirl.define do factory :product_build_list do association :product, :factory => :product + status 0 # BUILD_COMPLETED end end diff --git a/spec/models/build_list_spec.rb b/spec/models/build_list_spec.rb new file mode 100644 index 000000000..421e39726 --- /dev/null +++ b/spec/models/build_list_spec.rb @@ -0,0 +1,146 @@ +# -*- encoding : utf-8 -*- +require 'spec_helper' + +describe BuildList do + + context "#notify_users" do + before { stub_symlink_methods } + let!(:user) { FactoryGirl.create(:user) } + let!(:build_list) { FactoryGirl.create(:build_list_core, + :user => user, + :auto_publish => false) } + let!(:build_list_package) { FactoryGirl.create(:build_list_package, + :build_list => build_list, + :project => build_list.project) } + + + before(:all) { ActionMailer::Base.deliveries = [] } + before do + test_git_commit(build_list.project) + build_list.update_attributes(:commit_hash => build_list.project.repo.commits('master').last.id, + :status => BuildServer::BUILD_STARTED,) + end + after { ActionMailer::Base.deliveries = [] } + + shared_examples_for 'build list notifications by email' do + it "gets notification by email when status - Build complete" do + build_list.build_success + should have(1).item + end + + it "gets notification by email when status - Build error" do + build_list.build_error + should have(1).item + end + + it "gets notification by email when auto_publish and status - Build error" do + build_list.update_attributes(:auto_publish => true) + build_list.build_error + should have(1).item + end + + it "gets notification by email when status - Failed publish" do + build_list.update_attributes(:status => BuildList::BUILD_PUBLISH) + build_list.fail_publish + should have(1).item + end + + it "gets notification by email when auto_publish and status - Failed publish" do + build_list.update_attributes(:auto_publish => true, :status => BuildList::BUILD_PUBLISH) + build_list.fail_publish + should have(1).item + end + + it "gets notification by email when status - Build published" do + build_list.update_attributes(:status => BuildList::BUILD_PUBLISH) + build_list.published + should have(1).item + end + + it "gets notification by email when auto_publish and status - Build published" do + build_list.update_attributes(:auto_publish => true, :status => BuildList::BUILD_PUBLISH) + build_list.published + should have(1).item + end + + it "doesn't get notification by email when auto_publish and status - Build complete" do + build_list.update_attributes(:auto_publish => true) + build_list.build_success + should have(:no).items + end + + it "doesn't get notification by email when mass build" do + build_list.update_attributes(:mass_build_id => 1, :status => BuildList::BUILD_PUBLISH) + build_list.published + should have(:no).items + end + + it "doesn't get notification by email when notification by email has been disabled" do + notifier.update_attributes(:can_notify => false) + build_list.build_success + should have(:no).items + end + end + + subject { ActionMailer::Base.deliveries } + + context "user created build task" do + let!(:notifier) { user.notifier } + before do + notifier.update_attributes(:new_associated_build => false) + build_list.project.owner.notifier.update_attributes(:can_notify => false) + end + + it_should_behave_like 'build list notifications by email' + + it "doesn't get notification by email when 'build list' notifications has been disabled" do + notifier.update_attributes(:new_build => false) + build_list.build_success + should have(:no).items + end + + it "doesn't get notification by email when 'build list' notifications - enabled, email notifications - disabled" do + notifier.update_attributes(:can_notify => false, :new_build => true) + build_list.build_success + should have(:no).items + end + end + + context "build task has been created and associated user" do + let!(:notifier) { build_list.project.owner.notifier } + before do + notifier.update_attributes(:new_build => false) + user.notifier.update_attributes(:can_notify => false) + end + + it_should_behave_like 'build list notifications by email' + + it "doesn't get notification by email when 'associated build list' notifications has been disabled" do + notifier.update_attributes(:new_associated_build => false) + build_list.build_success + should have(:no).items + end + + it "doesn't get notification by email when 'associated build list' notifications - enabled, email notifications - disabled" do + notifier.update_attributes(:can_notify => false, :new_associated_build => true) + build_list.build_success + should have(:no).items + end + end + + it "doesn't get 2 notification by email when user associated to project and created task" do + bl = FactoryGirl.create(:build_list_core, + :user => user, + :auto_publish => true, + :project => FactoryGirl.create(:project, :owner => user)) + FactoryGirl.create(:build_list_package, :build_list => bl, :project => bl.project) + test_git_commit(bl.project) + bl.update_attributes(:commit_hash => bl.project.repo.commits('master').last.id, + :status => BuildList::BUILD_PUBLISH) + bl.published + should have(1).item + end + + end # notify_users + +end diff --git a/spec/models/cancan_spec.rb b/spec/models/cancan_spec.rb index 467b1e275..5f0ba821a 100644 --- a/spec/models/cancan_spec.rb +++ b/spec/models/cancan_spec.rb @@ -103,15 +103,15 @@ describe CanCan do @ability.should be_able_to(:read, @admin) end - pending "shoud be able to read index AutoBuildList" do - @ability.should be_able_to(:index, AutoBuildList) - end - it "shoud be able to read open projects" do @project = FactoryGirl.create(:project, :visibility => 'open') @ability.should be_able_to(:read, @project) end + it 'should be able to see open platform' do + @ability.should be_able_to(:show, open_platform) + end + it "shoud be able to create project" do @ability.should be_able_to(:create, Project) end @@ -123,7 +123,9 @@ describe CanCan do context "private users relations" do before(:each) do @private_user = FactoryGirl.create(:private_user) - @private_user.platform.update_attribute(:owner, @user) + + @private_user.platform.owner = @user + @private_user.platform.save end [:read, :create].each do |action| @@ -148,10 +150,6 @@ describe CanCan do @ability.should be_able_to(:read, @project) end - it 'should be able to read open platform' do - @ability.should be_able_to(:read, open_platform) - end - it 'should be able to read issue' do @ability.should be_able_to(:read, @issue) end @@ -207,7 +205,9 @@ describe CanCan do context 'with owner rights' do before(:each) do - @project.update_attribute(:owner, @user) + @project.owner = @user + @project.save + @project.relations.create!(:actor_id => @user.id, :actor_type => 'User', :role => 'admin') @issue.project.reload end @@ -241,7 +241,8 @@ describe CanCan do context 'with owner rights' do before(:each) do - @platform.update_attribute(:owner, @user) + @platform.owner = @user + @platform.save end [:read, :update, :destroy].each do |action| @@ -269,7 +270,8 @@ describe CanCan do context 'with owner rights' do before(:each) do - @repository.platform.update_attribute(:owner, @user) + @repository.platform.owner = @user + @repository.platform.save end [:read, :create, :update, :destroy, :add_project, :remove_project, :change_visibility, :settings].each do |action| diff --git a/spec/models/comment_for_commit_spec.rb b/spec/models/comment_for_commit_spec.rb index b56769e26..c27479215 100644 --- a/spec/models/comment_for_commit_spec.rb +++ b/spec/models/comment_for_commit_spec.rb @@ -91,7 +91,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_repo_owner' do it 'should not send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_repo_owner, false + @user.notifier.update_column :new_comment_commit_repo_owner, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 end @@ -99,7 +99,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_owner' do it 'should send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_owner, false + @user.notifier.update_column :new_comment_commit_owner, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@user.email).should == true @@ -108,7 +108,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_commentor' do it 'should send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_commentor, false + @user.notifier.update_column :new_comment_commit_commentor, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@user.email).should == true @@ -117,9 +117,9 @@ describe Comment do context 'for disabled all notify setting expect global' do it 'should not send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_repo_owner, false - @user.notifier.update_attribute :new_comment_commit_owner, false - @user.notifier.update_attribute :new_comment_commit_commentor, false + @user.notifier.update_column :new_comment_commit_repo_owner, false + @user.notifier.update_column :new_comment_commit_owner, false + @user.notifier.update_column :new_comment_commit_commentor, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 0 end @@ -135,7 +135,7 @@ describe Comment do context 'for disabled global notify setting' do it 'should not send an e-mail' do - @user.notifier.update_attribute :can_notify, false + @user.notifier.update_column :can_notify, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 0 end @@ -148,7 +148,10 @@ describe Comment do @user = FactoryGirl.create(:user) @stranger = FactoryGirl.create(:user) set_comments_data_for_commit - @project.update_attribute(:owner, @user) + + @project.owner = @user + @project.save + ActionMailer::Base.deliveries = [] end @@ -178,7 +181,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_repo_owner' do it 'should not send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_repo_owner, false + @user.notifier.update_column :new_comment_commit_repo_owner, false Comment.destroy_all comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 0 @@ -187,7 +190,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_owner' do it 'should send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_owner, false + @user.notifier.update_column :new_comment_commit_owner, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@user.email).should == true @@ -196,7 +199,7 @@ describe Comment do context 'for disabled notify setting new_comment_commit_commentor' do it 'should send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_commentor, false + @user.notifier.update_column :new_comment_commit_commentor, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@user.email).should == true @@ -205,9 +208,9 @@ describe Comment do context 'for disabled all notify setting expect global' do it 'should not send an e-mail' do - @user.notifier.update_attribute :new_comment_commit_repo_owner, false - @user.notifier.update_attribute :new_comment_commit_owner, false - @user.notifier.update_attribute :new_comment_commit_commentor, false + @user.notifier.update_column :new_comment_commit_repo_owner, false + @user.notifier.update_column :new_comment_commit_owner, false + @user.notifier.update_column :new_comment_commit_commentor, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 0 end @@ -223,7 +226,7 @@ describe Comment do context 'for disabled global notify setting' do it 'should not send an e-mail' do - @user.notifier.update_attribute :can_notify, false + @user.notifier.update_column :can_notify, false comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 0 end @@ -231,7 +234,7 @@ describe Comment do context 'for own commit' do it 'should send a one e-mail' do - @project.owner.update_attribute :email, 'code@tpope.net' + @project.owner.update_column :email, 'code@tpope.net' comment = create_comment(@stranger) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@project.owner.email).should == true @@ -298,8 +301,7 @@ describe Comment do context 'for committer' do it 'should send an e-mail' do - @simple.subscribes.destroy_all - @simple.update_attribute :email, 'test@test.test' + @simple.update_column :email, 'test@test.test' comment = create_comment(@user) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@simple.email).should == true @@ -307,31 +309,30 @@ describe Comment do it 'should send a one e-mail when subscribed to commit' do Subscribe.subscribe_to_commit @subscribe_params.merge(:user_id => @simple.id) - @simple.update_attribute :email, 'test@test.test' + @simple.update_column :email, 'test@test.test' comment = create_comment(@user) ActionMailer::Base.deliveries.count.should == 1 ActionMailer::Base.deliveries.last.to.include?(@simple.email).should == true end it 'should not send an e-mail for own comment' do - @simple.subscribes.destroy_all - @simple.update_attribute :email, 'test@test.test' + @simple.update_column :email, 'test@test.test' comment = create_comment(@simple) ActionMailer::Base.deliveries.count.should == 0 end it 'should not send an e-mail if global notify off' do - @project.owner.notifier.update_attribute :can_notify, false - @simple.update_attribute :email, 'test@test.test' - @simple.notifier.update_attribute :can_notify, false + @project.owner.notifier.update_column :can_notify, false + @simple.update_column :email, 'test@test.test' + @simple.notifier.update_column :can_notify, false comment = create_comment(@user) ActionMailer::Base.deliveries.count.should == 0 end it 'should not send an e-mail if notify for my commits off' do Comment.destroy_all - @simple.notifier.update_attribute :new_comment_commit_owner, false - @simple.update_attribute :email, 'test@test.test' + @simple.notifier.update_column :new_comment_commit_owner, false + @simple.update_column :email, 'test@test.test' comment = create_comment(@user) ActionMailer::Base.deliveries.count.should == 0 end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index afc99e0bc..6afc22242 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -84,7 +84,8 @@ describe Comment do set_commentable_data - @project.update_attribute(:owner, @user) + @project.owner = @user + @project.save @project.relations.create!(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 36209c472..e1bede7bf 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -61,8 +61,11 @@ describe Group do context 'for group owner' do before(:each) do - @user = FactoryGirl.create(:user) - @group.update_attribute(:owner, @user) + @user = FactoryGirl.create(:user) + + @group.owner = @user + @group.save + @group.actors.create(:actor_type => 'User', :actor_id => @user.id, :role => 'admin') @ability = Ability.new(@user) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b50c1e6a4..ab13f6ae3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Project do - before(:each) do + before do stub_symlink_methods @root_project = FactoryGirl.create(:project) @child_project = @root_project.fork(FactoryGirl.create(:user)) @@ -10,7 +10,7 @@ describe Project do end context 'for destroy root' do - before(:each) do + before do @root_project.destroy end @@ -23,6 +23,31 @@ describe Project do end end + context 'attach personal repository' do + let(:user) { FactoryGirl.create(:user) } + it "ensures that personal repository has been attached when project had been created as package" do + project = FactoryGirl.create(:project, :owner => user, :is_package => true) + project.repositories.should == [user.personal_repository] + end + + it "ensures that personal repository has not been attached when project had been created as not package" do + project = FactoryGirl.create(:project, :owner => user, :is_package => false) + project.repositories.should have(:no).items + end + + it "ensures that personal repository has been attached when project had been updated as package" do + project = FactoryGirl.create(:project, :owner => user, :is_package => false) + project.update_attribute(:is_package, true) + project.repositories.should == [user.personal_repository] + end + + it "ensures that personal repository has been removed from project when project had been updated as not package" do + project = FactoryGirl.create(:project, :owner => user, :is_package => true) + project.update_attribute(:is_package, false) + project.repositories.should have(:no).items + end + end + # uncommit when will be available :orphan_strategy => :adopt #context 'for destroy middle node' do @@ -38,4 +63,4 @@ describe Project do # Project.where(:id => @child_child_project).count.should == 1 # end #end -end \ No newline at end of file +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8e61b75c6..75e39175d 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -45,7 +45,9 @@ describe User do context 'for group project' do before(:each) do @project.relations.destroy_all - @project.update_attribute :owner, @group + + @project.owner = @group + @project.save @project.relations.create :actor_id => @project.owner.id, :actor_type => @project.owner.class.to_s, :role => 'admin' end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a29d72fa6..e7ce00299 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -25,6 +25,9 @@ RSpec.configure do |config| # examples within a transaction, remove the following line or assign false # instead of true. config.use_transactional_fixtures = true + + config.filter_run_excluding :anonymous_access => !(APP_CONFIG['anonymous_access']) + end def set_session_for(user=nil) diff --git a/spec/support/shared_examples/projects_controller.rb b/spec/support/shared_examples/projects_controller.rb deleted file mode 100644 index 0e4b4aff2..000000000 --- a/spec/support/shared_examples/projects_controller.rb +++ /dev/null @@ -1,42 +0,0 @@ -# -*- encoding : utf-8 -*- -shared_examples_for 'projects user with reader rights' do - include_examples 'user with rights to view projects' # nested shared_examples_for dont work - - it 'should be able to fork project' do - post :fork, :owner_name => @project.owner.uname, :project_name => @project.name - response.should redirect_to(project_path(Project.last)) - end - -end - -shared_examples_for 'projects user with admin rights' do - it 'should be able to perform update action' do - put :update, {:owner_name => @project.owner.uname, :project_name => @project.name}.merge(@update_params) - response.should redirect_to(project_path(@project)) - end -end - -shared_examples_for 'user with rights to view projects' do - it 'should be able to perform index action' do - get :index - response.should render_template(:index) - end -end - -shared_examples_for 'user without update rights' do - it 'should not be able to edit project' do - description = @project.description - put :update, :project=>{:description =>"hack"}, :owner_name => @project.owner.uname, :project_name => @project.name - Project.find(@project.id).description.should == description - response.should redirect_to(forbidden_path) - end - - it 'should not be able to edit project sections' do - has_wiki, has_issues = @project.has_wiki, @project.has_issues - post :sections, :project =>{:has_wiki => !has_wiki, :has_issues => !has_issues}, :owner_name => @project.owner.uname, :project_name => @project.name - project = Project.find(@project.id) - project.has_wiki.should == has_wiki - project.has_issues.should == has_issues - response.should redirect_to(forbidden_path) - end -end diff --git a/vendor/assets/javascripts/bootstrap-tab.js b/vendor/assets/javascripts/bootstrap-tab.js index b3938f671..a26da9b6c 100644 --- a/vendor/assets/javascripts/bootstrap-tab.js +++ b/vendor/assets/javascripts/bootstrap-tab.js @@ -127,4 +127,4 @@ }) }) -}( window.jQuery ); \ No newline at end of file +}( window.jQuery ); diff --git a/vendor/assets/javascripts/vendor.js b/vendor/assets/javascripts/vendor.js index b02bf3d4a..08fc17f77 100644 --- a/vendor/assets/javascripts/vendor.js +++ b/vendor/assets/javascripts/vendor.js @@ -14,6 +14,7 @@ // require bootstrap-tooltip // require bootstrap-popover //= require bootstrap-alert +//= require bootstrap-tab //= require chosen.jquery // require html5shiv // require_tree . diff --git a/vendor/assets/stylesheets/bootstrap.css b/vendor/assets/stylesheets/bootstrap.css index 6f3753414..f5b1a58bc 100644 --- a/vendor/assets/stylesheets/bootstrap.css +++ b/vendor/assets/stylesheets/bootstrap.css @@ -422,7 +422,7 @@ } .nav { margin-left: 0; - margin-bottom: 18px; + margin-bottom: 20px; list-style: none; } .nav > li > a { @@ -432,12 +432,15 @@ text-decoration: none; background-color: #eeeeee; } -.nav .nav-header { +.nav > .pull-right { + float: right; +} +.nav-header { display: block; padding: 3px 15px; font-size: 11px; font-weight: bold; - line-height: 18px; + line-height: 20px; color: #999999; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-transform: uppercase; @@ -469,13 +472,13 @@ margin-right: 2px; } .nav-list .divider { + *width: 100%; height: 1px; - margin: 8px 1px; + margin: 9px 1px; + *margin: -5px 0 5px; overflow: hidden; background-color: #e5e5e5; border-bottom: 1px solid #ffffff; - *width: 100%; - *margin: -5px 0 5px; } .nav-tabs, .nav-pills { @@ -487,6 +490,7 @@ .nav-pills:after { display: table; content: ""; + line-height: 0; } .nav-tabs:after, .nav-pills:after { @@ -512,7 +516,7 @@ .nav-tabs > li > a { padding-top: 8px; padding-bottom: 8px; - line-height: 18px; + line-height: 20px; border: 1px solid transparent; -webkit-border-radius: 4px 4px 0 0; -moz-border-radius: 4px 4px 0 0; @@ -559,14 +563,20 @@ border-radius: 0; } .nav-tabs.nav-stacked > li:first-child > a { - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; } .nav-tabs.nav-stacked > li:last-child > a { - -webkit-border-radius: 0 0 4px 4px; - -moz-border-radius: 0 0 4px 4px; - border-radius: 0 0 4px 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; } .nav-tabs.nav-stacked > li > a:hover { border-color: #ddd; @@ -578,46 +588,50 @@ .nav-pills.nav-stacked > li:last-child > a { margin-bottom: 1px; } -.nav-tabs .dropdown-menu, -.nav-pills .dropdown-menu { - margin-top: 1px; - border-width: 1px; +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; } .nav-pills .dropdown-menu { - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; } -.nav-tabs .dropdown-toggle .caret, -.nav-pills .dropdown-toggle .caret { +.nav .dropdown-toggle .caret { border-top-color: #0088cc; border-bottom-color: #0088cc; margin-top: 6px; } -.nav-tabs .dropdown-toggle:hover .caret, -.nav-pills .dropdown-toggle:hover .caret { +.nav .dropdown-toggle:hover .caret { border-top-color: #005580; border-bottom-color: #005580; } -.nav-tabs .active .dropdown-toggle .caret, -.nav-pills .active .dropdown-toggle .caret { - border-top-color: #333333; - border-bottom-color: #333333; +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; } .nav > .dropdown.active > a:hover { - color: #000000; cursor: pointer; } .nav-tabs .open .dropdown-toggle, .nav-pills .open .dropdown-toggle, -.nav > .open.active > a:hover { +.nav > li.dropdown.open.active > a:hover { color: #ffffff; background-color: #999999; border-color: #999999; } -.nav .open .caret, -.nav .open.active .caret, -.nav .open a:hover .caret { +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret { border-top-color: #ffffff; border-bottom-color: #ffffff; opacity: 1; @@ -633,17 +647,17 @@ .tabbable:after { display: table; content: ""; + line-height: 0; } .tabbable:after { clear: both; } .tab-content { - display: table; - width: 100%; + overflow: auto; } -.tabs-below .nav-tabs, -.tabs-right .nav-tabs, -.tabs-left .nav-tabs { +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { border-bottom: 0; } .tab-content > .tab-pane, @@ -654,74 +668,83 @@ .pill-content > .active { display: block; } -.tabs-below .nav-tabs { +.tabs-below > .nav-tabs { border-top: 1px solid #ddd; } -.tabs-below .nav-tabs > li { +.tabs-below > .nav-tabs > li { margin-top: -1px; margin-bottom: 0; } -.tabs-below .nav-tabs > li > a { +.tabs-below > .nav-tabs > li > a { -webkit-border-radius: 0 0 4px 4px; -moz-border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px; } -.tabs-below .nav-tabs > li > a:hover { +.tabs-below > .nav-tabs > li > a:hover { border-bottom-color: transparent; border-top-color: #ddd; } -.tabs-below .nav-tabs .active > a, -.tabs-below .nav-tabs .active > a:hover { +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover { border-color: transparent #ddd #ddd #ddd; } -.tabs-left .nav-tabs > li, -.tabs-right .nav-tabs > li { +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { float: none; } -.tabs-left .nav-tabs > li > a, -.tabs-right .nav-tabs > li > a { +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { min-width: 74px; margin-right: 0; margin-bottom: 3px; } -.tabs-left .nav-tabs { +.tabs-left > .nav-tabs { float: left; margin-right: 19px; border-right: 1px solid #ddd; } -.tabs-left .nav-tabs > li > a { +.tabs-left > .nav-tabs > li > a { margin-right: -1px; -webkit-border-radius: 4px 0 0 4px; -moz-border-radius: 4px 0 0 4px; border-radius: 4px 0 0 4px; } -.tabs-left .nav-tabs > li > a:hover { +.tabs-left > .nav-tabs > li > a:hover { border-color: #eeeeee #dddddd #eeeeee #eeeeee; } -.tabs-left .nav-tabs .active > a, -.tabs-left .nav-tabs .active > a:hover { +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover { border-color: #ddd transparent #ddd #ddd; *border-right-color: #ffffff; } -.tabs-right .nav-tabs { +.tabs-right > .nav-tabs { float: right; margin-left: 19px; border-left: 1px solid #ddd; } -.tabs-right .nav-tabs > li > a { +.tabs-right > .nav-tabs > li > a { margin-left: -1px; -webkit-border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; border-radius: 0 4px 4px 0; } -.tabs-right .nav-tabs > li > a:hover { +.tabs-right > .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #eeeeee #dddddd; } -.tabs-right .nav-tabs .active > a, -.tabs-right .nav-tabs .active > a:hover { +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover { border-color: #ddd #ddd #ddd transparent; *border-left-color: #ffffff; } +.nav > .disabled > a { + color: #999999; +} +.nav > .disabled > a:hover { + text-decoration: none; + background-color: transparent; + cursor: default; +} + .label-bootstrap, .badge { @@ -792,3 +815,4 @@ a.badge:hover { .badge-inverse[href] { background-color: #1a1a1a; } +