Git diff from the first to the last commit in Verso project. ============================================================ diff --git a/.gitignore b/.gitignore index 82f912d..5adae05 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ config/database.yml config/gitorious.yml config/ultrasphinx/*.conf config/broker.yml +config/environment.rb +config/environments/production.rb +script/ldap_settings.py db/*.sqlite3 .autotest db/sphinx/* @@ -24,4 +27,6 @@ public/images/sites/* public/stylesheets/sites/* public/javascripts/sites/* public/system/avatars -public/system/group_avatars \ No newline at end of file +public/system/group_avatars +app/views/site/_version.html.erb +*~ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2f2ff09..7e0b210 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # @@ -33,6 +35,8 @@ class ApplicationController < ActionController::Base layout :pick_layout_based_on_site + filter_parameter_logging :password + rescue_from ActiveRecord::RecordNotFound, :with => :render_not_found rescue_from ActionController::UnknownController, :with => :render_not_found rescue_from ActionController::UnknownAction, :with => :render_not_found @@ -130,7 +134,7 @@ class ApplicationController < ActionController::Base end return true end - + def find_repository_owner if params[:user_id] @owner = User.find_by_login!(params[:user_id]) @@ -142,6 +146,7 @@ class ApplicationController < ActionController::Base @owner = Project.find_by_slug!(params[:project_id]) @project = @owner else + logger.info("Cant find repository owner.") raise ActiveRecord::RecordNotFound end end @@ -162,6 +167,26 @@ class ApplicationController < ActionController::Base @repository = Repository.find_by_name_and_project_id!(params[:repository_id], @project.id) end + def require_view_right_to_repository + unless @repository.can_be_viewed_by?(current_user, logged_in?) + flash[:error] = I18n.t "application.require_current_user" + redirect_to root_path and return + end + end + + def require_view_right_to_project + if @project + return true if @project.public_to_world? + if logged_in? + return true if @project.public_to_site? + return true if @project.public_only_to_collaborators? && + @project.collaborator?(current_user) + end + end + flash[:error] = I18n.t "application.require_current_user" + redirect_to root_path and return + end + def check_repository_for_commits unless @repository.has_commits? flash[:notice] = I18n.t "application.no_commits_notice" @@ -223,12 +248,22 @@ class ApplicationController < ActionController::Base branch_ref = path = nil heads = Array(git.heads).map{|h| h.name }.sort{|a,b| b.length <=> a.length } heads.each do |head| - if branch_and_path.starts_with?(head) + if "#{branch_and_path}/".starts_with?("#{head}/") branch_ref = head path = ensplat_path(branch_and_path.sub(head, "")) || [] break end end + unless path + tags = Array(git.tags).map{|t| t.name }.sort{|a,b| b.length <=> a.length } + tags.each do |tag| + if "#{branch_and_path}/".starts_with?("#{tag}/") + branch_ref = tag + path = ensplat_path(branch_and_path.sub(tag, "")) || [] + break + end + end + end unless path # fallback path = ensplat_path(branch_and_path)[1..-1] branch_ref = ensplat_path(branch_and_path)[0] diff --git a/app/controllers/blobs_controller.rb b/app/controllers/blobs_controller.rb index 25e98d5..161c7e3 100644 --- a/app/controllers/blobs_controller.rb +++ b/app/controllers/blobs_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 Tor Arne Vestbø @@ -21,6 +23,7 @@ class BlobsController < ApplicationController before_filter :find_project_and_repository + before_filter :require_view_right_to_repository before_filter :check_repository_for_commits renders_in_site_specific_context diff --git a/app/controllers/commits_controller.rb b/app/controllers/commits_controller.rb index b14c1ec..4df869e 100644 --- a/app/controllers/commits_controller.rb +++ b/app/controllers/commits_controller.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -21,6 +22,7 @@ class CommitsController < ApplicationController before_filter :find_project_and_repository + before_filter :require_view_right_to_repository before_filter :check_repository_for_commits renders_in_site_specific_context diff --git a/app/controllers/committerships_controller.rb b/app/controllers/committerships_controller.rb index 7b6b475..5fadc5a 100644 --- a/app/controllers/committerships_controller.rb +++ b/app/controllers/committerships_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -46,9 +48,9 @@ class CommittershipsController < ApplicationController if @committership.save if @committership.committer.is_a?(User) - flash[:success] = "User added as committer" + flash[:success] = "User added as collaborator" else - flash[:success] = "Team added as committers" + flash[:success] = "Team added as collaborator" end redirect_to([@owner, @repository, :committerships]) else @@ -79,8 +81,13 @@ class CommittershipsController < ApplicationController def destroy @committership = @repository.committerships.find(params[:id]) - if @committership.destroy - flash[:notice] = "The team was removed as a committer" + + if (@committership.admin? && @repository.committerships.admins.length < 2) + flash[:error] = "Cannot remove the last admin. Repository must have at least one admin." + elsif @committership.destroy + flash[:notice] = "The collaborator #{@committership.committer.title} was removed succesfully." + else + flash[:error] = "Removing collaborator #{@committership.committer.title} failed." end redirect_to([@owner, @repository, :committerships]) end @@ -95,7 +102,7 @@ class CommittershipsController < ApplicationController def auto_complete_for_user_login @users = User.find(:all, - :conditions => [ 'lower(login) like :name or lower(email) like :name', + :conditions => [ 'lower(login) like :name or lower(fullname) like :name', {:name => '%' + params[:q].downcase + '%'} ], :limit => 10) render :text => @users.map{|u| u.login }.join("\n") diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index b2044f9..69c2201 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2008 David A. Cuadrado # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen @@ -21,8 +23,27 @@ class EventsController < ApplicationController def index + # FIXME This query doesn't find any events whose target_type is 'User' or 'Event' @events = Event.paginate(:all, :order => "events.created_at desc", - :page => params[:page], :include => [:user]) + :page => params[:page], :include => [:user], + :conditions => + ["(target_type = :repo and target_id in (:pub_repos) + and + exists (select id from projects + where + publicity = :publicity_world + and + id = (select project_id from repositories where target_id = id))) + or + (target_type = :proj + and + exists (select id from projects + where + publicity = :publicity_world + and + id = target_id))", + {:repo => 'Repository', :proj => 'Project', + :pub_repos => Repository.public_to_world_or_site, :publicity_world => Project::PUBLICITY_WORLD}]) @atom_auto_discovery_url = events_path(:format => :atom) respond_to do |if_format_is| diff --git a/app/controllers/keys_controller.rb b/app/controllers/keys_controller.rb index b73a026..fc1f640 100644 --- a/app/controllers/keys_controller.rb +++ b/app/controllers/keys_controller.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007 Johan Sørensen # Copyright (C) 2008 Tor Arne Vestbø @@ -29,6 +30,11 @@ class KeysController < ApplicationController def index @ssh_keys = current_user.ssh_keys @root = Breadcrumb::Keys.new(current_user) + not_ready_ssh_key = nil + @ssh_keys.each do |key| not_ready_ssh_key = key unless key.ready end + unless not_ready_ssh_key.nil? + response.headers['Refresh'] = "5" unless not_ready_ssh_key.ready + end respond_to do |format| format.html format.xml { render :xml => @ssh_keys } diff --git a/app/controllers/memberships_controller.rb b/app/controllers/memberships_controller.rb index 3431adc..70335ae 100644 --- a/app/controllers/memberships_controller.rb +++ b/app/controllers/memberships_controller.rb @@ -72,7 +72,7 @@ class MembershipsController < ApplicationController def destroy @membership = @group.memberships.find(params[:id]) - if @membership.destroy + if (!@membership.role.admin? || @group.admins.length > 1) && @membership.destroy flash[:success] = I18n.t("memberships_controller.membership_destroyed") else flash[:error] = I18n.t("memberships_controller.failed_to_destroy") @@ -82,7 +82,8 @@ class MembershipsController < ApplicationController def auto_complete_for_user_login @users = User.find(:all, - :conditions => [ 'LOWER(login) LIKE ?', '%' + params[:q].downcase + '%' ], + :conditions => [ 'lower(login) like :name or lower(fullname) like :name', + {:name => '%'+ params[:q].downcase + '%'} ], :limit => 10) render :text => @users.map{|u| u.login }.join("\n") #render :layout => false diff --git a/app/controllers/merge_request_versions_controller.rb b/app/controllers/merge_request_versions_controller.rb index 93f2802..80ce420 100644 --- a/app/controllers/merge_request_versions_controller.rb +++ b/app/controllers/merge_request_versions_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -17,6 +19,7 @@ #++ class MergeRequestVersionsController < ApplicationController + before_filter :require_view_right_to_repository renders_in_site_specific_context def show diff --git a/app/controllers/merge_requests_controller.rb b/app/controllers/merge_requests_controller.rb index 4ccf682..7bbd859 100644 --- a/app/controllers/merge_requests_controller.rb +++ b/app/controllers/merge_requests_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -26,6 +28,7 @@ class MergeRequestsController < ApplicationController :commit_status, :version] before_filter :find_repository_owner, :except => [:oauth_return, :direct_access] before_filter :find_repository, :except => [:oauth_return, :direct_access] + before_filter :require_view_right_to_repository before_filter :find_merge_request, :except => [:index, :show, :new, :create, :commit_list, :target_branches, :oauth_return, :direct_access, :terms_accepted] diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index 8e5d8b4..94f5db7 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -114,7 +114,8 @@ class MessagesController < ApplicationController def auto_complete_for_message_recipients @users = User.find(:all, - :conditions => [ 'LOWER(login) LIKE ?', '%' + params[:q].downcase + '%' ], + :conditions => [ 'LOWER(login) LIKE :name or LOWER(fullname) like :name', + {:name => '%' + params[:q].downcase + '%'} ], :limit => 10).reject{|u|u == current_user} render :text => @users.map{|u| u.login }.join("\n") #render :layout => false diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index b3853e6..81d4cb6 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -22,6 +24,7 @@ class PagesController < ApplicationController before_filter :check_if_wiki_enabled before_filter :assert_readyness before_filter :require_write_permissions, :only => [:edit, :update] + before_filter :require_view_right_to_project renders_in_site_specific_context def index @@ -30,7 +33,7 @@ class PagesController < ApplicationController @tree_nodes = @project.wiki_repository.git.tree.contents.select{|n| n.name =~ /\.#{Page::DEFAULT_FORMAT}$/ } - @root = Breadcrumb::Wiki.new(@project) + @root = Breadcrumb::Wiki.new(@project) @atom_auto_discovery_url = project_pages_path(:format => :atom) end format.atom do @@ -43,6 +46,7 @@ class PagesController < ApplicationController def show @atom_auto_discovery_url = project_pages_path(:format => :atom) @page, @root = page_and_root + @repository = @project.wiki_repository if @page.new? if logged_in? redirect_to edit_project_page_path(@project, params[:id]) and return diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 04fcf92..e2d7258 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,5 +1,8 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -28,18 +31,22 @@ class ProjectsController < ApplicationController before_filter :find_project, :only => [:show, :clones, :edit, :update, :confirm_delete, :edit_slug] before_filter :assure_adminship, :only => [:edit, :update, :edit_slug] + before_filter :require_view_right_to_project, + :except => [:new, :create, :index, :destroy, :preview] before_filter :require_user_has_ssh_keys, :only => [:new, :create] renders_in_site_specific_context :only => [:show, :edit, :update, :confirm_delete] renders_in_global_context :except => [:show, :edit, :update, :confirm_delete, :clones] def index - @projects = Project.paginate(:all, :order => "projects.created_at desc", + @projects = Project.public_to_world_or_site.paginate(:all, :order => "projects.created_at desc", :page => params[:page], :include => [:tags, { :repositories => :project } ]) @atom_auto_discovery_url = projects_path(:format => :atom) + @user_projects = current_user.projects.find(:all, + :order => "projects.updated_at desc")[0..9] if logged_in? respond_to do |format| format.html { - @active_recently = Project.most_active_recently + @active_recently = Project.most_active_recently() @tags = Project.top_tags } format.xml { render :xml => @projects } @@ -64,8 +71,12 @@ class ProjectsController < ApplicationController def show @owner = @project + @repositories = [] + @project.repositories.mainlines.each do |r| + @repositories << r if r.can_be_viewed_by?(current_user) + end @root = @project - @events = @project.events.top.paginate(:all, :page => params[:page], + @events = @project.events.excluding_private_repos.top.paginate(:all, :page => params[:page], :order => "created_at desc", :include => [:user, :project]) @group_clones = @project.recently_updated_group_repository_clones @user_clones = @project.recently_updated_user_repository_clones @@ -90,11 +101,13 @@ class ProjectsController < ApplicationController @project = Project.new @project.owner = current_user @root = Breadcrumb::NewProject.new + @admin_groups = current_user.groups.select {|g| g.admin? current_user } end def create @project = Project.new(params[:project]) @root = Breadcrumb::NewProject.new + @admin_groups = current_user.groups.select {|g| g.admin? current_user } @project.user = current_user @project.owner = case params[:project][:owner_type] when "User" @@ -137,9 +150,14 @@ class ProjectsController < ApplicationController @project.change_owner_to(current_user.groups.find(params[:project][:owner_id])) end + publicity = @project.publicity # This just for to test if publicity changes + @project.attributes = params[:project] changed = @project.changed? # Dirty attr tracking is cleared after #save if @project.save && @project.wiki_repository.save + + Rails.cache.delete("projects:most_active_recently") if @project.publicity != publicity + @project.create_event(Action::UPDATE_PROJECT, @project, current_user) if changed flash[:success] = "Project details updated" redirect_to project_path(@project) @@ -164,8 +182,10 @@ class ProjectsController < ApplicationController @project = Project.find_by_slug!(params[:id]) if @project.can_be_deleted_by?(current_user) project_title = @project.title - @project.destroy + if @project.destroy # Event.create(:action => Action::DELETE_PROJECT, :user => current_user, :data => project_title) # FIXME: project_id cannot be null + flash[:notice] = "Deleted project #{project_title}" + end else flash[:error] = I18n.t "projects_controller.destroy_error" end diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index c599c42..e170c1d 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -1,5 +1,8 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -20,23 +23,32 @@ # along with this program. If not, see . #++ +include GrmfHelper + class RepositoriesController < ApplicationController + + include FilenameSanitizer + before_filter :login_required, :except => [:index, :show, :writable_by, :config, :search_clones] before_filter :find_repository_owner before_filter :require_owner_adminship, :only => [:new, :create] before_filter :find_and_require_repository_adminship, :only => [:edit, :update, :confirm_delete, :destroy] + before_filter :find_and_require_repository_committership, + :only => [:update_repo_with_zip] + before_filter :find_and_require_repository_view_right, :except => [:index, :create, :new, :destroy, :config, :writable_by] before_filter :require_user_has_ssh_keys, :only => [:clone, :create_clone] before_filter :only_projects_can_add_new_repositories, :only => [:new, :create] skip_before_filter :public_and_logged_in, :only => [:writable_by, :config] renders_in_site_specific_context :except => [:writable_by, :config] + # Currently there aren't any links to this page so just hide every repo at least for now def index if term = params[:filter] - @repositories = @project.search_repositories(term) + @repositories = []#@project.search_repositories(term) else - @repositories = @owner.repositories.find(:all, :include => [:user, :events, :project]) + @repositories = []#@owner.repositories.find(:all, :include => [:user, :events, :project]) end respond_to do |wants| wants.html @@ -67,18 +79,30 @@ class RepositoriesController < ApplicationController @root = Breadcrumb::NewRepository.new(@project) @repository.kind = Repository::KIND_PROJECT_REPO @repository.owner = @project.owner + @repository.mirror_url = "http://" if @project.repositories.mainlines.count == 0 - @repository.name = @project.slug + @repository.name = "mainline" end + @projects = current_user.administrated_projects.find(:all, :order => "created_at desc") end def create + @projects = current_user.administrated_projects.find(:all, :order => "created_at desc") + @project = current_user.projects.find_by_title(params[:repo_in_project]) @repository = @project.repositories.new(params[:repository]) @root = Breadcrumb::NewRepository.new(@project) @repository.kind = Repository::KIND_PROJECT_REPO @repository.owner = @project.owner @repository.user = current_user - @repository.merge_requests_enabled = params[:repository][:merge_requests_enabled] + set_mirroring_options(@repository, params[:mirroring]) + + unless params[:local_package_file].blank? + package_file = params[:local_package_file] + zip_dir = Repository.dir_for_temp_zip(@repository.real_gitdir) + `mkdir #{zip_dir}` + path = File.join(zip_dir, "init.zip") + File.open(path, "wb") { |f| f.write(package_file.read) } + end if @repository.save flash[:success] = I18n.t("repositories_controller.create_success") @@ -98,11 +122,12 @@ class RepositoriesController < ApplicationController redirect_to [@owner, @repository_to_clone] return end + @admin_groups = current_user.groups.select {|g| g.admin? current_user } @repository = Repository.new_by_cloning(@repository_to_clone, current_user.login) end def create_clone - @repository_to_clone = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project) + @repository_to_clone = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project) @root = Breadcrumb::CloneRepository.new(@repository_to_clone) unless @repository_to_clone.has_commits? respond_to do |format| @@ -144,6 +169,47 @@ class RepositoriesController < ApplicationController end end + def update_repo_with_zip_form + @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project) + end + + def update_repo_with_zip + @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project) + @root = Breadcrumb::UpdateRepositoryWithZip.new(@repository) + target_head = "" + if params[:target_head_selector] == "" + target_head = params[:target_head] + else + target_head = params[:target_head_selector] + end + + if !params[:local_package_file].blank? + package_file = params[:local_package_file] + + temp_dir = Repository.dir_for_temp_zip(@repository.real_gitdir) + `mkdir #{temp_dir}` + file_path = File.join(temp_dir, sanitize_filename(package_file.original_filename)) + + File.open(file_path, "wb") { |f| f.write(package_file.read) } + + Repository.update_contents_from_zip(@repository.real_gitdir, file_path, + {"source_type" => "local_file", "target_branch" => target_head}) + + #@repository.project.create_push_event(@repository, target_head, current_user) # FIXME + redirect_to [@repository.project_or_owner, @repository] + + elsif !params[:package_url].blank? + + Repository.update_contents_from_zip(@repository.real_gitdir, params[:package_url], + {"source_type" => "url", "target_branch" => target_head}) + + #@repository.project.create_push_event(@repository, target_head, current_user) # FIXME + redirect_to [@repository.project_or_owner, @repository] + else + render :action => "update_repo_with_zip_form" + end + end + def edit @root = Breadcrumb::EditRepository.new(@repository) @groups = current_user.groups @@ -172,10 +238,17 @@ class RepositoriesController < ApplicationController @repository.replace_value(:name, params[:repository][:name]) @repository.replace_value(:description, params[:repository][:description], true) end + @repository.private_repo = params[:repository][:private_repo] @repository.deny_force_pushing = params[:repository][:deny_force_pushing] @repository.notify_committers_on_new_merge_request = params[:repository][:notify_committers_on_new_merge_request] @repository.merge_requests_enabled = params[:repository][:merge_requests_enabled] + @repository.mirror_url = params[:repository][:mirror_url] + @repository.license = params[:repository][:license] + set_mirroring_options(@repository, params[:mirroring]) @repository.save! + + GrmfHelper::update_metainfo_to_repository(@repository) + flash[:success] = "Repository updated" redirect_to [@repository.project_or_owner, @repository] end @@ -202,7 +275,6 @@ class RepositoriesController < ApplicationController render :text => 'false' and return end - def config @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project) @@ -234,6 +306,8 @@ class RepositoriesController < ApplicationController redirect_to @owner end + + private def require_owner_adminship unless @owner.admin?(current_user) @@ -252,6 +326,26 @@ class RepositoriesController < ApplicationController end end + def find_and_require_repository_committership + @repository = @owner.repositories.find_by_name_in_project!(params[:id], + @containing_project) + unless @repository.committer?(current_user) + respond_denied_and_redirect_to(repo_owner_path(@repository, + :project_repository_path, @owner, @repository)) + return + end + end + + def find_and_require_repository_view_right + @repository = @owner.repositories.find_by_name_in_project!(params[:id], + @containing_project) + unless @repository.can_be_viewed_by?(current_user, logged_in?) + flash[:error] = I18n.t "application.require_current_user" + redirect_to root_path + return + end + end + def respond_denied_and_redirect_to(target) respond_to do |format| format.html { @@ -296,4 +390,15 @@ class RepositoriesController < ApplicationController return end end + + def set_mirroring_options(repo, option) + if option == "svn_mirror" + repo.kind = Repository::KIND_SVN_MIRROR + elsif option == "zip_mirror" + repo.kind = Repository::KIND_ZIP_MIRROR + else + repo.kind = Repository::KIND_PROJECT_REPO + repo.mirror_url = "" + end + end end diff --git a/app/controllers/searches_controller.rb b/app/controllers/searches_controller.rb index 33774cf..3da72db 100644 --- a/app/controllers/searches_controller.rb +++ b/app/controllers/searches_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 Tor Arne Vestbø @@ -25,11 +27,16 @@ class SearchesController < ApplicationController def show unless params[:q].blank? @search = Ultrasphinx::Search.new({ - :query => params[:q], :page => (params[:page] || 1), + :query => params[:q], :filters => {}, :page => (params[:page] || 1), :per_page => 30, }) @search.run - @results = @search.results + @results = @search.results.delete_if do |r| + (r.class == Project && + (r.public_only_to_collaborators? || (r.public_to_site? && !logged_in?))) || + (r.class == Repository && + (r.private? || (r.project.public_to_site? && !logged_in?))) + end end rescue Ultrasphinx::UsageError @results = [] diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index c9f7ec5..f1cede2 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 August Lilleaas @@ -35,10 +36,10 @@ class SessionsController < ApplicationController end def create - if using_open_id? + if using_open_id? && GitoriousConfig["openid_enabled"] open_id_authentication(params[:openid_url]) else - password_authentication(params[:email], params[:password]) + password_authentication(params[:login], params[:password]) end end @@ -81,12 +82,22 @@ class SessionsController < ApplicationController redirect_to login_path(:method => 'openid') end - def password_authentication(email, password) - self.current_user = User.authenticate(email, password) - if logged_in? - successful_login + def password_authentication(login, password) + if !login.empty? and User.ldap_authenticate(login, password) + user = User.find_by_login(login) + if user.nil? + user = User.new + user.activate + user.login = login + user.email = login+'@jyu.fi' + user.password = "ldap-authenticated" + user.aasm_state = "terms_accepted" + user.save! + end + self.current_user = user + successful_login else - failed_login("Email and/or password didn't match, please try again.") + failed_login("Username and/or password didn't match, please try again.") end end diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb index cee4a6e..708c55f 100644 --- a/app/controllers/site_controller.rb +++ b/app/controllers/site_controller.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -22,7 +24,10 @@ class SiteController < ApplicationController skip_before_filter :public_and_logged_in, :only => [:index, :about, :faq] - before_filter :login_required, :only => [:dashboard] + before_filter :login_required, + :only => [:dashboard, :new_repo_and_project, :create_repo_and_project] + before_filter :require_user_has_ssh_keys, + :only => [:new_repo_and_project, :create_repo_and_project] renders_in_site_specific_context :except => [:about, :faq, :contact] renders_in_global_context :only => [:about, :faq, :contact] @@ -52,11 +57,82 @@ class SiteController < ApplicationController def contact end - protected + def repositories + @repositories = Repository.mainlines.public_to_world_or_site.paginate(:all, + :order => "repositories.updated_at desc", :page => params[:page]) + @active_recently = Repository.most_active_recently(current_user, logged_in?) + @user = current_user + @user_repositories = @user.commit_repositories.find(:all, + :order => "repositories.updated_at desc")[0..9] if logged_in? + end + + def new_repo_and_project + @project = Project.new + @project.owner = current_user + @project.user = current_user + @repository = @project.repositories.new + @repository.kind = Repository::KIND_PROJECT_REPO + @repository.owner = current_user + @repository.mirror_url = "http://" + @projects = current_user.administrated_projects.find(:all, :order => "created_at desc") + if @projects.empty? + @suggested_project_title = suggest_a_project_title + end + if @project.repositories.mainlines.count == 0 + @repository.name = "mainline" + end + end + + def create_repo_and_project + @projects = current_user.administrated_projects.find(:all, :order => "created_at desc") + + if @projects.empty? then + suggestion = suggest_a_project_title + @project = Project.new(:title => suggestion, :slug => suggestion, :description => "") + @project.owner = current_user + @project.user = current_user + @project.save + redirect_to :action => "new_repo_and_project" + return + end + + @root = Breadcrumb::NewRepository.new(@project) + @project = current_user.projects.find_by_title(params[:repo_in_project]) + + @repository = @project.repositories.new(params[:repository]) + @repository.owner = current_user + @repository.user = current_user + if params[:mirroring] == "svn_mirror" + @repository.kind = Repository::KIND_SVN_MIRROR + elsif params[:mirroring] == "zip_mirror" + @repository.kind = Repository::KIND_ZIP_MIRROR + else + @repository.kind = Repository::KIND_PROJECT_REPO + @repository.mirror_url = "" + end + unless params[:local_package_file].blank? + package_file = params[:local_package_file] + zip_dir = Repository.dir_for_temp_zip(@repository.real_gitdir) + `mkdir #{zip_dir}` + path = File.join(zip_dir, "init.zip") + File.open(path, "wb") { |f| f.write(package_file.read) } + end + + if @repository.save + flash[:success] = I18n.t("repositories_controller.create_success") + redirect_to [@repository.project_or_owner, @repository] + else + render :action => "new_repo_and_project" + end + end + + + protected + # Render a Site-specific index template def render_site_index - @projects = current_site.projects.find(:all, :order => "created_at asc") + @projects = current_site.projects.public_to_world.find(:all, :order => "created_at asc") @teams = Group.all_participating_in_projects(@projects) @top_repository_clones = Repository.most_active_clones_in_projects(@projects) @latest_events = Event.latest_in_projects(25, @projects.map{|p| p.id }) @@ -64,21 +140,21 @@ class SiteController < ApplicationController end def render_public_timeline - @projects = Project.find(:all, :limit => 10, :order => "id desc") + @projects = Project.find(:all, :limit => 10, :order => "id desc", + :conditions => ["publicity in (?)", Project::PUBLICITY_PUBLICS]) @top_repository_clones = Repository.most_active_clones - @active_projects = Project.most_active_recently(15) - @active_users = User.most_active - @active_groups = Group.most_active - @latest_events = Event.latest(25) + @active_projects = Project.most_active_recently(15) + @active_users = User.most_active + @active_groups = Group.most_active + @latest_events = Event.latest(25) + render :template => "site/index" end def render_dashboard - @page_title = "Gitorious: your dashboard" @user = current_user @projects = @user.projects.find(:all, :include => [:tags, { :repositories => :project }]) - @repositories = current_user.commit_repositories if current_user != @user @events = @user.paginated_events_in_watchlist(:page => params[:page]) @messages = @user.messages_in_inbox(3) if @user == current_user @favorites = @user.watched_objects @@ -108,4 +184,21 @@ class SiteController < ApplicationController end end + private + + #TODO try to suggest a name that definitely doesn't exist + def suggest_a_project_title + names = [current_user.login, + current_user.login+"_project", + current_user.login+"_personal_project"] + result = "" + names.each do |name| + if Project.find_by_title(name).nil? + result = name + break + end + end + result + end + end diff --git a/app/controllers/trees_controller.rb b/app/controllers/trees_controller.rb index cc5b4f3..0a62004 100644 --- a/app/controllers/trees_controller.rb +++ b/app/controllers/trees_controller.rb @@ -1,5 +1,8 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 Tor Arne Vestbø @@ -21,8 +24,10 @@ class TreesController < ApplicationController include ActiveMessaging::MessageSender + before_filter :find_project_and_repository before_filter :check_repository_for_commits + before_filter :require_view_right_to_repository renders_in_site_specific_context def index @@ -45,8 +50,36 @@ class TreesController < ApplicationController @tree = @git.tree(@commit.tree.id, path) expires_in 30.seconds end + + if @repository.committer?(current_user) && !params[:local_file].blank? && !@ref.eql?(GitoriousConfig["repo_meta_branch"]) + package_file = params[:local_file] + + temp_dir = Repository.dir_for_temp_use(@repository.real_gitdir, Repository::TEMP_UPLOAD_DIR_SUFFIX) + `mkdir #{temp_dir}` + + file_path = File.join(temp_dir, package_file.original_filename) + + file_path_in_repo = File.join(desplat_path(@path), package_file.original_filename) + file_path_in_repo = file_path_in_repo[1..file_path_in_repo.length] if file_path_in_repo[0].chr.eql?('/') + + File.open(file_path, "wb") { |f| f.write(package_file.read) } + + name = (current_user.fullname.blank? ? current_user.login : current_user.fullname) + email = (current_user.public_email ? current_user.email : "nobody@blankmail.invalid") + commit_message = params[:commit_msg] + commit_message = "Add file to repository" if commit_message.blank? + + Repository.add_file_to_repo(@repository.real_gitdir, file_path, file_path_in_repo, + name, email, commit_message, {"target_branch" => @ref}) + + `rm -rf #{temp_dir}` + + @repository.project.create_push_event(@repository, @ref, current_user) + redirect_to :action => "show" + end + end - + def archive @git = @repository.git # FIXME: update route when we've fixed rails bug #1939 @@ -91,12 +124,12 @@ class TreesController < ApplicationController end end end - + protected def set_xsendfile_headers(real_path, user_path, content_type = "application/x-gzip") response.headers["X-Sendfile"] = File.join(GitoriousConfig["archive_cache_dir"], real_path) response.headers["Content-Type"] = content_type - user_path = user_path.gsub("/", "_") + user_path = user_path.gsub("/", "_").gsub('"', '\"') response.headers["Content-Disposition"] = "Content-Disposition: attachment; filename=\"#{user_path}\"" end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index bf54098..54b3976 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,8 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -44,14 +47,21 @@ class UsersController < ApplicationController def show @projects = @user.projects.find(:all, - :include => [:tags, { :repositories => :project }]) - @repositories = @user.commit_repositories - @events = @user.events.excluding_commits.paginate( + :include => [:tags, { :repositories => :project }], + :conditions => ["publicity in (:pubs)", {:pubs => Project::PUBLICITY_PUBLICS}]) + @repositories = @user.commit_repositories.public_to_world_or_site + @events = @user.events.excluding_commits.excluding_private_repos.paginate( :page => params[:page], :order => "events.created_at desc", :include => [:user, :project]) + @events.delete_if do |e| + !(e.target_type == "Repository" && Repository.find(e.target_id).can_be_viewed_by?(current_user, logged_in?) || + e.target_type == "Project" && Project.find(e.target_id).can_be_viewed_by?(current_user, logged_in?)) + end @messages = @user.messages_in_inbox(3) if @user == current_user @favorites = @user.favorites.all(:include => :watchable) - + @favorites.delete_if do |f| + f.private_item? + end @atom_auto_discovery_url = feed_user_path(@user, :format => :atom) @atom_auto_discovery_title = "Public activity feed" @@ -64,7 +74,17 @@ class UsersController < ApplicationController def feed @user = User.find_by_login!(params[:id]) @events = @user.events.find(:all, :order => "events.created_at desc", - :include => [:user, :project], :limit => 30) + :include => [:user, :project], :limit => 30, + :conditions => + ["(target_type = :repo and target_id in (:public_repos) and + (select id from projects where id = + (select project_id from repositories where id = target_id)) + in (:public_projects) ) + or (target_type = :proj and target_id in (:public_projects))", + {:repo => 'Repository', + :proj => 'Project', + :public_repos => Repository.public_to_world, + :public_projects => Project.public_to_world}]) respond_to do |format| format.html { redirect_to user_path(@user) } format.atom { } @@ -81,7 +101,8 @@ class UsersController < ApplicationController def create @user = User.new(params[:user]) - @user.login = params[:user][:login] + @user.login = params[:user][:login] + @user.fullname = params[:user][:fullname] @user.save! if !@user.terms_of_use.blank? @user.accept_terms! @@ -108,9 +129,11 @@ class UsersController < ApplicationController redirect_back_or_default('/') end + #Commented out in routes.rb def forgot_password end + #Commented out in routes.rb def forgot_password_create if params[:user] && user = User.find_by_email(params[:user][:email]) if user.activated? @@ -155,19 +178,21 @@ class UsersController < ApplicationController @user.attributes = params[:user] if current_user.save flash[:success] = "Your account details were updated" - redirect_to user_path + redirect_to :controller => "site", :template => "site/dashboard" else render :action => "edit" end end + #Commented out in routes.rb def password @user = current_user end + #Commented out in routes.rb def update_password @user = current_user - if User.authenticate(current_user.email, params[:user][:current_password]) || @user.is_openid_only? + if User.authenticate(current_user.login, params[:user][:current_password]) || @user.is_openid_only? @user.password = params[:user][:password] @user.password_confirmation = params[:user][:password_confirmation] if @user.save diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 26d8af8..10e9248 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -308,15 +308,15 @@ module ApplicationHelper end def render_download_links(project, repository, head, options={}) + head = desplat_path(head) if head.is_a?(Array) + links = [] exceptions = Array(options[:except]) unless exceptions.include?(:source_tree) - links << content_tag(:li, link_to("View source tree for #{desplat_path(head)}", + links << content_tag(:li, link_to("View source tree for #{head}", tree_path(head)), :class => "tree") end - head = desplat_path(head) if head.is_a?(Array) - if head =~ /^[a-z0-9]{40}$/ # it looks like a SHA1 head = head[0..7] end @@ -325,12 +325,12 @@ module ApplicationHelper 'tar.gz' => 'tar', # 'zip' => 'zip', }.each do |extension, url_key| - archive_path = self.send("project_repository_archive_#{url_key}_path", project, repository, head) + archive_path = self.send("project_repository_archive_#{url_key}_path", project, repository, ensplat_path(head)) link_html = link_to("Download #{head} as #{extension}", archive_path, - :onclick => "Gitorious.DownloadChecker.checkURL('#{archive_path}?format=js', 'archive-box-#{head}');return false", + :onclick => "Gitorious.DownloadChecker.checkURL('#{archive_path}?format=js', 'archive-box-#{head.gsub("/", "_")}');return false", :class => "download-link") link_callback_box = content_tag(:div, "", :class => "archive-download-box round-5 shadow-2", - :id => "archive-box-#{head}", :style => "display:none;") + :id => "archive-box-#{head.gsub("/", "_")}", :style => "display:none;") links << content_tag(:li, link_html+link_callback_box, :class => extension.split('.').last) end diff --git a/app/helpers/event_rendering_helper.rb b/app/helpers/event_rendering_helper.rb index 24e7638..d1c9a3e 100644 --- a/app/helpers/event_rendering_helper.rb +++ b/app/helpers/event_rendering_helper.rb @@ -141,7 +141,9 @@ module EventRenderingHelper link_to(h(project.slug), project_path(project)) + "/" + link_to(h(event.target.name), project_repository_url(project, event.target)) end - body = link_to(h(event.data) + ": " + h(event.body), project_repository_commit_path(project, event.target, h(event.data))) + body = link_to(h(event.data) + ": " + h(event.body), + repo_owner_path(event.target, :project_repository_commits_in_ref_path, + project, event.target, ensplat_path(event.data))) category = "commit" [action, body, category] end @@ -321,7 +323,7 @@ module EventRenderingHelper def render_several_commit_push(event) project = event.target.project - commit_link = link_to_if(event.has_commits?, pluralize(event.events.size, 'commit'), + commit_link = link_to_if(event.has_commits?, pluralize(event.events.size, 'commit')+'', repo_owner_path(event.target, :project_repository_commits_in_ref_path, project, event.target, ensplat_path(event.data)), :id => "commits_in_event_#{event.to_param}_toggler", diff --git a/app/helpers/grmf_helper.rb b/app/helpers/grmf_helper.rb new file mode 100644 index 0000000..32f9dbf --- /dev/null +++ b/app/helpers/grmf_helper.rb @@ -0,0 +1,79 @@ +#-- +# Git Repository Metafile Helper +# +# Copyright (C) 2010 Tero Hanninen +# +# This helper is used for updating repositories' metainformation: +# adding/updating metafiles in repos and updating metainfo in the database. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#++ + +module GrmfHelper + +require "yaml" +require "fileutils" + + # Reads metainfo from repository and updates that info to database + def update_metainfo_to_database(repo) + path = full_path(repo.hashed_path) + rep = Grit::Repo.new(path) + blob = rep.tree(meta_branch_name)/'repository.yml' + obj = YAML::load(blob.data) + name = obj["name"] + merge_requests_enabled = obj["merge_request_enabled"] + + blob2 = rep.tree(meta_branch_name)/'description' + repo.description = blob2.data + repo.name = name + repo.merge_requests_enabled = merge_requests_enabled + repo.save! + end + + # Updates metafiles into repo from given repo object + def update_metainfo_to_repository(repo_obj) + path = full_path(repo_obj.hashed_path) + repo = Grit::Repo.new(path) + index = repo.index + + #author = repo_obj.owner.login + #if repo_obj.owner.fullname != nil then author = repo_obj.owner.fullname end + + content = to_yaml_obj({ "name" => repo_obj.name, + "merge_requests_enabled" => repo_obj.merge_requests_enabled }) + # "author" => author }) + + index.add('repository.yml', content) + index.add('description', repo_obj.description) + + parents = [repo.commits(meta_branch_name, 1, 0).first] + actor = Grit::Actor.new("yousource","nomail") + last_tree = nil #index.read_tree(repo.tree(meta_branch_name).id) + index.commit("Update metafiles.", parents, actor, last_tree, meta_branch_name) + end + + def meta_branch_name + GitoriousConfig['repo_meta_branch'] + end + + def full_path(repo_hashed_path) + File.join(GitoriousConfig['repository_base_path'], "#{repo_hashed_path}.git") + end + + # Returns any given ruby-object as a yaml-object which is a string. + def to_yaml_obj(obj) + YAML::dump(obj) + end + +end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 57126ad..b1c697e 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -34,11 +34,19 @@ module ProjectsHelper def wiki_permission_choices [ - ["Writable by everyone", Repository::WIKI_WRITABLE_EVERYONE], + ["Writable by everyone", Repository::WIKI_WRITABLE_EVERYONE], ["Writable by project members", Repository::WIKI_WRITABLE_PROJECT_MEMBERS], ] end + def publicity_choices + [ + ["Viewable by everyone", Project::PUBLICITY_WORLD], + ["Viewable by users logged in", Project::PUBLICITY_SITE], + ["Viewable by project members", Project::PUBLICITY_COLLABORATORS], + ] + end + def add_status_link(form_builder) link_to_function(image_tag("silk/add.png") + " Add status") do |page| form_builder.fields_for(:merge_request_statuses, MergeRequestStatus.new, diff --git a/app/helpers/site_helper.rb b/app/helpers/site_helper.rb index eef64e1..90ad00c 100644 --- a/app/helpers/site_helper.rb +++ b/app/helpers/site_helper.rb @@ -40,4 +40,10 @@ module SiteHelper %{#{word_broken}} } end + + # Returns the hash of the commit that is in use right now. + def current_version_hash + r = Grit::Repo.new(RAILS_ROOT) + r.head.commit + end end diff --git a/app/models/action.rb b/app/models/action.rb index 5ab47d4..3d8fc57 100644 --- a/app/models/action.rb +++ b/app/models/action.rb @@ -20,29 +20,29 @@ #++ class Action - CREATE_PROJECT = 0 - DELETE_PROJECT = 1 - UPDATE_PROJECT = 2 - CLONE_REPOSITORY = 3 - DELETE_REPOSITORY = 4 - COMMIT = 5 - CREATE_BRANCH = 6 - DELETE_BRANCH = 7 - CREATE_TAG = 8 - DELETE_TAG = 9 - ADD_COMMITTER = 10 - REMOVE_COMMITTER = 11 - COMMENT = 12 - REQUEST_MERGE = 13 - RESOLVE_MERGE_REQUEST = 14 - UPDATE_MERGE_REQUEST = 15 - DELETE_MERGE_REQUEST = 16 - UPDATE_WIKI_PAGE = 17 - PUSH = 18 - ADD_PROJECT_REPOSITORY = 19 - UPDATE_REPOSITORY = 20 - REOPEN_MERGE_REQUEST = 21 - ADD_FAVORITE = 22 + CREATE_PROJECT = 0 + DELETE_PROJECT = 1 + UPDATE_PROJECT = 2 + CLONE_REPOSITORY = 3 + DELETE_REPOSITORY = 4 + COMMIT = 5 + CREATE_BRANCH = 6 + DELETE_BRANCH = 7 + CREATE_TAG = 8 + DELETE_TAG = 9 + ADD_COMMITTER = 10 + REMOVE_COMMITTER = 11 + COMMENT = 12 + REQUEST_MERGE = 13 + RESOLVE_MERGE_REQUEST = 14 + UPDATE_MERGE_REQUEST = 15 + DELETE_MERGE_REQUEST = 16 + UPDATE_WIKI_PAGE = 17 + PUSH = 18 + ADD_PROJECT_REPOSITORY = 19 + UPDATE_REPOSITORY = 20 + REOPEN_MERGE_REQUEST = 21 + ADD_FAVORITE = 22 def self.name(action_id) case action_id @@ -91,7 +91,7 @@ class Action when UPDATE_REPOSITORY "update repository" when ADD_FAVORITE - "add favorite" + "add favorite" else "unknown event" end diff --git a/app/models/committership.rb b/app/models/committership.rb index 1097a45..e1b6c11 100644 --- a/app/models/committership.rb +++ b/app/models/committership.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -21,11 +22,13 @@ class Committership < ActiveRecord::Base + CAN_VIEW = 1 << 3 CAN_REVIEW = 1 << 4 CAN_COMMIT = 1 << 5 CAN_ADMIN = 1 << 6 PERMISSION_TABLE = { + :view => CAN_VIEW, :review => CAN_REVIEW, :commit => CAN_COMMIT, :admin => CAN_ADMIN @@ -42,20 +45,21 @@ class Committership < ActiveRecord::Base attr_protected :permissions - after_create :notify_repository_owners + after_create :notify_repository_owners_and_new_collaborators after_create :add_new_committer_event after_destroy :add_removed_committer_event before_destroy :nullify_messages named_scope :groups, :conditions => { :committer_type => "Group" } named_scope :users, :conditions => { :committer_type => "User" } + named_scope :viewers, :conditions => ["(permissions & ?)", CAN_VIEW] named_scope :reviewers, :conditions => ["(permissions & ?)", CAN_REVIEW] named_scope :committers, :conditions => ["(permissions & ?)", CAN_COMMIT] named_scope :admins, :conditions => ["(permissions & ?)", CAN_ADMIN] def self.create_for_owner!(an_owner) cs = new({:committer => an_owner}) - cs.permissions = (CAN_REVIEW | CAN_COMMIT | CAN_ADMIN) + cs.permissions = (CAN_VIEW | CAN_REVIEW | CAN_COMMIT | CAN_ADMIN) cs.save! cs end @@ -83,6 +87,10 @@ class Committership < ActiveRecord::Base (self.permissions & PERMISSION_TABLE[wants_to]) != 0 end + def viewer? + permitted?(:view) + end + def reviewer? permitted?(:review) end @@ -120,9 +128,9 @@ class Committership < ActiveRecord::Base end protected - def notify_repository_owners + def notify_repository_owners_and_new_collaborators return unless creator - recipients = repository.owners + recipients = repository.owners + members recipients.each do |r| message = Message.new({ :sender => creator, @@ -139,7 +147,7 @@ class Committership < ActiveRecord::Base message.save end end - + def add_new_committer_event repository.project.create_event(Action::ADD_COMMITTER, repository, creator, committer.title) diff --git a/app/models/event.rb b/app/models/event.rb old mode 100755 new mode 100644 index cc2519a..f4d9bc6 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2008 David A. Cuadrado # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen @@ -43,17 +45,49 @@ class Event < ActiveRecord::Base named_scope :top, {:conditions => ['target_type != ?', 'Event']} named_scope :excluding_commits, {:conditions => ["action != ?", Action::COMMIT]} + named_scope :excluding_private_repos, {:conditions => + ["target_type != ? or target_id in (?)", + "Repository", Repository.public_to_world_or_site]} + + def public_to_world_or_site? + if self.target_type == "Project" + return Project.find(self.target_id).public_to_world_or_site? + end + if self.target_type == "Repository" + return Repository.find(self.target_id).project.public_to_world_or_site? + end + return false + end + + def public_for_world? + if (self.target_type == "Project") + return Project.find(self.target_id).public_to_world? + end + if (self.target_type == "Repository") + return Repository.find(self.target_id).project.public_to_world? + end + return false + end def self.latest(count) - Rails.cache.fetch("events:latest_#{count}", :expires_in => 10.minutes) do + a = Rails.cache.fetch("events:latest_#{count}", :expires_in => 10.minutes) do latest_event_ids = Event.find_by_sql( - ["select id,action,created_at from events " + + ["select id,action,created_at, target_type, target_id from events " + "use index (index_events_on_created_at) where (action != ?) " + - "order by created_at desc limit ?", Action::COMMIT, count + "and (target_type != ? or target_id in (?))" + + "order by created_at desc limit ?", Action::COMMIT, "Repository", Repository.public_to_world, count ]).map(&:id) Event.find(latest_event_ids, :order => "created_at desc", :include => [:user, :project, :events]) end + + # A bit gummy workaround to filter projects that are private and + # repos that are private through project. + b = [] + a.each do |e| + b << e if e.public_to_world_or_site? + end + b end def self.latest_in_projects(count, project_ids) diff --git a/app/models/favorite.rb b/app/models/favorite.rb index b8060e5..e39956a 100644 --- a/app/models/favorite.rb +++ b/app/models/favorite.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -44,6 +46,24 @@ class Favorite < ActiveRecord::Base watchable end end + + def private_item? + case watchable + when MergeRequest + watchable.target_repository.private? + when Repository + watchable.private? + end + end + + def can_be_viewed_by?(user, logged_in = true) + case watchable + when MergeRequest + watchable.target_repository.can_be_viewed_by?(user, logged_in) + when Repository + watchable.can_be_viewed_by?(user, logged_in) + end + end def event_should_be_created? !event_exists? diff --git a/app/models/group.rb b/app/models/group.rb index 7607c04..49b6a73 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -101,6 +101,14 @@ class Group < ActiveRecord::Base nil end + def admins + ads = [] + memberships.each do |ms| + ads << ms.user if ms.role == Role.admin + end + ads + end + # is this +user+ a member of this group? def member?(user) members.include?(user) diff --git a/app/models/mailer.rb b/app/models/mailer.rb index 9181d9c..47c4a85 100644 --- a/app/models/mailer.rb +++ b/app/models/mailer.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 David Chelimsky @@ -42,7 +44,7 @@ class Mailer < ActionMailer::Base def notification_copy(recipient, sender, subject, body, notifiable, message_id) @recipients = recipient.email - @from = "Gitorious Messenger " + @from = "YouSource Messenger " @subject = "New message: " + sanitize(subject) @body[:url] = url_for({ :controller => 'messages', @@ -70,8 +72,8 @@ class Mailer < ActionMailer::Base end def new_email_alias(email) - @from = "Gitorious " - @subject = "[Gitorious] Please confirm this email alias" + @from = "YouSource " + @subject = "[YouSource] Please confirm this email alias" @sent_on = Time.now @recipients = email.address @body[:email] = email @@ -79,8 +81,8 @@ class Mailer < ActionMailer::Base end def message_processor_error(processor, err, message_body) - subject "[Gitorious Processor] fail in #{processor.class.name}" - from "Gitorious " + subject "[YouSource Processor] fail in #{processor.class.name}" + from "YouSource " recipients GitoriousConfig['exception_notification_emails'] body :error => err, :message => message_body, :processor => processor end @@ -95,8 +97,8 @@ class Mailer < ActionMailer::Base protected def setup_email(user) @recipients = "#{user.email}" - @from = "Gitorious " - @subject = "[Gitorious] " + @from = "YouSource " + @subject = "[YouSource] " @sent_on = Time.now @body[:user] = user end diff --git a/app/models/message.rb b/app/models/message.rb index d63c66a..0990686 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -77,7 +78,7 @@ class Message < ActiveRecord::Base def sender_name if notifiable - "Gitorious" + "YouSource" else sender.title end diff --git a/app/models/project.rb b/app/models/project.rb index 16d4a39..4a0e870 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,5 +1,7 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -29,6 +31,11 @@ class Project < ActiveRecord::Base include UrlLinting include Watchable + PUBLICITY_WORLD = 1 + PUBLICITY_SITE = 2 + PUBLICITY_COLLABORATORS = 3 + PUBLICITY_PUBLICS = [PUBLICITY_WORLD, PUBLICITY_SITE] + belongs_to :user belongs_to :owner, :polymorphic => true has_many :comments, :dependent => :destroy @@ -43,6 +50,9 @@ class Project < ActiveRecord::Base has_many :merge_request_statuses, :order => "id asc" accepts_nested_attributes_for :merge_request_statuses, :allow_destroy => true + named_scope :public_to_world, :conditions => ["publicity = ?", PUBLICITY_WORLD] + named_scope :public_to_world_or_site, :conditions => ["publicity in (?)", PUBLICITY_PUBLICS] + serialize :merge_request_custom_states, Array attr_protected :owner_id, :user_id, :site_id @@ -62,23 +72,15 @@ class Project < ActiveRecord::Base }] - URL_FORMAT_RE = /^(http|https|nntp):\/\//.freeze NAME_FORMAT = /[a-z0-9_\-]+/.freeze - validates_presence_of :title, :user_id, :slug, :description, :owner_id + validates_presence_of :title, :user_id, :slug, :owner_id validates_uniqueness_of :slug, :case_sensitive => false validates_format_of :slug, :with => /^#{NAME_FORMAT}$/i, :message => I18n.t( "project.format_slug_validation") validates_exclusion_of :slug, :in => Gitorious::Reservations.project_names - validates_format_of :home_url, :with => URL_FORMAT_RE, - :if => proc{|record| !record.home_url.blank? }, - :message => I18n.t( "project.ssl_required") - validates_format_of :mailinglist_url, :with => URL_FORMAT_RE, - :if => proc{|record| !record.mailinglist_url.blank? }, - :message => I18n.t( "project.ssl_required") - validates_format_of :bugtracker_url, :with => URL_FORMAT_RE, - :if => proc{|record| !record.bugtracker_url.blank? }, - :message => I18n.t( "project.ssl_required") - + validates_url_format_of :home_url + validates_url_format_of :mailinglist_url + validates_url_format_of :bugtracker_url before_validation :downcase_slug after_create :create_wiki_repository after_create :create_default_merge_request_statuses @@ -91,33 +93,6 @@ class Project < ActiveRecord::Base :conditions => proc{|record| {:user_id => record.user.id} }, :timeframe => 5.minutes - LICENSES = [ - 'Academic Free License v3.0', - 'MIT License', - 'BSD License', - 'Ruby License', - 'GNU General Public License version 2(GPLv2)', - 'GNU General Public License version 3 (GPLv3)', - 'GNU Lesser General Public License (LGPL)', - 'GNU Affero General Public License (AGPLv3)', - 'Mozilla Public License 1.0 (MPL)', - 'Mozilla Public License 1.1 (MPL 1.1)', - 'Qt Public License (QPL)', - 'Python License', - 'zlib/libpng License', - 'Apache License', - 'Apple Public Source License', - 'Perl Artistic License', - 'Microsoft Permissive License (Ms-PL)', - 'ISC License', - 'Lisp Lesser License', - 'Public Domain', - 'Other Open Source Initiative Approved License', - 'Other/Proprietary License', - 'Other/Multiple', - 'None', - ] - def self.human_name I18n.t("activerecord.models.project") end @@ -131,12 +106,13 @@ class Project < ActiveRecord::Base # Returns the projects limited by +limit+ who has the most activity within # the +cutoff+ period def self.most_active_recently(limit = 10, number_of_days = 3) - Rails.cache.fetch("projects:most_active_recently:#{limit}:#{number_of_days}", + a = Rails.cache.fetch("projects:most_active_recently", #:#{limit}:#{number_of_days}", :expires_in => 30.minutes) do find(:all, :joins => :events, :limit => limit, :select => 'distinct projects.*, count(events.id) as event_count', :order => "event_count desc", :group => "projects.id", - :conditions => ["events.created_at > ?", number_of_days.days.ago]) + :conditions => ["publicity in (:pubs) and events.created_at > :days_ago", + {:pubs => [PUBLICITY_WORLD, PUBLICITY_SITE], :days_ago => number_of_days.days.ago}]) end end @@ -184,6 +160,38 @@ class Project < ActiveRecord::Base owner == User ? owner == candidate : owner.committer?(candidate) end + def collaborator?(candidate) + repositories.mainlines.each do |repo| + return true if repo.collaborator?(candidate) + end + member?(candidate) + end + + def public_to_world? + self.publicity == PUBLICITY_WORLD + end + + def public_to_site? + self.publicity == PUBLICITY_SITE + end + + def public_to_world_or_site? + PUBLICITY_PUBLICS.include? self.publicity + end + + def public_only_to_collaborators? + self.publicity == PUBLICITY_COLLABORATORS + end + + # If logged_in is not given as parameter, doesn't care whether user is logged in: + # in that case returns true if publicity is +PUBLICITY_SITE+ + def can_be_viewed_by?(candidate, logged_in = nil) + return true if self.public_to_world? + return true if self.public_to_site? && (logged_in || logged_in.nil?) + return true if self.public_only_to_collaborators? && collaborator?(candidate) + return false + end + def owned_by_group? owner === Group end @@ -249,7 +257,7 @@ class Project < ActiveRecord::Base } super({ :procs => [info], - :only => [:slug, :title, :description, :license, :home_url, :wiki_enabled, + :only => [:slug, :title, :description, :home_url, :wiki_enabled, :created_at, :bugtracker_url, :mailinglist_url, :bugtracker_url], }.merge(opts)) end @@ -265,6 +273,34 @@ class Project < ActiveRecord::Base }) end + # FIXME event info has to be given through parameters and not figured out from the actual repo! + def create_push_event(repo, ref, user, time = Time.now.utc) + @git = repo.git + message = @git.commit(ref).message + + event = events.new( + :action => Action::PUSH, + :target => repo, + :user => user, + :body => message, + :data => ref, # branch + :created_at => time ) + + event.email = "emaili" if event.user.blank? + event.disable_notifications { event.save! } + + commit_event = event.build_commit({ + :user => user, + :created_at => time, + :email => user.email, + :body => message, + :data => @git.commit(ref).id + }) + commit_event.save! + event.notify_subscribers + end + + def new_event_required?(action_id, target, user, data) events_count = events.count(:all, :conditions => [ "action = :action_id AND target_id = :target_id AND target_type = :target_type AND user_id = :user_id and data = :data AND created_at > :date_threshold", @@ -334,7 +370,7 @@ class Project < ActiveRecord::Base end def search_repositories(term) - Repository.title_search(term, "project_id", id) + Repository.public_to_world_or_site.title_search(term, "project_id", id) end def wiki_permissions diff --git a/app/models/repository.rb b/app/models/repository.rb index a3adf52..f721708 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,5 +1,8 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David Chelimsky @@ -22,23 +25,30 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . #++ +require 'open-uri' class Repository < ActiveRecord::Base include ActiveMessaging::MessageSender include RecordThrottling include Watchable - KIND_PROJECT_REPO = 0 - KIND_WIKI = 1 - KIND_TEAM_REPO = 2 - KIND_USER_REPO = 3 - KIND_TRACKING_REPO = 4 + KIND_PROJECT_REPO = 0 + KIND_WIKI = 1 + KIND_TEAM_REPO = 2 + KIND_USER_REPO = 3 + KIND_TRACKING_REPO = 4 + KIND_SVN_MIRROR = 5 + KIND_ZIP_MIRROR = 6 KINDS_INTERNAL_REPO = [KIND_WIKI, KIND_TRACKING_REPO] - WIKI_NAME_SUFFIX = "-gitorious-wiki" + WIKI_NAME_SUFFIX = "-wiki" WIKI_WRITABLE_EVERYONE = 0 WIKI_WRITABLE_PROJECT_MEMBERS = 1 + TEMP_ZIP_DIR_SUFFIX = "-temp_zip" + TEMP_CLONE_DIR_SUFFIX = "-temp_clone" + TEMP_UPLOAD_DIR_SUFFIX = "-temp_file" + belongs_to :user belongs_to :project belongs_to :owner, :polymorphic => true @@ -53,17 +63,20 @@ class Repository < ActiveRecord::Base :class_name => 'MergeRequest', :order => "id desc", :dependent => :destroy has_many :cloners, :dependent => :destroy has_many :events, :as => :target, :dependent => :destroy NAME_FORMAT = /[a-z0-9_\-]+/i.freeze validates_presence_of :user_id, :name, :owner_id, :project_id validates_format_of :name, :with => /^#{NAME_FORMAT}$/i, :message => "is invalid, must match something like /[a-z0-9_\\-]+/" + validates_url_format_of :mirror_url, :message => 'is invalid, must start with http:// or https://.' validates_exclusion_of :name, :in => (Gitorious::Reservations.project_names + Gitorious::Reservations.repository_names) validates_uniqueness_of :name, :scope => :project_id, :case_sensitive => false validates_uniqueness_of :hashed_path, :case_sensitive => false before_validation :downcase_name + before_validation :remove_mirror_url_if_not_mirror before_create :set_repository_hash after_create :create_initial_committership after_create :create_add_event_if_project_repo @@ -90,16 +103,54 @@ class Repository < ActiveRecord::Base end named_scope :clones, :conditions => ["kind in (?) and parent_id is not null", [KIND_TEAM_REPO, KIND_USER_REPO]] - named_scope :mainlines, :conditions => { :kind => KIND_PROJECT_REPO } + named_scope :mainlines, :conditions => ["kind in (?)", + [KIND_PROJECT_REPO, KIND_SVN_MIRROR, KIND_ZIP_MIRROR]] + named_scope :public_to_world_or_site, :conditions => ["private_repo = ? + and (select publicity from projects where id = project_id) in (?)", + false, Project::PUBLICITY_PUBLICS] + named_scope :public_to_world, :conditions => ["private_repo = ? + and (select publicity from projects where id = project_id) = ?", + false, Project::PUBLICITY_WORLD] + named_scope :regular, :conditions => ["kind in (?)", + [KIND_TEAM_REPO, KIND_USER_REPO, + KIND_PROJECT_REPO, KIND_SVN_MIRROR, KIND_ZIP_MIRROR]] - named_scope :regular, :conditions => ["kind in (?)", [KIND_TEAM_REPO, KIND_USER_REPO, - KIND_PROJECT_REPO]] is_indexed :fields => ["name", "description"], :include => [{ :association_name => "project", :field => "slug", :as => "project" - }], :conditions => "kind in (#{[KIND_PROJECT_REPO, KIND_TEAM_REPO, KIND_USER_REPO].join(',')})" + }], :conditions => "kind in (#{[KIND_PROJECT_REPO, KIND_TEAM_REPO, KIND_USER_REPO, KIND_SVN_MIRROR, KIND_ZIP_MIRROR].join(',')})" + + #If you add a new license, add an abbreviation for it too! + #See license_abbreviation method. + LICENSES = [ + 'Academic Free License v3.0', + 'MIT License', + 'BSD License', + 'Ruby License', + 'GNU General Public License version 2 (GPLv2)', + 'GNU General Public License version 3 (GPLv3)', + 'GNU Lesser General Public License (LGPL)', + 'GNU Affero General Public License (AGPLv3)', + 'Mozilla Public License 1.0 (MPL)', + 'Mozilla Public License 1.1 (MPL 1.1)', + 'Qt Public License (QPL)', + 'Python License', + 'zlib/libpng License', + 'Apache License', + 'Apple Public Source License', + 'Perl Artistic License', + 'Microsoft Permissive License (Ms-PL)', + 'ISC License', + 'Lisp Lesser License', + 'Public Domain', + 'Other Open Source Initiative Approved License', + 'Other/Proprietary License', + 'Other/Multiple', + 'None', + ] + def self.human_name I18n.t("activerecord.models.repository") @@ -155,6 +206,94 @@ class Repository < ActiveRecord::Base self.create_hooks(full_path) end + # Returns as a string a path that can be used as a temp directory. + # Note: Does not create the dir, just forms the string. + def self.dir_for_temp_use(repo_hashed_path, suffix = "") + File.join(GitoriousConfig["repository_base_path"], repo_hashed_path.split("/").last.sub(/\.git$/, "") + suffix) + end + + def self.dir_for_temp_zip(repo_hashed_path) + self.dir_for_temp_use(repo_hashed_path, TEMP_ZIP_DIR_SUFFIX) + end + + def self.dir_for_temp_clone(repo_hashed_path) + self.dir_for_temp_use(repo_hashed_path, TEMP_CLONE_DIR_SUFFIX) + end + + # Adds a file into repository and makes a commit + def self.add_file_to_repo(target_path, file_path, file_path_in_repo, committer_name, committer_email, commit_message, options = {}) + options = {"target_branch" => "master"}.merge! options + + file = File.open(file_path, 'r') + repo_path = full_path_from_partial_path(target_path) + + repo = Grit::Repo.new(repo_path) + index = repo.index + index.add(file_path_in_repo, file.read) + parents = [repo.commits(options["target_branch"], 1, 0).first] + actor = Grit::Actor.new(committer_name, committer_email) + last_tree = index.read_tree(repo.tree(options["target_branch"]).id) + index.commit(commit_message, parents, actor, last_tree, options["target_branch"]) + end + + # Updates repository contents with the contents of a zip, + # then creates and pushes a new commit. + # Note: Removes temp dir for zip (the temp zip dir cannot be manually + # determined, the method dir_for_temp_zip is used). + def self.update_contents_from_zip(target_path, source_path, options = {}) + options = {"source_type" => "url", "target_branch" => "master"}.merge! options + + zip_dir = self.dir_for_temp_zip(target_path) + clone_dir = self.dir_for_temp_clone(target_path) + + source_package = source_path.split("/").last + + if options["source_type"] == "url" + `mkdir #{zip_dir}` + unless system("wget","-P", zip_dir, source_path) + `rm -rf #{zip_dir}` + return + end + source_path = File.join(zip_dir, source_package) + elsif options["source_type"] != "local_file" + return + end + + full_repo_path = full_path_from_partial_path(target_path) + + `git clone #{full_repo_path} #{clone_dir}` + + new_branch = true + heads = Grit::Repo.new(full_repo_path).heads + heads.each do |head| + new_branch = false if head.name == options["target_branch"] + end + + Dir.chdir(clone_dir) do + if new_branch + `git symbolic-ref HEAD refs/heads/#{options["target_branch"]}` + `rm .git/index` + `git clean -fdx` + else + `git checkout #{options["target_branch"]}` + `ls -A | grep --invert-match ".git" | xargs rm -rf` + end + system("unzip", "-u", source_path, "-d", ".") + `git add .` + `git commit -am 'Update files with #{source_package}'` + `git push #{full_repo_path} #{options["target_branch"]}` + end + `rm -rf #{clone_dir}` + `rm -rf #{zip_dir}` + end + + def self.create_git_repository_from_zip(target_path, source_path, options = {}) + full_path = full_path_from_partial_path(target_path) + git_backend.create(full_path) + self.update_contents_from_zip(target_path, source_path, options) + self.create_hooks(full_path) + end + def self.clone_git_repository(target_path, source_path, options = {}) full_path = full_path_from_partial_path(target_path) Grit::Git.with_timeout(nil) do @@ -164,10 +303,44 @@ class Repository < ActiveRecord::Base self.create_hooks(full_path) unless options[:skip_hooks] end + # CLONE SVN REPOSITORY + def self.clone_svn_repository(target_path, source_path, options = {}) + full_path = full_path_from_partial_path(target_path) + sp = full_path.split("/") # FIXME use some method for the path.. no splitting here + repos_path = sp[0, sp.size-3].join("/").sub(/\.git$/, "") + Dir.chdir(repos_path) do #going to the root repos directory + `git svn clone #{source_path} #{target_path} --no-minimize-url --localtime` + end + Dir.chdir(full_path) do + `git svn show-ignore > .gitignore` + end + self.create_hooks(full_path) + end + def self.delete_git_repository(path) git_backend.delete!(full_path_from_partial_path(path)) end + # Returns the repositories limited by +limit+ who has the most activity within + # the +cutoff+ period. + def self.most_active_recently(current_user, logged_in, limit = 10, number_of_days = 3) + a = Rails.cache.fetch("repositories:most_active_recently", #:#{limit}:#{number_of_days}", + :expires_in => 30.minutes) do + find(:all, :joins => :events, :limit => limit, + :select => 'distinct repositories.*, count(events.id) as event_count', + :order => "event_count desc", :group => "repositories.id", + :conditions => ["events.created_at > ? and repositories.private_repo = ?", number_of_days.days.ago, '0']) + end + # A gummy workaround to get rid of projects that shouldn't be shown. + # A proper SQL-query above would make this redundant. + b = [] + a.each do |r| + b << r if r.can_be_viewed_by?(current_user, logged_in) + end + b + end + + def self.most_active_clones_in_projects(projects, limit = 5) key = "repository:most_active_clones_in_projects:#{projects.map(&:id).join('-')}:#{limit}" Rails.cache.fetch(key, :expires_in => 2.hours) do @@ -209,7 +382,7 @@ class Repository < ActiveRecord::Base end def url_path - if project_repo? + if project_repo? || mirror_repo? File.join(project.to_param_with_prefix, name) else File.join(owner.to_param_with_prefix, project.slug, name) @@ -221,7 +394,11 @@ class Repository < ActiveRecord::Base end def clone_url - "git://#{GitoriousConfig['gitorious_host']}/#{gitdir}" + if self.private_by_attribute? + push_url + else + "git://#{GitoriousConfig['gitorious_host']}/#{gitdir}" + end end def http_clone_url @@ -249,6 +426,11 @@ class Repository < ActiveRecord::Base !git.heads.empty? end + def getting_started_message? + return false if new_record? || !ready? || git.heads.empty? + return true if git.heads.length == 1 && git.heads.first.name == (GitoriousConfig["repo_meta_branch"]) + end + def self.git_backend RAILS_ENV == "test" ? MockGitBackend : GitBackend end @@ -265,13 +447,13 @@ class Repository < ActiveRecord::Base info_proc = Proc.new do |options| builder = options[:builder] builder.owner(owner.to_param, :kind => (owned_by_group? ? "Team" : "User")) - builder.kind(["mainline", "wiki", "team", "user"][self.kind]) + builder.kind(["mainline", "wiki", "team", "user", "mirror"][self.kind]) ### builder.project(project.to_param) end super({ :procs => [info_proc], - :only => [:name, :created_at, :ready, :description, :last_pushed_at], + :only => [:name, :created_at, :ready, :description, :last_pushed_at, :license], :methods => [:clone_url, :push_url, :parent] }.merge(opts)) end @@ -321,6 +503,14 @@ class Repository < ActiveRecord::Base !mainline? && writable_by?(a_user) end + # Can +a_user+ view this repository + # If logged_in is not given as parameter, doesn't care whether user is logged in: + # in that case returns true if project's publicity is +PUBLICITY_SITE+ + def can_be_viewed_by?(a_user, logged_in = nil) + return self.viewer?(a_user) if private_by_attribute? + return self.project.can_be_viewed_by?(a_user, logged_in) + end + # changes the owner to +another_owner+, removes the old owner as committer # and adds +another_owner+ as committer def change_owner_to!(another_owner) @@ -330,7 +520,7 @@ class Repository < ActiveRecord::Base existing.destroy end self.owner = another_owner - if self.kind != KIND_PROJECT_REPO + if self.kind != KIND_PROJECT_REPO || self.kind != KIND_SVN_MIRROR || self.kind != KIND_ZIP_MIRROR case another_owner when Group self.kind = KIND_TEAM_REPO @@ -347,15 +537,57 @@ class Repository < ActiveRecord::Base end end + # Fetch remote changes from svn repository + def svn_fetch + return if !svn_mirror? + Dir.chdir(self.full_repository_path) do + `git svn fetch --localtime` + end + project.create_push_event(self, "master", self.owner) + end + + def zip_update + return if !zip_mirror? + self.class.update_contents_from_zip(self.real_gitdir, self.mirror_url) + project.create_push_event(self, "master", self.owner) + end + + def update_mirror + if svn_mirror? + svn_fetch + elsif zip_mirror? + zip_update + end + end + def post_repo_creation_message return if tracking_repo? + options = {:target_class => self.class.name, :target_id => self.id} options[:command] = parent ? 'clone_git_repository' : 'create_git_repository' options[:arguments] = parent ? [real_gitdir, parent.real_gitdir] : [real_gitdir] + + # If temporary zip for creating the repo exists, use it + source_zip = File.join(Repository.dir_for_temp_zip(real_gitdir), "init.zip") + if File.exist?(source_zip) + options[:command] = 'create_git_repository_from_zip' + options[:arguments] = [real_gitdir, source_zip, {"source_type" => "local_file"}] + end + + if self.svn_mirror? ### FIXME + options[:command] = 'clone_svn_repository' + options[:arguments] = [real_gitdir, self.mirror_url] + end + if self.zip_mirror? + options[:command] = 'create_git_repository_from_zip' + options[:arguments] = [real_gitdir, self.mirror_url] + end + publish :create_repo, options.to_json end def post_repo_deletion_message + Rails.cache.delete("repositories:most_active_recently") options = {:target_class => self.class.name, :command => 'delete_git_repository', :arguments => [real_gitdir]} publish :destroy_repo, options.to_json end @@ -467,9 +699,21 @@ class Repository < ActiveRecord::Base def project_repo? kind == KIND_PROJECT_REPO end + + def private_by_attribute? + private_repo + end + + def private_by_project? + project.public_only_to_collaborators? + end + + def private? + private_repo || private_by_project? + end def mainline? - project_repo? + project_repo? || mirror_repo? end def team_repo? @@ -484,6 +728,47 @@ class Repository < ActiveRecord::Base kind == KIND_TRACKING_REPO end + def mirror_repo? + svn_mirror? || zip_mirror? + end + + def svn_mirror? + kind == KIND_SVN_MIRROR + end + + def zip_mirror? + kind == KIND_ZIP_MIRROR + end + + def mirror_human_name + if svn_mirror? + return I18n.t("activerecord.attributes.repository.svn_mirror") + elsif zip_mirror? + return I18n.t("activerecord.attributes.repository.zip_mirror") + else + return I18n.t("activerecord.attributes.repository.no_mirror") + end + end + + def visibility_to_string + if self.private_by_attribute? + return I18n.t("activerecord.attributes.repository.private") + elsif self.private_by_project? + return I18n.t("activerecord.attributes.repository.private_by_project") + else + return I18n.t("activerecord.attributes.repository.public") + end + end + +=begin +### SVN URL ASSIGN -> UPDATE GIT CONFIG ### + def mirror_url=(url) + Dir.chdir(self.full_repository_path) do + `git config svn-remote.svn.url #{url}` + end + end +=end + # returns an array of users who have commit bits to this repository either # directly through the owner, or "indirectly" through the associated groups def committers @@ -495,6 +780,11 @@ class Repository < ActiveRecord::Base committerships.reviewers.map{|c| c.members }.flatten.compact.uniq end + # Returns a list of Users who can see this repo + def viewers + committerships.viewers.map{ |c| c.members }.flatten.compact.uniq + end + # The list of users who can admin this repo, either directly as # committerships or indirectly as members of a group def administrators @@ -509,10 +799,18 @@ class Repository < ActiveRecord::Base a_user.is_a?(User) ? self.reviewers.include?(a_user) : false end + def viewer?(a_user) + a_user.is_a?(User) ? self.viewers.include?(a_user) : false + end + def admin?(a_user) a_user.is_a?(User) ? self.administrators.include?(a_user) : false end + def collaborator?(a_user) + self.admin?(a_user) || self.committer?(a_user) || self.reviewer?(a_user) || self.viewer?(a_user) + end + # Is this repo writable by +a_user+, eg. does he have push permissions here # NOTE: this may be context-sensitive depending on the kind of repo def writable_by?(a_user) @@ -537,15 +835,73 @@ class Repository < ActiveRecord::Base def title name - end + end def owner_title mainline? ? project.title : owner.title end + def license_abbreviated + case + when license == 'Academic Free License v3.0' + return "AFLv3" + when license == 'MIT License' + return "MIT" + when license == 'BSD License' + return "BSD" + when license == 'Ruby License' + return "Ruby" + when license == 'GNU General Public License version 2 (GPLv2)' + return "GPLv2" + when license == 'GNU General Public License version 3 (GPLv3)' + return "GPLv3" + when license == 'GNU Lesser General Public License (LGPL)' + return "LGPL" + when license == 'GNU Affero General Public License (AGPLv3)' + return "AGPLv3" + when license == 'Mozilla Public License 1.0 (MPL)' + return "MPL" + when license == 'Mozilla Public License 1.1 (MPL 1.1)' + return "MPL 1.1" + when license == 'Qt Public License (QPL)' + return "QPL" + when license == 'Python License' + return "Python" + when license == 'zlib/libpng License' + return "zlib/libpng" + when license == 'Apache License' + return "Apache" + when license == 'Apple Public Source License' + return "APSL" + when license == 'Perl Artistic License' + "PAL" + when license == 'Microsoft Permissive License (Ms-PL)' + return "Ms-PL" + when license == 'ISC License' + return "ISC" + when license == 'Lisp Lesser License' + return "LLL" + when license == 'Public Domain' + return "PD" + when license == 'Other Open Source Initiative Approved License' + return "Open Source" + when license == 'Other/Proprietary License' + return "Proprietary" + when license == 'Other/Multiple' + return "Other" + else return 'None' + end + end + +### MODIFIED TO USE SVN MIRROR REPOS AS PROJECT REPOS ### # returns the project if it's a KIND_PROJECT_REPO, otherwise the owner def project_or_owner - project_repo? ? project : owner + if project_repo? || mirror_repo? + project + else + owner + end + #project_repo? ? project : owner end def full_hashed_path @@ -706,28 +1062,32 @@ class Repository < ActiveRecord::Base # # title_search("foo", "project_id", 1) # will find repositories in Project#1 # matching 'foo' + # FIXME sql-query: what about repos that are private throug project? def self.title_search(term, key, value) sql = "SELECT repositories.* FROM repositories - INNER JOIN users on repositories.user_id=users.id - INNER JOIN groups on repositories.owner_id=groups.id - WHERE repositories.#{key}=#{value} + INNER JOIN users on repositories.user_id = users.id + INNER JOIN groups on repositories.owner_id = groups.id + WHERE repositories.#{key} = #{value} AND (repositories.name LIKE :q OR repositories.description LIKE :q OR groups.name LIKE :q) - AND repositories.owner_type='Group' + AND repositories.owner_type = 'Group' AND kind in (:kinds) + AND private_repo = 'false' UNION ALL SELECT repositories.* from repositories - INNER JOIN users on repositories.user_id=users.id - INNER JOIN users owners on repositories.owner_id=owners.id - WHERE repositories.#{key}=#{value} + INNER JOIN users on repositories.user_id = users.id + INNER JOIN users owners on repositories.owner_id = owners.id + WHERE repositories.#{key} = #{value} AND (repositories.name LIKE :q OR repositories.description LIKE :q OR owners.login LIKE :q) - AND repositories.owner_type='User' - AND kind in (:kinds)" + AND repositories.owner_type = 'User' + AND kind in (:kinds) + AND private_repo = 'false'" self.find_by_sql([sql, {:q => "%#{term}%", :id => value, :kinds => - [KIND_TEAM_REPO, KIND_USER_REPO, KIND_PROJECT_REPO]}]) + [KIND_TEAM_REPO, KIND_USER_REPO, KIND_PROJECT_REPO, KIND_SVN_MIRROR, KIND_ZIP_MIRROR]}]) end + protected def sharded_hashed_path(h) first = h[0,3] @@ -748,11 +1108,17 @@ class Repository < ActiveRecord::Base name.downcase! if name end + def remove_mirror_url_if_not_mirror + unless self.mirror_repo? + self.mirror_url = "" + end + end + def create_add_event_if_project_repo - if project_repo? + if project_repo? || mirror_repo? #(action_id, target, user, data = nil, body = nil, date = Time.now.utc) self.project.create_event(Action::ADD_PROJECT_REPOSITORY, self, self.user, - nil, nil, date = created_at) + nil, nil, date = created_at) end end @@ -798,6 +1164,7 @@ class Repository < ActiveRecord::Base sp = path.split("/") file << sp[sp.size-1, sp.size].join("/").sub(/\.git$/, "") << "\n" end + end end diff --git a/app/models/site.rb b/app/models/site.rb index 057130f..3363e71 100644 --- a/app/models/site.rb +++ b/app/models/site.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -25,6 +26,6 @@ class Site < ActiveRecord::Base attr_protected :subdomain def self.default - new(:title => "Gitorious", :subdomain => nil) + new(:title => "YouSource") #:subdomain => nil end end diff --git a/app/models/user.rb b/app/models/user.rb index 7b55425..5208d84 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 David A. Cuadrado @@ -50,33 +51,27 @@ class User < ActiveRecord::Base # For new users we are a little more strict than for existing ones. USERNAME_FORMAT = /[a-z0-9\-_\.]+/i.freeze USERNAME_FORMAT_ON_CREATE = /[a-z0-9\-]+/.freeze - validates_presence_of :login, :email, :if => :password_required? + validates_presence_of :login, :email, :if => :password_required? validates_format_of :login, :with => /^#{USERNAME_FORMAT_ON_CREATE}$/i, :on => :create validates_format_of :login, :with => /^#{USERNAME_FORMAT}$/i, :on => :update validates_format_of :email, :with => Email::FORMAT validates_presence_of :password, :if => :password_required? - validates_presence_of :password_confirmation, :if => :password_required? - validates_length_of :password, :within => 4..40, :if => :password_required? - validates_confirmation_of :password, :if => :password_required? - validates_length_of :login, :within => 3..40 - validates_length_of :email, :within => 3..100 + validates_length_of :login, :within => 1..40 + validates_length_of :email, :within => 1..100 validates_uniqueness_of :login, :email, :case_sensitive => false - validates_acceptance_of :terms_of_use, :on => :create, :allow_nil => false - before_save :encrypt_password - before_create :make_activation_code before_validation :lint_identity_url, :downcase_login after_save :expire_avatar_email_caches_if_avatar_was_changed after_destroy :expire_avatar_email_caches state_machine :aasm_state, :initial => :pending do state :terms_accepted - + event :accept_terms do transition :pending => :terms_accepted end - + end has_many :received_messages, :class_name => "Message", @@ -142,11 +137,24 @@ class User < ActiveRecord::Base end # Authenticates a user by their login name and unencrypted password. Returns the user or nil. - def self.authenticate(email, password) - u = find :first, :conditions => ['email = ? and activated_at IS NOT NULL and suspended_at IS NULL', email] # need to get the salt + def self.authenticate(login, password) + u = find :first, :conditions => ['login = ? and activated_at IS NOT NULL and suspended_at IS NULL', login] # need to get the salt u && u.authenticated?(password) ? u : nil end + #TODO: remove hard coded path + def self.ldap_authenticate(login, password) + path = '/var/www/gitorious/script/ldap_authenticate.py' + credentials = "#{login}\n#{password}" + IO.popen("python #{path}", mode='w+') do |io| + io.write credentials + io.close_write + result = io.read + result.chomp! + result == "1" + end + end + # Encrypts some data with the salt. def self.encrypt(password, salt) Digest::SHA1.hexdigest("--#{salt}--#{password}--") @@ -300,6 +308,10 @@ class User < ActiveRecord::Base def can_write_to?(repository) repository.writable_by?(self) end + + def can_view?(repository) + repository.can_be_viewed_by?(self) + end def to_param login @@ -388,6 +400,16 @@ class User < ActiveRecord::Base items.replace(Event.find(watched.map(&:event_id), {:order => "created_at desc"})) end + def administrated_projects + result = self.projects + self.groups.each do |g| + if g.admin? self + result.push g.projects + end + end + result + end + protected # before filter def encrypt_password diff --git a/app/models/user_observer.rb b/app/models/user_observer.rb index 25522ed..848bcd5 100644 --- a/app/models/user_observer.rb +++ b/app/models/user_observer.rb @@ -20,10 +20,10 @@ class UserObserver < ActiveRecord::Observer def after_create(user) - Mailer.deliver_signup_notification(user) if user.identity_url.blank? + #Mailer.deliver_signup_notification(user) if user.identity_url.blank? end def after_save(user) - Mailer.deliver_activation(user) if user.recently_activated? + #Mailer.deliver_activation(user) if user.recently_activated? end end diff --git a/app/processors/push_event_processor.rb b/app/processors/push_event_processor.rb index a7d49ef..6045562 100644 --- a/app/processors/push_event_processor.rb +++ b/app/processors/push_event_processor.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -20,8 +21,8 @@ class PushEventProcessor < ApplicationProcessor PUSH_EVENT_GIT_OUTPUT_SEPARATOR = "\t" unless defined?(PUSH_EVENT_GIT_OUTPUT_SEPARATOR) PUSH_EVENT_GIT_OUTPUT_SEPARATOR_ESCAPED = "\\\t" unless defined?(PUSH_EVENT_GIT_OUTPUT_SEPARATOR_ESCAPED) subscribes_to :push_event - attr_reader :oldrev, :newrev, :action, :user, :identifier, :target - attr_accessor :repository + attr_reader :oldrev, :newrev, :action, :identifier, :target, :revname + attr_accessor :repository, :user def on_message(message) verify_connections! @@ -32,11 +33,37 @@ class PushEventProcessor < ApplicationProcessor @user = User.find_by_login(hash['username']) process_push_from_commit_summary(hash['message']) log_events + + GrmfHelper::update_metainfo_to_database(@repository) else logger.error("#{self.class.name} received message, but couldn't find repo with hashed_path #{hash['gitdir']}") end end def log_events logger.info("#{self.class.name} logging #{events.size} events") @events.each do |e| @@ -72,12 +99,17 @@ class PushEventProcessor < ApplicationProcessor end event.notify_subscribers end - - # Sets the commit summary, as served from git - def process_push_from_commit_summary(spec) + + # old_sha new_sha refs/heads/branch_name + def parse_git_spec(spec) @oldrev, @newrev, @revname = spec.split(' ') r, name, @identifier = @revname.split("/", 3) @target = {'tags' => :tag, 'heads' => :head, 'merge-requests' => :review}[name] + end + + # Sets the commit summary, as served from git + def process_push_from_commit_summary(spec) + parse_git_spec(spec) if @target != :review && @repository @repository.update_disk_usage @repository.register_push @@ -111,10 +143,13 @@ class PushEventProcessor < ApplicationProcessor def events @events end + + def events=(events) + @events = events + end class EventForLogging - attr_accessor :event_type, :identifier, :email, :message, :commit_time, :user - attr_reader :commits + attr_accessor :event_type, :identifier, :email, :message, :commit_time, :user, :commit_details def to_s "" end @@ -122,6 +157,32 @@ class PushEventProcessor < ApplicationProcessor def commits=(commits) @commits = commits end + + def commits + @commits || [] + end + end @@ -162,6 +223,7 @@ class PushEventProcessor < ApplicationProcessor e.event_type = Action::PUSH e.message = "#{identifier} changed from #{@oldrev[0,7]} to #{@newrev[0,7]}" e.identifier = identifier + e.user = user e.email = user.email e.commits = events_from_git_log("#{@oldrev}..#{@newrev}") @events << e @@ -228,6 +290,12 @@ class PushEventProcessor < ApplicationProcessor e.commit_time = Time.at(timestamp.to_i).utc e.event_type = Action::COMMIT e.message = message + e.commit_details = { + :sha => sha, + :email => email, + :committed_at => e.commit_time.xmlschema, + :message => message + } result << e end diff --git a/app/processors/repository_creation_processor.rb b/app/processors/repository_creation_processor.rb index 19d35ce..e8b964a 100644 --- a/app/processors/repository_creation_processor.rb +++ b/app/processors/repository_creation_processor.rb @@ -1,5 +1,6 @@ # encoding: utf-8 #-- +# Copyright (C) 2010 Tero Hänninen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -15,6 +16,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . #++ +include GrmfHelper + class RepositoryCreationProcessor < ApplicationProcessor subscribes_to :create_repo @@ -25,15 +28,29 @@ class RepositoryCreationProcessor < ApplicationProcessor target_id = message_hash['target_id'] command = message_hash['command'] arguments = message_hash['arguments'] - - logger.info("Processor: #{target_class}(#{target_id.inspect})::#{command}(#{arguments.inspect}..)") + + logprefix = "#{self.class.name}: #{target_class}(#{target_id.inspect}): " + + logger.info(logprefix + "#{command}(#{arguments.inspect}..)") target_class.constantize.send(command, *arguments) unless target_id.blank? - obj = target_class.constantize.find_by_id(target_id) - if obj && obj.respond_to?(:ready) - obj.ready = true - obj.save! + #Trying max 5 times if find_by_id would return a correct value + 5.times do + obj = target_class.constantize.find_by_id(target_id) + if obj && obj.respond_to?(:ready) + obj.ready = true + obj.save! + + # Add metafiles to repository if not an internal repo like wiki repo + GrmfHelper::update_metainfo_to_repository(obj) if !Repository::KINDS_INTERNAL_REPO.include?(obj.kind) + logger.info(logprefix + "Success") + return + else + logger.error(logprefix + "Problem: obj.respond_to?(:ready).to_s == #{obj.respond_to?(:ready)}") + sleep(3) + end end end + logger.fatal(logprefix + "FATAL: Repository was not marked ready.") end end diff --git a/app/processors/ssh_key_processor.rb b/app/processors/ssh_key_processor.rb index ca2bcd6..99be7d9 100644 --- a/app/processors/ssh_key_processor.rb +++ b/app/processors/ssh_key_processor.rb @@ -21,18 +21,29 @@ class SshKeyProcessor < ApplicationProcessor def on_message(message) verify_connections! json = ActiveSupport::JSON.decode(message) - logger.info "#{self.class.name} consuming message. Command: #{json['command']}. Arguments: #{json['arguments']}. Target_id: #{json['target_id']}" - logger.debug("#{self.class.name} processing message #{json}") + logprefix = "#{self.class.name} ssh-key(#{json['target_id']}): " + logger.info(logprefix + "Consuming message. Command: #{json['command']}. Arguments: #{json['arguments']}. Target_id: #{json['target_id']}") + logger.debug(logprefix + "Processing message #{json}") unless %w(add_to_authorized_keys delete_from_authorized_keys).include?(json['command']) raise "Unknown command" end - logger.debug("Processor sending message: #{json['command']} #{json['arguments']}") - SshKey.send(json['command'], *json['arguments']) - if target_id = json['target_id'] - if obj = SshKey.find_by_id(target_id.to_i) - obj.ready = true - obj.save! + logger.debug(logprefix + "Processor sending message: #{json['command']} #{json['arguments']}") + SshKey.send(json['command'], *json['arguments']) + if target_id = json['target_id'] #Existing target id means we are creating a new key + #Trying max 5 times if find_by_id would return a correct value + 5.times do + obj = SshKey.find_by_id(target_id.to_i) + if obj && obj.respond_to?(:ready) + obj.ready = true + obj.save! + logger.info(logprefix + "Success") + return + else + logger.error(logprefix + "Problem: SshKey.find_by_id(target_id.to_i).to_s == #{SshKey.find_by_id(target_id.to_i)}") + sleep(3) + end end + logger.fatal(logprefix + "FATAL: SshKey was not marked ready.") end end end diff --git a/app/views/blobs/show.html.erb b/app/views/blobs/show.html.erb index 1da6fca..fd29980 100644 --- a/app/views/blobs/show.html.erb +++ b/app/views/blobs/show.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2008 Johan Sørensen # Copyright (C) 2008 Tor Arne Vestbø @@ -23,6 +24,8 @@ <% @page_title = t("views.blobs.page_title", :path => current_path.join("/"), :repo => @repository.name, :title => @project.title) -%> <%= breadcrumbs_from(@root) -%> +

<%= current_path.last -%>

+ <% content_for :extra_head do -%> <% if highlightable?(@blob) -%> <%= stylesheet_link_tag("prettify/prettify.css") -%> diff --git a/app/views/commits/feed.atom.builder b/app/views/commits/feed.atom.builder index e134dd9..79d486f 100644 --- a/app/views/commits/feed.atom.builder +++ b/app/views/commits/feed.atom.builder @@ -23,7 +23,7 @@ atom_feed do |feed| @commits.each do |commit| item_url = "http://#{GitoriousConfig['gitorious_host']}" item_url << repo_owner_path(@repository, :project_repository_commit_path, - @project, @repository, commit.id, :html) + @project, @repository, commit.id, :format => :html) feed.entry(commit, { :url => item_url, :updated => commit.committed_date.utc, diff --git a/app/views/commits/index.html.erb b/app/views/commits/index.html.erb index 805e61f..291335a 100644 --- a/app/views/commits/index.html.erb +++ b/app/views/commits/index.html.erb @@ -23,7 +23,7 @@
  • <%= link_to "Source tree", repo_owner_path(@repository, :project_repository_tree_path, - @project, @repository, desplat_path(params[:branch])) -%> + @project, @repository, params[:branch]) -%>

diff --git a/app/views/committerships/_permission_explanation.html.erb b/app/views/committerships/_permission_explanation.html.erb index f1201d0..54506d9 100644 --- a/app/views/committerships/_permission_explanation.html.erb +++ b/app/views/committerships/_permission_explanation.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -18,9 +19,12 @@ %>
-

What does the different permissions mean?

+

What do the different permissions mean?

+
Viewers:
+
Can see the repository
+
Reviewers:
Can update merge request statuses
Will receive notifications on new merge requests
@@ -33,6 +37,6 @@
Can manage collaborators
Can delete the repository
-

Note that the permissions doesn't cascade down, meaning +

Note that the permissions don't cascade down, meaning that for instance a committer isn't automatically a reviewer

diff --git a/app/views/committerships/edit.html.erb b/app/views/committerships/edit.html.erb index 9ab011b..71bbf9f 100644 --- a/app/views/committerships/edit.html.erb +++ b/app/views/committerships/edit.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -27,6 +28,10 @@ +
<%= f.label :permissions -%>: + <%= f.label "View" %> + <%= check_box_tag "permissions[]", "view", @committership.viewer? -%> + <%= f.label "Review" %> <%= check_box_tag "permissions[]", "review", @committership.reviewer? -%> diff --git a/app/views/committerships/new.html.erb b/app/views/committerships/new.html.erb index 444f9ef..32e0b6b 100644 --- a/app/views/committerships/new.html.erb +++ b/app/views/committerships/new.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -36,8 +37,9 @@ $("#add_user_tab").addClass("selected"); event.preventDefault(); }); - $("input#user_login_autocomplete").autocomplete("<%= url_for(:action => :auto_complete_for_user_login) -%>"); - $("input#group_name_autocomplete").autocomplete("<%= url_for(:action => :auto_complete_for_group_name) -%>"); + options = {matchSubset: false} + $("input#user_login_autocomplete").autocomplete("<%= url_for(:action => :auto_complete_for_user_login) -%>", options); + $("input#group_name_autocomplete").autocomplete("<%= url_for(:action => :auto_complete_for_group_name) -%>", options); }); <% end -%> @@ -72,6 +74,10 @@ +
<%= f.label :permissions -%>: + <%= f.label "View" %> + <%= check_box_tag "permissions[]", "view", false -%> + <%= f.label "Review" %> <%= check_box_tag "permissions[]", "review", false -%> diff --git a/app/views/events/_event.html.erb b/app/views/events/_event.html.erb index a86bc39..6a3b9f6 100644 --- a/app/views/events/_event.html.erb +++ b/app/views/events/_event.html.erb @@ -20,6 +20,7 @@ # along with this program. If not, see . #++ %> + <% relative_time ||= false -%> <% action, body, category = action_and_body_for_event(event) %> diff --git a/app/views/groups/edit.html.erb b/app/views/groups/edit.html.erb index e440127..f645bcb 100644 --- a/app/views/groups/edit.html.erb +++ b/app/views/groups/edit.html.erb @@ -36,7 +36,7 @@ <%= image_tag @group.avatar.url(:thumb) %>
<%- if @group.avatar? -%> <%= button_to_function("Delete current team image", - "if (confirm('Are you sure you want to delete the team image?')) $('delete_avatar').submit()") %>
+ "if (confirm('Are you sure you want to delete the team image?')) $('#delete_avatar').submit()") %>
<%- end -%>
<%= f.file_field :avatar %> @@ -56,4 +56,4 @@ <% form_for(@group, :url => avatar_group_path, :html => {:id => "delete_avatar", :method => :delete}) do |f| -%> -<% end -%> \ No newline at end of file +<% end -%> diff --git a/app/views/groups/index.html.erb b/app/views/groups/index.html.erb index 3ac40bb..32e4de5 100644 --- a/app/views/groups/index.html.erb +++ b/app/views/groups/index.html.erb @@ -26,6 +26,9 @@

<%= link_to h(group.name), group_path(group) -%> + <% unless group.description.blank? -%> + <%= truncate h(group.description), :length => 100 -%> + <% end -%> (<%= pluralize group.members.count, t("views.groups.member_singular"), t("views.groups.member_plural") -%>, <%= pluralize group.repositories.count, t("views.groups.repo_singular"), t("views.groups.repo_plural") -%>) @@ -39,7 +42,9 @@ <%= will_paginate @groups -%> <% content_for :sidebar do -%> - + <% if logged_in? -%> + + <% end -%> <% end -%> diff --git a/app/views/groups/show.html.erb b/app/views/groups/show.html.erb index 12c071b..b8b6de5 100644 --- a/app/views/groups/show.html.erb +++ b/app/views/groups/show.html.erb @@ -1,5 +1,7 @@ <% #-- +# Copyright (C) 2010 Tero Hänninen +# Copyright (C) 2010 Marko Peltola # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -53,13 +55,15 @@
  • None
  • <% end -%> <% @group.repositories.mainlines.each do |repository| -%> -
  • - <%= link_to "#{h(repository.project.title)}/#{h(repository.name)}", - group_project_repository_path(@group, repository.project, repository) -%> - - <%= paragraphs_with_more render_markdown(repository.description), repository.id -%> - -
  • + <% if repository.can_be_viewed_by?(current_user, logged_in?) -%> +
  • + <%= link_to "#{h(repository.project.title)}/#{h(repository.name)}", + group_project_repository_path(@group, repository.project, repository) -%> + + <%= paragraphs_with_more render_markdown(repository.description), repository.id -%> + +
  • + <% end -%> <% end -%> <% end -%> @@ -71,7 +75,9 @@
  • None
  • <% end -%> <% @group.projects.each do |project| -%> -
  • <%= link_to h(project.title), project -%>
  • + <% if project.can_be_viewed_by?(current_user, logged_in?) -%> +
  • <%= link_to h(project.title), project -%>
  • + <% end -%> <% end -%> <% end -%> diff --git a/app/views/keys/index.html.erb b/app/views/keys/index.html.erb index f272561..2e7fc37 100644 --- a/app/views/keys/index.html.erb +++ b/app/views/keys/index.html.erb @@ -43,7 +43,7 @@ <%= wbr_middle(h(ssh_key.fingerprint)) -%>

    - <%= image_tag(ssh_key.ready? ? "silk/tick.png" : "silk/cross.png", + <%= image_tag(ssh_key.ready? ? "silk/tick.png" : "spinner.gif", :title => ssh_key.ready? ? 'Ready' : 'Not ready') -%> diff --git a/app/views/keys/new.html.erb b/app/views/keys/new.html.erb index 40cb2c1..79cef31 100644 --- a/app/views/keys/new.html.erb +++ b/app/views/keys/new.html.erb @@ -34,3 +34,27 @@ <%= f.submit t("views.common.save") -%> <% end -%> +
    + +

    SSH key help

    +

    + For Windows users:
    + If you have MsysGit installed you can generate a SSH key with MsysGit bash. Use command ssh-keygen -t rsa. + Then open the public key (in notepad for example) and copy and paste the file contents to the form on the left. +

    + +

    + If you don't have MsysGit installed, or you want to generate the key with a graphical interface, you can use + <%= link_to "PuttyGen (puttygen.exe)", "http://the.earth.li/~sgtatham/putty/latest/x86/puttygen.exe" -%>. + In trouble, see <%= link_to "instructions", "http://www.plcs-resources.org/plcs/dexlib/help/dex/sw_putty.htm#Generateandsaveapublic/privatekeypair" -%>. +

    + +

    + For Linux users:
    The public key is generally located in ~/.ssh/id_rsa.pub or ~/.ssh/id_dsa.pub. + If not, you can generate the key with command ssh-keygen -t rsa. When finished, you can view the key with command + cat ~/.ssh/id_rsa.pub and then copy it to the form on the left. +

    + +
    + + diff --git a/app/views/layouts/_login_logout.html.erb b/app/views/layouts/_login_logout.html.erb index 592ebe1..68f84fc 100644 --- a/app/views/layouts/_login_logout.html.erb +++ b/app/views/layouts/_login_logout.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # # This program is free software: you can redistribute it and/or modify @@ -20,22 +21,29 @@ <% if current_user.site_admin? -%>
  • <%= link_to t("views.layout.admin"), admin_users_path -%>
  • <% end -%> - -
  • <%= link_to "Your dashboard", dashboard_path -%>
  • -
  • - <%= link_to h(current_user.to_param_with_prefix), user_path(current_user) -%> + +
  • + + <% if current_user.fullname.blank? -%> + <%= link_to h(current_user.to_param_with_prefix), dashboard_path -%> + <% else -%> + <%= link_to h(current_user.fullname), dashboard_path -%> + <% end -%> + +
  • +
  • <%= link_to "Home", dashboard_path -%>
  • +
  • + <%=link_to "Profile", user_path(current_user) -%> + <% # link_to h(current_user.to_param_with_prefix), user_path(current_user) -%>
  • <% if current_user.received_messages.unread_count > 0 -%> <%= link_to current_user.received_messages.unread_count, messages_path -%> <% else -%> <%= link_to "#{current_user.received_messages.unread_count}", messages_path -%> - <% end -%> + <% end -%>
  • <%= link_to t("views.layout.logout"), logout_path -%>
  • <%- else -%> - <% if GitoriousConfig['public_mode'] -%> -
  • <%= link_to t("views.layout.register"), new_user_path -%>
  • - <% end -%>
  • <%= link_to t("views.layout.login"), login_path -%>
  • <%- end -%> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 19e3f1b..600f7de 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,5 +1,6 @@ <% #-- +# Copyright (C) 2010 Juho Nieminen # Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) # Copyright (C) 2007, 2008 Johan Sørensen # Copyright (C) 2008 August Lilleaas @@ -36,30 +37,32 @@ <% end -%> <% end -%> - - \ No newline at end of file + diff --git a/public/favicon.ico b/public/favicon.ico index 0537d7d..11123d6 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/images/footer_top_border_gradient.png b/public/images/footer_top_border_gradient.png new file mode 100644 index 0000000..218836f Binary files /dev/null and b/public/images/footer_top_border_gradient.png differ diff --git a/public/images/header_bottom_border_gradient.png b/public/images/header_bottom_border_gradient.png new file mode 100644 index 0000000..6d50f08 Binary files /dev/null and b/public/images/header_bottom_border_gradient.png differ diff --git a/public/images/logo-yousource.png b/public/images/logo-yousource.png new file mode 100644 index 0000000..4e5a946 Binary files /dev/null and b/public/images/logo-yousource.png differ diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 976f8da..e349546 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -31,7 +31,7 @@ $(document).ready(function() { slug.val( lintName(this.value) ); }); - + // Line highlighting/selection $("#codeblob").highlightSelectedLines(); diff --git a/public/stylesheets/base.css b/public/stylesheets/base.css index ccab1eb..5f71e0a 100644 --- a/public/stylesheets/base.css +++ b/public/stylesheets/base.css @@ -161,14 +161,14 @@ abbr.timeago { #header { height: 112px; - background: #111 url("../images/header_bg.png") repeat-x 0 0; + background: #aac1ac url(../images/header_bottom_border_gradient.png) left bottom repeat-x; overflow: hidden; + text-align: center; } #header #logo { - padding: 10px 0 0 20px; line-height: 25px; - margin:0; + margin:15px 0px 0px 15px; float: left; border:0; } @@ -177,19 +177,19 @@ abbr.timeago { float: right; margin-top: 15px; padding: 0px; - margin-right: 24px; + margin-right: 17px; list-style: none; - padding-top: 2px; - clear: right; + padding-top: 2px; + font-size:0.8em; } #header #searchbox .search-field, #header #searchbox .search-submit { - background: #1F1F1F; + background: #E0EEE9; border: 1px solid #666; - color: #fff; + color: #000; } #header #searchbox .search-submit { - color: #aaa; + color: #000; } #header #searchbox input { margin: 0; @@ -197,7 +197,7 @@ abbr.timeago { } #header #searchbox .search-field { - width: 250px; + width: 220px; } #header #searchbox .search-hint { @@ -205,7 +205,7 @@ abbr.timeago { } -#header #logo a { color: #666; } +#header #logo a { color: #aac1ac; } #header #project_title { font-size: 39px; @@ -218,6 +218,18 @@ abbr.timeago { letter-spacing: 0.05em; } + +#header #usermenu { + background: #E8EEE9; + float: right; + clear:right; + height: 35px; + margin-top:17px; + padding-right: 10px; + text-align: right; +} + + #container { min-height: 300px; /* margin-right: 40px; @@ -319,15 +331,16 @@ abbr.timeago { color: #666; text-align: center; clear: both; - background: #222 url("../images/header_bg.png") repeat-x; + background: #aac1ac url(../images/footer_top_border_gradient.png) left top repeat-x; } #container #footer a { - color: #92B92D; + color: #314232; } #container #footer ul { margin: 0; + clear:right; } #container #footer ul li { display: inline; @@ -342,7 +355,7 @@ abbr.timeago { color: #666; font-size: 10px; } -#container #footer ul.legal a:hover { color: #92B92D; } +#container #footer ul.legal a:hover { color: #314232; } #container #footer .powered-by { float: right; @@ -383,47 +396,88 @@ abbr.timeago { /* menu */ #menu { - float: right; - margin-right: 20px; - margin-top: 20px; - margin-bottom: 0; + /*float: right;*/ + margin-top: 45px; } #menu li { display: inline; margin-right: 20px; } -#menu li.secondary { - margin-right: 5px; - color: #666; -} + #menu li a { font-size: 16px; - color: #92B92D; + font-weight: bold; + color: #314232; +} + +#menu li a:hover { + text-decoration: none; + border-bottom: 2px solid #314232; } -#menu li.subtle a { color: #666; margin-left: 10px;} -#menu li.subtle a:hover { color: #92B92D; } -#menu li.secondary a { +#usermenu ul { + margin: 10px 10px 5px 0px; + float: right; + font-size: 11px; + padding-left: 25px; +} + +#usermenu li{ + display: inline; + margin-right: 5px; + color: #666; +} + +#usermenu li a { font-size: 11px; + color: #314232; +} + +#usermenu li.secondary.user a { + font-size: 12px; + padding-right: 10px; + color: #666; +} + +#usermenu li.secondary.home { + background: transparent url('../images/silk/house.png') no-repeat 0px -1px; +} + +#usermenu li.secondary.home a { + padding-left: 20px; +} + +#usermenu li.secondary.profile { + background: transparent url('../images/silk/user.png') no-repeat 0px -1px; +} + +#usermenu li.secondary.profile a { + padding-left: 20px; } -#menu li.secondary.messages { - background: transparent url('../images/silk/email.png') no-repeat 0px 1px; + +#usermenu li.secondary.messages { + background: transparent url('../images/silk/email.png') no-repeat 0px 0px; } -#menu li.secondary.messages a > span { + +#usermenu li.secondary.messages a > span { color: #666; } -#menu li.secondary.messages a { + +#usermenu li.secondary.messages a { width: 100%; padding-left: 20px; } -#menu li a:hover { - text-decoration: none; - border-bottom: 2px solid #92B92D; +#usermenu li.subtle a { color: #888; margin-left: 10px;} +#usermenu li.subtle a:hover { color: #314232; } + +#usermenu li.about { + padding-right: 30px; } + /* Round corners */ .round-top-5 { -moz-border-radius-topleft: 5px; @@ -437,6 +491,10 @@ abbr.timeago { -webkit-border-top-left-radius: 10px; -webkit-border-top-right-radius: 10px; } +.round-top-left-40 { + -moz-border-radius-topleft: 30px; + -webkit-border-top-left-radius: 30px; +} .round-bottom-5 { -moz-border-radius-bottomleft: 5px; -moz-border-radius-bottomright: 5px; @@ -559,7 +617,6 @@ li.branch > a { background-image: url("../images/silk/arrow_branch.png") !import li.branch.head > a { background-image: url("../images/arrow_branch_head.png") !important; } li.tag > a { background-image: url("../images/silk/tag_blue.png") !important; } li.tree > a { background-image: url("../images/silk/chart_organisation.png") !important; } -li.home > a { background-image: url("../images/silk/house.png") !important; } li.account > a { background-image: url("../images/silk/user.png") !important; } li.edit_account > a, .white-button.edit_account a { background-image: url("../images/silk/user_edit.png") !important; @@ -590,14 +647,15 @@ li.sent_emails > a { background-image: url("../images/silk/email_go.png") !impor li.received_emails > a { background-image: url("../images/silk/email_open.png") !important; } li.all_emails > a { background-image: url("../images/silk/folder.png") !important; } li.favorite > a { background-image: url("../images/silk/star.png"); } -li.dashboard > a { background-image: url("../images/silk/shield.png") !important; } +li.dashboard > a { background-image: url("../images/silk/house.png") !important; } /* Icons used both for events and actions/breadcrums */ li.event_instance.create_repository, li.create_repository > a { background-image: url("../images/silk/database_add.png") !important; } li.event_instance.add_project_repository, li.add_project_repository > a { background-image: url("../images/silk/database_add.png") !important; } li.event_instance.clone_repository, li.clone_repository > a { background-image: url("../images/silk/database_clone.png") !important; } -li.event_instance.add_collaborator, li.add_collaborator > a { background-image: url("../images/silk/group_add.png") !important; } li.event_instance.update_repository, li.update_repository > a { background-image: url("../images/silk/database_edit.png") !important; } +li.event_instance.add_collaborator, li.add_collaborator > a { background-image: url("../images/silk/group_add.png") !important; } +li.event_instance.update_repo_with_zip, li.update_repo_with_zip > a { background-image: url("../images/silk/database_refresh.png") !important; } li.event_instance.delete_repository, li.delete_repository > a { background-image: url("../images/silk/database_delete.png") !important; } li.event_instance.commit, li.commit > a { background-image: url("../images/silk/brick.png") !important; } li.event_instance.merge_commit, li.merge_commit > a { background-image: url("../images/silk/arrow_merge.png") !important; } @@ -616,6 +674,7 @@ li.event_instance.create_branch { background-image: url("../images/silk/arrow_br li.event_instance.delete_branch { background-image: url("../images/silk/delete.png") !important; } li.event_instance.create_tag { background-image: url("../images/silk/tag_blue_add.png") !important; } li.event_instance.delete_tag { background-image: url("../images/silk/tag_blue_delete.png") !important; } +li.event_instance.add_committer { background-image: url("../images/silk/group_add.png") !important; } li.event_instance.remove_committer { background-image: url("../images/silk/group_delete.png") !important; } li.event_instance.comment { background-image: url("../images/silk/comment.png") !important; } li.event_instance.request_merge { background-image: url("../images/silk/chart_line_add.png") !important; } @@ -718,10 +777,54 @@ li.merge_requests a.selected { color: #09460F;} background: transparent url('../images/silk/error.png') no-repeat 0 0; } +.help-box .icon.info { + background: transparent url('../images/silk/information.png') no-repeat 0 0; +} + .help-box :last-child { margin-bottom: 0; } +.help-box span.right { + float: right; +} + +.getting_started_button { + float:right; + margin:-30px 20px 10px 0px; + cursor:pointer; +} + +.getting_started_button a:hover { + text-decoration:none; +} + +#getting_started_message { + margin: -30px auto 30px; +} + +#getting_started_message a { + cursor:pointer; +} + +#getting_started_message a:hover { + text-decoration:none; +} + +#getting_started_message #icon { + margin-top: -20px; +} + + +#getting_started_message #icon:hover{ + cursor:pointer; +} + +.mirror_settings { + margin: 0 0 20px 0; +} + + /* White buttons */ #new-project-button, .white-button { @@ -802,14 +905,17 @@ textarea { } fieldset { - padding: 3px; + padding: 3px 5px 3px 5px; margin: 0 0 10px 0; - width: 100%; + width: 80%; + min-width: 350px; + border: thin solid #888; } legend { font-weight: bold; - font-size:12px; + font-size:14px; + color: #999; } label { @@ -826,6 +932,11 @@ form { margin-bottom: 1.5em; } +form.new_ssh_key { + float:left; + margin-right:3%; +} + form.button-to, form.button-to div { display: inline; } form.button-to div { margin: 0 5px 0 5px; } form.button-to div input { @@ -847,7 +958,9 @@ input[type='text'], input[type='password'] { } textarea { - width: 350px; + min-width: 350px; + max-width: 600px; + width: 50%; height: 150px; padding: 3px; } @@ -856,7 +969,7 @@ textarea.wide { width: 500px; } textarea.tall { height: 300px; } textarea.taller {height: 500px;} textarea.medium { width: 450px; height: 250px; } -textarea.fill { width: 95%; } +textarea.fill { width: 95%; max-width: 95%; } select { width: 200px; } input[type='text'], input[type='password'], textarea, select { @@ -864,6 +977,10 @@ input[type='text'], input[type='password'], textarea, select { border: 1px solid #bbb; } +#repository_mirror_url { + margin: 3px 0 0 0; +} + input.compact, select.compact { width: 100px; margin-right: 5px; @@ -887,6 +1004,10 @@ input#openid_url { padding-left: 18px; } +select#repository_license { + width: 300px; +} + .fieldWithErrors { margin: 0; display: inline-block;} .fieldWithErrors input, .fieldWithErrors textarea, .fieldWithErrors select { @@ -1131,6 +1252,19 @@ div.meta-info { padding: 0 !important; } +.mirror_settings ul.horizontal { + margin-left: 0px !important; + padding-left: 0px; + display: inline; +} + +.mirror_settings ul.horizontal li { + display: inline; + padding-right: 10px; + padding-left: 0px; + margin-left: 0px; +} + td.meta-info { padding: 0 !important; text-align: right; @@ -1450,7 +1584,7 @@ table.tree tr td.node a { #site_intro { width: 65%; margin: 2em auto 2em auto; - font-size: 1.5em; + font-size: 1.2em; } #site_intro .hint { @@ -2417,6 +2551,24 @@ li.event_instance.delete_merge_request { background-image: url("../images/silk/c .event_instance .event_category.project { background: #98a49f; } .event_instance .event_category.merge_request { background: #a2a498; } + +/* repository browser */ + +.repository_browser .header h3 { + margin: 0; + padding: 0; + font-size: 120%;; + display: inline; +} + +/* Doesn't work yet...*/ +.repository_browser ul.horizontal.meta_links.repo_browser li { + border-style:none; +} + + + + /* repository meta (overview page) */ .repository_meta { /* border: 1px solid #ddd;*/ @@ -2483,6 +2635,11 @@ li.event_instance.delete_merge_request { background-image: url("../images/silk/c } .repository_meta .branches ul li.head { font-weight: bold; } +.repository_meta .license { + float:right; + text-align:right; + padding-bottom:10px !important; +} .feed_icon { margin: 0px 4px -1px 4px; } @@ -2511,7 +2668,9 @@ li.event_instance.delete_merge_request { background-image: url("../images/silk/c margin-top: 10px; padding-top: 10px; } -.page-meta p { margin:0; padding:0;} +.page-meta p { float:left; margin:0; padding:0;} +.page-meta p.clone_urls { margin-left:10%;} +.page-meta p span.center {text-align:center;} .page-meta .page-actions, .page-header .page-actions { font-size:11px; float:right; color:#666;} .page-meta .page-actions li { padding:0; @@ -2767,8 +2926,8 @@ div.archive-download-box p { { max-height: 200px; padding: 10px; - max-height: 200px; - overflow-y: scroll; + overflow-y: auto; + margin-bottom: 20px; } ul#commit_selector @@ -3455,4 +3614,4 @@ body#users h2.activities small { width: 50px; margin: 0 auto 0 auto; float:none; -} \ No newline at end of file +} diff --git a/script/git-daemon b/script/git-daemon index bd05fc2..daa9ae5 100755 --- a/script/git-daemon +++ b/script/git-daemon @@ -90,6 +90,10 @@ module Git begin ActiveRecord::Base.verify_active_connections! repository = ::Repository.find_by_path(path) + log(Process.pid, "try to access repo: #{path} #{repository.private?}:\n") + if repository.private? # forbid git:// for those private repositories + repository = nil + end rescue => e log(Process.pid, "AR error: #{e.class.name} #{e.message}:\n #{e.backtrace.join("\n ")}") end diff --git a/script/gitorious b/script/gitorious index 2cffec2..150035d 100755 --- a/script/gitorious +++ b/script/gitorious @@ -49,7 +49,7 @@ end def gitorious_says(msg) $stderr.puts - $stderr.puts "== Gitorious: " + ("=" * 59) + $stderr.puts "== YouSource: " + ("=" * 58) $stderr.puts msg $stderr.puts "="*72 $stderr.puts diff --git a/script/ldap_authenticate.py b/script/ldap_authenticate.py new file mode 100644 index 0000000..8bbef04 --- /dev/null +++ b/script/ldap_authenticate.py @@ -0,0 +1,92 @@ +""" +A lightweight authentication module that checks +if username + password was ok against given LDAP server. +The script takes its input from stdin and prints output +to stdout. + +This script was developed in May 2010 for Verso project. + +Author: +Heikki Salo +heikki.ao.salo@iki.fi + +""" + +import ldap +import sys +import logging +import logging.handlers + +from ldap_settings import server, base, log_filename, log_level +logger = logging.getLogger('ldap_authenticate') + +if log_filename is not None: + # Writes log only if filename is specified. + if log_level: + logger.setLevel(log_level) + else: + logger.setLevel(logging.DEBUG) + handler = logging.handlers.RotatingFileHandler( \ + log_filename, maxBytes=20000000, backupCount=5) + formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + + +def main(): + """ + Reads username\npassword from stdin and uses authenticate + method to determine successful authentication. The specification + is that on and only on successful authentication "1" is printed, + in all other cases "0". This should be the only things this + program prints to stdout. + + Some logging is also saved for timing the LDAP server and for + latter debugging purposes. + + """ + + try: + logger.debug("Parsing input.") + line = sys.stdin.read() + username, password = line.split("\n") + logger.info("Authenticating %s begins." % username) + result = authenticate(username, password) + if result: + print "1" + logger.info("Authenticating %s is over: success." % username) + return + else: + logger.info("Authenticating %s is over: failed." % username) + except ValueError: + logger.error("Malformed input, got ValueError.") + except: + logger.fatal("Caught '%s'." % sys.exc_info()[0]) + print "0" + + +def authenticate(username, password): + """ + Authenticates user with defined LDAP server. This method simply takes + username and password, then returns if the pair was correct. + + Documentation for Python LDAP can be found here: + http://www.python-ldap.org/doc/html/ldap.html + + """ + + conn = ldap.initialize(server) + dn = "cn=%s,%s" % (username, base) + + try: + conn.bind(dn, password) + result = conn.result() + return result[0] == ldap.RES_BIND + except ldap.INVALID_CREDENTIALS: + return False + except: + logger.fatal("Caught '%s' while authenticating %s." % (sys.exc_info()[0], username)) + return False + +if __name__ == "__main__": + main() diff --git a/script/ldap_settings.py.template b/script/ldap_settings.py.template new file mode 100644 index 0000000..1874f83 --- /dev/null +++ b/script/ldap_settings.py.template @@ -0,0 +1,6 @@ +import logging + +base = "" +server = "" +log_filename = None +log_level = logging.DEBUG diff --git a/script/update_mirror b/script/update_mirror new file mode 100755 index 0000000..7dba70c --- /dev/null +++ b/script/update_mirror @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby + +ENV["PATH"] = "/usr/local/bin/:/opt/local/bin:#{ENV["PATH"]}" +ENV["RAILS_ENV"] ||= "production" +require File.dirname(__FILE__)+'/../config/environment' + +puts "Updating mirror repositories..." + +#Repository.mirrors when named_scope is set +Repository.find_each(:conditions => ["kind in (?)",[Repository::KIND_SVN_MIRROR, Repository::KIND_ZIP_MIRROR]]) do |repo| + print "Updating: " + repo.name + "... " + begin + repo.update_mirror + puts "Done." + rescue + puts "FAILED." + end +end + +puts "Updating mirror repositories... Done." + diff --git a/test/fixtures/views/site/qt/index.html.erb~ b/test/fixtures/views/site/qt/index.html.erb~ deleted file mode 100644 index 9eff638..0000000 --- a/test/fixtures/views/site/qt/index.html.erb~ +++ /dev/null @@ -1,148 +0,0 @@ -<% -#-- -# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) -# Copyright (C) 2008 Johan Sørensen -# Copyright (C) 2008 David A. Cuadrado -# Copyright (C) 2008 Tor Arne Vestbø -# Copyright (C) 2009 Fabio Akita -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#++ -%> - -<% @page_title = t("views.site.page_title") -%> - -
    -

    <%= t("views.site.description") %>

    -
    - -
    - <% unless logged_in? -%> - <% if false -%> - - - - - - - - - -

    <%= t("views.site.for_projects") %>

    <%= t("views.site.for_contributors") %>

    <%= image_tag("overview_projects.png") -%><%= image_tag("overview_contributors.png") -%>
    - <% end -%> - <% if GitoriousConfig['public_mode'] || logged_in? -%> - - <% end -%> - <% end -%> -
    - -<% if GitoriousConfig['public_mode'] -%> -
    - <% pull_box(nil, :class => "expose") do -%> -

    - Gitorious provides open source infrastructure - for open source projects which use Git. - Read more… -

    - <% end -%> - -<% cache([:site_index, :recent_activities], :expires_in => 10.minutes) do -%> - <% pull_box("Recent activities") do -%> -
      - <% @latest_events.each do |event| -%> - <%= render :partial => event, :locals => { :relative_time => true } -%> - <% end -%> -
    - <% end -%> -<% end -%> -
    - - -<% end -%> diff --git a/test/functional/commits_controller_test.rb b/test/functional/commits_controller_test.rb index fac82ea..11c82d4 100644 --- a/test/functional/commits_controller_test.rb +++ b/test/functional/commits_controller_test.rb @@ -123,6 +123,7 @@ class CommitsControllerTest < ActionController::TestCase @repository = @project.repositories.first @repository.update_attribute(:ready, true) @sha = "3fa4e130fa18c92e3030d4accb5d3e0cadd40157" + @weird_id = '!"#$%&\'()+,-.0123456789;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ]_`abcdefghijklmnopqrstuvwxyz{|}' end should "route commits format" do @@ -161,6 +162,40 @@ class CommitsControllerTest < ActionController::TestCase }) end + should "route tags with dots in the id" do + assert_recognizes({ + :controller => "commits", + :action => "show", + :project_id => @project.to_param, + :repository_id => @repository.to_param, + :id => "v0.7.0", + }, {:path => "/#{@project.to_param}/#{@repository.to_param}/commit/v0.7.0", :method => :get}) + assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/v0.7.0", { + :controller => "commits", + :action => "show", + :project_id => @project.to_param, + :repository_id => @repository.to_param, + :id => "v0.7.0", + }) + end + + should "route branches with weird characters in the id" do + assert_recognizes({ + :controller => "commits", + :action => "show", + :project_id => @project.to_param, + :repository_id => @repository.to_param, + :id => @weird_id, + }, {:path => "/#{@project.to_param}/#{@repository.to_param}/commit/#{@weird_id}", :method => :get}) + assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/#{URI.escape(@weird_id, ActionController::Routing::Segment::UNSAFE_PCHAR)}", + :controller => "commits", + :action => "show", + :project_id => @project.to_param, + :repository_id => @repository.to_param, + :id => @weird_id, + }) + end + should "route diff format" do assert_recognizes({ :controller => "commits", @@ -169,14 +204,21 @@ class CommitsControllerTest < ActionController::TestCase :repository_id => @repository.to_param, :id => @sha, :format => "diff", - }, {:path => "/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}.diff", :method => :get}) - assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}.diff", { + }, { + :path => "/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}", + :method => :get + }, { + :format => "diff", + }) + assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}", { :controller => "commits", :action => "show", :project_id => @project.to_param, :repository_id => @repository.to_param, :id => @sha, :format => "diff" + }, {}, { + :format => "diff", }) end @@ -188,14 +230,21 @@ class CommitsControllerTest < ActionController::TestCase :repository_id => @repository.to_param, :id => @sha, :format => "patch", - }, {:path => "/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}.patch", :method => :get}) - assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}.patch", { + }, { + :path => "/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}", + :method => :get + }, { + :format => "patch", + }) + assert_generates("/#{@project.to_param}/#{@repository.to_param}/commit/#{@sha}", { :controller => "commits", :action => "show", :project_id => @project.to_param, :repository_id => @repository.to_param, :id => @sha, :format => "patch" + }, {}, { + :format => "patch", }) end end diff --git a/test/unit/processors/push_event_processor_test.rb b/test/unit/processors/push_event_processor_test.rb index dd9f799..e2a9906 100644 --- a/test/unit/processors/push_event_processor_test.rb +++ b/test/unit/processors/push_event_processor_test.rb @@ -31,6 +31,7 @@ class PushEventProcessorTest < ActiveSupport::TestCase repo = repositories(:johans) repo.update_attribute(:last_pushed_at, nil) @processor.expects(:log_events).returns(true) json = { :gitdir => repo.hashed_path, :username => "johan", @@ -244,6 +245,62 @@ class PushEventProcessorTest < ActiveSupport::TestCase git.stubs(:show).returns(output) @processor.stubs(:git).returns(git) end # describe 'with stubbing towards a live repo' do # before(:each) do diff --git a/vendor/grit/lib/grit/git-ruby.rb b/vendor/grit/lib/grit/git-ruby.rb index c5ba0a0..2cb8507 100644 --- a/vendor/grit/lib/grit/git-ruby.rb +++ b/vendor/grit/lib/grit/git-ruby.rb @@ -12,7 +12,7 @@ module Grit def init(options) if options.size == 0 - Grit::GitRuby::Repository.init(@git_dir) + Grit::GitRuby::Repository.init(@git_dir) else method_missing('init', options) end diff --git a/vendor/plugins/gitorious_sites b/vendor/plugins/gitorious_sites deleted file mode 160000 index 1db91b7..0000000 --- a/vendor/plugins/gitorious_sites +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1db91b7d102354acd9a5d7d32b47f9f76958fd18 diff --git a/vendor/plugins/validates_url_format_of/README.markdown b/vendor/plugins/validates_url_format_of/README.markdown new file mode 100644 index 0000000..b6713d4 --- /dev/null +++ b/vendor/plugins/validates_url_format_of/README.markdown @@ -0,0 +1,54 @@ +# validates\_url\_format\_of + +Rails plugin that provides a `validates_url_format_of` method to `ActiveRecord` models. URLs are validated by regexp. + +Known to be compatible with ActiveRecord 3.0.0.beta, 2.3.5, 2.2.2, 2.1.2. + + +## Usage + +After installing the plugin, it's used like + + class User < ActiveRecord::Base + validates_url_format_of :url, + :allow_nil => true, + :message => 'is completely unacceptable' + end + +Takes the same arguments as [`validates_format_of`](http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M001052) except for the `:with` regexp. + +The default `:message` is different depending on whether the attribute name contains the word "URL". So you will get "Homepage URL does not appear to be valid" but "Homepage does not appear to be a valid URL" without having to customize the `:message`. + +Please note that the regexp used to validate URLs is not perfect, but hopefully good enough. See the test suite. Patches are very welcome. + + +## Limitations and design choices + +Does not handle IPv6. + +By design, the plugin does not allow e.g. "http://localhost" or "http://my.localurl", which are valid URLs but not suitable in most web apps. It also requires a "http://" or "https://" prefix, so just "example.com" is not valid. Fix that in the setter. + + +## Credits and license + +By [Henrik Nyh](http://henrik.nyh.se/) under the MIT license: + +> Copyright (c) 2008 Henrik Nyh +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. diff --git a/vendor/plugins/validates_url_format_of/init.rb b/vendor/plugins/validates_url_format_of/init.rb new file mode 100644 index 0000000..464f8d2 --- /dev/null +++ b/vendor/plugins/validates_url_format_of/init.rb @@ -0,0 +1,31 @@ +module ValidatesUrlFormatOf + IPv4_PART = /\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]/ # 0-255 + REGEXP = %r{ + \A + https?:// # http:// or https:// + ([^\s:@]+:[^\s:@]*@)? # optional username:pw@ + ( (([^\W_]+\.)*xn--)?[^\W_]+([-.][^\W_]+)*\.[a-z]{2,6}\.? | # domain (including Punycode/IDN)... + #{IPv4_PART}(\.#{IPv4_PART}){3} ) # or IPv4 + (:\d{1,5})? # optional port + ([/?]\S*)? # optional /whatever or ?whatever + \Z + }iux + + DEFAULT_MESSAGE = 'does not appear to be a valid URL' + DEFAULT_MESSAGE_URL = 'does not appear to be valid' + + def validates_url_format_of(*attr_names) + options = { :allow_nil => true, + :allow_blank => true, + :with => REGEXP } + options = options.merge(attr_names.pop) if attr_names.last.is_a?(Hash) + + attr_names.each do |attr_name| + message = attr_name.to_s.match(/(_|\b)URL(_|\b)/i) ? DEFAULT_MESSAGE_URL : DEFAULT_MESSAGE + validates_format_of(attr_name, { :message => message }.merge(options)) + end + end + +end + +ActiveRecord::Base.extend(ValidatesUrlFormatOf) diff --git a/vendor/plugins/validates_url_format_of/test/validates_url_format_of_test.rb b/vendor/plugins/validates_url_format_of/test/validates_url_format_of_test.rb new file mode 100644 index 0000000..9c165d4 --- /dev/null +++ b/vendor/plugins/validates_url_format_of/test/validates_url_format_of_test.rb @@ -0,0 +1,117 @@ +require 'rubygems' +require 'test/unit' +require 'active_record' +require "#{File.dirname(__FILE__)}/../init" + +class Model + begin # Rails 3 + include ActiveModel::Validations + rescue NameError # Rails 2.* + # ActiveRecord validations without database + # Thanks to http://www.prestonlee.com/archives/182 + def save() end + def save!() end + def new_record?() false end + def update_attribute() end # Needed by Rails 2.1. + def self.human_name() end + def self.human_attribute_name(_) end + def initialize + @errors = ActiveRecord::Errors.new(self) + def @errors.[](key) # Return errors in same format as Rails 3. + Array(on(key)) + end + end + def self.self_and_descendants_from_active_record() [self] end + def self.self_and_descendents_from_active_record() [self] end # Needed by Rails 2.2. + include ActiveRecord::Validations + end + + extend ValidatesUrlFormatOf + + attr_accessor :homepage + validates_url_format_of :homepage + + attr_accessor :my_UrL_hooray + validates_url_format_of :my_UrL_hooray + + attr_accessor :custom_url + validates_url_format_of :custom_url, :message => 'custom message' +end + +class ValidatesUrlFormatOfTest < Test::Unit::TestCase + + def setup + @model = Model.new + end + + def test_should_allow_valid_urls + [ + 'http://example.com', + 'http://example.com/', + 'http://www.example.com/', + 'http://sub.domain.example.com/', + 'http://bbc.co.uk', + 'http://example.com?foo', + 'http://example.com?url=http://example.com', + 'http://example.com:8000', + 'http://www.sub.example.com/page.html?foo=bar&baz=%23#anchor', + 'http://user:pass@example.com', + 'http://user:@example.com', + 'http://example.com/~user', + 'http://example.xy', # Not a real TLD, but we're fine with anything of 2-6 chars + 'http://example.museum', + 'http://1.0.255.249', + 'http://1.2.3.4:80', + 'HttP://example.com', + 'https://example.com', + 'http://räksmörgås.nu', # IDN + 'http://xn--rksmrgs-5wao1o.nu', # Punycode + 'http://www.xn--rksmrgs-5wao1o.nu', + 'http://foo.bar.xn--rksmrgs-5wao1o.nu', + 'http://example.com.', # Explicit TLD root period + 'http://example.com./foo' + ].each do |url| + @model.homepage = url + @model.valid? + assert @model.errors[:homepage].empty?, "#{url.inspect} should have been accepted" + end + end + + def test_should_reject_invalid_urls + [ + nil, 1, "", " ", "url", + "www.example.com", + "http://ex ample.com", + "http://example.com/foo bar", + 'http://256.0.0.1', + 'http://u:u:u@example.com', + 'http://r?ksmorgas.com', + + # These can all be valid local URLs, but should not be considered valid + # for public consumption. + "http://example", + "http://example.c", + 'http://example.toolongtld' + ].each do |url| + @model.homepage = url + @model.valid? + assert !@model.errors[:homepage].empty?, "#{url.inspect} should have been rejected" + end + end + + def test_different_defaults_based_on_attribute_name + @model.homepage = 'x' + @model.my_UrL_hooray = 'x' + @model.valid? + assert_not_equal ValidatesUrlFormatOf::DEFAULT_MESSAGE, ValidatesUrlFormatOf::DEFAULT_MESSAGE_URL + assert_equal [ValidatesUrlFormatOf::DEFAULT_MESSAGE], @model.errors[:homepage] + assert_equal [ValidatesUrlFormatOf::DEFAULT_MESSAGE_URL], @model.errors[:my_UrL_hooray] + end + + def test_can_override_defaults + @model.custom_url = 'x' + @model.valid? + assert_equal ['custom message'], @model.errors[:custom_url] + end + +end diff --git a/vendor/rails/actionpack/test/fixtures/test/hello_world.erb~ b/vendor/rails/actionpack/test/fixtures/test/hello_world.erb~ deleted file mode 100644 index 21934a1..0000000 --- a/vendor/rails/actionpack/test/fixtures/test/hello_world.erb~ +++ /dev/null @@ -1 +0,0 @@ -Don't pick me! \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.en.html.erb~ b/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.en.html.erb~ deleted file mode 100644 index d009950..0000000 --- a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.en.html.erb~ +++ /dev/null @@ -1 +0,0 @@ -Don't render me! \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.erb~ b/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.erb~ deleted file mode 100644 index d009950..0000000 --- a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.erb~ +++ /dev/null @@ -1 +0,0 @@ -Don't render me! \ No newline at end of file diff --git a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.html.erb~ b/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.html.erb~ deleted file mode 100644 index d009950..0000000 --- a/vendor/rails/actionpack/test/fixtures/test/malformed/malformed.html.erb~ +++ /dev/null @@ -1 +0,0 @@ -Don't render me! \ No newline at end of file diff --git a/vendor/ruby-git/lib/git/base.rb b/vendor/ruby-git/lib/git/base.rb index 5082b8f..00e0232 100644 --- a/vendor/ruby-git/lib/git/base.rb +++ b/vendor/ruby-git/lib/git/base.rb @@ -41,7 +41,7 @@ module Git # run git_init there Git::Lib.new(git_options).init - self.new(git_options) + self.new(git_options) end # clones a git repository locally @@ -70,7 +70,7 @@ module Git @working_directory = Git::WorkingDirectory.new(options[:working_directory]) if options[:working_directory] @repository = Git::Repository.new(options[:repository]) if options[:repository] - @index = Git::Index.new(options[:index], false) if options[:index] + @index = Git::Index.new(options[:index], false) if options[:index] end @@ -429,4 +429,4 @@ module Git end -end \ No newline at end of file +end