[refs #2106] Add personal repos to project. Some changes of private repos controller and logic

This commit is contained in:
konstantin.grabar 2011-10-27 17:04:03 +04:00
parent 7fd5f3484f
commit 3114eb1ecc
26 changed files with 371 additions and 16 deletions

View File

@ -0,0 +1,67 @@
class PersonalRepositoriesController < ApplicationController
before_filter :authenticate_user!
before_filter :find_repository#, :only => [:show, :destroy, :add_project, :remove_project, :make_private, :settings]
def show
if params[:query]
@projects = @repository.projects.recent.by_name(params[:query]).paginate :page => params[:project_page], :per_page => 30
else
@projects = @repository.projects.recent.paginate :page => params[:project_page], :per_page => 30
end
end
#TODO: Add git repo move into private repos path.
def change_visibility
@repository.change_visibility
redirect_to settings_personal_repository_path(@repository)
end
def settings
if @repository.hidden?
@urmpi_command = "urpmi -add http://login@password:#{ request.host }/privates/#{ @repository.platform.name }/main/"
else
@urmpi_command = "urpmi -add http://#{ request.host }/downloads/#{ @repository.platform.name }/main/"
end
end
def add_project
if params[:project_id]
@project = Project.find(params[:project_id])
params[:project_id] = nil
unless @repository.projects.include? @project
@repository.projects << @project
flash[:notice] = t('flash.repository.project_added')
redirect_to platform_repository_path(@repository.platform, @repository)
else
flash[:error] = t('flash.repository.project_not_added')
redirect_to url_for(:action => :add_project)
end
else
@projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page])
render 'projects_list'
end
end
def remove_project
if params[:project_id]
@project = Project.find(params[:project_id])
params[:project_id] = nil
if @repository.projects.include? @project
@repository.projects.delete @project
flash[:notice] = t('flash.repository.project_removed')
redirect_to platform_repository_path(@repository.platform, @repository)
else
redirect_to url_for(:action => :remove_project)
end
else
redirect_to platform_repository_path(@repository.platform, @repository)
end
end
protected
def find_repository
@repository = Repository.find(params[:id])
end
end

View File

@ -7,7 +7,7 @@ class PrivateUsersController < ApplicationController
end
def create
pair = PrivateUser.generate_pair(params[:platform_id])
pair = PrivateUser.generate_pair(params[:platform_id], current_user.id)
redirect_to platform_private_users_path(params[:platform_id]),
:notice => "Логин: #{ pair[:login] } Пароль: #{ pair[:pass] }"
end

View File

@ -7,7 +7,7 @@ class PrivatesController < ApplicationController
before_filter :authenticate
def show
file_name = "#{APP_CONFIG['private_repo_path']}/#{params[:platform_name]}/#{params[:file_path]}"
file_name = "#{APP_CONFIG['root_path']}/platforms/#{params[:platform_name]}/repository/#{params[:file_path]}"
if File.directory?(file_name) || !File.exists?(file_name)
render :file => "#{Rails.root}/public/404.html", :layout => false, :status => 404

View File

@ -58,7 +58,7 @@ class RepositoriesController < ApplicationController
redirect_to url_for(:action => :add_project)
end
else
@projects = (Project.all - @repository.projects).paginate(:page => params[:project_page])
@projects = Project.addable_to_repository(@repository.id).paginate(:page => params[:project_page])
render 'projects_list'
end
end

View File

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

View File

@ -6,6 +6,10 @@ class Group < ActiveRecord::Base
validates :name, :uname, :owner_id, :presence => true
validates :name, :uname, :uniqueness => true
validates :uname, :format => { :with => /^[a-zA-Z0-9_]+$/ }, :allow_nil => false, :allow_blank => false
#TODO: Replace this simple cross-table uniq validation by more progressive analog
validate lambda {
errors.add(:uname, I18n.t('flash.group.user_uname_exists')) if User.exists? :uname => uname
}
belongs_to :global_role, :class_name => 'Role'
@ -20,6 +24,8 @@ class Group < ActiveRecord::Base
has_many :platforms, :through => :targets, :source => :target, :source_type => 'Platform', :autosave => true
has_many :repositories, :through => :targets, :source => :target, :source_type => 'Repository', :autosave => true
include PersonalRepository
before_save :create_dir
after_destroy :remove_dir

