[issue #347] Merge branch 'master' into 347-javascript_refactoring

Conflicts:
	app/assets/stylesheets/design/custom.scss
	app/models/group.rb
	app/models/user.rb
This commit is contained in:
George Vinogradov 2012-04-19 19:32:11 +04:00
commit 510b0c0c50
185 changed files with 2549 additions and 1171 deletions

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -60,4 +60,15 @@ $(document).ready(function() {
return false;
});
$('.description-top .git_help').click(function() {
$('#git_help_data').toggle();
var desc = $('.description-top');
if ($('#git_help_data').css('display') == 'none') {
desc.css('height', '38px');
} else {
desc.css('height', '196px');
}
});
});

View File

@ -3,32 +3,35 @@ $(document).ready(function() {
var platform_id = $(this).val();
var base_platforms = $('.all_platforms input[type=checkbox].build_bpl_ids');
//$('#include_repos').html($('.preloaded_include_repos .include_repos_' + platform_id).html());
base_platforms.each(function(){
if ($.inArray(platform_id, base_platforms.map(function(){ return $(this).val() }).get()) >= 0) {
if ($(this).val() == platform_id) {
$(this).attr('checked', 'checked');
$(this).removeAttr('disabled');
$(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled');
if ($(this).parent().find('.offset25 label').text() == 'main') {
$(this).parent().find('.offset25 input[type="checkbox"]').attr('checked', 'checked');
if ($(this).attr('data-released') === '1') {
$('#build_list_auto_publish').removeAttr('checked').attr('disabled', 'disabled');
} else {
$('#build_list_auto_publish').removeAttr('disabled').attr('checked', 'checked');
}
$(this).attr('checked', 'checked').removeAttr('disabled').trigger('change');
$(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled');
var rep_name = $('#build_list_pl_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');
$(this).attr('disabled', 'disabled');
$(this).parent().find('.offset25 input[type="checkbox"]').attr('disabled', 'disabled');
$(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('checked');
$(this).removeAttr('checked').attr('disabled', 'disabled').trigger('change');
$(this).parent().find('.offset25 input[type="checkbox"]').attr('disabled', 'disabled').removeAttr('checked');
}
//$('.additional_pl').parent().find('.offset25 input[type="checkbox"]').attr('disabled', 'disabled');
} else {
$(this).removeAttr('disabled');
$(this).removeAttr('checked');
$(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled');
$(this).parent().find('.offset25 input[type="checkbox"]').removeAttr('checked');
//$('.additional_pl').parent().find('.offset25 input[type="checkbox"]').removeAttr('disabled');
$(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');
}
});
setBranchSelected();
});
$('#build_list_pl_id').trigger('change');
@ -38,11 +41,45 @@ $(document).ready(function() {
$('.offset25 input[type="checkbox"]').click(function() {
setPlChecked(this, $(this).attr('checked'));
});
$('.build_bpl_ids').click(function() {
return false;
});
});
function setPlChecked(pointer, checked) {
pl_cbx = $(pointer).parent().parent().parent().find('input[type="checkbox"].build_bpl_ids');
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');
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() {
var pl_id = $('#build_list_pl_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_pl_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 platformChange() {
var rel = !!$('input[type="checkbox"].build_bpl_ids').filter(function(index) {
var $this = $(this);
return !!$this.attr('checked') && ($this.attr('data-released') === '1');
}).length;
if (rel) {
$('#build_list_auto_publish').removeAttr('checked').attr('disabled', 'disabled');
} else {
$('#build_list_auto_publish').removeAttr('disabled').attr('checked', 'checked');
}
}

View File

@ -128,28 +128,28 @@ $(document).ready(function() {
return false;
});
function remExecutor(form) {
var el = form.find('.people.selected.remove_executor');
function remAssignee(form) {
var el = form.find('.people.selected.remove_assignee');
var id = el.attr('id');
$('#manage_issue_users_list .add_executor.people.selected').removeClass('select');
$('#manage_issue_users_list .add_assignee.people.selected').removeClass('select');
el.remove();
}
$('.add_executor.people.selected').live('click', function() {
$('.add_assignee.people.selected').live('click', function() {
var form_new = $('form.issue');
var form_edit = $('form.edit_form.issue');
form_new.find('#people-span').fadeOut(0);
remExecutor(form_new);
var clone = $(this).clone().removeClass('add_executor').addClass('remove_executor');
form_new.find('#issue_executor').html(clone);
$('.current_executor').html(clone.removeClass('select'));
remAssignee(form_new);
var clone = $(this).clone().removeClass('add_assignee').addClass('remove_assignee');
form_new.find('#issue_assignee').html(clone);
$('.current_assignee').html(clone.removeClass('select'));
$(this).addClass('select');
});
$('.remove_executor.people.selected').live('click', function() {
$('.remove_assignee.people.selected').live('click', function() {
var form = $('form.issue, form.edit_form issue');
form.find('#people-span').fadeIn(0);
remExecutor(form);
remAssignee(form);
});
function remLabel(form, id) {
@ -241,9 +241,9 @@ $(document).ready(function() {
return false;
});
$('.button.manage_executor').live('click', function() {
$('form#search_user, .button.update_executor').fadeIn(0);
$('.current_executor .people').addClass('remove_executor selected').removeClass('nopointer');
$('.button.manage_assignee').live('click', function() {
$('form#search_user, .button.update_assignee').fadeIn(0);
$('.current_assignee .people').addClass('remove_assignee selected').removeClass('nopointer');
$(this).fadeOut(0);
});
@ -254,16 +254,16 @@ $(document).ready(function() {
$(this).fadeOut(0);
});
$('.button.update_executor').live('click', function() {
var form = $('form.edit_executor.issue');
$('.button.update_assignee').live('click', function() {
var form = $('form.edit_assignee.issue');
$.ajax({
type: 'POST',
url: form.attr("action"),
data: form.serialize(),
success: function(data){
$('.current_executor .people').removeClass('remove_executor selected').addClass('nopointer');
$('form#search_user, .button.update_executor').fadeOut(0);
$('.button.manage_executor').fadeIn(0);
$('.current_assignee .people').removeClass('remove_assignee selected').addClass('nopointer');
$('form#search_user, .button.update_assignee').fadeOut(0);
$('.button.manage_assignee').fadeIn(0);
$('#manage_issue_users_list').html('');
},
error: function(data){

View File

@ -851,3 +851,73 @@ input.placeholder,
textarea.placeholder {
color: #CFCFCF;
}
div.description-top div.git_help {
float: left;
margin-top: 11px;
margin-left: 10px;
font-size: 11px;
color: green;
cursor: pointer;
}
div#git_help_data {
display: none;
padding-left: 10px;
}
div#git_help_data p {
padding-bottom: 5px;
}
// for bootstrap
.close {
float: right;
font-size: 20px;
font-weight: bold;
line-height: 18px;
color: #000000;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.2;
filter: alpha(opacity=20);
}
.close:hover {
color: #000000;
text-decoration: none;
opacity: 0.4;
filter: alpha(opacity=40);
cursor: pointer;
}
.modal {
margin: -150px 0 0 -280px;
}
#forkModal.modal .btn.btn-primary {
width: 100%;
}
.center {
text-align: center;
}
.zip {
float: left;
padding-left: 5px;
margin-top: 6px;
list-style: none;
}
.nav > li > a:hover {
text-decoration: none;
background-color: #DCECFA;
}
.dropdown-menu {
min-width: 0;
}
.dropdown.open .dropdown-toggle {
background: none repeat scroll 0 0;
}

View File

@ -7,10 +7,9 @@ class ActivityFeedsController < ApplicationController
@activity_feeds = current_user.activity_feeds
@activity_feeds = @activity_feeds.where(:kind => "ActivityFeed::#{@filter.upcase}".constantize) unless @filter == :all
@activity_feeds = @activity_feeds.paginate :page => params[:page]
if request.format == '*/*'
render '_list', :layout => false
else
render 'index'
respond_to do |format|
format.html { request.xhr? ? render('_list', :layout => false) : render('index') }
format.atom
end
end
end

View File

@ -4,6 +4,9 @@ class ApplicationController < ActionController::Base
layout :layout_by_resource
# Hack to prevent token auth on all pages except atom feed:
prepend_before_filter lambda { redirect_to(new_user_session_path) if params[:token] && params[:format] != 'atom'}
before_filter :set_locale
before_filter lambda { EventLog.current_controller = self },
:only => [:create, :destroy, :open_id, :cancel, :publish, :change_visibility] # :update

View File

@ -7,8 +7,9 @@ class BuildListsController < ApplicationController
before_filter :authenticate_build_service!, :only => CALLBACK_ACTIONS
skip_before_filter :authenticate_user!, :only => [:show, :index, :search] if APP_CONFIG['anonymous_access']
before_filter :find_project, :only => NESTED_ACTIONS
before_filter :find_build_list, :only => [:show, :publish, :cancel]
before_filter :find_build_list, :only => [:show, :publish, :cancel, :reject_publish]
before_filter :find_build_list_by_bs, :only => [:publish_build, :status_build, :pre_build, :post_build, :circle_build]
before_filter :find_platform, :only => [:create]
load_and_authorize_resource :project, :only => NESTED_ACTIONS
load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true
@ -40,12 +41,13 @@ class BuildListsController < ApplicationController
def create
notices, errors = [], []
params[:build_list].delete(:auto_publish) if @platform.released
Arch.where(:id => params[:arches]).each do |arch|
Platform.main.where(:id => params[:bpls]).each do |bpl|
@build_list = @project.build_lists.build(params[:build_list])
@build_list.commit_hash = @project.git_repository.commits(@build_list.project_version.match(/^latest_(.+)/).to_a.last || @build_list.project_version).first.id if @build_list.project_version
@build_list.bpl = bpl; @build_list.arch = arch; @build_list.user = current_user
@build_list.include_repos = @build_list.include_repos.select { |ir| @build_list.bpl.repository_ids.include? ir.to_i }
@build_list.include_repos = @build_list.include_repos.select {|ir| @build_list.bpl.repository_ids.include? ir.to_i}
@build_list.priority = 100 # User builds more priority than mass rebuild with zero priority
flash_options = {:project_version => @build_list.project_version, :arch => arch.name, :bpl => bpl.name, :pl => @build_list.pl}
if @build_list.save
@ -78,6 +80,14 @@ class BuildListsController < ApplicationController
end
end
def reject_publish
if @build_list.reject_publish
redirect_to :back, :notice => t('layout.build_lists.reject_publish_success')
else
redirect_to :back, :notice => t('layout.build_lists.reject_publish_fail')
end
end
def cancel
if @build_list.cancel
redirect_to :back, :notice => t('layout.build_lists.cancel_success')
@ -94,7 +104,6 @@ class BuildListsController < ApplicationController
else
@build_list.status = BuildList::FAILED_PUBLISH
end
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -106,7 +115,6 @@ class BuildListsController < ApplicationController
@item.save
@build_list.container_path = params[:container_path]
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -114,7 +122,6 @@ class BuildListsController < ApplicationController
def pre_build
@build_list.status = BuildServer::BUILD_STARTED
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -123,7 +130,6 @@ class BuildListsController < ApplicationController
def post_build
@build_list.status = params[:status]
@build_list.container_path = params[:container_path]
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -134,7 +140,6 @@ class BuildListsController < ApplicationController
def circle_build
@build_list.is_circle = true
@build_list.container_path = params[:container_path]
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -147,7 +152,6 @@ class BuildListsController < ApplicationController
@build_list.set_items(ActiveSupport::JSON.decode(params[:items]))
@build_list.is_circle = (params[:is_circular].to_i != 0)
@build_list.bs_id = params[:id]
@build_list.notified_at = Time.current
@build_list.save
render :nothing => true, :status => 200
@ -159,6 +163,10 @@ class BuildListsController < ApplicationController
@project = Project.find_by_id params[:project_id]
end
def find_platform
@platform = Platform.find params[:build_list][:pl_id]
end
def find_build_list
@build_list = BuildList.find(params[:id])
end

View File

@ -1,26 +1,17 @@
# -*- encoding : utf-8 -*-
class CommentsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource :project
before_filter :find_commentable
before_filter :find_or_build_comment
load_and_authorize_resource
load_resource :project
before_filter :set_commentable
before_filter :find_comment, :only => [:edit, :update, :destroy]
authorize_resource
def index
@comments = @commentable.comments
end
include CommentsHelper
def create
@comment = @commentable.comments.build(params[:comment]) if @commentable.class == Issue
if @commentable.class == Grit::Commit
@comment = Comment.new(params[:comment].merge(:commentable_id => @commentable.id.hex, :commentable_type => @commentable.class.name))
end
@comment.project = @project
@comment.user_id = current_user.id
if @comment.save
flash[:notice] = I18n.t("flash.comment.saved")
redirect_to commentable_path
redirect_to project_commentable_path(@project, @commentable)
else
flash[:error] = I18n.t("flash.comment.save_error")
render :action => 'new'
@ -28,19 +19,12 @@ class CommentsController < ApplicationController
end
def edit
@update_url = case @commentable.class.name
when "Issue"
project_issue_comment_path(@project, @commentable, @comment)
when "Grit::Commit"
project_commit_comment_path(@project, @commentable, @comment)
end
@commentable_path = commentable_path
end
def update
if @comment.update_attributes(params[:comment])
flash[:notice] = I18n.t("flash.comment.saved")
redirect_to commentable_path
redirect_to project_commentable_path(@project, @commentable)
else
flash[:error] = I18n.t("flash.comment.save_error")
render :action => 'new'
@ -49,30 +33,19 @@ class CommentsController < ApplicationController
def destroy
@comment.destroy
flash[:notice] = t("flash.comment.destroyed")
redirect_to commentable_path
redirect_to project_commentable_path(@project, @commentable)
end
private
protected
def set_commentable
@commentable = if params[:issue_id].present?
@project.issues.find_by_serial_id params[:issue_id]
elsif params[:commit_id].present?
@project.git_repository.commit params[:commit_id]
end
def find_commentable
@commentable = params[:issue_id].present? && @project.issues.find_by_serial_id(params[:issue_id]) ||
params[:commit_id].present? && @project.git_repository.commit(params[:commit_id])
end
def find_comment
@comment = Comment.find(params[:id])
if @comment.commit_comment?
@comment.project = @project
end
def find_or_build_comment
@comment = params[:id].present? && Comment.find(params[:id]) ||
current_user.comments.build(params[:comment]) {|c| c.commentable = @commentable; c.project = @project}
end
def commentable_path
@commentable.class == Issue ? [@project, @commentable] : commit_path(@project, @commentable.id)
end
end

View File

@ -1,16 +0,0 @@
# -*- encoding : utf-8 -*-
class DownloadsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource
def index
@downloads = Download.paginate :page => params[:page], :per_page => 30
end
def refresh
Download.rotate_nginx_log
Download.parse_and_remove_nginx_log
redirect_to downloads_path, :notice => t('flash.downloads.statistics_refreshed')
end
end

View File

@ -1,7 +1,7 @@
# -*- encoding : utf-8 -*-
class Git::BaseController < ApplicationController
before_filter :authenticate_user!
skip_before_filter :authenticate_user!, :only => [:show, :index, :blame, :raw] if APP_CONFIG['anonymous_access']
skip_before_filter :authenticate_user!, :only => [:show, :index, :blame, :raw, :archive] if APP_CONFIG['anonymous_access']
load_and_authorize_resource :project
before_filter :find_git_repository

View File

@ -15,4 +15,18 @@ class Git::TreesController < Git::BaseController
@tree = @tree / @path if @path
render :template => "git/trees/show"
end
def archive
treeish = params[:treeish].presence || @project.default_branch
format = params[:format] || 'tar'
commit = @project.git_repository.log(treeish, nil, :max_count => 1).first
name = "#{@project.owner.uname}-#{@project.name}#{@project.tags.include?(treeish) ? "-#{treeish}" : ''}-#{commit.id[0..19]}"
fullname = "#{name}.#{format == 'tar' ? 'tar.gz' : 'zip'}"
file = Tempfile.new fullname, 'tmp'
system("cd #{@project.path}; git archive --format=#{format} --prefix=#{name}/ #{treeish} #{format == 'tar' ? ' | gzip -9' : ''} > #{file.path}")
file.close
send_file file.path, :disposition => 'attachment', :type => "application/#{format == 'tar' ? 'x-tar-gz' : 'zip'}",
:filename => fullname
end
end

View File

@ -14,7 +14,7 @@ class IssuesController < ApplicationController
@status = params[:status] == 'closed' ? 'closed' : 'open'
@labels = params[:labels] || []
@issues = @project.issues
@issues = @issues.where(:user_id => current_user.id) if @is_assigned_to_me
@issues = @issues.where(:assignee_id => current_user.id) if @is_assigned_to_me
@issues = @issues.joins(:labels).where(:labels => {:name => @labels}) unless @labels == []
if params[:search_issue]
@ -25,7 +25,7 @@ class IssuesController < ApplicationController
@issues = @issues.where(:status => @status)
@issues = @issues.includes(:creator, :user).order('serial_id desc').uniq.paginate :per_page => 10, :page => params[:page]
@issues = @issues.includes(:assignee, :user).order('serial_id desc').uniq.paginate :per_page => 10, :page => params[:page]
if status == 200
render 'index', :layout => request.format == '*/*' ? 'issues' : 'application' # maybe FIXME '*/*'?
else
@ -38,9 +38,9 @@ class IssuesController < ApplicationController
end
def create
@user_uname = params[:user_uname]
@assignee_uname = params[:assignee_uname]
@issue = @project.issues.new(params[:issue])
@issue.creator_id = current_user.id
@issue.user_id = current_user.id
if @issue.save
@issue.subscribe_creator(current_user.id)

View File

@ -1,10 +1,7 @@
# -*- encoding : utf-8 -*-
class ProductsController < ApplicationController
before_filter :authenticate_user!
before_filter :find_product, :only => [:show, :edit, :update, :destroy]
before_filter :find_platform
before_filter :build_product_stub, :only => [:new, :create]
load_and_authorize_resource :platform
load_and_authorize_resource :product, :through => :platform
@ -20,24 +17,17 @@ class ProductsController < ApplicationController
@product.build_script = DEFAULT_BUILD
end
# def clone
# @template = @platform.products.find(params[:id])
# @product = @platform.products.new
# @product.clone_from!(@template)
#
# render :template => "products/new"
# end
def edit
end
def create
@product = @platform.products.new params[:product]
if @product.save
flash[:notice] = t('flash.product.saved')
redirect_to platform_product_path(@platform, @product)
else
flash[:error] = t('flash.product.save_error')
flash[:warning] = @product.errors.full_messages.join('. ')
render :action => :new
end
end
@ -45,9 +35,10 @@ class ProductsController < ApplicationController
def update
if @product.update_attributes(params[:product])
flash[:notice] = t('flash.product.saved')
redirect_to @platform
redirect_to platform_product_path(@platform, @product)
else
flash[:error] = t('flash.product.save_error')
flash[:warning] = @product.errors.full_messages.join('. ')
render :action => "edit"
end
end
@ -61,17 +52,4 @@ class ProductsController < ApplicationController
redirect_to platform_products_path(@platform)
end
protected
def find_product
@product = Product.find params[:id]
end
def find_platform
@platform = Platform.find params[:platform_id]
end
def build_product_stub
@product = Product.new(:platform_id => params[:platform_id])
end
end

View File

@ -26,9 +26,10 @@ class ProjectsController < ApplicationController
@project = Project.new params[:project]
@project.owner = choose_owner
@who_owns = (@project.owner_type == 'User' ? :me : :group)
authorize! :update, @project.owner if @project.owner.class == Group
if @project.save
flash[:notice] = t('flash.project.saved')
flash[:notice] = t('flash.project.saved')
redirect_to @project
else
flash[:error] = t('flash.project.save_error')
@ -56,7 +57,9 @@ class ProjectsController < ApplicationController
end
def fork
if forked = @project.fork(current_user) and forked.valid?
owner = (Group.find params[:group] if params[:group].present?) || current_user
authorize! :update, owner if owner.class == Group
if forked = @project.fork(owner) and forked.valid?
redirect_to forked, :notice => t("flash.project.forked")
else
flash[:warning] = t("flash.project.fork_error")

View File

@ -3,4 +3,11 @@ module ActivityFeedsHelper
def render_activity_feed(activity_feed)
render :partial => activity_feed.partial, :locals => activity_feed.data.merge(:activity_feed => activity_feed)
end
def get_feed_title_from_content(content)
# removes html tags and haml generator indentation whitespaces and new line chars:
feed_title = strip_tags(content).gsub(/(^\s+|\n| )/, ' ')
# removes multiple whitespaces in a row and strip it:
feed_title = feed_title.gsub(/\s{2,}/, ' ').strip
end
end

View File

@ -5,7 +5,8 @@ module BuildListsHelper
return 'success'
end
if [BuildServer::BUILD_ERROR, BuildServer::PLATFORM_NOT_FOUND,
BuildServer::PROJECT_NOT_FOUND,BuildServer::PROJECT_VERSION_NOT_FOUND, BuildList::FAILED_PUBLISH].include? status
BuildServer::PROJECT_NOT_FOUND, BuildServer::PROJECT_VERSION_NOT_FOUND,
BuildList::FAILED_PUBLISH, BuildList::REJECTED_PUBLISH].include? status
return 'error'
end

View File

@ -1,3 +1,20 @@
# -*- encoding : utf-8 -*-
module CommentsHelper
def project_commentable_comment_path(project, commentable, comment)
case
when Comment.issue_comment?(commentable.class)
project_issue_comment_path(project, commentable, comment)
when Comment.commit_comment?(commentable.class)
project_commit_comment_path(project, commentable, comment)
end
end
def project_commentable_path(project, commentable)
case
when Comment.issue_comment?(commentable.class)
polymorphic_path [project, commentable]
when Comment.commit_comment?(commentable.class)
commit_path project, commentable.id
end
end
end

View File

@ -14,6 +14,7 @@ class Ability
# Shared rights between guests and registered users
can :show, Project, :visibility => 'open'
can :archive, Project, :visibility => 'open'
can :read, Issue, :project => {:visibility => 'open'}
can :search, BuildList
can :read, BuildList, :project => {:visibility => 'open'}
@ -51,13 +52,15 @@ class Ability
can :create, Project
can :read, Project, :visibility => 'open'
can :read, Project, :owner_type => 'User', :owner_id => user.id
can :read, Project, :owner_type => 'Group', :owner_id => user.group_ids
can [:read, :archive], Project, :owner_type => 'User', :owner_id => user.id
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(:fork, Project) {|project| can? :read, project}
can(:fork_to_group, 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.objects.exists?(:object_type => 'User', :object_id => user.id, :role => 'admin')}
can :remove_user, Project
can [:read, :owned], BuildList, :user_id => user.id
@ -65,7 +68,12 @@ class Ability
can [:read, :related], BuildList, :project => {:owner_type => 'Group', :owner_id => user.group_ids}
can(:read, BuildList, read_relations_for('build_lists', 'projects')) {|build_list| can? :read, build_list.project}
can(:create, BuildList) {|build_list| build_list.project.is_rpm && can?(:write, build_list.project)}
can(:publish, BuildList) {|build_list| build_list.can_publish? && can?(:write, build_list.project)}
can(:publish, BuildList) do |build_list|
build_list.can_publish? and build_list.pl.released ? local_admin?(build_list.pl) : can?(:write, build_list.project)
end
can(:reject_publish, BuildList) do |build_list|
build_list.can_reject_publish? and build_list.pl.released and local_admin?(build_list.pl)
end
can(:cancel, BuildList) {|build_list| build_list.can_cancel? && can?(:write, build_list.project)}
can [:read, :members], Platform, :visibility => 'open'

View File

@ -19,23 +19,23 @@ class ActivityFeedObserver < ActiveRecord::Observer
ActivityFeed.create(
:user => recipient,
:kind => 'new_issue_notification',
:data => {:user_name => record.creator.name, :user_email => record.creator.email, :user_id => record.creator_id,:issue_serial_id => record.serial_id,
:data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id,:issue_serial_id => record.serial_id,
:issue_title => record.title, :project_id => record.project.id, :project_name => record.project.name, :project_owner => record.project.owner.uname}
)
end
if record.user_id_changed?
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
if record.assignee_id_changed?
UserMailer.delay.issue_assign_notification(record, record.assignee) if record.assignee.notifier.issue_assign && record.assignee.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:kind => 'issue_assign_notification',
:data => {:user_name => record.creator.name, :user_email => record.creator.email, :user_id => record.creator_id, :issue_serial_id => record.serial_id,
:data => {:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id, :issue_serial_id => record.serial_id,
:project_id => record.project.id, :issue_title => record.title, :project_name => record.project.name, :project_owner => record.project.owner.uname}
)
end
when 'Comment'
if record.commentable.class == Issue
if record.issue_comment?
subscribes = record.commentable.subscribes
subscribes.each do |subscribe|
if record.user_id != subscribe.user_id
@ -70,6 +70,7 @@ class ActivityFeedObserver < ActiveRecord::Observer
end
when 'GitHook'
return unless record.project
change_type = record.change_type
branch_name = record.refname.split('/').last
@ -120,12 +121,12 @@ class ActivityFeedObserver < ActiveRecord::Observer
def after_update(record)
case record.class.to_s
when 'Issue'
if record.user_id && record.user_id_changed?
UserMailer.delay.issue_assign_notification(record, record.user) if record.user.notifier.issue_assign && record.user.notifier.can_notify
if record.assignee_id && record.assignee_id_changed?
UserMailer.delay.issue_assign_notification(record, record.assignee) if record.assignee.notifier.issue_assign && record.assignee.notifier.can_notify
ActivityFeed.create(
:user => record.user,
:user => record.assignee,
:kind => 'issue_assign_notification',
:data => {:user_name => record.user.name, :user_email => record.user.email, :issue_serial_id => record.serial_id, :issue_title => record.title,
:data => {:user_name => record.assignee.name, :user_email => record.assignee.email, :issue_serial_id => record.serial_id, :issue_title => record.title,
:project_id => record.project.id, :project_name => record.project.name, :project_owner => record.project.owner.uname}
)
end
@ -138,7 +139,7 @@ class ActivityFeedObserver < ActiveRecord::Observer
ActivityFeed.create(
:user => User.find(recipient),
:kind => 'build_list_notification',
:data => {:task_num => record.bs_id, :build_list_id => record.id, :status => record.status, :notified_at => record.notified_at,
:data => {:task_num => record.bs_id, :build_list_id => record.id, :status => record.status, :updated_at => record.updated_at,
:project_id => record.project_id, :project_name => record.project.name, :project_owner => record.project.owner.uname,
:user_name => record.user.name, :user_email => record.user.email, :user_id => record.user_id}
)

View File

@ -23,6 +23,7 @@ class BuildList < ActiveRecord::Base
BUILD_PUBLISHED = 6000
BUILD_PUBLISH = 7000
FAILED_PUBLISH = 8000
REJECTED_PUBLISH = 9000
STATUSES = [ WAITING_FOR_RESPONSE,
BUILD_CANCELED,
@ -30,6 +31,7 @@ class BuildList < ActiveRecord::Base
BUILD_PUBLISHED,
BUILD_PUBLISH,
FAILED_PUBLISH,
REJECTED_PUBLISH,
BuildServer::SUCCESS,
BuildServer::BUILD_STARTED,
BuildServer::BUILD_ERROR,
@ -47,6 +49,7 @@ class BuildList < ActiveRecord::Base
BUILD_PUBLISHED => :build_published,
BUILD_PUBLISH => :build_publish,
FAILED_PUBLISH => :failed_publish,
REJECTED_PUBLISH => :rejected_publish,
BuildServer::BUILD_ERROR => :build_error,
BuildServer::BUILD_STARTED => :build_started,
BuildServer::SUCCESS => :success,
@ -59,32 +62,23 @@ class BuildList < ActiveRecord::Base
}
scope :recent, order("#{table_name}.updated_at DESC")
# scope :current, lambda {
# outdatable_statuses = [BuildServer::SUCCESS, BuildServer::ERROR, BuildServer::PLATFORM_NOT_FOUND, BuildServer::PLATFORM_PENDING, BuildServer::PROJECT_NOT_FOUND, BuildServer::PROJECT_VERSION_NOT_FOUND]
# where(["status in (?) OR (status in (?) AND notified_at >= ?)", [WAITING_FOR_RESPONSE, BUILD_PENDING, BuildServer::BUILD_STARTED], outdatable_statuses, Time.now - 2.days])
# }
scope :for_status, lambda {|status| where(:status => status) }
scope :for_user, lambda { |user| where(:user_id => user.id) }
scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) }
scope :scoped_to_project_version, lambda {|project_version| where(:project_version => project_version) }
scope :scoped_to_is_circle, lambda {|is_circle| where(:is_circle => is_circle) }
scope :for_creation_date_period, lambda{|start_date, end_date|
if start_date && end_date
where(["#{table_name}.created_at BETWEEN ? AND ?", start_date, end_date])
elsif start_date && !end_date
where(["#{table_name}.created_at >= ?", start_date])
elsif !start_date && end_date
where(["#{table_name}.created_at <= ?", end_date])
end
s = scoped
s = s.where(["build_lists.created_at >= ?", start_date]) if start_date
s = s.where(["build_lists.created_at <= ?", end_date]) if end_date
s
}
scope :for_notified_date_period, lambda{|start_date, end_date|
if start_date && end_date
where(["notified_at BETWEEN ? AND ?", start_date, end_date])
elsif start_date && !end_date
where(["notified_at >= ?", start_date])
elsif !start_date && end_date
where(["notified_at <= ?", end_date])
end
s = scoped
s = s.where(["build_lists.updated_at >= ?", start_date]) if start_date
s = s.where(["build_lists.updated_at <= ?", end_date]) if end_date
s
}
scope :scoped_to_project_name, lambda {|project_name| joins(:project).where('projects.name LIKE ?', "%#{project_name}%")}
@ -123,6 +117,15 @@ class BuildList < ActiveRecord::Base
status == BuildServer::SUCCESS or status == FAILED_PUBLISH
end
def reject_publish
return false unless can_reject_publish?
update_attribute(:status, REJECTED_PUBLISH)
end
def can_reject_publish?
can_publish? and pl.released
end
def cancel
return false unless can_cancel?
has_canceled = BuildServer.delete_build_list bs_id
@ -139,6 +142,23 @@ class BuildList < ActiveRecord::Base
{:project => project.name, :version => project_version, :arch => arch.name}.inspect
end
def current_duration
(Time.now.utc - started_at.utc).to_i
end
def human_current_duration
I18n.t("layout.build_lists.human_current_duration", {:hours => (current_duration/3600).to_i, :minutes => (current_duration%3600/60).to_i})
end
def human_duration
I18n.t("layout.build_lists.human_duration", {:hours => (duration/3600).to_i, :minutes => (duration%3600/60).to_i})
end
def in_work?
status == BuildServer::BUILD_STARTED
#[WAITING_FOR_RESPONSE, BuildServer::BUILD_PENDING, BuildServer::BUILD_STARTED].include?(status)
end
private
def set_default_status
self.status = WAITING_FOR_RESPONSE unless self.status.present?

View File

@ -22,8 +22,8 @@ class BuildList::Filter
if @options[:created_at_start] || @options[:created_at_end]
build_lists = build_lists.for_creation_date_period(@options[:created_at_start], @options[:created_at_end])
end
if @options[:notified_at_start] || @options[:notified_at_end]
build_lists = build_lists.for_notified_date_period(@options[:notified_at_start], @options[:notified_at_end])
if @options[:updated_at_start] || @options[:updated_at_end]
build_lists = build_lists.for_notified_date_period(@options[:updated_at_start], @options[:updated_at_end])
end
end
@ -47,8 +47,8 @@ class BuildList::Filter
:status => nil,
:created_at_start => nil,
:created_at_end => nil,
:notified_at_start => nil,
:notified_at_end => nil,
:updated_at_start => nil,
:updated_at_end => nil,
:arch_id => nil,
:is_circle => nil,
:project_version => nil,
@ -60,8 +60,8 @@ class BuildList::Filter
@options[:status] = @options[:status].present? ? @options[:status].to_i : nil
@options[:created_at_start] = build_date_from_params(:created_at_start, @options)
@options[:created_at_end] = build_date_from_params(:created_at_end, @options)
@options[:notified_at_start] = build_date_from_params(:notified_at_start, @options)
@options[:notified_at_end] = build_date_from_params(:notified_at_end, @options)
@options[:updated_at_start] = build_date_from_params(:updated_at_start, @options)
@options[:updated_at_end] = build_date_from_params(:updated_at_end, @options)
@options[:project_version] = @options[:project_version].presence
@options[:arch_id] = @options[:arch_id].present? ? @options[:arch_id].to_i : nil
@options[:is_circle] = @options[:is_circle].present? ? @options[:is_circle] == "1" : nil

View File

@ -1,7 +1,7 @@
# -*- encoding : utf-8 -*-
class BuildList::Item < ActiveRecord::Base
belongs_to :build_list
belongs_to :build_list, :touch => true
attr_protected :build_list_id

View File

@ -0,0 +1,20 @@
class BuildListObserver < ActiveRecord::Observer
observe :build_list
def before_update(record)
if record.status_changed?
record.started_at = Time.now if record.status == BuildServer::BUILD_STARTED
if [BuildServer::BUILD_ERROR, BuildServer::SUCCESS].include? record.status
# stores time interval beetwin build start and finish in seconds
record.duration = record.current_duration
if record.status == BuildServer::SUCCESS
# Update project average build time
build_count = record.project.build_count
new_av_time = ( record.project.average_build_time * build_count + record.duration ) / ( build_count + 1 )
record.project.update_attributes({ :average_build_time => new_av_time, :build_count => build_count + 1 }, :without_protection => true)
end
end
end
end
end

View File

@ -6,23 +6,45 @@ class Comment < ActiveRecord::Base
validates :body, :user_id, :commentable_id, :commentable_type, :project_id, :presence => true
scope :for_commit, lambda {|c| where(:commentable_id => c.id.hex, :commentable_type => c.class)}
default_scope order('created_at')
after_create :subscribe_on_reply, :unless => lambda {|c| c.commit_comment?}
after_create :subscribe_users
after_initialize do |comment|
class_eval { def commentable; project.git_repository.commit(commentable_id.to_s(16)); end } if commit_comment?
attr_accessible :body
def commentable
commit_comment? ? project.git_repository.commit(commentable_id.to_s(16)) : super
end
attr_accessible :body, :commentable_id, :commentable_type
attr_readonly :commentable_id, :commentable_type
def commentable=(c)
if self.class.commit_comment?(c.class)
self.commentable_id = c.id.hex
self.commentable_type = c.class.name
else
super
end
end
def own_comment?(user)
user_id == user.id
def self.commit_comment?(class_name)
class_name.to_s == 'Grit::Commit'
end
def commit_comment?
commentable_type == 'Grit::Commit'
self.class.commit_comment?(commentable_type)
end
def self.issue_comment?(class_name)
class_name.to_s == 'Issue'
end
def issue_comment?
self.class.issue_comment?(commentable_type)
end
def own_comment?(user)
user_id == user.id
end
def can_notify_on_new_comment?(subscribe)
@ -32,19 +54,19 @@ class Comment < ActiveRecord::Base
protected
def subscribe_on_reply
self.commentable.subscribes.create(:user_id => self.user_id) if !self.commentable.subscribes.exists?(:user_id => self.user_id)
commentable.subscribes.create(:user_id => user_id) if !commentable.subscribes.exists?(:user_id => user_id)
end
def subscribe_users
if self.commentable.class == Issue
self.commentable.subscribes.create(:user => self.user) if !self.commentable.subscribes.exists?(:user_id => self.user.id)
elsif self.commit_comment?
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map &:object # admins
recipients << self.user << User.where(:email => self.commentable.committer.email).first # commentor and committer
recipients << self.project.owner if self.project.owner_type == 'User' # project owner
if issue_comment?
commentable.subscribes.create(:user => user) if !commentable.subscribes.exists?(:user_id => user.id)
elsif commit_comment?
recipients = project.relations.by_role('admin').where(:object_type => 'User').map &:object # admins
recipients << user << User.where(:email => commentable.committer.email).first # commentor and committer
recipients << project.owner if project.owner_type == 'User' # project owner
recipients.compact.uniq.each do |user|
options = {:project_id => self.project.id, :subscribeable_id => self.commentable_id, :subscribeable_type => self.commentable.class.name, :user_id => user.id}
Subscribe.subscribe_to_commit(options) if Subscribe.subscribed_to_commit?(self.project, user, self.commentable)
options = {:project_id => project.id, :subscribeable_id => commentable_id, :subscribeable_type => commentable.class.name, :user_id => user.id}
Subscribe.subscribe_to_commit(options) if Subscribe.subscribed_to_commit?(project, user, commentable)
end
end
end

View File

@ -4,30 +4,28 @@ class Issue < ActiveRecord::Base
belongs_to :project
belongs_to :user
belongs_to :creator, :class_name => 'User', :foreign_key => 'creator_id'
belongs_to :assignee, :class_name => 'User', :foreign_key => 'assignee_id'
belongs_to :closer, :class_name => 'User', :foreign_key => 'closed_by'
has_many :comments, :as => :commentable, :dependent => :destroy #, :finder_sql => proc { "comments.commentable_id = '#{self.id}' AND comments.commentable_type = '#{self.class.name}'"}
has_many :subscribes, :as => :subscribeable, :dependent => :destroy #, :finder_sql => proc { "subscribes.subscribeable_id = '#{self.id}' AND subscribes.subscribeable_type = '#{self.class.name}'"}
has_many :comments, :as => :commentable, :dependent => :destroy
has_many :subscribes, :as => :subscribeable, :dependent => :destroy
has_many :labels, :through => :labelings, :uniq => true
has_many :labelings
validates :title, :body, :project_id, :presence => true
#attr_readonly :serial_id
after_create :set_serial_id
after_create :subscribe_users
after_update :subscribe_issue_assigned_user
attr_accessible :labelings_attributes, :title, :body, :user_id
attr_accessible :labelings_attributes, :title, :body, :assignee_id
accepts_nested_attributes_for :labelings, :allow_destroy => true
scope :opened, where(:status => 'open', :closed_by => nil, :closed_at => nil)
scope :closed, where(:status => 'closed').where("closed_by is not null and closed_at is not null")
def assign_uname
user.uname if user
assignee.uname if assignee
end
def to_param
@ -57,7 +55,7 @@ class Issue < ActiveRecord::Base
def collect_recipient_ids
recipients = self.project.relations.by_role('admin').where(:object_type => 'User').map { |rel| rel.read_attribute(:object_id) }
recipients = recipients | [self.user_id] if self.user_id
recipients = recipients | [self.assignee_id] if self.assignee_id
recipients = recipients | [self.project.owner_id] if self.project.owner_type == 'User'
recipients
@ -80,10 +78,10 @@ class Issue < ActiveRecord::Base
end
def subscribe_issue_assigned_user
if self.user_id && self.user_id_changed?
self.subscribes.where(:user_id => self.user_id_was).first.destroy unless self.user_id_was.blank?
if self.user.notifier.issue_assign && !self.subscribes.exists?(:user_id => self.user_id)
self.subscribes.create(:user_id => self.user_id)
if self.assignee_id && self.assignee_id_changed?
self.subscribes.where(:user_id => self.assignee_id_was).first.destroy unless self.assignee_id_was.blank?
if self.assignee.notifier.issue_assign && !self.subscribes.exists?(:user_id => self.assignee_id)
self.subscribes.create(:user_id => self.assignee_id)
end
end
end

View File

@ -29,7 +29,7 @@ class Platform < ActiveRecord::Base
after_update :update_owner_relation
scope :search_order, order("CHAR_LENGTH(name) ASC")
scope :search, lambda {|q| where("name ILIKE ?", "%#{q.strip}%")}
scope :search, lambda {|q| where("name ILIKE ?", "%#{q.to_s.strip}%")}
scope :by_visibilities, lambda {|v| where(:visibility => v)}
scope :opened, where(:visibility => 'open')
scope :hidden, where(:visibility => 'hidden')

View File

@ -15,6 +15,10 @@ class Product < ActiveRecord::Base
scope :recent, order("name ASC")
attr_accessible :name, :counter, :ks, :menu, :tar, :cron_tab, :use_cron
attr_accessible :description, :build_script, :delete_tar
attr_readonly :platform_id
def delete_tar
@delete_tar ||= "0"
end

View File

@ -16,17 +16,20 @@ class ProductBuildList < ActiveRecord::Base
belongs_to :product
validates :product, :status, :presence => true
validates :product_id, :status, :presence => true
validates :status, :inclusion => { :in => [BUILD_STARTED, BUILD_COMPLETED, BUILD_FAILED] }
attr_accessor :base_url
attr_accessible :status, :notified_at, :base_url
attr_readonly :product_id
scope :default_order, order('notified_at DESC')
scope :for_status, lambda {|status| where(:status => status) }
scope :for_user, lambda { |user| where(:user_id => user.id) }
scope :scoped_to_product_name, lambda {|product_name| joins(:product).where('products.name LIKE ?', "%#{product_name}%")}
scope :recent, order("#{table_name}.updated_at DESC")
attr_accessor :base_url
after_create :xml_rpc_create
after_destroy :xml_delete_iso_container

View File

@ -20,7 +20,7 @@ class Project < ActiveRecord::Base
validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, :format => {:with => /^[a-zA-Z0-9_\-\+\.]+$/}
validates :owner, :presence => true
validate { errors.add(:base, :can_have_less_or_equal, :count => MAX_OWN_PROJECTS) if owner.projects.size >= MAX_OWN_PROJECTS }
validates_attachment_size :srpm, :less_than => 500.megabytes
validates_attachment_content_type :srpm, :content_type => ['application/octet-stream', "application/x-rpm", "application/x-redhat-package-manager"], :message => I18n.t('layout.invalid_content_type')
@ -29,7 +29,7 @@ class Project < ActiveRecord::Base
scope :recent, order("name ASC")
scope :search_order, order("CHAR_LENGTH(name) ASC")
scope :search, lambda {|q| by_name("%#{q.strip}%")}
scope :search, lambda {|q| by_name("%#{q.to_s.strip}%")}
scope :by_name, lambda {|name| where('projects.name ILIKE ?', name)}
scope :by_visibilities, lambda {|v| where(:visibility => v)}
scope :opened, where(:visibility => 'open')
@ -37,11 +37,12 @@ class Project < ActiveRecord::Base
after_create :attach_to_personal_repository
after_create :create_git_repo
after_create {|p| p.delay(:queue => 'fork', :priority => 20).fork_git_repo unless is_root?}
after_save :create_wiki
after_destroy :destroy_git_repo
after_destroy :destroy_wiki
after_save {|p| p.delay.import_attached_srpm if p.srpm?} # should be after create_git_repo
after_save {|p| p.delay(:queue => 'import', :priority => 10).import_attached_srpm if p.srpm?} # should be after create_git_repo
# after_rollback lambda { destroy_git_repo rescue true if new_record? }
has_ancestry
@ -50,18 +51,25 @@ class Project < ActiveRecord::Base
include Modules::Models::Owner
def build_for(platform, user, arch = 'i586') # Return i586 after mass rebuild
def build_for(platform, user, arch = 'i586')
# Select main and project platform repository(contrib, non-free and etc)
# If main does not exist, will connect only project platform repository
# If project platform repository is main, only main will be connect
build_reps = [platform.repositories.find_by_name('main')]
build_reps += platform.repositories.select {|rep| self.repository_ids.include? rep.id}
build_ids = build_reps.compact.map(&:id).uniq
arch = Arch.find_by_name(arch) if arch.acts_like?(:string)
build_lists.create do |bl|
bl.pl = platform
bl.bpl = platform
bl.update_type = 'newpackage'
bl.arch = arch
bl.project_version = "latest_#{platform.name}" # "latest_import_mandriva2011"
bl.project_version = "latest_#{platform.name}"
bl.build_requires = false # already set as db default
bl.user = user
bl.auto_publish = true # already set as db default
bl.include_repos = [platform.repositories.find_by_name('main').id]
bl.include_repos = build_ids
end
end
@ -117,6 +125,13 @@ class Project < ActiveRecord::Base
tags.map(&:name) + branches.map{|b| "latest_#{b.name}"}
end
def versions_for_group_select
[
['Branches', branches.map{|b| "latest_#{b.name}"}],
['Tags', tags.map(&:name)]
]
end
def members
collaborators + groups.map(&:members).flatten
end
@ -180,10 +195,6 @@ class Project < ActiveRecord::Base
system("#{Rails.root.join('bin', 'import_srpm.sh')} #{srpm_path} #{path} #{branch_name} >> /dev/null 2>&1")
end
def self.commit_comments(commit, project)
comments = Comment.where(:commentable_id => commit.id.hex, :commentable_type => 'Grit::Commit')
end
def owner?(user)
owner == user
end
@ -199,6 +210,11 @@ class Project < ActiveRecord::Base
recipients
end
def human_average_build_time
time = average_build_time
I18n.t("layout.projects.human_average_build_time", {:hours => (time/3600).to_i, :minutes => (time%3600/60).to_i})
end
protected
def build_path(dir)
@ -214,8 +230,15 @@ class Project < ActiveRecord::Base
end
def create_git_repo
is_root? ? Grit::Repo.init_bare(path) : parent.git_repository.repo.delay.fork_bare(path)
write_hook.delay
if is_root?
Grit::Repo.init_bare(path)
write_hook.delay(:queue => 'fork', :priority => 15)
end
end
def fork_git_repo
dummy = Grit::Repo.new(path) rescue parent.git_repository.repo.fork_bare(path)
write_hook
end
def destroy_git_repo
@ -248,7 +271,7 @@ class Project < ActiveRecord::Base
hook = File.join(::Rails.root.to_s, 'tmp', "post-receive-hook")
FileUtils.cp(File.join(::Rails.root.to_s, 'bin', "post-receive-hook.partial"), hook)
File.open(hook, 'a') do |f|
s = "\n /bin/bash -l -c \"cd #{is_production ? '/srv/rosa_build/current' : Rails.root.to_s} && #{is_production ? 'RAILS_ENV=production' : ''} bundle exec rails runner 'Project.delay.process_hook(\\\"$owner\\\", \\\"$reponame\\\", \\\"$newrev\\\", \\\"$oldrev\\\", \\\"$ref\\\", \\\"$newrev_type\\\", \\\"$oldrev_type\\\")'\""
s = "\n /bin/bash -l -c \"cd #{is_production ? '/srv/rosa_build/current' : Rails.root.to_s} && #{is_production ? 'RAILS_ENV=production' : ''} bundle exec rails runner 'Project.delay(:queue => \\\"hook\\\").process_hook(\\\"$owner\\\", \\\"$reponame\\\", \\\"$newrev\\\", \\\"$oldrev\\\", \\\"$ref\\\", \\\"$newrev_type\\\", \\\"$oldrev_type\\\")'\""
s << " > /dev/null 2>&1" if is_production
s << "\ndone\n"
f.write(s)

View File

@ -5,7 +5,7 @@ class User < ActiveRecord::Base
LANGUAGES = LANGUAGES_FOR_SELECT.map(&:last)
MAX_AVATAR_SIZE = 5.megabyte
devise :database_authenticatable, :registerable, :omniauthable, # :token_authenticatable, :encryptable, :timeoutable
devise :database_authenticatable, :registerable, :omniauthable, :token_authenticatable,# :encryptable, :timeoutable
:recoverable, :rememberable, :validatable, :lockable, :confirmable#, :reconfirmable, :trackable
has_attached_file :avatar, :styles =>
{ :micro => { :geometry => "16x16#", :format => :jpg, :convert_options => '-strip -background white -flatten -quality 70'},
@ -55,6 +55,7 @@ class User < ActiveRecord::Base
include Modules::Models::ActsLikeMember
after_create lambda { self.create_notifier }
before_create :ensure_authentication_token
def admin?
role == 'admin'

View File

@ -30,10 +30,10 @@ class CommentPresenter < ApplicationPresenter
def buttons
project = options[:project]
commentable = options[:commentable]
(ep, dp) = if commentable.class == Issue
(ep, dp) = if Comment.issue_comment?(commentable.class)
[edit_project_issue_comment_path(project, commentable, comment),
project_issue_comment_path(project, commentable, comment)]
elsif commentable.class == Grit::Commit
elsif Comment.commit_comment?(commentable.class)
[edit_project_commit_comment_path(project, commentable, comment),
project_commit_comment_path(project, commentable, comment)]
end

View File

@ -1,6 +1,6 @@
- @activity_feeds.each do |activity_feed|
.activity
.activity{:id => "#feed#{activity_feed.id}"}
= render_activity_feed(activity_feed)
- if @activity_feeds.next_page
%a{:href => root_path({:filter => @filter, :page => @activity_feeds.next_page}), :class => 'more_activities'}
.messages.messages-last=t('layout.activity_feed.load_messages')
.messages.messages-last=t('layout.activity_feed.load_messages')

View File

@ -18,30 +18,15 @@
%td
= link_to t("layout.activity_feed.all_my_projects"), projects_path
.block
- midnight = Time.new.midnight
- midnight = Date.today
%h3= t("layout.activity_feed.my_builds_by_day")
%table{:cellpadding => "0", :cellspacing => "0"}
%tbody
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{:build_published}"), build_lists_path(:filter => {:status => BuildList::BUILD_PUBLISHED, :notified_at => midnight})
%td= BuildList.for_status(BuildList::BUILD_PUBLISHED).for_user(current_user).for_notified_date_period(midnight, nil).count
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{:success}"), build_lists_path(:filter => {:status => BuildServer::SUCCESS, :notified_at => midnight})
%td= BuildList.for_status(BuildServer::SUCCESS).for_user(current_user).for_notified_date_period(midnight, nil).count
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{:build_started}"), build_lists_path(:filter => {:status => BuildServer::BUILD_STARTED, :notified_at => midnight})
%td= BuildList.for_status(BuildServer::BUILD_STARTED).for_user(current_user).for_notified_date_period(midnight, nil).count
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{:build_pending}"), build_lists_path(:filter => {:status => BuildList::BUILD_PENDING, :notified_at => midnight})
%td= BuildList.for_status(BuildList::BUILD_PENDING).for_user(current_user).for_notified_date_period(midnight, nil).count
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{:build_error}"), build_lists_path(:filter => {:status => BuildServer::BUILD_ERROR, :notified_at => midnight})
%td= BuildList.for_status(BuildServer::BUILD_ERROR).for_user(current_user).for_notified_date_period(midnight, nil).count
- ['BuildList::BUILD_PUBLISHED', 'BuildServer::SUCCESS', 'BuildServer::BUILD_STARTED', 'BuildList::BUILD_PENDING', 'BuildServer::BUILD_ERROR'].each do |state|
%tr
%td.first
= link_to t("layout.build_lists.statuses.#{state.demodulize.downcase}"), build_lists_path(:filter => {:status => state.constantize, :'updated_at_start(1i)' => midnight.year, :'updated_at_start(2i)' => midnight.month, :'updated_at_start(3i)' => midnight.day})
%td= BuildList.for_status(state.constantize).for_user(current_user).for_notified_date_period(midnight, nil).count
%tr
%td.first
= link_to t("layout.activity_feed.all_my_builds"), build_lists_path

View File

@ -0,0 +1,18 @@
atom_feed do |feed|
feed.title(t("layout.activity_feed.atom_title"))
feed.updated(@activity_feeds[0].created_at) if @activity_feeds.length > 0
@activity_feeds.each do |activity_feed|
feed.entry(activity_feed, :url => root_url(:anchor => "feed#{activity_feed.id}")) do |entry|
feed_content = raw(render(:inline => true, :partial => activity_feed.partial, :locals => activity_feed.data.merge(:activity_feed => activity_feed)))
entry.title(truncate(get_feed_title_from_content(feed_content), :length => 50))
entry.content(feed_content, :type => 'html')
entry.author do |author|
author.name(activity_feed.data[:user_name])
author.email(activity_feed.data[:user_email])
end if activity_feed.kind != 'git_delete_branch_notification'
end
end
end

View File

@ -15,5 +15,5 @@
- else ['failed', t("layout.build_lists.statuses.#{BuildList::HUMAN_STATUSES[status]}")]
= raw t("notifications.bodies.build_status.#{message}", :error => error)
.both
%span.date= notified_at
%span.date= updated_at
.both

View File

@ -6,4 +6,4 @@
%td= build_list.arch.name
%td= link_to build_list.user.try(:fullname), build_list.user
%td= link_to image_tag('x.png', :class => 'delete-row', :id => "delete-row#{build_list_counter}"), cancel_build_list_path(build_list), :method => :put, :confirm => t('layout.confirm') if build_list.can_cancel? and can?(:cancel, build_list)
%td= build_list.notified_at
%td= build_list.updated_at

View File

@ -47,14 +47,14 @@
.date_select= f.date_select(:created_at_start, :include_blank => true, :selected => @filter.created_at_start)
%h3.small= t("layout.build_lists.created_at_end")
.date_select= f.date_select(:created_at_end, :include_blank => true, :selected => @filter.created_at_end)
%h3.small= t("layout.build_lists.notified_at_start")
.date_select= f.date_select(:notified_at_start, :include_blank => true, :selected => @filter.notified_at_start)
%h3.small= t("layout.build_lists.notified_at_end")
.date_select= f.date_select(:notified_at_end, :include_blank => true, :selected => @filter.notified_at_end)
%h3.small= t("layout.build_lists.updated_at_start")
.date_select= f.date_select(:updated_at_start, :include_blank => true, :selected => @filter.updated_at_start)
%h3.small= t("layout.build_lists.updated_at_end")
.date_select= f.date_select(:updated_at_end, :include_blank => true, :selected => @filter.updated_at_end)
%h3.small= t("layout.build_lists.project_name_search")
= f.text_field :project_name
%h3.small= t("layout.build_lists.bs_id_search")
= f.text_field :bs_id
%br
%br
= f.submit t("layout.search.header")
= f.submit t("layout.search.header")

View File

@ -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}" # (params[:build_list]||[]).fetch(:include_repos, []).include?(repo.id.to_s)
= label_tag "include_repos_#{repo.id}", 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}", :pl_id => platform.id, :rep_name => repo.name
= label_tag "include_repos_#{repo.id}", repo.name

View File

@ -2,4 +2,4 @@
%td= link_to (platform_build_list.bs_id.present? ? platform_build_list.bs_id : t("layout.build_lists.bs_id_not_set")), platform_build_list
%td= platform_build_list.human_status
%td= link_to platform_build_list.project.name, platform_build_list.project
%td= platform_build_list.notified_at
%td= platform_build_list.updated_at

View File

@ -10,7 +10,7 @@
%th.lpadding16= t("activerecord.attributes.build_list.arch")
%th.lpadding16= t("activerecord.attributes.build_list.user")
%th= t("layout.build_lists.action")
%th.lpadding16= t("activerecord.attributes.build_list.notified_at")
%th.lpadding16= t("activerecord.attributes.build_list.updated_at")
%tbody= render @build_lists
.both

View File

@ -1,25 +1,31 @@
-set_meta_tags :title => [title_object(@project), t('layout.build_lists.new_header')]
= form_for [@project, @build_list], :html => { :class => :form, :method => :post } do |f|
%section.left
%h3= t("activerecord.attributes.build_list.project_version")
.lineForm= f.select :project_version, @project.versions
%h3= t("activerecord.attributes.build_list.bpl")
.all_platforms
- Platform.main.each do |pl|
- if pl.repository_ids.size > 0
.both
= check_box_tag "bpls[]", pl.id, (params[:bpls]||[]).include?(pl.id.to_s), :class => 'build_bpl_ids', :id => "bpls_#{pl.id}"
= check_box_tag "bpls[]", pl.id, (params[:bpls]||[]).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
%section.right
%h3= t("activerecord.attributes.build_list.pl")
.lineForm= f.select :pl_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.platform.id]}
%h3= t("activerecord.attributes.build_list.project_version")
- if controller.action_name == 'new'
.lineForm= f.select :project_version, @project.versions_for_group_select, :selected => "latest_" + @project.default_branch
- else
.lineForm= f.select :project_version, @project.versions_for_group_select
%h3= t("activerecord.attributes.build_list.arch")
- Arch.recent.each do |arch|
.both
= check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}"
- if controller.action_name == 'new'
= check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}", :checked => 'checked'
- else
= check_box_tag "arches[]", arch.id, (params[:arches]||[]).include?(arch.id.to_s), :id => "arches_#{arch.id}"
= label_tag "arches_#{arch.id}", arch.name
%h3= t("activerecord.attributes.build_list.pl")
.lineForm= f.select :pl_id, @project.repositories.collect{|r| ["#{r.platform.name}/#{r.name}", r.platform.id]}
%h3= t("activerecord.attributes.build_list.update_type")
.lineForm= f.select :update_type, BuildList::UPDATE_TYPES
%h3= t("activerecord.attributes.build_list.preferences")

View File

@ -18,6 +18,7 @@
= link_to @build_list.user.try(:fullname), @build_list.user
.both
= link_to t("layout.publish"), publish_build_list_path(@build_list), :method => "put", :confirm => t("layout.confirm"), :class => "button tmargin10" if @build_list.can_publish? and can?(:publish, @build_list)
= link_to t("layout.reject_publish"), reject_publish_build_list_path(@build_list), :method => "put", :confirm => t("layout.confirm"), :class => "button tmargin10" if @build_list.can_reject_publish? and can?(:reject_publish, @build_list)
.hr
%h3= t("layout.build_lists.main_data")
.leftside.width125= t("activerecord.attributes.build_list.bpl")
@ -46,12 +47,24 @@
.leftside.width125= t("activerecord.attributes.build_list.arch")
.leftside= @build_list.arch.name
.both
.leftside.width125= t("activerecord.attributes.build_list.notified_at")
.leftside= @build_list.notified_at
.leftside.width125= t("activerecord.attributes.build_list.updated_at")
.leftside= @build_list.updated_at
.both
.leftside.width125= t("activerecord.attributes.build_list.is_circle")
.leftside= t("layout.#{@build_list.is_circle?}_")
.both
- if !@build_list.in_work? && @build_list.started_at
%br
.leftside.width125
.leftside= @build_list.human_duration
.both
- if @build_list.in_work?
%br
.leftside.width125
.leftside
= "#{@build_list.human_current_duration} / #{@build_list.project.human_average_build_time}"
.both
.hr
%h3= t("layout.build_lists.items_header")
- if @item_groups.blank?

View File

@ -1,10 +1,10 @@
#open-comment.comment.view
%h3.tmargin0= t("layout.comments.new_header")
- if commentable.class == Issue
- if Comment.issue_comment?(commentable.class)
- new_path = project_issue_comments_path(project, commentable)
- is_subscribed = commentable.subscribes.exists?(:user_id => current_user.id)
- subscribe_path = is_subscribed ? project_issue_subscribe_path(project, commentable, current_user.id) : project_issue_subscribes_path(project, commentable)
- else commentable.class == Grit::Commit
- else Comment.commit_comment?(commentable.class)
- new_path = project_commit_comments_path(project, commentable)
- is_subscribed = Subscribe.subscribed_to_commit?(project, current_user, commentable)
- subscribe_path = is_subscribed ? unsubscribe_commit_path(project, commentable) : subscribe_commit_path(project, commentable)

View File

@ -1,14 +1,2 @@
.wrapper
= f.text_area :body, :cols => 80
.comment-right
= submit_tag t("layout.save")
-#.group
= f.label :body, t("activerecord.attributes.comment.body"), :class => :label
= f.text_area :body, :class => 'text_field', :cols => 80
-#.group.navform.wat-cf
%button.button{:type => "submit"}
= image_tag("choose.png", :alt => t("layout.save"))
= t("layout.save")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), @commentable_path , :class => "text_button_padding link_button"
.wrapper= f.text_area :body, :cols => 80
.comment-right= submit_tag t("layout.save")

View File

@ -3,37 +3,4 @@
%h3= t("layout.comments.comments_header")
- list.each do |comment|
- CommentPresenter.present(comment, :project => project, :commentable => commentable) do |presenter|
= render :partial => 'shared/feed_message', :locals => {:presenter => presenter}
-#.block#block-list
.content
%h2.title
= t("layout.issues.comments_header")
.inner
%ul.list
- list.each do |comment|
%li
.left
= link_to comment.user.uname, user_path(comment.user.uname)
.item
= comment.body
%br
%br
- if commentable.class == Issue
- edit_path = edit_project_issue_comment_path(project, commentable, comment)
- delete_path = project_issue_comment_path(project, commentable, comment)
- elsif commentable.class == Grit::Commit
- edit_path = edit_project_commit_comment_path(project, commentable, comment)
- delete_path = project_commit_comment_path(project, commentable, comment)
= link_to t("layout.edit"), edit_path if can? :update, comment
=# link_to image_tag("x.png", :alt => t("layout.delete")) + " " + t("layout.delete"), delete_path, :method => "delete", :class => "button", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
= link_to t("layout.delete"), delete_path, :method => "delete", :confirm => t("layout.comments.confirm_delete") if can? :delete, comment
-#.block
.content
%h2.title
= t("layout.comments.new_header")
.inner
- new_path = project_issue_comments_path(project, commentable) if commentable.class == Issue
- new_path = project_commit_comments_path(project, commentable) if commentable.class == Grit::Commit
= form_for :comment, :url => new_path, :method => :post, :html => { :class => :form } do |f|
= render :partial => "comments/form", :locals => {:f => f}
= render :partial => 'shared/feed_message', :locals => {:presenter => presenter}

View File

@ -2,10 +2,10 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.comments.back"), @commentable_path
%li.first= link_to t("layout.comments.back"), project_commentable_path(@project, @commentable)
.content
%h2.title
= t("layout.comments.edit_header")
.inner
= form_for @comment, :url => @update_url, :html => { :class => :form } do |f|
= form_for @comment, :url => project_commentable_comment_path(@project, @commentable, @comment), :html => {:class => :form} do |f|
= render :partial => "form", :locals => {:f => f}

View File

@ -2,10 +2,8 @@
%nav
%a{:href => new_register_request_path}
%p=t("layout.devise.shared_links.sign_up")
/ Top block
%header
.logo
/ Page
%article
%br
%h1= title t('devise.passwords.edit')
@ -17,11 +15,11 @@
= f.hidden_field :reset_password_token
.left.first=t('activerecord.attributes.user.password')
.right.first
= f.text_field :password, :id => 'password', :class => "registartion-input #{password_error ? 'registartion-input-error' : ''}"
= f.password_field :password, :id => 'password', :class => "registartion-input #{password_error ? 'registartion-input-error' : ''}"
.both
.left=t('activerecord.attributes.user.password_confirm')
.right
= f.text_field :password_confirmation, :id => 'password2', :class => "registartion-input #{password_error ? 'registartion-input-error' : ''}"
= f.password_field :password_confirmation, :id => 'password2', :class => "registartion-input #{password_error ? 'registartion-input-error' : ''}"
.both
%br
=f.submit t("devise.passwords.edit_button"), :class => 'button', :id => 'btnLogin'

View File

@ -1,30 +0,0 @@
.block
.content
%h2.title
= title t("layout.downloads.title")
.inner
%h3= t("layout.downloads.message")
.inner
%table.table
%tr
%th.first= t("activerecord.attributes.download.name")
%th= t("activerecord.attributes.download.version")
%th= t("activerecord.attributes.download.distro")
%th= t("activerecord.attributes.download.platform")
%th.last= t("activerecord.attributes.download.counter")
- @downloads.each do |download|
%tr{:class => cycle("odd", "even")}
%td
= download.name
%td
= download.version
%td
= download.distro
%td
= download.platform
%td.last
= download.counter
.actions-bar.wat-cf
.actions
= will_paginate @downloads
= render 'admin/submenu'

View File

@ -16,5 +16,5 @@
#repo-wrapper
= render :partial => 'show'
= render :partial => "comments/list", :locals => {:list => Project.commit_comments(@commit, @project), :project => @project, :commentable => @commit}
= render :partial => "comments/list", :locals => {:list => Comment.for_commit(@commit), :project => @project, :commentable => @commit}
= render :partial => "comments/add", :locals => {:project => @project, :commentable => @commit} if current_user

View File

@ -0,0 +1,14 @@
- if owner.projects.exists? :name => @project.name
- is_group = owner.class == Group ? "(#{t 'activerecord.models.group'})" : ''
%p.center
=t 'layout.projects.already_exists'
=link_to "#{owner.uname}/#{@project.name} #{is_group}", project_path(owner.projects.by_name(@project.name).first.id)
- else
= form_for @project, :url => fork_project_path(@project), :html => { :class => :form, :multipart => true, :method => :post } do |f|
= hidden_field_tag :group, owner.id if owner.class == Group
=f.submit t('layout.projects.fork_to', :to => "#{owner.uname} #{is_group}"), :class => 'btn btn-primary disabled', 'data-loading-text' => t('layout.processing'), :id => 'create_fork'
:javascript
$('#create_fork').click(function () {
$(this).button('loading');
})

View File

@ -1,2 +1,11 @@
- if can? :fork, @project
.r#fork-and-edit= link_to t('layout.projects.fork_and_edit'), fork_project_path(@project), :method => :post, :confirm => t("layout.confirm"), :class => 'button'
.r#fork-and-edit= link_to t('layout.projects.fork_and_edit'), '#forkModal', :class => 'button', 'data-toggle' => 'modal'
#forkModal.modal{:style => 'display: none;'}
.modal-header
%a.close{"data-dismiss" => "modal"} ×
%h3=t 'layout.projects.fork_modal_header'
.modal-footer=render :partial => 'git/shared/choose_fork', :locals => {:owner => current_user}
- Group.can_own_project(current_user).each do |group|
.modal-footer=render :partial => 'git/shared/choose_fork', :locals => {:owner => group}
- if can? :create, @project.build_lists.new
.r{:style => "display: block"}= link_to t('layout.projects.new_build_list'), new_project_build_list_path(@project), :class => 'button'

View File

@ -3,12 +3,12 @@
.leftlist= t('activerecord.attributes.issue.body') + ':'
.rightlist= f.text_area :body
.both
.leftlist= t('activerecord.attributes.issue.user') + ':'
.leftlist= t('activerecord.attributes.issue.assignee') + ':'
.rightlist
%span#people-span.small-text= t('layout.issues.choose_user_on_left')
#issue_executor
#issue_assignee
.both
.leftlist= t('layout.issues.labels')
.leftlist= t('layout.issues.labels') + ':'
.rightlist
%span#flag-span.small-text= t('layout.issues.choose_labels_on_left')
#issue_labels

View File

@ -11,7 +11,7 @@
%tr
%td=radio_button_tag :myradio, 'to_me', @is_assigned_to_me, {:id => 'myradio1', :class => 'niceRadio', :name => 'filter'}
%td=t("layout.issues.to_me")
%td.width30.right=@project.issues.where(:user_id => current_user).count
%td.width30.right=@project.issues.where(:assignee_id => current_user.id).count
=form_tag project_issues_path(@project), :id => 'search_issue', :method => :get do
.bordered.bpadding20
=tracker_search_field(:search_issue, t('layout.issues.search'))

View File

@ -7,15 +7,15 @@
%div.issue_title=issue.title
.smalltext
=issue.created_at.to_s(:long)
=t("layout.by") if issue.creator
=link_to(issue.creator.uname, user_path(issue.creator)) if issue.creator
=t("layout.by") if issue.user
=link_to(issue.user.uname, user_path(issue.user)) if issue.user
-issue.labels.each do |label|
.left.nomargin
.label.selected.tracker.left
.labeltext.selected{:style => "background: ##{label.color};"}=label.name
%td.td3
.avatar
=link_to image_tag(avatar_url(issue.user), :alt => 'avatar'), user_path(issue.user) if issue.user
=link_to image_tag(avatar_url(issue.assignee), :alt => 'avatar'), user_path(issue.assignee) if issue.assignee
%a{:href => "#{project_issue_path @project, issue}#block-list"}
.answers
.pic= image_tag 'answers.png'

View File

@ -10,30 +10,30 @@
=form_tag [@project, @issue], :id => 'update_issue_status', :method => :put do
=hidden_field_tag "issue_status", @issue.closed? ? 'closed' : 'open', :name => "issue[status]"
.bordered.nopadding
%h3=t('layout.issues.executor')
%h3=t('layout.issues.assignee')
- if @issue.persisted?
-if can_manage
=form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_executor issue'} do |f|
=hidden_field_tag "user-default_executor", nil, :name => 'issue[user_id]'
.current_executor
- if @issue.user
=form_for :issue, :url => [@project, @issue], :method => :put, :html => { :class => 'edit_assignee issue'} do |f|
=hidden_field_tag "user-default_assignee", nil, :name => 'issue[assignee_id]'
.current_assignee
- if @issue.assignee
#user-0.people.nopointer
.avatar=image_tag avatar_url(@issue.user), :alt => 'avatar'
.name="#{@issue.user.uname} (#{@issue.user.name})"
=hidden_field_tag "user-0", @issue.user.id, :name => 'issue[user_id]'
.avatar=image_tag avatar_url(@issue.assignee), :alt => 'avatar'
.name="#{@issue.user.uname} (#{@issue.assignee.name})"
=hidden_field_tag "user-0", @issue.assignee.id, :name => 'issue[assignee_id]'
.both
- elsif @issue.user
- elsif @issue.assignee
.people.nopointer
.avatar=image_tag avatar_url(@issue.user), :alt => 'avatar'
.name="#{@issue.user.uname} (#{@issue.user.name})"
.avatar=image_tag avatar_url(@issue.assignee), :alt => 'avatar'
.name="#{@issue.assignee.uname} (#{@issue.assignee.name})"
.both
=link_to(t('layout.issues.label_manage'), '#', :class => "button tmargin10 manage_executor") if can_manage
=link_to(t('layout.issues.label_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'))
#manage_issue_users_list
=render 'issues/search_collaborators'
=link_to(t('layout.issues.done'), '#', :class => "button tmargin10 update_executor", :style => 'display:none') if can_manage
=link_to(t('layout.issues.done'), '#', :class => "button tmargin10 update_assignee", :style => 'display:none') if can_manage
.block
%h3=t('layout.issues.labels')

View File

@ -1,6 +1,6 @@
- (@users || []).each_with_index do |user, index|
.people.selected{:id => "user-#{index}", :class => 'add_executor'}
.people.selected{:id => "user-#{index}", :class => 'add_assignee'}
.avatar= image_tag(avatar_url(user), :alt => 'avatar')
.name="#{user.uname} (#{user.name})"
=hidden_field_tag "user-#{index}", user.id, :name => 'issue[user_id]'
=hidden_field_tag "user-#{index}", user.id, :name => 'issue[assignee_id]'
.both

View File

@ -7,9 +7,9 @@
.activity
.top
.image
=image_tag(avatar_url(@issue.creator, :medium), :alt => 'avatar') if @issue.creator
=image_tag(avatar_url(@issue.user, :medium), :alt => 'avatar') if @issue.user
.text
%span.name=link_to("#{@issue.creator.uname} (#{@issue.creator.name})", user_path(@issue.creator)) if @issue.creator
%span.name=link_to("#{@issue.user.uname} (#{@issue.user.name})", user_path(@issue.user)) if @issue.user
%br/
%span.date=@issue.created_at.to_s(:long)
%br/

View File

@ -9,6 +9,8 @@
= javascript_include_tag "application"
= csrf_meta_tag
= display_meta_tags :site => APP_CONFIG['project_name'], :reverse => true, :separator => '-'
- if user_signed_in?
= auto_discovery_link_tag :atom, root_path(:format => 'atom', :token => current_user.authentication_token), :title => t("layout.atom_link_tag_title", :nickname => current_user.name, :app_name => APP_CONFIG['project_name'])
%body
.wrap{:class => content_for?(:sidebar) ? 'columns' : ''}

View File

@ -3,7 +3,6 @@
%table.tablesorter{:cellpadding => "0", :cellspacing => "0"}
%thead
%tr
-#%th.lpadding16= t("activerecord.attributes.product_build_list.bs_id")
%th.lpadding16= t("activerecord.attributes.product_build_list.id")
%th.lpadding16= t("activerecord.attributes.product_build_list.status")
%th.lpadding16= t("activerecord.attributes.product_build_list.container_path")

View File

@ -37,16 +37,9 @@
= render :partial => "products/crontab", :locals => { :form => f }
- content_for :commented do
.leftlist= f.label :system_wide, :class => :label
.rightlist= f.check_box :system_wide, :class => 'check_box'
.both
.button_block
= submit_tag t("layout.save")
-#%input.button{:type => "submit", :class => "button"}
-#= image_tag("choose.png", :alt => t("layout.save"))
-#= t("layout.clone")
%span.text_button_padding= t("layout.or")
= link_to t("layout.cancel"), @product.new_record? ? platform_path(@platform) : platform_product_path(@platform, @product), :class => "button"

View File

@ -10,15 +10,3 @@
= form_for [@platform, @product], :html => { :class => :form, :multipart => true } do |f|
= render :partial => "form", :locals => {:f => f}
-#.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to @platform.name, platform_path(@platform) + "#products"
%li.active= link_to @product.name, edit_platform_product_path(@platform, @product)
.content
%h2.title= t("layout.products.edit_header")
.inner
= form_for [@platform, @product], :html => { :class => :form, :multipart => true } do |f|
= render :partial => "form", :locals => {:f => f}
-#- content_for :sidebar, render(:partial => 'sidebar')

View File

@ -4,16 +4,4 @@
= form_for [@platform, @product], :html => { :class => :form, :multipart => true } do |f|
= render :partial => "form", :locals => {:f => f}
-#.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to @platform.name, platform_path(@platform) + "#products"
%li.active= link_to t("layout.products.new"), new_platform_product_path(@platform)
.content
%h2.title= t("layout.products.new_header")
.inner
= form_for [@platform, @product], :html => { :class => :form, :multipart => true } do |f|
= render :partial => "form", :locals => {:f => f}
-#- content_for :sidebar, render(:partial => 'sidebar')

View File

@ -11,8 +11,6 @@
= link_to image_tag("code.png", :alt => t("layout.edit")) + " " + t("layout.edit"), edit_platform_product_path(@platform, @product), :class => "button"
- if can? :destroy, @product
= link_to image_tag("x.png", :alt => t("layout.delete")) + " " + t("layout.delete"), platform_product_path(@platform, @product), :method => "delete", :class => "button", :confirm => t("layout.products.confirm_delete")
-# if @product.can_clone?
=# link_to t("layout.products.clone"), clone_platform_product_path(@platform, @product), :class => "button"
- if can?(:create, @product.product_build_lists.build)
= link_to t("layout.products.build"), platform_product_product_build_lists_path(@platform, @product), :class => "button", :method => 'post', :confirm => t("layout.confirm")

View File

@ -1,7 +1,28 @@
.description-top
%ul.nav.zip
%li#menu-archive.dropdown
%a.dropdown-toggle{"data-toggle" => "dropdown", :href => "#menu-archive"}
=image_tag 'zip.png', :alt => 'ZIP'
%b.caret
%ul.dropdown-menu
%li=link_to "tar.gz", archive_path(project.id, 'tar', @treeish)
%li=link_to "zip", archive_path(project.id, 'zip', @treeish)
= text_field_tag :url, git_repo_url(project.git_repo_name), :class => 'name', :spellcheck => 'false', :readonly => true
.git_help ?
.role= can?(:write, project) ? t("layout.read_write_access") : t("layout.read_access")
= render :partial => 'projects/branch_select', :locals => {:project => project}
#git_help_data
%p= t("layout.projects.git_help.cloning") + ":"
%p
%p~ "git clone #{git_repo_url(project.git_repo_name)} #{project.name}"
%p~ "cd #{project.name}"
%p
%p= t("layout.projects.git_help.remote") + ":"
%p
%p~ "git remote add #{project.name} #{git_repo_url(project.git_repo_name)}"
%p~ "git fetch #{project.name}"
%p~ "git checkout -b my-local-tracking-branch #{project.name}/master_or_other_branch"
.both
:javascript
$(document).ready(function() {

View File

@ -1,6 +1,6 @@
%p== Hello, #{@user.user_appeal}.
- if @comment.commentable.class == Issue
- if @comment.issue_comment?
- link = link_to @comment.commentable.title, project_issue_url(@comment.commentable.project, @comment.commentable)
- object = 'issue'
- elsif @comment.commit_comment?

View File

@ -1,6 +1,6 @@
%p== Здравствуйте, #{@user.user_appeal}.
- if @comment.commentable.class == Issue
- if @comment.issue_comment?
- link = link_to @comment.commentable.title, project_issue_url(@comment.commentable.project, @comment.commentable)
- object = 'задаче'
- elsif @comment.commit_comment?

View File

@ -31,7 +31,7 @@ module Rosa
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
# Activate observers that should always be running.
config.active_record.observers = :event_log_observer, :activity_feed_observer
config.active_record.observers = :event_log_observer, :activity_feed_observer, :build_list_observer
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.

View File

@ -11,7 +11,6 @@ set :default_environment, {
require 'rvm/capistrano'
require 'bundler/capistrano'
require 'delayed/recipes'
require 'airbrake/capistrano'
set :whenever_command, "bundle exec whenever"
@ -38,6 +37,7 @@ set :deploy_via, :remote_cache
require 'lib/recipes/nginx'
require 'lib/recipes/unicorn'
require 'lib/recipes/bluepill'
require 'lib/recipes/delayed_job'
namespace :deploy do
task :stub_xml_rpc do
@ -49,11 +49,11 @@ namespace :deploy do
task :symlink_all, :roles => :app do
run "mkdir -p #{fetch :shared_path}/config"
# Setup DB
run "cp -n #{fetch :release_path}/config/database.yml.sample #{fetch :shared_path}/config/database.yml"
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 "ln -nfs #{fetch :shared_path}/config/application.yml #{fetch :release_path}/config/application.yml"
@ -66,7 +66,7 @@ namespace :deploy do
task :symlink_pids, :roles => :app do
run "cd #{fetch :shared_path}/tmp && ln -nfs ../pids pids"
end
# Speed up precompile (http://www.bencurtis.com/2011/12/skipping-asset-compilation-with-capistrano )
# namespace :assets do
# task :precompile, :roles => :web, :except => { :no_release => true } do
@ -77,7 +77,7 @@ namespace :deploy do
# logger.info "Skipping asset pre-compilation because there were no asset changes"
# end
# end
# end
# end
end
after "deploy:finalize_update", "deploy:symlink_all"
@ -88,7 +88,7 @@ after "deploy:setup", "deploy:symlink_pids"
# DJ
after "deploy:stop", "delayed_job:stop"
after "deploy:start", "delayed_job:start"
# after "deploy:restart", "delayed_job:restart"
after "deploy:restart", "delayed_job:restart"
after "deploy:restart", "deploy:cleanup"

View File

@ -36,6 +36,7 @@ en:
true_: True
false_: False
publish: Publish
reject_publish: Reject
add: Add
upload: Upload
not_access: Access denied!
@ -44,6 +45,7 @@ en:
back: Back
processing: working ...
invalid_content_type: incorrect type
atom_link_tag_title: Private feed for %{nickname} | %{app_name}
settings:
label: Settings
@ -64,11 +66,6 @@ en:
unlock: Do not receive unlock instructions?
sign_in_through: Sign in by %{provider}
downloads:
title: Downloads statistic
message: Automatically updated every 24 hours
refresh_btn: Refresh
weekdays:
Monday: Monday
Tuesday: Tuesday
@ -137,9 +134,6 @@ en:
exception_message: Access violation to this page!
downloads:
statistics_refreshed: Statistics refreshed
collaborators:
successfully_changed: Collaborators list successfully changed
error_in_changing: Collaborators list changing error
@ -172,7 +166,6 @@ en:
arch: Arch
private_user: Private user
product_build_list: Product build list
download: Statistics
attributes:
settings:
@ -195,9 +188,3 @@ en:
created_at: Created
updated_at: Updated
download:
name: Name
version: Version
distro: Source
platform: Platform
counter: Downloads

View File

@ -3,7 +3,6 @@ en:
repositories: Repositories
personal_repository: My repository
products: Products
downloads: Statistics
top_menu:
platforms: Platforms
projects: Projects
@ -38,5 +37,4 @@ en:
users: Users
register_requests: Invites
event_logs: Event log
downloads: Download statistic

View File

@ -3,7 +3,6 @@ ru:
products: Продукты
repositories: Репозитории
personal_repository: Мой репозиторий
downloads: Статистика
top_menu:
platforms: Платформы
projects: Проекты
@ -38,4 +37,3 @@ ru:
users: Пользователи
register_requests: Инвайты
event_logs: Лог событий
downloads: Статистика закачек пакетов

View File

@ -8,6 +8,7 @@ en:
my_builds_by_day: My today builds
new_project: Create project
load_messages: show previous messages
atom_title: Activity Feed
notifications:
subjects:

View File

@ -8,6 +8,7 @@ ru:
my_builds_by_day: Мои сборки за день
new_project: Создать проект
load_messages: показать предыдущие сообщения
atom_title: Лента активности
notifications:
subjects:

View File

@ -14,10 +14,9 @@ en:
arch_id: Architecture
arch: Architecture
is_circle: Recurrent build
notified_at: Notified at
updated_at: Notified at
additional_repos: Additional repositories
include_repos: Included repositories
updated_at: Updated on
created_at: Created on
pl: Repository for package storage
pl_id: Repository for package storage
@ -29,6 +28,8 @@ en:
project_version: Version
user: User
preferences: Preferences
started_at: Build started at
duration: Build duration in seconds
build_list/item:
name: Name
@ -43,8 +44,8 @@ en:
current: Curent
created_at_start: "Build to start on:"
created_at_end: "Build to start until:"
notified_at_start: "Last update from BS on:"
notified_at_end: " Last update from BS until:"
updated_at_start: "Last update from BS on:"
updated_at_end: " Last update from BS until:"
bs_id_search: 'Search by Id'
project_name_search: Search by project name
bs_id_not_set: Id has not been configured yet
@ -54,11 +55,15 @@ en:
cancel_success: 'Build canceled'
cancel_fail: 'Errors during build cancelation!'
publish_success: 'Build is queued for publishing'
reject_publish_success: 'Publishing rejected'
publish_fail: 'Errors during build publishing!'
reject_publish_fail: 'Rejecting publishing failed'
container_published: 'Container published in a repository'
action: Action
new_header: New build
main_data: Main data
human_current_duration: Build currently takes %{hours} h. %{minutes} min.
human_duration: Builded in %{hours} h. %{minutes} min.
ownership:
header: Build list ownership
@ -84,6 +89,7 @@ en:
statuses:
build_error: Build error
build_published: Build has been published
rejected_publish: Publishing rejected
build_publish: Build is being publishing
failed_publish: Publishing error
dependencies_fail: Dependences not found
@ -95,7 +101,7 @@ en:
success: Build complete
build_started: Build started
platform_not_found: Platform not found
platform_pending: Platforn pending
platform_pending: Platform pending
project_not_found: Project not found
project_version_not_found: Project version not found

View File

@ -14,10 +14,9 @@ ru:
arch_id: Архитектура
arch: Архитектура
is_circle: Циклическая сборка
notified_at: Информация получена
updated_at: Информация получена
additional_repos: Дополнительные репозитории
include_repos: Подключаемые репозитории
updated_at: Обновлен
created_at: Создан
pl: Репозиторий для сохранения пакетов
pl_id: Репозиторий для сохранения пакетов
@ -29,6 +28,7 @@ ru:
project_version: Версия
user: Пользователь
preferences: Настройки
duration: Длительность билда в секундах
build_list/item:
name: Название
@ -43,8 +43,8 @@ ru:
current: Текущие
created_at_start: "Время постановки на сборку с:"
created_at_end: "Время постановки на сборку по:"
notified_at_start: "Время последнего обновления от BS с:"
notified_at_end: "Время последнего обновления от BS по:"
updated_at_start: "Время последнего обновления от BS с:"
updated_at_end: "Время последнего обновления от BS по:"
bs_id_search: 'Поиск по Id'
project_name_search: Поиск по названию проекта
bs_id_not_set: Id еще не присвоен
@ -54,12 +54,17 @@ ru:
cancel_success: 'Сборка отменена.'
cancel_fail: 'При отмене сборки произошла ошибка!'
publish_success: 'Сборка поставлена в очередь на публикацию.'
reject_publish_success: 'Публикация отклонена'
publish_fail: 'При публикации сборки произошла ошибка!'
reject_publish_fail: 'Не удалось отклонить публикацию сборки'
container_published: 'Контейнер размещен в репозитории'
action: Действие
new_header: Новая сборка
main_data: Основные данные
human_current_duration: Сборка длится уже %{hours} ч. %{minutes} мин.
human_duration: Собрано за %{hours} ч. %{minutes} мин.
ownership:
header: Принадлежность заданий
owned: Мне
@ -84,6 +89,7 @@ ru:
statuses:
build_error: ошибка сборки
build_published: опубликован
rejected_publish: публикация отклонена
build_publish: публикуется
failed_publish: ошибка публикации
dependencies_fail: зависимости не найдены

View File

@ -4,8 +4,8 @@ en:
issue:
title: Name
body: Description
user: Assigned
user_id: Assigned
assignee: Assigned
assignee_id: Assigned
project: Project
status: Status
@ -41,10 +41,10 @@ en:
update_label: Update label
label_custom_color: Custom color
label_manage: Manage labels
executor: Executor
assignee: Assignee
search_user: Find user...
search_labels: Find labels...
choose_user_on_left: Choose executor on the left
choose_user_on_left: Choose assignee on the left
choose_labels_on_left: Choose labels on the left
at: at
cancel_button: Cancel

View File

@ -4,8 +4,8 @@ ru:
issue:
title: Название
body: Описание
user: Назначена
user_id: Назначена
assignee: Назначена
assignee_id: Назначена
project: Проект
status: Статус
@ -41,7 +41,7 @@ ru:
update_label: Обновить метку
label_custom_color: Свой цвет
label_manage: Управление
executor: Исполнитель
assignee: Исполнитель
search_user: Найти пользователя...
search_labels: Найти метки...
choose_user_on_left: выберите исполнителя слева

View File

@ -4,12 +4,16 @@ en:
add: Add
edit: Settings
fork_and_edit: Fork
fork_to: Fork to %{to}
fork_modal_header: Where do you want to fork this project?
already_exists: Project already exists
list: List
list_header: Projects
edit_header: Edit project
show: Project
build: Build
new_build: New build %{project_name}
new_build_list: New build
confirm_delete: Are you sure you want to delete this project?
new: New project
location: Location
@ -29,6 +33,11 @@ en:
sections: Sections
has_issue_description: Tracker adds a lightweight issue management system tightly integrated with your repository.
has_wiki_description: Wikis are the simplest way to allow other users to contribute content. Any user can create and edit pages for documentation, examples, support or anything you wish.
human_average_build_time: Expected time is %{hours} h. %{minutes} min.
git_help:
cloning: Cloning the repository
remote: Add this repository as a remote to an existing local repository
diff_show_header: "%{files} with %{additions} and %{deletions}."
about_subheader: "About project"

View File

@ -4,12 +4,16 @@ ru:
add: Добавить
edit: Настройки
fork_and_edit: Клонировать
fork_to: Клонировать в %{to}
fork_modal_header: Куда Вы хотите клонировать проект?
already_exists: Проект уже существует
list: Список
list_header: Проекты
edit_header: Редактировать проект
show: Проект
build: Собрать
new_build: Новая сборка %{project_name}
new_build_list: Новая сборка
confirm_delete: Вы уверены, что хотите удалить этот проект?
new: Новый проект
location: Расположение
@ -29,6 +33,7 @@ ru:
sections: Разделы
has_issue_description: Трэкер предоставляет лекговесный менеджер для задач по разработке Вашего проекта.
has_wiki_description: Wiki - это самый простой способ предоставить другим вносить свой вклад в развитие Вашего проекта. Каждый пользователь нашего сервиса может использовать Wiki для документирования, примеров, поддержки или всего другого, в чем у Вас появится необходимость.
human_average_build_time: 'Ожидаемое время: %{hours} ч. %{minutes} мин.'
diff_show_header: "%{files} с %{additions} и %{deletions}."
about_subheader: "О проекте"
@ -44,7 +49,11 @@ ru:
current_branch: Текущая ветка/тег
current_commit: Текущий коммит
files_in_project: Файлы в
git_help:
cloning: Клонирование этого репозитория
remote: Добавление этого репозитория как удаленного к существующему локальному репозиторию
flash:
project:
saved: Проект успешно сохранен

View File

@ -36,6 +36,7 @@ ru:
true_: Да
false_: Нет
publish: Опубликовать
reject_publish: Отклонить
add: Добавить
upload: Загрузить
not_access: Нет доступа!
@ -44,6 +45,7 @@ ru:
back: Назад
processing: Обрабатывается...
invalid_content_type: имеет неверный тип
atom_link_tag_title: Приватная лента для %{nickname} | %{app_name}
settings:
label: 'Настройки'
@ -64,11 +66,6 @@ ru:
unlock: Не получили инструкции по разблокировке?
sign_in_through: Войти через %{provider}
downloads:
title: Статистика закачек пакетов
message: Обновляется автоматически каждые 24 часа
refresh_btn: Обновить
weekdays:
Monday: Понедельник
Tuesday: Вторник
@ -137,9 +134,6 @@ ru:
exception_message: У Вас нет доступа к этой странице!
downloads:
statistics_refreshed: Статистика обновлена
collaborators:
successfully_changed: Список коллабораторов успешно изменен
error_in_changing: Ошибка изменения списка коллабораторов
@ -172,7 +166,6 @@ ru:
arch: Arch
private_user: Приватный пользователь
product_build_list: Сборочный лист продукта
download: Статистика
auto_build_list: Автоматическая пересборка пакетов
attributes:
@ -196,9 +189,3 @@ ru:
created_at: Создана
updated_at: Обновлена
download:
name: Название
version: Версия
distro: Дистрибутив
platform: Архитектура
counter: Закачки

View File

@ -4,16 +4,18 @@ app_name = ENV['APP_NAME'] || 'rosa_build'
Bluepill.application(app_name) do |app|
app.uid = app.gid = 'rosa'
app.working_dir = "/srv/#{app_name}/current"
app.process("delayed_job") do |process|
process.start_grace_time = 10.seconds
process.stop_grace_time = 10.seconds
process.restart_grace_time = 10.seconds
%w(fork import hook default).each do |queue|
app.process("delayed_job_#{queue}_queue") do |process|
process.start_grace_time = 10.seconds
process.stop_grace_time = 10.seconds
process.restart_grace_time = 10.seconds
process.start_command = "/usr/bin/env ruby script/delayed_job start"
process.stop_command = "/usr/bin/env ruby script/delayed_job stop"
process.pid_file = File.join(app.working_dir, 'tmp', 'pids', 'delayed_job.pid')
process.start_command = "/usr/bin/env ruby script/delayed_job --queue=#{queue} -p #{queue} --pid-dir=/srv/#{app_name}/current/tmp/#{queue}_pids start"
process.stop_command = "/usr/bin/env ruby script/delayed_job --pid-dir=/srv/#{app_name}/current/tmp/#{queue}_pids stop"
process.pid_file = File.join(app.working_dir, 'tmp', "#{queue}_pids", 'delayed_job.pid')
end
end
app.process("newrelic") do |process|
process.start_grace_time = 10.seconds
process.stop_grace_time = 10.seconds

View File

@ -8,9 +8,11 @@ Rosa::Application.routes.draw do
get '/users/new' => 'admin/users#new', :as => :new_user
get '/users/list' => 'admin/users#list', :as => :users_list
post '/users/create' => 'admin/users#create', :as => :create_user
get '/users/:id/edit' => 'admin/users#profile', :as => :edit_user
put '/users/:id/edit' => 'admin/users#update', :as => :update_user
delete '/users/:id/delete' => 'admin/users#destroy', :as => :delete_user
constraints :id => /\d+/ do
get '/users/:id/edit' => 'admin/users#profile', :as => :edit_user
put '/users/:id/edit' => 'admin/users#update', :as => :update_user
delete '/users/:id/delete' => 'admin/users#destroy', :as => :delete_user
end
end
devise_for :users, :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'}
resources :users, :only => [:show, :profile, :update] do
@ -28,7 +30,7 @@ Rosa::Application.routes.draw do
end
end
get 'users/:id/settings/private' => 'users#private', :as => :user_private_settings
get 'users/:id/settings/private' => 'users#private', :as => :user_private_settings
put 'users/:id/settings/private' => 'users#private'
resources :groups do
get :autocomplete_group_uname, :on => :collection
@ -144,6 +146,8 @@ Rosa::Application.routes.draw do
# Raw
get '/projects/:project_id/raw/:treeish/*path' => "git/blobs#raw", :defaults => {:treeish => :master}, :as => :raw, :format => false
get '/projects/:project_id/archive/:format/tree/:treeish' => "git/trees#archive", :defaults => {:treeish => :master}, :as => :archive, :format => /zip|tar/
# Core callbacks
match 'build_lists/publish_build', :to => "build_lists#publish_build"
match 'build_lists/status_build', :to => "build_lists#status_build"
@ -157,6 +161,7 @@ Rosa::Application.routes.draw do
member do
put :cancel
put :publish
put :reject_publish
end
collection { post :search }
end
@ -166,18 +171,15 @@ Rosa::Application.routes.draw do
resources :event_logs, :only => :index
match 'statistics/' => 'downloads#index', :as => :downloads
match 'statistics/refresh' => 'downloads#refresh', :as => :downloads_refresh
match '/forbidden', :to => 'pages#forbidden', :as => 'forbidden'
match '/terms-of-service', :to => 'pages#tos', :as => 'tos'
if APP_CONFIG['anonymous_access']
authenticated do
root :to => "activity_feeds#index"
get "/(.:format)" => "activity_feeds#index", :as => :root
end
root :to => 'pages#root'
else
root :to => "activity_feeds#index"
get "/(.:format)" => "activity_feeds#index", :as => :root
end
end

View File

@ -7,12 +7,6 @@
# runner "Download.parse_and_remove_nginx_log"
#end
every 1.day, :at => '5:00' do
#rake "sudo_test:projects"
runner "Download.rotate_nginx_log"
runner "Download.parse_and_remove_nginx_log"
end
every 1.day, :at => '4:00 am' do
rake "import:sync:all", :output => 'log/sync.log'
end

View File

@ -1,3 +1,4 @@
# -*- encoding : utf-8 -*-
class RemoveContainersAndRpms < ActiveRecord::Migration
def up
drop_table :containers

View File

@ -1,7 +1,8 @@
# -*- encoding : utf-8 -*-
class CustomizePlatform < ActiveRecord::Migration
def self.up
change_column_null :platforms, :name, false
change_column_null :platforms, :distrib_type, false
#change_column_null :platforms, :distrib_type, false
change_column_null :platforms, :platform_type, false
change_column_null :platforms, :released, false
change_column_null :platforms, :visibility, false
@ -16,4 +17,4 @@ class CustomizePlatform < ActiveRecord::Migration
change_column_null :platforms, :visibility, true
remove_index "platforms", ["name"]
end
end
end

View File

@ -0,0 +1,14 @@
# -*- encoding : utf-8 -*-
class ClearProduct < ActiveRecord::Migration
def self.up
remove_column :products, :build_status
remove_column :products, :build_path
remove_column :products, :system_wide
end
def self.down
add_column :products, :build_status, :integer, :default => 2, :null => false
add_column :products, :build_path, :string
add_column :products, :system_wide, :boolean, :default => false
end
end

View File

@ -0,0 +1,11 @@
class ModifyDefaultQueue < ActiveRecord::Migration
def up
change_column :delayed_jobs, :queue, :string, :default => 'default'
execute "UPDATE delayed_jobs SET queue = 'default'"
end
def down
change_column :delayed_jobs, :queue, :string, :default => nil
execute "UPDATE delayed_jobs SET queue = null"
end
end

View File

@ -0,0 +1,6 @@
class AddStartedAtAndDurationToBuildLists < ActiveRecord::Migration
def change
add_column :build_lists, :started_at, :datetime
add_column :build_lists, :duration, :integer
end
end

View File

@ -0,0 +1,9 @@
class BuildAverageTime < ActiveRecord::Migration
def change
add_column :projects, :average_build_time, :integer, :null => false, :default => 0
add_column :projects, :build_count, :integer, :null => false, :default => 0
end
ActivityFeed.where(:kind => 'build_list_notification').destroy_all
end

View File

@ -0,0 +1,11 @@
class AddAssigneeToIssues < ActiveRecord::Migration
def up
rename_column :issues, :user_id, :assignee_id
rename_column :issues, :creator_id, :user_id
end
def down
rename_column :issues, :user_id, :creator_id
rename_column :issues, :assignee_id, :user_id
end
end

View File

@ -0,0 +1,17 @@
class RemoveDownloads < ActiveRecord::Migration
def up
drop_table :downloads
end
def down
create_table "downloads", :force => true do |t|
t.string "name", :null => false
t.string "version"
t.string "distro"
t.string "platform"
t.integer "counter", :default => 0
t.datetime "created_at"
t.datetime "updated_at"
end
end
end

View File

@ -0,0 +1,15 @@
class AddTokenAuthenticatableToUsers < ActiveRecord::Migration
def self.up
add_column :users, :authentication_token, :string
add_index :users, :authentication_token
User.all.each do |user|
user.ensure_authentication_token!
end
end
def self.down
remove_column :users, :authentication_token
remove_index :users, :authentication_token
end
end

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20120331180541) do
ActiveRecord::Schema.define(:version => 20120418100619) do
create_table "activity_feeds", :force => true do |t|
t.integer "user_id", :null => false
@ -75,6 +75,8 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.string "package_version"
t.string "commit_hash"
t.integer "priority", :default => 0, :null => false
t.datetime "started_at"
t.integer "duration"
end
add_index "build_lists", ["arch_id"], :name => "index_build_lists_on_arch_id"
@ -102,21 +104,11 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.string "locked_by"
t.datetime "created_at"
t.datetime "updated_at"
t.string "queue"
t.string "queue", :default => "default"
end
add_index "delayed_jobs", ["priority", "run_at"], :name => "delayed_jobs_priority"
create_table "downloads", :force => true do |t|
t.string "name", :null => false
t.string "version"
t.string "distro"
t.string "platform"
t.integer "counter", :default => 0
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "event_logs", :force => true do |t|
t.integer "user_id"
t.string "user_name"
@ -145,13 +137,13 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
create_table "issues", :force => true do |t|
t.integer "serial_id"
t.integer "project_id"
t.integer "user_id"
t.integer "assignee_id"
t.string "title"
t.text "body"
t.string "status", :default => "open"
t.string "status", :default => "open"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "creator_id"
t.integer "user_id"
t.datetime "closed_at"
t.integer "closed_by"
end
@ -188,7 +180,7 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.string "owner_type"
t.string "visibility", :default => "open", :null => false
t.string "platform_type", :default => "main", :null => false
t.string "distrib_type", :null => false
t.string "distrib_type"
end
add_index "platforms", ["name"], :name => "index_platforms_on_name", :unique => true, :case_sensitive => false
@ -215,8 +207,6 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
create_table "products", :force => true do |t|
t.string "name", :null => false
t.integer "platform_id", :null => false
t.integer "build_status", :default => 2, :null => false
t.string "build_path"
t.datetime "created_at"
t.datetime "updated_at"
t.text "build_script"
@ -227,7 +217,6 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.string "tar_content_type"
t.integer "tar_file_size"
t.datetime "tar_updated_at"
t.boolean "system_wide", :default => false
t.text "cron_tab"
t.boolean "use_cron", :default => false
t.text "description"
@ -258,29 +247,29 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.datetime "updated_at"
t.integer "owner_id"
t.string "owner_type"
t.string "visibility", :default => "open"
t.string "visibility", :default => "open"
t.text "description"
t.string "ancestry"
t.boolean "has_issues", :default => true
t.boolean "has_issues", :default => true
t.boolean "has_wiki", :default => false
t.string "srpm_file_name"
t.string "srpm_content_type"
t.integer "srpm_file_size"
t.datetime "srpm_updated_at"
t.boolean "has_wiki", :default => false
t.string "default_branch", :default => "master"
t.boolean "is_rpm", :default => true
t.string "default_branch", :default => "master"
t.boolean "is_rpm", :default => true
t.integer "average_build_time", :default => 0, :null => false
t.integer "build_count", :default => 0, :null => false
end
add_index "projects", ["owner_id"], :name => "index_projects_on_name_and_owner_id_and_owner_type", :unique => true, :case_sensitive => false
create_table "register_requests", :force => true do |t|
t.string "name"
t.string "email"
t.string "token"
t.boolean "approved", :default => false
t.boolean "rejected", :default => false
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "interest"
t.text "more"
end
@ -342,6 +331,9 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.string "uname"
t.string "role"
t.string "language", :default => "en"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.integer "own_projects_count", :default => 0, :null => false
t.datetime "reset_password_sent_at"
t.text "professional_experience"
@ -355,11 +347,10 @@ ActiveRecord::Schema.define(:version => 20120331180541) do
t.integer "failed_attempts", :default => 0
t.string "unlock_token"
t.datetime "locked_at"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "authentication_token"
end
add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token"
add_index "users", ["confirmation_token"], :name => "index_users_on_confirmation_token", :unique => true
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true

View File

@ -1,323 +0,0 @@
КРАТКОЕ ОПИСАНИЕ ACL
====================
Предназначение
--------------
ACL предназначена для контроля прав пользователя на выполнение действий в
системе и доступа к моделям по областям видимости.
Решаемые задачи
---------------
* Проверка наличия у пользователя прав для выполнения метода контроллера;
* Прозрачная фильтрация моделей для исключения невидимых для пользователя
записей.
Возможности
-----------
* Неограниченное количество моделей, над которыми могут выполняться
действия (`target`);
* Неограниченное количество моделей, которые могут выполнять действия над
другими (`acter`);
* Геренатор прав основывающийся на структуре приложения (см. далее);
* Неограниченное количество ролей, которые могут назначаться для `acter` и
содержать любую комбинацию прав и доступных видимостей;
* Объединение прав `acter`-ов на глубину одной модели (см. далее);
* Разграничение назначения ролей по классам (не завершено, на данный
момент не критично);
* Разграничение ролей на глобальные и локальные (см. далее).
Типы моделей, с которыми взаимодействует ACL
--------------------------------------------
* __ActerModel__ -- модель, которая может выполнять действия, разрешенные
ролями, над другими моделями;
* __TargetTarget__ -- модель, над которой могут выполняться действия,
разрешенные ролями.
__ActerModel__ может иметь глобальную роль, которая определяет возможность
выполнения действий без привязки к конкретному экземпляру __TargetModel__ и
неограниченное количество прав по отношению к конкретному экземпляру
__TargetModel__.
__TODO__: *Реализовать дополнение необходимым функционалом моделей, выбранных
в качестве __ActerModel__ или __TargetModel__ при декларировании их роли в
системе*
Схема взаимодействия объектов ACL
---------------------------------
Функционал ACL реализуется путем взаимодействия моделей `Right, Role, Relation`,
реализующих основной функционал и особых моделей проекта, обозначенных на схеме
как `ActerModel` и `TargetModel`.
Экземпляры __ActerModel__ и __TargetModel__ связываются посредством модели
`Relation`, через которую экземпляр __ActerModel__ получает неограниченное
количество ролей по отношению к экземпляру __TargetModel__.
### Схема связей моделей:
--------------
| ActerModel |
/ --------------
--------- -------- |
| Right | | Role | V
--------- -------- ------------
... <= ... <= | Relation |
--------- -------- ------------
| Right | | Role | |
--------- -------- V
---------------
| TargetModel |
---------------
* Обозначения: <= -- Связь с несколькими моделями
<-,/,| -- Связь с одной моделью
Генератор прав
--------------
Генератор ролей является Rake-task-ом и запускается командой
`rake rights:generate`.
Желательно запускать после добавления нового метода в контроллер для того,
чтобы на этот метод в системе появилось право.
Загрузка ролей из дампа
-----------------------
Загрузку ролей из заранее подготовленного дампа можно произвести двумя
способами:
* В консоли, используя Rake task `rake roles:load`, который загрузит в базу
роли, описанные в `config/roles.yml`
* Через Web-интерфейс, на странице `/roles`, если у пользователя есть
соответствующие права. Для загрузки через Web-интерфейс необходимо выбрать
файл в поле выбора вверху страницы и нажать __Загрузить__.
Получение дампа ролей
---------------------
Дамп ролей может получить пользователь, имеющий на это права, зайдя на страницу
`roles` и нажав кнопку `Скачать в YML`.
Задание областей видимости моделей
----------------------------------
*Этот функционал скорее всего будет изменяться*
Если модель должна иметь несколько областей видимости, нужно сделать следующее:
* Добавить в модель константу `VISIBILITIES`, в которой задать названия областей
видимости;
* Добавить к таблице моделей поле `visibility:text`;
* Добавить `attr_accessible :visibility` в модель;
* Создать `scope :by_visibility`, принимающий аргументом массив областей
видимости.
После выполнения этих действий на странице редактирования роли появится поле
выбора областей видимости для этой модели.
### Пример:
model VisibilitiesExample < ActiveRecord::Base
VISIBILITIES = ['open', 'hidden', 'open_for_admins']
attr_accessible :visibility
scope :by_visibility, lambda {|v| {:conditions => ['visibility in (?)', v]}}
end
*Назначение методов описано в API*
Задание типа модели
-------------------
*Этот функционал скорее всего будет изменяться*
Если модель должна иметь возможность быть связанной с другими с использованием
ролей, необходимо произвести следующие действия:
* Добавить в модель декларацию `relationable`, с аргументом `:as`, который
может принимать заначения из `[:object, :target]`. Если модель будет
__acter__-ом, передается `:object`, иначе `:target`
Пример: `relationable :as => :object`
* Добавить в модель связь `belongs_to :global_role, :class_name => 'Role'`
* Добавить в модель связь с моделью `Relation`
* Если модель -- __acter__ и она должна использовать как свои роли, так и
роли из другой модели, необходимо добавить декларацию `inherit_rights_from`
которой аргументом присвоить имя/имена связей с моделями, из которых должны
браться роли.
### Примеры:
* Модель, являющаяся __acter__:
class ActerModel < ActiveRecord::Base
relationable :as => :object
belongs_to :global_role, :class_name => 'Role'
has_many :targets, :as => :object, :class_name => 'Relation'
end
* Модель, являющаяся __acter__ и наследующая права другой модели:
class ActerWithInheritableRolesModel < ActiveRecord::Base
relationable :as => :object
ingerit_rights_from :another_acter_model
has_many :another_acters_models
belongs_to :global_role, :class_name => 'Role'
has_many :targets, :as => :object, :class_name => 'Relation'
end
* Модель, являющаяся __target__:
class TargetModel < ActiveRecord::Base
relationable :as => :target
has_many :objects, :as => :target, :class_name => 'Relation'
end
* Модель, являющаяся и __acter__, и __target__:
class ActerAndTargetModel < ActiveRecord::Base
relationable :as => :object
relationable :as => :target
belongs_to :global_role, :class_name => 'Role'
has_many :targets, :as => :object, :class_name => 'Relation'
has_many :objects, :as => :target, :class_name => 'Relation'
end
*Назначение методов описано в API*
Использование ACL в контроллере
-------------------------------
Если необходимо ограничить доступ ко всем методам контроллера по глобальной
роли пользователя вне зависимости от текущей модели, необходимо установить
`before_filter :check_global_rights`.
В случае, если у пользователя нет прав для выполнения текущего действия, он
будет переотправлен на предыдущую страницу.
Если необходимо проверить, может ли пользователь выполнить конкретное действие,
необходимо в начале этого метода вызвать метод `can_perform?`. Если методу
передан параметр, являющийся экземпляром класса __TargetModel__, метод возвратит
`true`, если одна или несколько ролей пользователя над этой моделью позволяет
ему выполнить может выполнить действие и `false` в противном случае. Если
необязательный параметр опущен, или в качестве параметра передано `:system`,
учитываются только глобальные роли.
### Примеры
* Контроллер, некоторые методы которого доступны для всех:
class StuffController < ApplicationController
def index # доступ у всех
...
end
def show # 'Что-то полезное' выполнится только у тех, чьи роли над
# @data позволяют выполнить конкретное действие.
@data = Stuff.find(params[:id])
if can_perform? @data
#что-то полезное
else
# сообщаем пользователю, что он не может выполнить действие
end
end
def create # 'Что-то полезное' выполнится только у тех, чьи
# глобальные роли позволяют выполнить метод
if can_perform?
# что-то полезное
else
# сообщаем пользователю, что он не может выполнить действие
end
end
end
* Контроллер, доступ к методам которого возможен только при наличии необходимых
прав в глобальных ролях:
class StuffController < ApplicationController
before_filter :check_global_rights # разрешаем доступ только тем,
# чьи роли это позволяют.
def index # доступ только у тех, кому это позволяет глобальная роль
...
end
def show # 'Что-то полезное' выполнится только у тех, чьи роли
# над @data это позволяют
@data = Stuff.find(params[:id])
if can_perform? @data
#что-то полезное
else
# сообщаем пользователю, что он не может выполнить действие
end
end
end
Использование ACL во view
-------------------------
Используется метод `can_perform?` модели, для которой нужно проверить права
доступа. Обычно этой моделью является `current_user`.
### Примеры:
* Проверка на возможность выполнения глобального действия:
-if current_user.can_perform?('some_controller', 'some_aciton')
%a{:href => controller_action_path}= Some description
* Проверка на возможность выполнения действия над текущей моделью:
-if current_user.can_perform?('some_controller', 'some_aciton', @data)
%a{:href => controller_action_path(@data)}= Some description
API для работы с ACL
--------------------
*Этот функционал скорее всего будет изменяться*
### Методы потомков `ActiveRecord::Base`
*  Методы классов:
- `relationable` -- устанавливает, кем является модель (acter/target)
- `relationable?` -- может ли иметь связь с ролью/ролями с другими
- `relation_acters` -- список моделей, которые могут иметь роли
по отношению к другим (след. метод)
- `relation_targets` -- список моделей, над которыми могут совершаться
действия
- `relation_acter? (model)`, `relation_target? (model)` -- является ли
тем или другим
  - `inherit_rights_from (:relation_name | [:relation_names])` -- права из
каких связанных моделей наследовать
  - `visible_to (model)` -- все видимые для модели записи, может
включаться в цепочку (например, для paginate)
* Методы инстансов:
- `add_role_to(acter, role)` -- привязать acter-а с ролью к текущей записи
- `add_role_on(target, role)` -- привязать текущую модель с ролью
- `roles_to(object)` -- если object == :system, возвращает глобальные роли
текущей записи, если передана запись -- то роли текущей модели над записью
- `rights_to(object)` -- аргументы те же, но возвращается список прав,
собранный из всех ролей
- `right_to(controller_name, action)` -- возвращает запись с правом на
выполнение действия action в контроллере c именем `controller_name`
- `can_perform? (controller_name, action, target = :system)` -- показывает,
может ли текущая модель выполнить действие контроллера над целью
### Методы потомков `ActiveController::Base`
*Возможно, будут вынесены в хелпер для универсализации системы*
- `can_perform? (target = :system)` -- может ли `current_user` выполнить
текущее действие
- `check_global_access` -- делает редирект назад или на главную, если
пользователь вообще не может совершить текущее действие
- `roles_to(object)` -- возвращает список ролей `current_user`-а по отношению
к объекту
- `rights_to(object)` -- возвращает список прав `current_user`-а по отношению
к объекту

View File

@ -23,9 +23,12 @@
<div class="middle">
<!--Main menu-->
<menu>
<div class="logo">
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
</div>
<ul>
<li>
<a href="#" class="first">Главная</a>
<a href="#">Главная</a>
</li>
<li>
<a href="#" class="active">Проекты</a>
@ -43,9 +46,7 @@
<a href="#">Документация</a>
</li>
</ul>
<div class="logo">
<img src="pics/logo-mini.png" alt="logo" />
</div>
</menu>
<div class="information">
<!--Search-->

View File

@ -22,9 +22,12 @@
<div class="middle">
<!--Main menu-->
<menu>
<div class="logo">
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
</div>
<ul>
<li>
<a href="#" class="first">Главная</a>
<a href="#">Главная</a>
</li>
<li>
<a href="#" class="active">Проекты</a>
@ -42,9 +45,6 @@
<a href="#">Документация</a>
</li>
</ul>
<div class="logo">
<img src="pics/logo-mini.png" alt="logo" />
</div>
</menu>
<div class="information">
<!--Search-->

View File

@ -24,9 +24,12 @@
<div class="middle">
<!--Main menu-->
<menu>
<div class="logo">
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
</div>
<ul>
<li>
<a href="#" class="first">Главная</a>
<a href="#">Главная</a>
</li>
<li>
<a href="#" class="active">Проекты</a>
@ -44,9 +47,7 @@
<a href="#">Документация</a>
</li>
</ul>
<div class="logo">
<img src="pics/logo-mini.png" alt="logo" />
</div>
</menu>
<div class="information">
<!--Search-->

View File

@ -18,9 +18,12 @@
<div class="middle">
<!--Main menu-->
<menu>
<div class="logo">
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
</div>
<ul>
<li>
<a href="#" class="first">Главная</a>
<a href="#">Главная</a>
</li>
<li>
<a href="#" class="active">Проекты</a>
@ -38,9 +41,7 @@
<a href="#">Документация</a>
</li>
</ul>
<div class="logo">
<img src="pics/logo-mini.png" alt="logo" />
</div>
</menu>
<div class="information">
<!--Search-->

View File

@ -20,9 +20,12 @@
<div class="middle">
<!--Main menu-->
<menu>
<div class="logo">
<a href="#"><img src="pics/logo-mini.png" alt="Главная" /></a>
</div>
<ul>
<li>
<a href="#" class="first">Главная</a>
<a href="#">Главная</a>
</li>
<li>
<a href="#" class="active">Проекты</a>
@ -40,9 +43,6 @@
<a href="#">Документация</a>
</li>
</ul>
<div class="logo">
<img src="pics/logo-mini.png" alt="logo" />
</div>
</menu>
<div class="information">
<!--Search-->

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