Merge pull request #924 from warpc/907-project-tags-page

[refs #907]: project tags and branchs pages, new look of projects submenu in main project page.
This commit is contained in:
Vladimir Sharshov 2013-02-13 10:50:21 -08:00
commit 7e165b8ba4
17 changed files with 233 additions and 86 deletions

View File

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

View File

@ -28,16 +28,77 @@ header menu ul li a {
padding: 15px 8px 15px 8px;
}
div.description-top input.name {
width: 350px;
padding: 0;
#description-top {
height: auto;
input.name {
width: 350px;
padding: 0;
margin: 8px 0 20px 10px;
}
div.name input {
width: 100%;
height: 100%;
}
.git_help {
float: left;
margin-top: 11px;
margin-left: 10px;
font-size: 11px;
color: green;
cursor: pointer;
}
.project-tabnav .tabnav-tabs {
height: 26px;
margin: 0;
li {
display: inline-block;
padding: 5px 10px 0;
height: 21px;
margin: 0 5px;
border: 1px solid #a9c6dd;
border-bottom: none;
float: left;
}
li.selected {
background-color: #FFF;
}
.tags { float: right; }
}
}
div.description-top div.name input {
#project-branches {
width: 100%;
height: 100%;
border-spacing: 0;
margin-top: 20px;
tr.base {
background: #dcecfa;
}
td.actions ul {
float: right;
}
.text { font-size: 12px; }
td {
border-bottom: 1px solid #a9c6dd;
padding: 0 20px;
}
.name {
font-weight: bold;
}
}
#project-tags {
.release-list {
padding: 0;
li {
list-style-type: none;
border-bottom: 1px solid #a9c6dd;
padding: 10px 20px;
}
}
.name { font-weight: bold; }
.detail-link {
margin: 0 5px;
float: right;
}
}
article div.activity, .commits_activity {
border: 1px solid #D6D6D6;
border-radius: 5px 5px 5px 5px;
@ -832,22 +893,10 @@ textarea.placeholder {
color: #CFCFCF;
}
div.description-top div.git_help {
float: left;
margin-top: 11px;
margin-left: 10px;
font-size: 11px;
color: green;
cursor: pointer;
}
div#git_help_data {
#git_help_data {
display: none;
padding-left: 10px;
}
div#git_help_data p {
padding-bottom: 5px;
p { padding-bottom: 5px; }
}
// for bootstrap
@ -885,7 +934,7 @@ div#git_help_data p {
.zip {
float: left;
padding-left: 5px;
margin-top: 6px;
margin: 9px 0 0;
list-style: none;
}

View File