View File

@ -0,0 +1,34 @@
module PersonalRepository
extend ActiveSupport::Concern
included do
after_create :create_personal_repository
end
module InstanceMethods
def create_personal_repository
pl = platforms.build
pl.owner = self
pl.name = "#{self.uname}_personal"
pl.unixname = "#{self.uname}_personal"
pl.platform_type = 'personal'
pl.save
rep = pl.repositories.build
rep.owner = pl.owner
rep.name = 'main'
rep.unixname = 'main'
rep.visibility = 'open'
rep.save
end
def personal_platform
platforms.personal.first
end
def personal_repository
personal_platform.repositories.first
end
end
end

View File

@ -17,7 +17,7 @@ class Platform < ActiveRecord::Base
validates :unixname, :uniqueness => true, :presence => true, :format => { :with => /^[a-zA-Z0-9_]+$/ }, :allow_nil => false, :allow_blank => false
#after_create :make_owner_rel
before_save :create_directory
# before_save :create_directory
before_save :make_owner_rel
after_destroy :remove_directory
# before_create :xml_rpc_create
@ -25,9 +25,10 @@ class Platform < ActiveRecord::Base
# before_update :check_freezing
scope :by_visibilities, lambda {|v| {:conditions => ['visibility in (?)', v.join(',')]}}
scope :main, where(:platform_type => 'main')
scope :personal, where(:platform_type => 'personal')
attr_accessible :visibility
scope :main, where(:platform_type => 'main')
def path
build_path(unixname)

View File

@ -2,17 +2,25 @@ class PrivateUser < ActiveRecord::Base
require 'digest/sha2'
require 'active_support/secure_random'
belongs_to :platform
belongs_to :user
validate :login, :uniqueness => true
class << self
def generate_pair(platform_id)
def can_generate_more?(user_id)
!PrivateUser.exists?(:user_id => user_id)
end
def generate_pair(platform_id, user_id)
login = "login_#{ActiveSupport::SecureRandom.hex(16)}"
pass = "pass_#{ActiveSupport::SecureRandom.hex(16)}"
PrivateUser.create(
:login => login,
:password => Digest::SHA2.new.hexdigest(pass),
:platform_id => platform_id
:platform_id => platform_id,
:user_id => user_id
)
{:login => login, :pass => pass}

View File

@ -26,13 +26,22 @@ class Project < ActiveRecord::Base
scope :by_visibilities, lambda {|v| {:conditions => ['visibility in (?)', v.join(',')]}}
scope :addable_to_repository, lambda { |repository_id| where("projects.id NOT IN (SELECT project_to_repositories.project_id FROM project_to_repositories WHERE (project_to_repositories.repository_id != #{ repository_id }))") }
before_save :create_directory#, :create_git_repo
before_save :make_owner_rel
after_destroy :remove_directory
after_create :attach_to_personal_repository
# before_create :xml_rpc_create
# before_destroy :xml_rpc_destroy
attr_accessible :visibility
def attach_to_personal_repository
repositories << self.owner.personal_repository if !repositories.exists?(:id => self.owner.personal_repository)
end
def project_versions
self.git_repository.tags
end

View File

