|
- # Redmine - project management software
- # Copyright (C) 2006-2013 Jean-Philippe Lang
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # 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 General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
- require 'SVG/Graph/Bar'
- require 'SVG/Graph/BarHorizontal'
- require 'digest/sha1'
- require 'redmine/scm/adapters/abstract_adapter'
- require 'tempfile'
- require 'json'
- require 'open-uri'
-
- class ChangesetNotFound < Exception; end
- class InvalidRevisionParam < Exception; end
-
- class RepositoriesController < ApplicationController
- include ApplicationHelper
- menu_item :repository
- menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
- default_search_scope :changesets
-
- before_filter :find_project_by_project_id, :only => [:new, :create, :newrepo, :stats, :quality_analysis]
- before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
-
- before_filter :find_project_repository, :except => [:new, :create, :newcreate, :edit, :update, :destroy, :committers, :newrepo, :to_gitlab, :forked, :project_archive, :export_rep_static]
- # 连接gitlab
- # before_filter :connect_gitlab, :only => [:quality_analysis, :commit_diff]
-
- before_filter :find_changeset, :only => [:revision, :add_related_issue, :remove_related_issue]
- before_filter :authorize , :except => [:newrepo,:newcreate,:fork, :to_gitlab, :forked, :project_archive, :quality_analysis, :commit_diff]
- # 版本库新增权限
- # before_filter :show_rep, :only => [:show, :stats, :revisions, :revision, :diff, :commit_diff ]
- accept_rss_auth :revisions
- # hidden repositories filter // 隐藏代码过滤器
- # before_filter :check_hidden_repo, :only => [:stats, :revisions, :revision, :diff ]
- helper :repositories
- include RepositoriesHelper
- helper :project_score
- require 'ostruct'
- #@root_path = RepositoriesHelper::ROOT_PATH
- # require 'net/ssh'
-
- rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
- def new
- if @project.repositories.count == 0
- scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
- @repository = Repository.factory(scm)
- @repository.is_default = @project.repository.nil?
- @repository.project = @project
- @course_tag = params[:course]
- if @course_tag == 1
- render :layout => 'base_courses'
- else
- render :layout => 'base_projects'
- end
- else
- render_403
- end
-
- end
-
- def export_rep_static
- # 管理员界面导出所有项目
- @project = Project.find(params[:id])
- gpid = @project.gpid
- rev = params[:rev]
- cycle = params[:cycle]
- respond_to do |format|
- format.html
- format.xls{
- filename = "#{@project.name.to_s}_#{l(:label_rep_xls)}.xls"
- send_data(export_rep_xls(gpid, :rev => rev, :cycle => cycle), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename))
- }
- end
- end
-
- def forked
- @project = Project.find(params[:id])
- @repository = Repository.where("project_id =? and type =?", @project.id, "Repository::Gitlab")
- # 如果当前用户已经fork过该项目,不会新fork项目,则跳至已fork的项
- unless has_forked?(@project, User.current)
- project = project_from_current_project(@project.id, User.current.id)
- redirect_to project_path(project)
- else
- # 自己不能fork自己的项目
- if User.current.id == @project.user_id
- flash[:notice] = l(:project_gitlab_fork_own)
- redirect_to repository_url(@repository)
- else
- g = Gitlab.client
- if User.current.gid.nil?
- begin
- s = Trustie::Gitlab::Sync.new
- s.sync_user(User.current)
- ensure
- logger.error "Synv user failed ==>#{User.current.id}"
- end
- end
- gproject = g.fork(@project.gpid, User.current.gid)
- if gproject
- copy_project(@project, gproject)
- forked_count = @project.forked_count.to_i + 1
- @project.update_attributes(:forked_count => forked_count)
- end
- end
- end
- end
-
- # 一键ZIP下载
- def project_archive
- g = Gitlab.client
- g.get()
- # g.project_archive(params[:gpid].to_i, params[:rev])
- end
-
- # 判断用户是否已经fork过该项目
- def has_forked?(project, user)
- projects = Project.where("user_id =?", user)
- projects.map(&:forked_from_project_id).detect{|s| s == @project.id}.nil? ? true : false
- end
-
- # 获取当前用户fork过的项目
- def project_from_current_project(project, user)
- project = Project.where("user_id =? and forked_from_project_id =?",user, project).first
- end
-
- # copy a project for fork
- def copy_project(tproject, gproject)
- project = Project.new
- project.name = tproject.name
- project.is_public = tproject.is_public
- project.status = tproject.status
- project.description = tproject.description
- project.hidden_repo = tproject.hidden_repo
- project.user_id = User.current.id
- project.project_type = 0
- project.project_new_type = tproject.project_new_type
- project.gpid = gproject.id
- project.forked_from_project_id = tproject.id
- if project.save
- r = Role.givable.find_by_id(Setting.new_project_user_role_id.to_i) || Role.givable.first
- m = Member.new(:user => User.current, :roles => [r])
- if ProjectScore.where("project_id=?", project.id).first.nil?
- ProjectScore.create(:project_id => project.id, :score => false)
- end
- project_info = ProjectInfo.new(:user_id => User.current.id, :project_id => project.id)
- user_grades = UserGrade.create(:user_id => User.current.id, :project_id => project.id)
- Rails.logger.debug "UserGrade created: #{user_grades.to_json}"
- project_status = ProjectStatus.create(:project_id => @project.id, :watchers_count => 0, :changesets_count => 0, :project_type => @project.project_type,:grade => 0)
- Rails.logger.debug "ProjectStatus created: #{project_status.to_json}"
- project.members << m
- project.project_infos << project_info
- copy_repository(project, gproject)
- respond_to do |format|
- format.html {
- flash[:notice] = l(:notice_successful_create)
- if params[:continue]
- attrs = {:parent_id => project.parent_id}.reject {|k,v| v.nil?}
- redirect_to new_project_url(attrs, :course => '0')
- else
- redirect_to project_path(project)
- end
- }
- format.api { render :action => 'show', :status => :created, :location => url_for(:controller => 'projects', :action => 'show', :id => project.id) }
- format.js
- end
- else
- respond_to do |format|
- format.html { render :action => 'forked', :layout => 'base_projects'}
- format.api { render_validation_errors(@project) }
- end
- end
- end
-
- def copy_repository(project, gproject)
- # 避免
- # if is_sigle_identifier?(project.user_id, gproject.name)
- repository = Repository.factory('Git')
- repository.project_id = project.id
- repository.type = 'Repository::Gitlab'
- repository.url = gproject.name
- repository.identifier = gproject.name
- repository = repository.save
- # else
- # flash[:notice] = l(:project_gitlab_create_double_message)
- # end
- end
-
- def newrepo
- scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
- @repository = Repository.factory(scm)
- @repository.is_default = @project.repository.nil?
- @repository.project = @project
- @course_tag = params[:course]
- if @course_tag == 1
- render :layout => 'base_courses'
- else
- render :layout => 'base_projects'
- end
- end
-
- def fork
- @repository_url = params[:repository_url]
-
- # @repository.url
- # system "htpasswd -mb "+@root_path+"user.passwd "+params[:repository][:identifier]+" "+@upasswd
- # system "echo -e '"+params[:project_id]+"-"+params[:repository][:identifier]+"-write:"+
- # " "+params[:repository][:identifier]+"' >> "+@root_path+"group.passwd"
- system "git clone --bare "+@repository_url
- # system "mv "+@project_path+"/hooks/post-update{.sample,}"
- # system "chmod a+x "+@project_path+"/hooks/post-update"
- # system "."+@project_path+"/hooks/post-update"
- # system "echo -e 'Allow from all \n Order Deny,Allow \n "+
- # "<Limit PUT POST DELETE PROPPATCH MKCOL COPY MOVE LOCK UNLOCK> \n"+
- # "Require group "+params[:project_id]+"-"+params[:repository][:identifier]+"-write \n "+
- # "</Limit> \n ' >>"+
- # @project_path+"/.htaccess"
- flash[:notice] = l(:label_notice_fork_successed)
- @repositories = @project.repositories
- render :action => 'show', :layout => 'base_projects'
- end
-
-
- HOOK_TEMPLATE = %Q{#!/bin/sh
- exec sh -c '
- function update()
- {
- CMD_PATH=`dirname $0`;
- cd $CMD_PATH;
- PY_PATH=$PWD/../../git_refresh_changes.py;
- [[ -s "$PY_PATH" ]] && $(which python) $PY_PATH $PWD;
- cd -;
- }
- git update-server-info
- update
- '
- }
-
- def create
- # 判断版本库创建者是否有同名版本库,避免版本库路径一致问题
- unless is_sigle_identifier?(@project.user_id, params[:repository].first[1])
- flash[:notice] = l(:project_gitlab_create_double_message)
- redirect_to settings_project_url(@project, :tab => 'repositories')
- else
- attrs = pickup_extra_info
- @repository = Repository.factory('Git')
- @repository.safe_attributes = params[:repository]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
- @repository.type = 'Repository::Gitlab'
- @repository.identifier = @repository.identifier.downcase
- @repository.url = @repository.identifier
- if request.post? && @repository.save
- s = Trustie::Gitlab::Sync.new
- s.create_project(@project, @repository)
- redirect_to(:controller => 'repositories', :action => 'show', :id => @project, :repository_id => gitlab_repository(@project).try(:identifier))
- else
- redirect_to settings_project_url(@project, :tab => 'repositories',:repository_error_message=>@repository.errors.full_messages)
- end
- end
- end
-
- def edit
- end
-
- def update
- attrs = pickup_extra_info
- @repository.safe_attributes = attrs[:attrs]
- if attrs[:attrs_extra].keys.any?
- @repository.merge_extra_info(attrs[:attrs_extra])
- end
- @repository.project = @project
- if request.put? && @repository.save
- redirect_to settings_project_url(@project, :tab => 'repositories')
- else
- render :action => 'edit'
- end
- end
-
- def pickup_extra_info
- p = {}
- p_extra = {}
- params[:repository].each do |k, v|
- if k =~ /^extra_/
- p_extra[k] = v
- else
- p[k] = v
- end
- end
- {:attrs => p, :attrs_extra => p_extra}
- end
- private :pickup_extra_info
-
- def committers
- @committers = @repository.committers
- @users = @project.users
- additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
- @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
- @users.compact!
- @users.sort!
- if request.post? && params[:committers].is_a?(Hash)
- # Build a hash with repository usernames as keys and corresponding user ids as values
- @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
- flash[:notice] = l(:notice_successful_update)
- respond_to do |format|
- format.html{
- render :layout => "base_projects"
- }
- end
- elsif request.get?
- respond_to do |format|
- format.html{
- render :layout => "base_projects"
- }
- end
- end
- end
-
- def quality_analysis
- gitlab_branches = @g.branches(@project.gpid)
- @branch_names = gitlab_branches.map{|b| b.name}
- @gitlab_default_branch = @g_project.default_branch
- # language = params[:language]
- # branch = params[:branch]
- # path = params[:path]
- # user_name = User.find(@project.user_id).try(:login)
- # rep_name = params[:repository_id]
- # host = "192.168.0.200"
- # port = "1125"
- # username = "git"
- # password = "123123"
- # server_cmd1 = "sh gitclone.sh" + " " + user_name + " " + rep_name
- # # 连接到远程主机 foobar
- # ssh = Net::SSH.start(host, username, :port => port, :password => password) do |ssh|
- # result = ssh.exec!(server_cmd1)
- # path = "/home/git/repo/" + user_name + "/" + rep_name
- # # sonar 分析
- # # server_cmd2
- # # 删除clone的版本库
- # # server_cmd3
- # end
- respond_to do |format|
- format.js
- format.html{
- render :layout => "base_projects"
- }
- end
- end
-
- def destroy
- DestroyRepositoryTask.new.destroy(User.current.id, @repository.id)
- @repository.hidden = true
- @repository.save
- redirect_to settings_project_url(@project, :tab => 'repositories')
- end
-
- def to_gitlab
- @project = Project.find(params[:project_id])
- @repository = Repository.find(params[:id])
- s = Trustie::Gitlab::Sync.new
- s.sync_project(@project, path: params[:repo_name], import_url: @repository.url)
- @repository.type = 'Repository::Gitlab'
- @repository.save
- redirect_to :controller => 'repositories', :action => 'show', :id => @project.id, to: 'gitlab'
- end
-
- def show
- # 顶部导航
- @project_menu_type = 5
-
- # TODO: the below will move to filter, done.
- # 获取版本库目录结构
- @entries = @repository.entries(@path, @rev)
- if request.xhr?
- @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
- else
- unless @entries.blank?
- @changesets_latest_coimmit = @g.commit(@project.gpid, @entries.first.try(:lastrev))
- # 总的提交数
- @changesets_all_count = @g.user_static(@project.gpid, :rev => @rev).count
- # 获取默认分支
- @g_default_branch = @g_project.default_branch.nil? ? "master" : @g_project.default_branch
-
- @creator = @project.owner.to_s
- gitlab_address = Redmine::Configuration['gitlab_address']
- # REDO:需优化,仅测试用
- @zip_path = Gitlab.endpoint.to_s + "/projects/" + @project.gpid.to_s + "/repository/archive?&private_token=" + Gitlab.private_token
- end
-
- @creator = @project.owner.to_s
- gitlab_address = Redmine::Configuration['gitlab_address']
- @repos_url = gitlab_address.to_s+"/" + @creator + "/" + @repository.identifier+"."+"git"
-
- # 一些数据的异步同步更新
- # 访问版本庫后更新project_score表数据;changeset_num为提交总数
- project_score = @project.project_score
- if project_score.nil?
- ProjectScore.create(:project_id => @project.id, :score => false)
- else
- project_score.update_column(:changeset_num, @changesets_all_count)
- end
- # 更新提交时间,用于课程
- unless @changesets_latest_coimmit.blank?
- update_commits_date(@project, @changesets_latest_coimmit)
- end
-
- render :layout => 'base_projects'
- end
- end
-
- alias_method :browse, :show
-
- # 获取版本文件目录的
- def tree_head_message
-
- end
-
- # 注:由于考虑到性能所以commits api每次返回20条记录
- def changes
- # 顶部导航
- @project_menu_type = 5
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
- g = Gitlab.client
- limit = 10
- # 每次页面的换回值从1开始,但是gitlab的页面查询是从0开始,所以先改变page的类型减一在改回来
- @commits = g.commits(@project.gpid, page:(params[:page].to_i - 1).to_s, ref_name:@rev)
- @commits_count = params[:commit_count].nil? ? @g.user_static(@project.gpid, :rev => @rev).count : params[:commit_count].to_i
- @commits_pages = Paginator.new @commits_count, limit, params[:page]
- # @offset ||= @commits_pages.offset
- # @commits = paginateHelper @commits, limit
-
- render :layout => 'base_projects'
- end
-
-
- def revisions
- @changeset_count = @repository.changesets.count
- @changeset_pages = Paginator.new @changeset_count,
- per_page_option,
- params['page']
- @changesets = @repository.changesets.
- limit(@changeset_pages.per_page).
- offset(@changeset_pages.offset).
- includes(:user, :repository, :parents).
- all
-
- respond_to do |format|
- format.html { render :layout => 'base_projects' }
- format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
- end
- end
-
- def raw
- entry_and_raw(true)
- end
-
- def entry
- # 顶部导航
- @project_menu_type = 5
-
- entry_and_raw(false)
- @content = @repository.cat(@path, @rev)
- @changesets_latest_coimmit = @g.commit(@project.gpid, @entry.try(:lastrev))
- # 总的提交数
- @changesets_all_count = @g.user_static(@project.gpid, :rev => @rev).count
- if is_entry_text_data?(@content, @path)
- render :layout => 'base_projects'
- end
- end
-
- def entry_and_raw(is_raw)
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
-
- # If the entry is a dir, show the browser
- (show; return) if @entry.is_dir?
-
- @content = @repository.cat(@path, @rev)
- (show_error_not_found; return) unless @content
- if is_raw || (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) || !is_entry_text_data?(@content, @path)
- # Force the download
- send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
- send_type = Redmine::MimeType.of(@path)
- send_opt[:type] = send_type.to_s if send_type
- send_opt[:disposition] = (Redmine::MimeType.is_type?('image', @path) && !is_raw ? 'inline' : 'attachment')
- send_data @content, send_opt
- else
- # Prevent empty lines when displaying a file with Windows style eol
- # TODO: UTF-16
- # Is this needs? AttachmentsController reads file simply.
- @content.gsub!("\r\n", "\n")
- @changeset = @repository.find_changeset_by_name(@rev)
- end
- end
- private :entry_and_raw
-
- def is_entry_text_data?(ent, path)
- # UTF-16 contains "\x00".
- # It is very strict that file contains less than 30% of ascii symbols
- # in non Western Europe.
- return true if Redmine::MimeType.is_type?('text', path)
- # Ruby 1.8.6 has a bug of integer divisions.
- # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
- return false if ent.is_binary_data?
- true
- end
- private :is_entry_text_data?
-
- def annotate
- @entry = @repository.entry(@path, @rev)
- (show_error_not_found; return) unless @entry
-
- @annotate = @repository.scm.annotate(@path, @rev)
- if @annotate.nil? || @annotate.empty?
- (render_error l(:error_scm_annotate); return)
- end
- ann_buf_size = 0
- @annotate.lines.each do |buf|
- ann_buf_size += buf.size
- end
- if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
- (render_error l(:error_scm_annotate_big_text_file); return)
- end
- @changeset = @repository.find_changeset_by_name(@rev)
- end
-
- def revision
- respond_to do |format|
- format.html{render :layout => 'base_projects'}
- format.js {render :layout => false}
- end
- end
-
- # Adds a related issue to a changeset
- # POST /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues
- def add_related_issue
- @issue = @changeset.find_referenced_issue_by_id(params[:issue_id])
- if @issue && (!@issue.visible? || @changeset.issues.include?(@issue))
- @issue = nil
- end
-
- if @issue
- @changeset.issues << @issue
- end
- end
-
- # Removes a related issue from a changeset
- # DELETE /projects/:project_id/repository/(:repository_id/)revisions/:rev/issues/:issue_id
- def remove_related_issue
- @issue = Issue.visible.find_by_id(params[:issue_id])
- if @issue
- @changeset.issues.delete(@issue)
- end
- end
-
- # 每次提交对应的文件差异
- def commit_diff
- # 顶部导航
- @project_menu_type = 5
- @commit_diff = @g.commit_diff(@project.gpid, params[:changeset])
- diff = ActiveSupport::JSON.decode(@commit_diff.to_json).first
- diff = OpenStruct.new(diff)
- @diff_file = Trustie::Gitlab::Diff::File.new(diff)
-
-
- @commit_details = @g.commit(@project.gpid, params[:changeset])
- render :layout => 'base_projects'
- end
-
- def diff
- if params[:format] == 'diff'
- @diff = @repository.diff(@path, @rev, @rev_to)
- (show_error_not_found; return) unless @diff
- filename = "changeset_r#{@rev}"
- filename << "_r#{@rev_to}" if @rev_to
- send_data @diff.join, :filename => "#{filename}.diff",
- :type => 'text/x-patch',
- :disposition => 'attachment'
- else
- @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
- @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
-
- # Save diff type as user preference
- if User.current.logged? && @diff_type != User.current.pref[:diff_type]
- User.current.pref[:diff_type] = @diff_type
- User.current.preference.save
- end
- @cache_key = "repositories/diff/#{@repository.id}/" +
- Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
- unless read_fragment(@cache_key)
- @diff = @repository.diff(@path, @rev, @rev_to)
- unless @diff
- show_error_not_found
- return
- end
- end
-
- @changeset = @repository.find_changeset_by_name(@rev)
- @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
- @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
- end
- render :layout => 'base_projects'
- end
-
- def stats
- if @project.gpid.nil?
- render 404
- return
- end
- project_id = @project.gpid
- # @repository_id = @repository.identifier
- # creator = params[:creator]
- rev = params[:rev]
- g = Gitlab.client
- begin
- @static_total_per_user = g.rep_stats(project_id, :rev => rev)
- # 更新rep_statics统计数
- @static_total_per_user.each do |static|
- rep_static = RepStatics.where("project_id =? and email =?", @project.id, static.email.to_s).first
- if rep_static.nil?
- RepStatics.create(:project_id => @project.id, :uname => static.uname, :commits_num => static.commits_num, :email => static.email, :add => static.add, :del => static.del, :changeset => static.changes)
- else
- if @rev == params[:default_branch]
- rep_static.update_attributes(:uname => static.uname, :commits_num => static.commits_num, :email => static.email, :add => static.add, :del => static.del, :changeset => static.changes)
- end
- end
- end
- rescue
- render_404
- return
- end
- render :layout => 'base_projects'
- end
-
- def graph
- data = nil
- case params[:graph]
- when "commits_per_month"
- data = graph_commits_per_month(@repository)
- when "commits_per_author"
- data = graph_commits_per_author(@repository)
- when "author_commits_per_month"
- data = graph_author_commits_per_month(@repository)
- when "author_commits_six_month"
- data = author_commits_six_month(@repository)
- when "author_code_six_months"
- data = author_code_six_month(@repository)
- end
- if data
- headers["Content-Type"] = "image/svg+xml"
- send_data(data, :type => "image/svg+xml", :disposition => "inline")
- else
- render_404
- end
- end
-
- private
- # 更新项目统计数
- def update_commits_count project, count
- project.project_score.update_attribute(:changeset_num, count)
- end
-
- # 更新项目提交次数时间
- def update_commits_date project, date
- project.project_score.update_attribute(:commit_time, date.created_at)
- end
-
- # 链接gitlab
- def connect_gitlab
- begin
- @g = Gitlab.client
- unless @project.gpid.nil?
- @g_project = @g.project(@project.gpid)
- end
- rescue => e
- logger.error("connect gitlab failed ==> #{e}")
- end
- end
-
- def show_rep
- visible_repository?(@project)
- end
-
- def find_repository
- @repository = Repository.find(params[:id])
- @project = @repository.project
- rescue ActiveRecord::RecordNotFound
- render_404
- end
-
- REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
- REP_TYPE = "Repository::Gitlab"
-
- # 获取项目、版本库、路劲、默认分支
- def find_project_repository
- @project = Project.find(params[:id])
- @repository = Repository.where(:type => REP_TYPE, :project_id => @project).first
- (render_404; return false) unless (@repository || @project.gpid)
- @path = params[:path].is_a?(Array) ? params[:path].join('/') : params[:path].to_s
- @g = Gitlab.client
- @g_project = @g.project(@project.gpid)
- @g_default_branch = @g_project.default_branch
- # gitlab端获取默认分支
- @rev = params[:rev].blank? ? @g_default_branch : params[:rev].to_s.strip
- @rev_to = params[:rev_to]
- unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
- if @repository.branches.blank?
- raise InvalidRevisionParam
- end
- end
- rescue ActiveRecord::RecordNotFound
- render_404
- rescue InvalidRevisionParam
- show_error_not_found
- end
-
- def find_changeset
- if @rev.present?
- @changeset = @repository.find_changeset_by_name(@rev)
- end
- show_error_not_found unless @changeset
- end
-
- def show_error_not_found
- render_error :message => l(:error_scm_not_found), :status => 404
- end
-
- def show_error_forbidden
- render_error :status => 403
- end
-
- # Handler for Redmine::Scm::Adapters::CommandFailed exception
- def show_error_command_failed(exception)
- render_error l(:error_scm_command_failed, exception.message)
- end
-
- def graph_commits_per_month(repository)
- @date_to = Date.today
- @date_from = @date_to << 11
- @date_from = Date.civil(@date_from.year, @date_from.month, 1)
- commits_by_day = Changeset.count(
- :all, :group => :commit_date,
- :conditions => ["repository_id = ? AND commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- commits_by_month = [0] * 12
- commits_by_day.each {|c| commits_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
-
- changes_by_day = Change.count(
- :all, :group => :commit_date, :include => :changeset,
- :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- changes_by_month = [0] * 12
- changes_by_day.each {|c| changes_by_month[(@date_to.month - c.first.to_date.month) % 12] += c.last }
-
- fields = []
- 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
-
- graph = SVG::Graph::Bar.new(
- :height => 300,
- :width => 600,
- :fields => fields.reverse,
- :stack => :side,
- :scale_integers => true,
- :step_x_labels => 2,
- :show_data_values => true,
- :graph_title => l(:label_commits_per_month),
- :show_graph_title => true
- )
-
- # 具状图
- graph.add_data(
- :data => commits_by_month[0..11].reverse,
- :title => l(:label_revision_plural)
- )
-
- graph.add_data(
- :data => changes_by_month[0..11].reverse,
- :title => l(:label_change_plural)
- )
-
- graph.burn
- end
-
- def graph_commits_per_author(repository)
- commits_by_author = Changeset.count(:all, :group => :committer, :conditions => ["repository_id = ?", repository.id])
- commits_by_author = commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}.last(25)
-
- changes_by_author = Change.count(:all, :group => :committer, :include => :changeset, :conditions => ["#{Changeset.table_name}.repository_id = ?", repository.id])
- h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
-
- fields = commits_by_author.collect {|r| r.first}
- commits_data = commits_by_author.collect {|r| r.last}
- changes_data = commits_by_author.collect {|r| h[r.first] || 0}
-
- fields = fields + [""]*(10 - fields.length) if fields.length<10
- commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
- changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
-
- # Remove email adress in usernames
- fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
-
- graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
- :width => 600,
- :fields => fields,
- :stack => :side,
- :scale_integers => true,
- :show_data_values => true,
- :rotate_y_labels => false,
- :graph_title => l(:label_commits_per_author),
- :show_graph_title => true
- )
- graph.add_data(
- :data => commits_data,
- :title => l(:label_revision_plural)
- )
- graph.add_data(
- :data => changes_data,
- :title => l(:label_change_plural)
- )
- graph.burn
- end
-
- # 用户最近一年的提交次数
- def graph_author_commits_per_month(repository)
- @date_to = Date.today
- @date_from = @date_to << 12
- @date_from = Date.civil(@date_from.year, @date_from.month, @date_from.day)
- commits_by_author = Changeset.count(:all, :group => :committer,
- :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- commits_by_author = commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}.last(25)
-
- fields = commits_by_author.collect {|r| r.first}
- commits_data = commits_by_author.collect {|r| r.last}
-
- fields = fields + [""]*(10 - fields.length) if fields.length<10
- commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
-
- # Remove email adress in usernames
- fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
-
- graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
- :width => 600,
- :fields => fields,
- :stack => :side,
- :scale_integers => true,
- :show_data_values => true,
- :rotate_y_labels => false,
- :graph_title => l(:label_author_commits_year),
- :show_graph_title => true,
- :no_css => true
- )
- graph.add_data(
- :data => commits_data,
- :title => l(:label_revision_commit_count)
- )
- graph.burn
- end
-
- # 用户最近六个月的提交次数
- def author_commits_six_month(repository)
- @date_to = Date.today
- @date_from = @date_to << 6
- @date_from = Date.civil(@date_from.year, @date_from.month, @date_from.day)
- commits_by_author = Changeset.count(:all, :group => :committer,
- :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- commits_by_author = commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}.last(25)
-
- fields = commits_by_author.collect {|r| r.first}
- commits_data = commits_by_author.collect {|r| r.last}
-
- fields = fields + [""]*(10 - fields.length) if fields.length<10
- commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
-
- # Remove email adress in usernames
- fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
-
- graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
- :width => 600,
- :fields => fields,
- :stack => :side,
- :scale_integers => true,
- :show_data_values => true,
- :rotate_y_labels => false,
- :graph_title => l(:label_author_commits_six_month),
- :show_graph_title => true
- )
- graph.add_data(
- :data => commits_data,
- :title => l(:label_revision_commit_count)
- )
- graph.burn
- end
-
- # 最近六个月代码量统计
- def author_code_six_month(repository)
- @date_to = Date.today
- @date_from = @date_to << 6
- @date_from = Date.civil(@date_from.year, @date_from.month, @date_from.day)
- commits_by_author = Changeset.count(:group => :committer, :conditions => ["#{Changeset.table_name}.repository_id = ? AND #{Changeset.table_name}.commit_date BETWEEN ? AND ?", repository.id, @date_from, @date_to])
- commits_by_author = commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}.last(40)
- all_author = []
- commits_by_author.each do |cba|
- all_author << cba.first
- end
- # all_author = all_author.collect {|c| c.gsub(%r{/ /<.+@.+>}, '') }
- all_author = all_author.collect {|c| c.split.first }
- commits_by_author = repository.commits(all_author, "#{@date_from}", "#{@date_to}", repository.id == 150 ? "szzh" : 'master')
-
- fields = commits_by_author.collect {|r| r.first}
- commits_data = commits_by_author.collect {|r| r.last}
-
- fields = fields + [""]*(10 - fields.length) if fields.length<10
- commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
-
- # Remove email adress in usernames
- fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
-
- graph = SVG::Graph::BarHorizontal.new(
- :height => 400,
- :width => 600,
- :fields => fields,
- :stack => :side,
- :scale_integers => true,
- :show_data_values => true,
- :rotate_y_labels => false,
- :graph_title => l(:label_author_code_six_month),
- :show_graph_title => true
- )
- graph.add_data(
- :data => commits_data,
- :title => l(:lable_revision_code_count)
- )
- graph.burn
- end
-
- end
|