@ -1428,7 +1428,7 @@ h3.bmargin10 {
padding-top: 9px;
}
div.description-top {
#description-top {
background: #dcecfa;
font-size: 12px;
color: #292929;
@ -1436,49 +1436,50 @@ div.description-top {
height: 38px;
width: 100%;
margin-bottom: 20px;
div.img {
float: left;
padding-left: 10px;
margin-top: 14px;
}
input.name {
float: left;
margin-top: 5px;
margin-left: 10px;
background: #FFF;
border: 1px solid #d1deeb;
height: 21px;
width: auto;
color: #292929;
font-size: 11px;
width: 415px;
padding: 2px 5px 3px;
}
div.role {
float: left;
margin-top: 11px;
margin-left: 10px;
font-size: 11px;
}
div.fork {
margin-right: 10px;
}
}
div.description-top div.img {
float: left;
padding-left: 10px;
margin-top: 14px;
}
div.description-top input.name {
float: left;
margin-top: 5px;
margin-left: 10px;
background: #FFF;
border: 1px solid #d1deeb;
height: 21px;
width: auto;
color: #292929;
font-size: 11px;
width: 415px;
padding: 2px 5px 3px;
}
div.description-top div.role {
float: left;
margin-top: 11px;
margin-left: 10px;
font-size: 11px;
}
div.fork {
div.fork {
float: right;
margin-top: 5px;
}
div.fork p {
div.fork p {
float: right;
margin-top: 5px;
margin-right: 2px;
}
div.description-top div.fork {
margin-right: 10px;
}
.all div.description {
text-align: left;
}

View File

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

View File

@ -24,4 +24,14 @@ class Projects::Git::TreesController < Projects::Git::BaseController
send_file file.path, :disposition => 'attachment', :type => "application/#{format == 'zip' ? 'zip' : 'x-tar-gz'}", :filename => fullname
end
def tags
@tags = @project.repo.tags.select{ |t| t.commit }.sort_by(&:name)
render 'refs'
end
def branches
@branches = @project.repo.branches.sort_by(&:name).select{ |b| b.name != @branch.name }.unshift(@branch)
render 'refs'
end
end

View File

@ -1,4 +1,5 @@
.description-top
- act = action_name.to_sym; contr = controller_name.to_sym; treeish = project.default_head(params[:treeish])
#description-top
-if @commit
%ul.nav.zip
%li#menu-archive.dropdown
@ -13,7 +14,8 @@
= text_field_tag :url, git_repo_url(project.git_repo_name), :class => 'name', :spellcheck => 'false', :readonly => true
.git_help ?
.role= can?(:write, project) ? t("layout.read_write_access") : t("layout.read_access")
= render 'branch_select', :project => project
= render 'branch_select', :project => project if act != :tags
.both
#git_help_data
%p= t("layout.projects.git_help.cloning") + ":"
%p
@ -25,6 +27,16 @@
%p~ "git remote add #{project.name} #{git_repo_url(project.git_repo_name)}"
%p~ "git fetch #{project.name}"
%p~ "git checkout -b my-local-tracking-branch #{project.name}/master_or_other_branch"
.project-tabnav
%ul.tabnav-tabs
%li{:class => ('selected' if act == :show && contr == :trees )}
= link_to t('project_menu.files'), tree_path(project, treeish)
%li{:class => ('selected' if act == :index && contr == :commits )}
= link_to t('project_menu.commits'), commits_path(project, treeish)
%li{:class => ('selected' if act == :branches && contr == :trees )}
= link_to t('project_menu.branches', :count => project.repo.branches.count), branches_path(project, treeish)
%li.tags{:class => ('selected' if act == :tags && contr == :trees )}
= link_to t('project_menu.tags', :count => project.repo.tags.count), tags_path(project)
.both
:javascript
$(document).ready(function() {

View File

@ -5,8 +5,7 @@
.table-sort-right=@project.name
%nav
%ul
%li= link_to t("project_menu.project"), tree_path(@project, treeish), :class => (act.in?([:show, :edit]) && contr.in?([:trees, :blobs]) ? 'active' : nil)
%li= link_to t("project_menu.commits"), commits_path(@project, treeish), :class => (act.in?([:index, :show]) && contr == :commits ? 'active' : nil)
%li= link_to t("project_menu.code"), tree_path(@project, treeish), :class => (act.in?([:show, :edit, :branches, :tags]) && contr.in?([:trees, :blobs]) || contr == :commits ? 'active' : nil)
- if @project.is_package and can?(:read, @project => BuildList)
%li= link_to t("project_menu.builds"), project_build_lists_path(@project), :class => (contr == :build_lists ? 'active' : nil)
- if @project.has_issues

View File

@ -0,0 +1,10 @@
- base = branch.name == @branch.name
%tr{:class => (base ? 'base' : '')}
%td.name= link_to branch.name, tree_path(@project, branch.name)
%td.actions
%ul.actions
- if base
%li.text= t('layout.projects.base_branch')
- else
%li
= link_to t('layout.projects.compare'), diff_path(@project, "#{@branch.name}...#{branch.name}")

View File

@ -0,0 +1,10 @@
- subjects_name = action_name.to_s
%h3= t("layout.projects.#{subjects_name}")
- if subject.empty?
%p= t("layout.projects.no_#{subjects_name}")
- elsif subject.count == 1
%p= t("layout.projects.showing_#{subjects_name.singularize}")
- else
%p= t("layout.projects.showing_#{subjects_name}", :count => subject.count)
.both

View File

@ -0,0 +1,6 @@
%li
= link_to t('layout.projects.browse_code'), tree_path(@project, tag.name), :class => 'detail-link'
- file_name = "#{@project.owner.uname}-#{@project.name}-#{tag.name}"
- %w(zip tar.gz).each do |type|
= link_to t('layout.projects.source_code', :type => type), archive_path(@project, file_name, type), :class => 'detail-link'
%p.name~ "#{tag.name} (#{tag.commit.authored_date.strftime('%d.%m.%Y')})"

View File

@ -0,0 +1,14 @@
-set_meta_tags :title => "#{title_object @project}"
= render 'submenu'
= render 'repo_block', :project => @project
= render 'header', :subject => (@branches || @tags)
- if @tags.present?
%div#project-tags
%ol.release-list
= render :partial => 'tag', :collection => @tags
- elsif @branches.present?
%table#project-branches
%tbody
= render :partial => 'branch', :collection => @branches

View File

@ -22,8 +22,11 @@ en:
developer_api_url: http://abf-doc.rosalinux.ru
abf-ideas: ABF Ideas
project_menu:
project: Project
code: Code
files: Files
commits: Commits
branches: Branches (%{count})
tags: Tags (%{count})
builds: Builds
tracker: Tracker
wiki: Wiki

View File

@ -22,8 +22,11 @@ ru:
developer_api_url: http://abf-doc.rosalinux.ru
abf-ideas: Идеи для ABF
project_menu:
project: Проект
code: Код
files: Файлы
commits: Коммиты
branches: Ветки (%{count})
tags: Теги (%{count})
builds: Сборки
tracker: Трекер
wiki: Wiki

View File

@ -1,6 +1,18 @@
en:
layout:
projects:
branches: Branches
showing_branches: Showing %{count} branches
showing_branch: Showing 1 branch
no_branches: No branches
base_branch: Base branch
compare: Compare
browse_code: Browse code
source_code: Source code (%{type})
tags: Tags
showing_tags: Showing %{count} tags
showing_tag: Showing 1 tag
no_tags: No tags
add: Add
public_projects_list: Public projects list
edit: Settings

View File

@ -1,6 +1,18 @@
ru:
layout:
projects:
branches: Ветки
showing_branches: Показано %{count} веток
showing_branch: Показана 1 ветка
no_branch: Нет веток
base_branch: Текущая ветка
compare: Сравнить
browse_code: Просмотреть код
source_code: Исходный код (%{type})
tags: Теги
showing_tags: Показано %{count} тегов
showing_tag: Показан 1 тег
no_tags: Нет тегов
add: Добавить
public_projects_list: Список публичных проектов
edit: Настройки

View File

@ -289,6 +289,10 @@ Rosa::Application.routes.draw do
# Tree
get '/' => "git/trees#show", :as => :project
get '/tree/:treeish(/*path)' => "git/trees#show", :as => :tree, :format => false
# Tags
get '/tags' => "git/trees#tags", :as => :tags
# Branches
get '/branches/:treeish' => "git/trees#branches", :as => :branches
# Commits
get '/commits/:treeish(/*path)' => "git/commits#index", :as => :commits, :format => false
get '/commit/:id(.:format)' => "git/commits#show", :as => :commit

View File

@ -11,52 +11,61 @@ describe Projects::Git::TreesController do
@params = { :owner_name => @project.owner.uname,
:project_name => @project.name,
:treeish => "#{@project.owner.uname}-#{@project.name}-master"}
fill_project @project
end
context 'for guest' do
it 'should be able to perform archive action with anonymous acccess', :anonymous_access => true do
[:tags, :branches].each do |action|
it "should be able to perform #{action} action with anonymous acccess", :anonymous_access => true do
get action, @params.merge(:treeish => 'master')
response.should be_success
end
it "should not be able to perform #{action} action without anonymous acccess", :anonymous_access => false do
get action, @params.merge(:treeish => 'master')
response.should_not be_success
end
end
it "should be able to perform archive action with anonymous acccess", :anonymous_access => true do
stub(controller).render
fill_project @project
get :archive, @params.merge(:format => 'tar.gz')
response.should be_success
end
it 'should not be able to perform archive action without anonymous acccess', :anonymous_access => false do
fill_project @project
it "should not be able to perform archive action without anonymous acccess", :anonymous_access => false do
get :archive, @params.merge(:format => 'tar.gz')
response.code.should == '401'
end
end
context 'for other user' do
before { set_session_for FactoryGirl.create(:user) }
it 'should not be able to archive empty project' do
@user = FactoryGirl.create(:user)
set_session_for(@user)
%x(rm -rf #{@project.path})
expect { get :archive, @params.merge(:format => 'tar.gz') }.to raise_error(ActionController::RoutingError)
end
it 'should not be able to injection code with format' do
fill_project @project
@user = FactoryGirl.create(:user)
set_session_for(@user)
expect { get :archive, @params.merge(:format => "tar.gz master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError)
end
it 'should not be able to injection code with treeish' do
fill_project @project
@user = FactoryGirl.create(:user)
set_session_for(@user)
expect { get :archive, @params.merge(:treeish => "master > /dev/null; echo 'I am hacker!';\#") }.to raise_error(ActionController::RoutingError)
end
it 'should be able to perform archive action' do
stub(controller).render
fill_project @project
@user = FactoryGirl.create(:user)
set_session_for(@user)
get :archive, @params.merge(:format => 'tar.gz')
response.should be_success
end
[:tags, :branches].each do |action|
it "should be able to perform #{action} action" do
get action, @params.merge(:treeish => 'master')
response.should be_success
end
end
end
after(:all) {clean_projects_dir}