@ -1,5 +1,7 @@
class Repository < ActiveRecord::Base
DOWNLOADS_PATH = RAILS_ROOT + '/public/downloads'
VISIBILITIES = ['open', 'hidden']
relationable :as => :target
@ -20,19 +22,27 @@ class Repository < ActiveRecord::Base
scope :recent, order("name ASC")
scope :by_visibilities, lambda {|v| {:conditions => ['visibility in (?)', v.join(',')]}}
before_save :create_directory
#before_save :create_directory
before_save :make_owner_rel
after_destroy :remove_directory
#after_destroy :remove_directory
# before_create :xml_rpc_create
# before_destroy :xml_rpc_destroy
after_create lambda {
add_downloads_symlink unless self.hidden?
}
attr_accessible :visibility
def path
build_path(unixname)
end
def hidden?
self.visibility == 'hidden'
end
def clone
r = Repository.new
r.name = name
@ -41,6 +51,16 @@ class Repository < ActiveRecord::Base
return r
end
def change_visibility
if !self.hidden?
self.update_attribute(:visibility, 'hidden')
remove_downloads_symlink
else
self.update_attribute(:visibility, 'open')
add_downloads_symlink
end
end
protected
def make_owner_rel
@ -89,4 +109,18 @@ class Repository < ActiveRecord::Base
end
end
def symlink_downloads_path
"#{ DOWNLOADS_PATH }/#{ self.platform.unixname }"
end
def add_downloads_symlink
raise "Personal platform path #{ symlink_downloads_path } already exists!" if File.exists?(symlink_downloads_path) && File.directory?(symlink_downloads_path)
FileUtils.symlink platform.path, symlink_downloads_path
end
def remove_downloads_symlink
raise "Personal platform path #{ symlink_downloads_path } does not exists!" if !(File.exists?(symlink_downloads_path) && File.directory?(symlink_downloads_path))
FileUtils.rm_rf symlink_downloads_path
end
end

View File

@ -20,7 +20,13 @@ class User < ActiveRecord::Base
has_many :platforms, :through => :targets, :source => :target, :source_type => 'Platform', :autosave => true
has_many :repositories, :through => :targets, :source => :target, :source_type => 'Repository', :autosave => true
include PersonalRepository
validates :uname, :presence => true, :uniqueness => {:case_sensitive => false}, :format => { :with => /^[a-zA-Z0-9_]+$/ }, :allow_nil => false, :allow_blank => false
#TODO: Replace this simple cross-table uniq validation by more progressive analog
validate lambda {
errors.add(:uname, I18n.t('flash.user.group_uname_exists')) if Group.exists? :uname => uname
}
attr_accessible :email, :password, :password_confirmation, :remember_me, :login, :name, :ssh_key, :uname
attr_readonly :uname

View File

@ -37,6 +37,9 @@
-if current_user.can_perform?('roles', 'index')
%li{:class => controller.controller_path == 'roles' ? 'active' : '' }
%a{:href => roles_path}= t("layout.menu.roles")
-if current_user.can_perform?('personal_repositories', 'index')
%li{:class => controller.controller_path == 'personal_repositories' ? 'active' : '' }
%a{:href => personal_repository_path( current_user.personal_repository.id )}= t("layout.menu.personal_repository")
#wrapper.wat-cf
= render :partial => "layouts/flashes"
#main

View File

@ -0,0 +1,11 @@
%table.table
%tr
%th.first= t("activerecord.attributes.project.name")
%th.last &nbsp;
- @projects.each do |project|
%tr{:class => cycle("odd", "even")}
%td
= link_to project.owner.name + '/' + project.name, project_path(project)
%td.last
#{link_to t("layout.show"), project_path(project)} | #{link_to t("layout.add"), url_for(:controller => :personal_repositories, :action => :add_project, :project_id => project.id)}

View File

@ -0,0 +1,10 @@
%table.table
%tr
%th.first= t("activerecord.attributes.project.name")
%th.last &nbsp;
- @projects.each do |project|
%tr{:class => cycle("odd", "even")}
%td
= link_to project.name, project_path(project)
%td.last
#{link_to t("layout.show"), project_path(project)} | #{link_to t("layout.delete"), url_for (:action => :remove_project, :project_id => project.id), :confirm => t("layout.projects.confirm_delete")}

