diff --git a/.gitignore b/.gitignore index f98b0074d..7f6472601 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,7 @@ crash.log config/newrelic.yml config/deploy/*.rb config/deploy.rb -*.swo +.swo +.swn +.ruby-gemset +.ruby-version \ No newline at end of file diff --git a/app/assets/images/bg_blue.png b/app/assets/images/bg_blue.png new file mode 100644 index 000000000..ecfede360 Binary files /dev/null and b/app/assets/images/bg_blue.png differ diff --git a/app/assets/images/profile-hover.png b/app/assets/images/profile-hover.png new file mode 100644 index 000000000..09427d06b Binary files /dev/null and b/app/assets/images/profile-hover.png differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 1672028d3..9946b1663 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -100,4 +100,33 @@ $(document).ready(function() { updateTime(); setInterval( updateTime, 15000 ); + + window.updatePagination = function(link) { + var page = parseInt($('.pagination .current').text()); + if (link.hasClass('next_page')) { + page += 1; + } else { + if (link.hasClass('previous_page')) { + page -= 1; + } else { + page = link.text(); + } + } + $('.pagination .current').html(page); + }; + + window.isSearchUser = null; + window.search_items = function(path, fdata, dom) { + if (window.isSearchUser != null) { window.isSearchUser.abort(); } + window.isSearchUser = $.ajax({ + type: 'GET', + url: path, + data: fdata, + success: function(data) { + dom.html(data); + updateTime(); + } + }); + return false; + } }); diff --git a/app/assets/javascripts/extra/profile.js b/app/assets/javascripts/extra/profile.js new file mode 100644 index 000000000..668632151 --- /dev/null +++ b/app/assets/javascripts/extra/profile.js @@ -0,0 +1,43 @@ +$(document).ready(function() { + var profile_table = $('.profile-table'); + var profile_path = $('#profile_path').text(); + var profile_vis_buttons = $('.profile-content .span12.sub-menu nav a'); + var profile_search_field = $('.profile-content .search #query_projects'); + + var load_profile_projects = function (page_number) { + var visibility = $('.profile-content .span12.sub-menu nav a.active').hasClass('public-projects') ? 'open' : 'hidden'; + var search = profile_search_field.val(); + page = page_number || $('.pagination .current').text(); + $.ajax({ + type: 'GET', + url: profile_path, + data: {visibility: visibility, search: search, page: page}, + success: function(data){ + profile_table.html(data); + updateTime(); + }, + error: function(data){ + alert('error') // TODO remove + } + }); + return false; + } + + profile_vis_buttons.live('click', function () { + profile_vis_buttons.toggleClass('active'); + return load_profile_projects(); + }); + + $(document).on('click','.profile-table .pagination a', function(){ + updatePagination($(this)); + return load_profile_projects(); + }); + + $('#query_projects').on('keyup', function() { + var visibility = $('.profile-content .span12.sub-menu nav a.active').hasClass('public-projects') ? 'open' : 'hidden'; + var search = profile_search_field.val(); + data = {visibility: visibility, search: search}; + return search_items(profile_path, data, profile_table); + }); +}); + diff --git a/app/assets/javascripts/extra/tracker.js b/app/assets/javascripts/extra/tracker.js index 94fe055d7..072e1fcdf 100644 --- a/app/assets/javascripts/extra/tracker.js +++ b/app/assets/javascripts/extra/tracker.js @@ -18,18 +18,7 @@ $(document).ready(function() { }); $("#table1.issues-table .pagination a").live('click', function() { - var a = $(this); - var page = parseInt($('.pagination .current').text()); - if (a.hasClass('next_page')) { - page += 1; - } else { - if (a.hasClass('previous_page')) { - page -= 1; - } else { - page = a.text(); - } - } - $('.pagination .current').html(page); + updatePagination($(this)); return send_index_tracker_request('GET'); }); @@ -116,18 +105,11 @@ $(document).ready(function() { return false; }; - var isSearchUser = null; $('#search_user').on('keyup', function() { - if (isSearchUser != null) { isSearchUser.abort(); } - isSearchUser = $.ajax({ - type: 'GET', - url: $('#search_user_path').attr('path'), - data: $(this).serialize(), - success: function(data){ - $('#manage_issue_users_list').html(data); - } - }); - return false; + path = $('#search_user_path').attr('path'); + data = $(this).serialize(); + dom = $('#manage_issue_users_list'); + return search_items(path, data, dom); }); $('.users-search-popup .header .icon-remove-circle').live('click', function() { diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index c908583cb..f54b1889b 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -4,3 +4,4 @@ @import "design/common"; @import "design/custom"; @import "design/build_lists_monitoring"; +@import "design/profile"; diff --git a/app/assets/stylesheets/design/custom.scss b/app/assets/stylesheets/design/custom.scss index b583bf3be..37ee3d4ee 100644 --- a/app/assets/stylesheets/design/custom.scss +++ b/app/assets/stylesheets/design/custom.scss @@ -1918,7 +1918,7 @@ table#myTable thead tr.search th form.button_to div input { } article .activity .top { - + .created { margin-left: 50px; span, a { @@ -1941,7 +1941,7 @@ article .activity .top { margin: 10px -7px; border-left: none; border-right: none; - } + } } #assigned-container { @@ -2058,7 +2058,7 @@ a.button.reject_publish, a.button.create_container { border-left: 12px solid #dcecfa; float: left; } - } + } } @@ -2105,37 +2105,3 @@ table tbody { cursor: default; } } - -.row { - .span3.profile { - .avatar { - float: left; - width: 81px; - height: 81px; - } - .base_info { - float: left; - width: 134px; - h3 { - margin: 0 0 0 10px; - } - p { - height: 35px; - margin-left: 10px; - } - a { - margin: 16px 0 0 10px; - } - } - p.first { - margin-top: 10px; - } - p.info { - max-width: 220px; - } - } -} - -hr.profile_line { - margin-top: 20px; -} \ No newline at end of file diff --git a/app/assets/stylesheets/design/profile.scss b/app/assets/stylesheets/design/profile.scss new file mode 100644 index 000000000..c38277c3d --- /dev/null +++ b/app/assets/stylesheets/design/profile.scss @@ -0,0 +1,145 @@ +.row { + .span3.profile { + .avatar { + float: left; + width: 81px; + height: 81px; + } + .base_info { + float: left; + width: 134px; + h3 { + margin: 0 0 0 10px; + } + p { + height: 35px; + margin-left: 10px; + } + a { + margin: 16px 0 0 10px; + } + } + p.first { + margin-top: 10px; + } + p.info { + max-width: 220px; + } + } +} +hr.profile_line { + margin: 20px 0; + width: 865px; +} +.profile-content { + border: 3px solid #D4D4D4; + .search { + border: 2px solid #D4D4D4; + float: left; + margin: 15px 10px; + width: 837px; + .pic { + background: url("/assets/search-button.png") repeat scroll 0 0 transparent; + float: left; + height: 22px; + width: 24px; + } + .field { + float: left; + margin: -1px 0 0; + width: 750px; + input { + background: none repeat scroll 0 0 transparent; + border: medium none; + font-family: Arial; + font-size: 12px; + height: 18px; + padding: 2px 0 0; + width: 700px; + } + input.gray { + color: #CFCFCF; + } + input.black { + color: #333333; + } + } + } + table { + border: none; + border-collapse:collapse; + margin: 0 9px 10px 9px; + width: 844px; + th { + padding-left: 10px; + width: 411px; + .project-link { + margin-top: 5px; + float: left; + } + } + tr.odd { + } + tr.even { + background: #EDEDED; + } + .row-fluid { + max-height: 16px; + .span3 { + font-size: 10px; + font-weight: normal; + max-height: 16px; + min-height: 16px; + } + .span3.datetime_moment { + margin-right: 15px; + color: gray; + } + } + } + .span12.content { + background: url(/assets/bg_blue.png); + height: 30px; + margin-bottom: 0px; + nav { + ul { + list-style: none; + padding-left: 0; + margin: 4px 0 0 5px; + li { + text-decoration: none; + padding: 0 10px 6px 0; + a { + color: white; + font-weight: bold; + font-size: 14px; + padding: 0 10px 9px 10px; + } + a.active { + background: image-url("profile-hover.png") repeat-x scroll 0 100% transparent; + } + } + } + } + } + .span12.sub-menu { + height: 30px; + background: #EDEDED; + margin: 0; + box-shadow: none; + padding-left: 0px; + nav { + ul { + list-style: none; + padding: 0; + margin: 6px 0 0 5px; + a { + padding: 0 10px 9px 10px; + } + a.active { + background: image-url("profile-hover.png") repeat-x scroll 0 100% transparent; + } + } + } + } +} diff --git a/app/controllers/groups/profile_controller.rb b/app/controllers/groups/profile_controller.rb index b6fb5000b..6f7146f6d 100644 --- a/app/controllers/groups/profile_controller.rb +++ b/app/controllers/groups/profile_controller.rb @@ -10,8 +10,19 @@ class Groups::ProfileController < Groups::BaseController end def show + @path, page = group_path, params[:page].to_i @projects = @group.projects.opened.search(params[:search]).recent - .paginate(:page => params[:page], :per_page => 25) + if request.xhr? + if params[:visibility] != 'hidden' + @projects = @projects.opened + @hidden = true + else + @projects = @projects.by_visibilities('hidden').accessible_by(current_ability, :read) + end + render :partial => 'shared/profile_projects', :layout => nil, :locals => {:projects => paginate_projects(page)} + else + @projects = paginate_projects(page) + end end def new @@ -53,4 +64,10 @@ class Groups::ProfileController < Groups::BaseController Relation.by_actor(current_user).by_target(@group).destroy_all redirect_to groups_path end + + protected + + def paginate_projects(page) + @projects.paginate(:page => (page>0 ? page : nil), :per_page => 24) + end end diff --git a/app/controllers/platforms/products_controller.rb b/app/controllers/platforms/products_controller.rb index 1bf862f5e..9f22988d4 100644 --- a/app/controllers/platforms/products_controller.rb +++ b/app/controllers/platforms/products_controller.rb @@ -54,7 +54,7 @@ class Platforms::ProductsController < Platforms::BaseController def autocomplete_project items = Project.accessible_by(current_ability, :membered) .search(params[:term]).limit(20) - items.select! {|e| e.repo.branches.count > 0} + #items.select! {|e| e.repo.branches.count > 0} render :json => items.map{ |p| { :id => p.id, diff --git a/app/controllers/users/profile_controller.rb b/app/controllers/users/profile_controller.rb index 80a1599df..e1a695568 100644 --- a/app/controllers/users/profile_controller.rb +++ b/app/controllers/users/profile_controller.rb @@ -3,7 +3,24 @@ class Users::ProfileController < Users::BaseController skip_before_filter :authenticate_user!, :only => :show if APP_CONFIG['anonymous_access'] def show - @projects = @user.projects.opened.search(params[:search]).recent - .paginate(:page => params[:page], :per_page => 25) + @path, page = user_path, params[:page].to_i + @projects = @user.projects.search(params[:search]).recent + if request.xhr? + if params[:visibility] != 'hidden' + @projects = @projects.opened + @hidden = true + else + @projects = @projects.by_visibilities('hidden').accessible_by(current_ability, :read) + end + render :partial => 'shared/profile_projects', :layout => nil, :locals => {:projects => paginate_projects(page)} + else + @projects = paginate_projects(page) + end + end + + protected + + def paginate_projects(page) + @projects.paginate(:page => (page>0 ? page : nil), :per_page => 24) end end diff --git a/app/helpers/commit_helper.rb b/app/helpers/commit_helper.rb index 5110b2e9c..9fb195721 100644 --- a/app/helpers/commit_helper.rb +++ b/app/helpers/commit_helper.rb @@ -1,6 +1,5 @@ # -*- encoding : utf-8 -*- module CommitHelper - def render_commit_stats(stats) res = [""] ind=0 @@ -44,4 +43,14 @@ module CommitHelper u = User.where(:email => email).first u.present? ? link_to(name, user_path(u)) : mail_to(email, name) end + + def commits_pluralize(commits_count) + Russian.p(commits_count, *commits_pluralization_arr) + end + + protected + + def commits_pluralization_arr + pluralize ||= t('layout.commits.pluralize').map {|base, title| title.to_s} + end end diff --git a/app/views/home/partials/_git_new_push_notification.haml b/app/views/home/partials/_git_new_push_notification.haml index d144beed8..f9553f91f 100644 --- a/app/views/home/partials/_git_new_push_notification.haml +++ b/app/views/home/partials/_git_new_push_notification.haml @@ -15,6 +15,6 @@ = commit[1] %br - if defined? other_commits - -pluralize = t('layout.commits.pluralize').map {|base, title| title.to_s} %br - =link_to t('notifications.bodies.more_commits', :count => other_commits_count, :commits => Russian.p(other_commits_count, *pluralize)), diff_path(project_owner, project_name, :diff => other_commits) + =link_to t('notifications.bodies.more_commits', :count => other_commits_count, :commits => commits_pluralize(other_commits_count)), + diff_path(project_owner, project_name, :diff => other_commits) diff --git a/app/views/shared/_profile.html.haml b/app/views/shared/_profile.html.haml index 119a93076..9fdcc553a 100644 --- a/app/views/shared/_profile.html.haml +++ b/app/views/shared/_profile.html.haml @@ -8,6 +8,7 @@ - [group.description , t('activerecord.attributes.group.description')] - max_length = 35 += hidden_field_tag :profile_path, @profile_path .row .span3.profile .avatar= image_tag avatar_url(user || group, :big), :alt => (user || group).uname @@ -35,16 +36,19 @@ %hr.profile_line{:color => 'dfe8ef', :size => '3'} -.content - %h4= t("layout.projects.public_projects_list") + ":" - %p - =form_tag search_path, :id => 'filter_projects', :method => :get do - =tracker_search_field(:search, t('layout.find_project')) - %br - %p - - projects.each do |project| - = link_to project.name, project - %br - %br - = will_paginate projects - %br +.row-fluid.profile-content + .span12.content + %nav + %ul + %li + = link_to t('layout.projects.list_header'), '#', :class => 'projects active' + .span12.sub-menu + %nav + %ul + %li= link_to t('layout.projects.public'), '#', :class => "public-projects #{!@hidden ? 'active' : ''}" + %li= link_to t('layout.projects.private'), '#', :class => "private-projects #{@hidden ? 'active' : ''}" + .search + .pic + .field= text_field_tag :query_projects, @query, :placeholder => t('layout.find_project') + .both + .profile-table= render 'shared/profile_projects', :projects => projects diff --git a/app/views/shared/_profile_projects.html.haml b/app/views/shared/_profile_projects.html.haml new file mode 100644 index 000000000..c832cadab --- /dev/null +++ b/app/views/shared/_profile_projects.html.haml @@ -0,0 +1,17 @@ +- pr_groups = projects.in_groups(2) +%table + %tbody + - pr_groups[0].each_with_index do |project, ind| + %tr{:class => ind.odd? ? 'odd' : 'even'} + - [project, pr_groups[1][ind]].each do |project| + %th + - if project.present? + .project-link= link_to short_message(project.name, 60), project, :title => project.name + .both + .row-fluid + = datetime_moment project.updated_at, :class => :span3 + - commits_count = project.total_commits_count + .span3= "#{commits_count > 10000 ? '10000+' : commits_count} #{commits_pluralize(commits_count)}" +%br +%div{:style => 'margin: 10px;'}= will_paginate projects + diff --git a/config/locales/models/project.en.yml b/config/locales/models/project.en.yml index 542571860..d616729d1 100644 --- a/config/locales/models/project.en.yml +++ b/config/locales/models/project.en.yml @@ -71,6 +71,9 @@ en: current_commit: Current commit files_in_project: Files in + public: Public + private: Private + flash: project: saved: Project saved diff --git a/config/locales/models/project.ru.yml b/config/locales/models/project.ru.yml index 560f96151..5360e237c 100644 --- a/config/locales/models/project.ru.yml +++ b/config/locales/models/project.ru.yml @@ -71,6 +71,9 @@ ru: cloning: Клонирование этого репозитория remote: Добавление этого репозитория как удаленного к существующему локальному репозиторию + public: Публичные + private: Приватные + flash: project: saved: Проект успешно сохранен diff --git a/lib/modules/models/git.rb b/lib/modules/models/git.rb index 032d8bb57..246652074 100644 --- a/lib/modules/models/git.rb +++ b/lib/modules/models/git.rb @@ -115,6 +115,11 @@ module Modules repo.branches.count == 0 end + def total_commits_count + return 0 if is_empty? + %x(cd #{path} && git rev-list --all | wc -l).to_i + end + protected def build_path(dir)