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:
commit
7e165b8ba4
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}")
|
|
@ -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
|
|
@ -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')})"
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: Настройки
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
Loading…
Reference in New Issue