View File

@ -0,0 +1,59 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first.active= link_to t("layout.personal_repositories.show"), personal_repository_path(@repository)
%li= link_to t("layout.personal_repositories.settings"), settings_personal_repository_path(@repository)
.content
.inner
%p
%b
= t("activerecord.attributes.repository.name")
\:
= @repository.name
%p
%b
= t("activerecord.attributes.repository.unixname")
\:
= @repository.unixname
%p
%b
= t("activerecord.attributes.repository.platform")
\:
= link_to @repository.platform.name, url_for(@repository.platform)
%p
%b
= t("activerecord.attributes.repository.owner")
\:
= link_to @repository.owner.name, url_for(@repository.owner)
%p
%b
= t("activerecord.attributes.repository.visibility")
\:
= @repository.visibility
%p
%b
= t("activerecord.attributes.repository.platform")
\:
= link_to @repository.platform.name, platform_path(@platform)
.wat-cf
= link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), @repository_path, :method => "delete", :class => "button", :confirm => t("layout.repositories.confirm_delete")
%a{ :name => "projects" }
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.projects.list"), personal_repository_path(@repository) + "#projects"
%li.active= link_to t("layout.projects.add"), add_project_personal_repository_path(@repository)
.content
%h2.title
= t("layout.projects.list_header")
.inner
= render :partial => 'shared/search_form'
= render :partial => 'proj_list', :object => @projects
.actions-bar.wat-cf
.actions
= will_paginate @projects, :param_name => :project_page
-# content_for :sidebar, render(:partial => 'sidebar')

View File

@ -0,0 +1,20 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first= link_to t("layout.personal_repositories.show"), personal_repository_path(@repository)
%li.active= link_to t("layout.personal_repositories.settings"), settings_personal_repository_path(@repository)
.content
%h2.title= t("layout.personal_repositories.settings_header")
.inner
.group
%span= t("activerecord.attributes.repository.visibility") + ":"
%span
%i= t("activerecord.attributes.repository.visibility_types.#{ @repository.visibility }")
.group
= link_to t("layout.personal_repositories.change_visibility_from_#{ @repository.visibility }"), change_visibility_personal_repository_path(@repository)
%br
.group
%span= @urmpi_command
-# content_for :sidebar, render(:partial => 'sidebar')

View File

@ -0,0 +1,33 @@
.block
.secondary-navigation
%ul.wat-cf
%li.first.active= link_to t("layout.personal_repositories.show"), personal_repository_path(@repository)
%li= link_to t("layout.personal_repositories.settings"), settings_personal_repository_path(@repository)
.content
.inner
%p
%b
= t("activerecord.attributes.repository.owner")
\:
= link_to @repository.owner.name, url_for(@repository.owner)
.wat-cf
=# link_to image_tag("web-app-theme/icons/cross.png", :alt => t("layout.delete")) + " " + t("layout.delete"), @repository_path, :method => "delete", :class => "button", :confirm => t("layout.repositories.confirm_delete")
%a{ :name => "projects" }
.block
.secondary-navigation
%ul.wat-cf
%li.first.active= link_to t("layout.projects.list"), personal_repository_path(@repository) + "#projects"
%li= link_to t("layout.projects.add"), add_project_personal_repository_path(@repository)
.content
%h2.title
= t("layout.projects.list_header")
.inner
= render :partial => 'shared/search_form'
= render :partial => 'proj_list1', :object => @projects
.actions-bar.wat-cf
.actions
= will_paginate @projects, :param_name => :project_page
-# content_for :sidebar, render(:partial => 'sidebar')

View File

@ -23,7 +23,7 @@
.secondary-navigation
%ul.wat-cf
%li.first.active= link_to t("layout.private_users.list"), platform_private_users_path(@platform)
%li= link_to t("layout.private_users.new"), platform_private_users_path(@platform), :method => :post
%li= link_to t("layout.private_users.new"), platform_private_users_path(@platform), :method => :post if PrivateUser.can_generate_more?(current_user.id)
.content
%h2.title
= t("layout.private_users.list_header")

View File

@ -48,6 +48,7 @@ ru:
rights: Права
roles: Роли
users: Пользователи
personal_repository: Мой репозиторий
sessions:
sign_in_header: Вход в систему
@ -123,6 +124,13 @@ ru:
confirm_delete: Вы уверены, что хотите удалить этот репозиторий?
current_repository_header: Текущий репозиторий
personal_repositories:
settings_header: Настройки
change_visibility_from_hidden: Сменить статус на "Открытый"
change_visibility_from_open: Сменить статус на "Приватный"
settings: Настройки
show: Мой репозиторий
products:
list: Список
new: Новый продукт
@ -255,11 +263,13 @@ ru:
saved: Пользователь успешно сохранен
save_error: Не удалось сохранить данные о пользователе
destroyed: Учетная запись успешно удалена
group_uname_exists: Группа с таким именем уже зарегестрирована
group:
saved: Группа успешно сохранена
save_error: Не удалось сохранить группу
destroyed: Группа успешно удалена
user_uname_exists: Пользователь с таким именем уже зарегестрирован
repository:
saved: Репозиторий успешно добавлен
@ -335,6 +345,11 @@ ru:
unixname: Unixname
created_at: Создан
updated_at: Обновлен
owner: Владелец
visibility: Статус
visibility_types:
open: Открытый
hidden: Закрытый
product:
name: Название

View File

@ -2,7 +2,6 @@ Rosa::Application.routes.draw do
# XML RPC
match 'api/xmlrpc' => 'rpc#xe_index'
devise_for :users, :controllers => {:omniauth_callbacks => 'users/omniauth_callbacks'} do
get '/users/auth/:provider' => 'users/omniauth_callbacks#passthru'
end
@ -31,6 +30,15 @@ Rosa::Application.routes.draw do
match 'build_lists/' => 'build_lists#all', :as => :all_build_lists
match 'build_lists/:id/cancel/' => 'build_lists#cancel', :as => :build_list_cancel
resources :personal_repositories, :only => [:show] do
member do
get :settings
get :change_visibility
get :add_project
get :remove_project
end
end
resources :platforms do
resources :private_users

View File

@ -0,0 +1,9 @@
class AddUserIdToPrivateUsers < ActiveRecord::Migration
def self.up
add_column :private_users, :user_id, :integer
end
def self.down
remove_column :private_users, :user_id
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20111026135125) do
ActiveRecord::Schema.define(:version => 20111026152530) do
create_table "arches", :force => true do |t|
t.string "name", :null => false
@ -158,6 +158,7 @@ ActiveRecord::Schema.define(:version => 20111026135125) do
t.string "password"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_id"
end
create_table "products", :force => true do |t|
@ -207,7 +208,6 @@ ActiveRecord::Schema.define(:version => 20111026135125) do
t.string "object_type"
t.integer "target_id"
t.string "target_type"
t.integer "role_id"
t.datetime "created_at"
t.datetime "updated_at"
end
@ -268,9 +268,8 @@ ActiveRecord::Schema.define(:version => 20111026135125) do
t.datetime "remember_created_at"
t.datetime "created_at"
t.datetime "updated_at"
t.string "uname"
t.text "ssh_key"
t.integer "role_id"
t.string "uname"
t.integer "global_role_id"
end

View File

@ -0,0 +1 @@
Hello! This is a directory to share open repositories within.

View File

@ -0,0 +1,5 @@
require 'spec_helper'
describe PersonalRepositoriesController do
end

View File

@ -0,0 +1,15 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the PersonalRepositoriesHelper. For example:
#
# describe PersonalRepositoriesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe PersonalRepositoriesHelper do
pending "add some examples to (or delete) #{__FILE__}"
end