Compare commits

...

54 Commits

Author SHA1 Message Date
yystopf c105785105 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-21 15:17:44 +08:00
yystopf c2bd78d70d Merge branch 'develop' into standalone_develop 2021-12-21 15:17:08 +08:00
yystopf df69dee282 fix: some error for read raw data 2021-12-21 15:14:10 +08:00
yystopf ee6404a3b3 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-21 14:31:34 +08:00
yystopf fae8313df6 Merge branch 'develop' into standalone_develop 2021-12-21 14:31:24 +08:00
yystopf 45a77e5c07 fix: readme private file path use authorize 2021-12-21 14:24:27 +08:00
yystopf 7849bd0ebf fix: remove pr permission 2021-12-21 10:16:29 +08:00
yystopf 0c7499842f Merge branch 'standalone_develop' into pre_trustie_server 2021-12-20 15:53:54 +08:00
yystopf ab361d8009 Merge branch 'develop' into standalone_develop 2021-12-20 15:53:40 +08:00
yystopf 3649bf1fd0 fix 2021-12-20 15:53:24 +08:00
yystopf edb1114d39 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-20 15:42:36 +08:00
yystopf a24d33d2ee Merge pull request '同步到测试环境' (#294) from develop into standalone_develop 2021-12-20 15:42:34 +08:00
yystopf 6ffc2938a1 fix: download file url escape 2021-12-20 15:39:33 +08:00
yystopf aa60de1dac fix: valid pr title length 2021-12-20 15:38:08 +08:00
yystopf 29f98513e2 fix: org update error 2021-12-20 15:38:08 +08:00
yystopf e35cdf39fb fix: login password validate 2021-12-20 15:34:55 +08:00
yystopf bec98bbc9d Merge branch 'standalone_develop' into pre_trustie_server 2021-12-17 15:45:57 +08:00
yystopf af160084c4 fix: merge from develop 2021-12-17 15:45:28 +08:00
yystopf e51b86538b Merge pull request '更改易修为疑修' (#292) from yystopf/forgeplus:develop into develop 2021-12-17 15:44:20 +08:00
yystopf da4fd3a762 fix: 易修 to 疑修 2021-12-17 15:35:30 +08:00
yystopf 96d1a7440d Merge branch 'standalone_develop' into pre_trustie_server 2021-12-14 10:20:54 +08:00
yystopf 8a34699acd fix: merge from develop 2021-12-14 10:20:35 +08:00
xiaoxiaoqiong efc82b9841 fix: issue is collaborators 2021-12-14 10:17:15 +08:00
yystopf e3df503dd2 fix 2021-12-14 10:13:28 +08:00
yystopf 6a2ad67386 fix 2021-12-14 09:53:54 +08:00
yystopf 3d12db3a07 fix: atme service use perform later 2021-12-14 09:39:26 +08:00
yystopf 82bae9159b Merge branch 'standalone_develop' into pre_trustie_server 2021-12-13 16:40:14 +08:00
yystopf 1d13b3686b Merge branch 'develop' into standalone_develop 2021-12-13 16:39:47 +08:00
yystopf 1faaf1d7d1 fix: project menu list wiki location 2021-12-13 11:45:58 +08:00
yystopf f8a6f62342 fix: project rank include educoder 2021-12-10 09:35:03 +08:00
yystopf b816b6a449 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-10 09:20:04 +08:00
yystopf d48700d200 Merge pull request '同步代码' (#288) from develop into standalone_develop 2021-12-10 09:19:42 +08:00
yystopf 66d1246a6c fix: educoder project menu return 2021-12-09 15:54:21 +08:00
yystopf f79beef3f9 fix: issues branch educoder use master 2021-12-09 14:33:46 +08:00
yystopf 81e6691632 fix: educoder entries image file type 2021-12-09 11:33:43 +08:00
yystopf 38d6519381 fix 2021-12-09 11:08:54 +08:00
yystopf 16418675ac fix 2021-12-09 11:08:10 +08:00
yystopf 102858bc8c fix: educoder entries file use hash 2021-12-09 11:06:49 +08:00
yystopf 0d64768e4b fix: educoder file visit delay 2021-12-09 09:37:28 +08:00
yystopf beccac652f fix 2021-12-08 18:25:31 +08:00
yystopf 30eb1958c1 fix 2021-12-08 18:23:29 +08:00
yystopf 33e17e8439 fix: educoder sub_entries and commit author 2021-12-08 18:18:19 +08:00
yystopf 5445206716 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-08 16:26:15 +08:00
yystopf 1c19c05fd8 fix 2021-12-08 16:26:06 +08:00
yystopf 957980fc71 Merge branch 'standalone_develop' into pre_trustie_server 2021-12-08 16:23:21 +08:00
yystopf 657e2cf169 fix: merge from develop 2021-12-08 16:22:34 +08:00
yystopf e0c47c2abd fix: educoder clone url 2021-12-08 16:18:13 +08:00
xiaoxiaoqiong 9e64a4842d Merge branch 'standalone_develop' into pre_trustie_server 2021-12-08 15:32:47 +08:00
xiaoxiaoqiong ba9e74f373 fixed db:migrate 2021-12-08 15:32:28 +08:00
xxq250 93dd5b5d84 Merge pull request '易修声明功能' (#284) from starlee/forgeplus:lzx-claim-2.0 into standalone_develop 2021-12-08 15:12:15 +08:00
yystopf 422549f9e7 fix 2021-12-08 15:01:33 +08:00
yystopf 0358229b95 fix: educoder api delay 2021-12-08 14:47:15 +08:00
yystopf c4586fbb36 fix: remove detail language and contributors 2021-12-08 14:11:37 +08:00
starlee e25e2fdd77 add the claim mechanism 2021-11-25 13:33:32 +08:00
55 changed files with 1668 additions and 551 deletions

View File

@ -23,7 +23,7 @@
* Fix 版本库中附件下载400(#51625)
* Fix loading页面优化(#51588)
* Fix 提交详情页面优化(#51577)
* Fix 修复修复制功能(#51569)
* Fix 修复修复制功能(#51569)
* Fix 修复新建发行版用户信息显示错误的问题(#51665)
* Fix 修复查看文件详细信息报错的问题(#51561)
* Fix 修复提交记录中时间显示格式问题(#51526)
@ -62,7 +62,7 @@
## [v3.0.3](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-08
* BUGFIXES
* Fix 解决修标题过长导致的排版问题(45469)
* Fix 解决修标题过长导致的排版问题(45469)
* Fix 解决合并请求详情页面排版错误的问题(45457)
* FIX 解决转移仓库界面专有名词描述错误的问题(45455)
* Fix 解决markdown格式文件自动生成数字排序的问题(45454)

View File

@ -0,0 +1,138 @@
#coding=utf-8
class ClaimsController < ApplicationController
# skip_before_action :verify_authenticity_token
protect_from_forgery with: :null_session
before_action :require_login, except: [:index]
before_action :set_issue
def index
@user_claimed = 0
@claims = @issue.claims.claim_includes.order("created_at desc")
@claims.each do |claim|
if claim.user_id == current_user.id
@user_claimed = 1
break
end
end
render file: 'app/views/claims/list.json.jbuilder'
end
def create
@claim = Claim.find_by_sql(["select id from claims where issue_id=? and user_id=?",params[:issue_id], current_user.id])
if @claim.present?
return normal_status(-1,"您已经声明过该易修")
end
ActiveRecord::Base.transaction do
@claim = Claim.new(parse_issue_params(params))
if @claim.save
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 1
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "新建声明: #{params[:claim_note]}",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"新建声明操作失败")
end
end
end
def update
@claim = Claim.find_by_id(params[:claim_id])
if @claim.blank?
return normal_status(-1,"易修不存在")
end
if @claim.user_id != current_user.id
return normal_status(-1,"你不能更新别人的声明")
end
ActiveRecord::Base.transaction do
if @claim.update_attribute(:note,params[:claim_note])
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 1
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "更新声明: #{params[:claim_note]}",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"声明更新操作失败")
end
end
end
def destroy
@claim = Claim.find_by_sql(["select id from claims where issue_id=? and user_id=?",params[:issue_id], current_user.id])
if @claim.blank?
normal_status(-1,"您未曾声明过该易修")
else
@claim = @claim[0]
# 判断current user是否是claimer
ActiveRecord::Base.transaction do
if @claim.destroy
@claims = @issue.claims.claim_includes.order("created_at desc")
@user_claimed = 0
journal_params = {
journalized_id: params[:issue_id],
journalized_type: "Issue",
user_id: current_user.id ,
notes: "取消声明",
}
journal = Journal.new(journal_params)
if journal.save
render file: 'app/views/claims/list.json.jbuilder'
else
normal_status(-1,"新建声明关联评论操作失败")
end
else
normal_status(-1,"取消声明操作失败")
end
end
end
end
private
def parse_issue_params(params)
{
issue_id: params[:issue_id],
user_id: current_user.id,
note: params[:claim_note],
}
end
def set_issue
@issue = Issue.find_by_id(params[:issue_id])
unless @issue.present?
normal_status(-1, "易修不存在")
end
end
end

View File

@ -28,7 +28,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
def create
ActiveRecord::Base.transaction do
tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name])
Organizations::CreateForm.new(organization_params).validate!
Organizations::CreateForm.new(organization_params.merge(original_name: @organization.login)).validate!
@organization = Organizations::CreateService.call(current_user, organization_params)
Util.write_file(@image, avatar_path(@organization)) if params[:image].present?
end
@ -39,7 +39,7 @@ class Organizations::OrganizationsController < Organizations::BaseController
def update
ActiveRecord::Base.transaction do
Organizations::CreateForm.new(organization_params).validate!
Organizations::CreateForm.new(organization_params.merge(original_name: @organization.login)).validate!
login = @organization.login
@organization.login = organization_params[:name] if organization_params[:name].present?
@organization.nickname = organization_params[:nickname] if organization_params[:nickname].present?

View File

@ -16,13 +16,13 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("home"))
menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code")
menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues")
menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls")
menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki")
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops")
menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls") && @project.forge?
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") && @project.forge?
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions")
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources")
menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") && @project.forge?
menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") && @project.forge?
menu.append(menu_hash_by_name("activity"))
menu.append(menu_hash_by_name("settings")) if current_user.admin? || @project.manager?(current_user)
menu.append(menu_hash_by_name("settings")) if (current_user.admin? || @project.manager?(current_user)) && @project.forge?
render json: menu
end

View File

@ -56,7 +56,7 @@ class PullRequestsController < ApplicationController
end
def create
return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user)
# return normal_status(-1, "您不是目标分支开发者,没有权限,请联系目标分支作者.") unless @project.operator?(current_user)
ActiveRecord::Base.transaction do
@pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params)
if @gitea_pull_request[:status] == :success
@ -89,6 +89,7 @@ class PullRequestsController < ApplicationController
else
ActiveRecord::Base.transaction do
begin
return normal_status(-1, "title不能超过255个字符") if params[:title].length > 255
merge_params
@issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank?

View File

@ -1,368 +1,387 @@
class RepositoriesController < ApplicationController
include RepositoriesHelper
include ApplicationHelper
include OperateProjectAbilityAble
include Repository::LanguagesPercentagable
before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
before_action :require_profile_completed, only: [:create_file]
before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
before_action :get_ref, only: %i[entries sub_entries top_counts file archive]
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts]
def files
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
render json: result
end
# 新版项目详情
def detail
@user = current_user
@result = Repositories::DetailService.call(@owner, @repository, @user)
@project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def show
@user = current_user
@repo = @project.repository
@result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil
@project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def entries
@project.increment!(:visits)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
@entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
end
end
def top_counts
@result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call
end
def sub_entries
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
if @project.educoder?
if params[:type] === 'file'
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
logger.info "######### sub_entries: #{@sub_entries}"
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = [{
"content" => @sub_entries['data']['content'],
"type" => "blob"
}]
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
else
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
end
else
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
else
render_error(interactor.error)
end
end
end
def commits
if @project.educoder?
@hash_commit = nil
else
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end
end
end
def commits_slice
@hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner&.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
end
def commit
@sha = params[:sha]
if @project.educoder?
@commit = {}
@commit_diff ={}
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
end
end
def tags
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
def contributors
if params[:filepath].present?
@contributors = []
else
@contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
end
end
def edit
return render_forbidden if !@project.manager?(current_user) && !current_user.admin?
end
def create_file
interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params)
if interactor.success?
@file = interactor.result
# create_new_pr(params)
#如果是更新流水线文件
if params[:pipeline_id]
update_pipeline(params[:pipeline_id])
end
else
render_error(interactor.error)
end
end
def update_pipeline(pipeline_id)
pipeline = Ci::Pipeline.find(pipeline_id)
if pipeline
pipeline.update!(sync: 1)
end
end
def update_file
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
if interactor.success?
@file = interactor.result
# TODO: 是否创建pr
# create_new_pr(params)
render_result(1, "更新成功")
else
render_error(interactor.error)
end
end
def delete_file
interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
if interactor.success?
@file = interactor.result
render_result(1, "文件删除成功")
else
render_error(interactor.error)
end
end
def repo_hook
end
def sync_mirror
return render_error("正在镜像中..") if @repository.mirror.waiting?
@repository.sync_mirror!
SyncMirroredRepositoryJob.perform_later(@repository.id, current_user.id)
render_ok
end
def readme
if params[:filepath].present?
result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha")
rescue
render json: nil
end
def languages
render json: languages_precentagable
end
def archive
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{params[:archive]}"
file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
return render_not_found if !request.format.zip? && !request.format.gzip?
redirect_to file_path
end
def raw
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{params[:filepath]}?ref=#{params[:ref]}"
file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") if @repository.hidden?
redirect_to URI.escape(file_path)
end
private
def find_project
@project = Project.find params[:id]
render_not_found("未找到相关的仓库") unless @project
end
def find_project_with_includes
@project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id]
end
def authorizate!
return if current_user && current_user.admin?
if @project.repository.hidden? && !@project.member?(current_user)
render_forbidden
end
end
# TODO 获取最新commit信息
def project_commits
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
else
Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
end
end
def get_statistics
@branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size
@tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size
end
def get_ref
@ref = params[:ref] || @project&.default_branch
end
def get_latest_commit
latest_commit = @project.educoder? ? nil : project_commits
@latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil
@commits_count = latest_commit.present? ? latest_commit[:total_count] : 0
end
def content_params
{
filepath: params[:filepath],
branch: params[:branch],
new_branch: params[:new_branch],
content: params[:content],
message: params[:message],
committer: {
email: current_user.mail,
name: current_user.login
},
identifier: @project.identifier
}
end
def hook_params(hook_type, params)
# if hook_type == "push"
# # TODO hook返回的记录中暂时没有文件代码数量的增减暂时根据 commits数量来计算
# uploadPushInfo = {
# "sha": params["commits"].present? ? params["commits"].last : "",
# "branch": params["ref"].to_s.split("/").last,
# "modification_lines": params["commits"].length
# }
# elsif hook_type == "pull_request" && params["action"].to_s == "closed" #合并请求合并后才会有上链操作
# uploadPushInfo = {
# "branch": params["base"]["ref"].to_s.split("/").last,
# "sha": params["pull_request"]["merge_base"],
# "modification_lines": 1 #pull_request中没有commits数量
# }
# else
# uploadPushInfo = {}
# end
# uploadPushInfo
end
def create_new_pr(params)
if params[:new_branch].present? && params[:new_branch] != params[:branch]
local_params = {
title: params[:message], #标题
body: params[:content], #内容
head: params[:new_branch], #源分支
base: params[:branch], #目标分支
milestone: 0 #里程碑,未与本地的里程碑关联
}
requests_params = local_params.merge({
assignee: current_user.try(:login),
assignees: [],
labels: [],
due_date: Time.now
})
issue_params = {
author_id: current_user.id,
project_id: @project.id,
subject: params[:message],
description: params[:content],
assigned_to_id: nil,
fixed_version_id: nil,
issue_tags_value: nil,
issue_classify: "pull_request",
issue_type: "1",
tracker_id: 2,
status_id: 1,
priority_id: params[:priority_id] || "2"
}
@pull_issue = Issue.new(issue_params)
if @pull_issue.save!
local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id))
if local_requests.save
gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call
if gitea_request[:status] == :success && local_requests.update_attributes(gitea_number: gitea_request["body"]["number"], gpid: gitea_request["body"]["number"])
local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
end
end
end
end
end
end
class RepositoriesController < ApplicationController
include RepositoriesHelper
include ApplicationHelper
include OperateProjectAbilityAble
include Repository::LanguagesPercentagable
before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
before_action :require_profile_completed, only: [:create_file]
before_action :load_repository
before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
before_action :get_ref, only: %i[entries sub_entries top_counts file archive]
before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
before_action :get_statistics, only: %i[top_counts]
def files
result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
render json: result
end
# 新版项目详情
def detail
@user = current_user
@result = Repositories::DetailService.call(@owner, @repository, @user)
@project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def show
@user = current_user
@repo = @project.repository
@result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil
@project_fork_id = @project.try(:forked_from_project_id)
if @project_fork_id.present?
@fork_project = Project.find_by(id: @project_fork_id)
@fork_project_user = @fork_project.owner
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
end
def entries
@project.increment!(:visits)
CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
if @project.educoder?
@entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
else
@entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
@entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
end
end
def top_counts
@result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call
end
def sub_entries
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
if @project.educoder?
if params[:type] === 'file'
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
logger.info "######### sub_entries: #{@sub_entries}"
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
}
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
else
begin
@sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
if @sub_entries.blank? || @sub_entries['status'].to_i === -1
@sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
return render_error('该文件暂未开放,敬请期待.') if @sub_entries['status'].to_i === -1
tmp_entries = {
"content" => @sub_entries['data']['content'],
"type" => "blob"
}
@sub_entries = {
"trees"=>tmp_entries,
"commits" => [{}]
}
end
rescue
return render_error('该文件暂未开放,敬请期待.')
end
end
else
@path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
if interactor.success?
result = interactor.result
@sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
else
render_error(interactor.error)
end
end
end
def commits
if @project.educoder?
@commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
else
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
@hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
else
@hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call
end
end
end
def commits_slice
@hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
end
def commit
@sha = params[:sha]
if @project.educoder?
return render_error('暂未开放,敬请期待.')
else
@commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token)
@commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token, {diff: true})
end
end
def tags
result = Gitea::Repository::Tags::ListService.call(current_user&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})
@tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
end
def contributors
if params[:filepath].present? || @project.educoder?
@contributors = []
else
@contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier)
end
end
def edit
return render_forbidden if !@project.manager?(current_user) && !current_user.admin?
end
def create_file
interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params)
if interactor.success?
@file = interactor.result
# create_new_pr(params)
#如果是更新流水线文件
if params[:pipeline_id]
update_pipeline(params[:pipeline_id])
end
else
render_error(interactor.error)
end
end
def update_pipeline(pipeline_id)
pipeline = Ci::Pipeline.find(pipeline_id)
if pipeline
pipeline.update!(sync: 1)
end
end
def update_file
interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
if interactor.success?
@file = interactor.result
# TODO: 是否创建pr
# create_new_pr(params)
render_result(1, "更新成功")
else
render_error(interactor.error)
end
end
def delete_file
interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
if interactor.success?
@file = interactor.result
render_result(1, "文件删除成功")
else
render_error(interactor.error)
end
end
def repo_hook
end
def sync_mirror
return render_error("正在镜像中..") if @repository.mirror.waiting?
@repository.sync_mirror!
SyncMirroredRepositoryJob.perform_later(@repository.id, current_user.id)
render_ok
end
def readme
if params[:filepath].present?
result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token)
else
result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token)
end
@path = Gitea.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
@readme = result[:status] === :success ? result[:body] : nil
@readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha")
rescue
render json: nil
end
def languages
if @project.educoder?
render json: {}
else
render json: languages_precentagable
end
end
def archive
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{URI.escape(params[:archive])}"
file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
return render_not_found if !request.format.zip? && !request.format.gzip?
redirect_to file_path
end
def raw
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{params[:filepath]}?ref=#{params[:ref]}"
file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
redirect_to URI.escape(file_path)
end
private
def find_project
@project = Project.find params[:id]
render_not_found("未找到相关的仓库") unless @project
end
def find_project_with_includes
@project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id]
end
def authorizate!
return if current_user && current_user.admin?
if @project.repository.hidden? && !@project.member?(current_user)
render_forbidden
end
end
# TODO 获取最新commit信息
def project_commits
if params[:filepath].present?
file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
else
Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call
end
end
def get_statistics
@branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size
@tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(current_user&.gitea_token, @project.owner.login, @project.identifier).call&.size
end
def get_ref
@ref = params[:ref] || @project&.default_branch
end
def get_latest_commit
latest_commit = @project.educoder? ? nil : project_commits
@latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil
@commits_count = latest_commit.present? ? latest_commit[:total_count] : 0
end
def content_params
{
filepath: params[:filepath],
branch: params[:branch],
new_branch: params[:new_branch],
content: params[:content],
message: params[:message],
committer: {
email: current_user.mail,
name: current_user.login
},
identifier: @project.identifier
}
end
def hook_params(hook_type, params)
# if hook_type == "push"
# # TODO hook返回的记录中暂时没有文件代码数量的增减暂时根据 commits数量来计算
# uploadPushInfo = {
# "sha": params["commits"].present? ? params["commits"].last : "",
# "branch": params["ref"].to_s.split("/").last,
# "modification_lines": params["commits"].length
# }
# elsif hook_type == "pull_request" && params["action"].to_s == "closed" #合并请求合并后才会有上链操作
# uploadPushInfo = {
# "branch": params["base"]["ref"].to_s.split("/").last,
# "sha": params["pull_request"]["merge_base"],
# "modification_lines": 1 #pull_request中没有commits数量
# }
# else
# uploadPushInfo = {}
# end
# uploadPushInfo
end
def create_new_pr(params)
if params[:new_branch].present? && params[:new_branch] != params[:branch]
local_params = {
title: params[:message], #标题
body: params[:content], #内容
head: params[:new_branch], #源分支
base: params[:branch], #目标分支
milestone: 0 #里程碑,未与本地的里程碑关联
}
requests_params = local_params.merge({
assignee: current_user.try(:login),
assignees: [],
labels: [],
due_date: Time.now
})
issue_params = {
author_id: current_user.id,
project_id: @project.id,
subject: params[:message],
description: params[:content],
assigned_to_id: nil,
fixed_version_id: nil,
issue_tags_value: nil,
issue_classify: "pull_request",
issue_type: "1",
tracker_id: 2,
status_id: 1,
priority_id: params[:priority_id] || "2"
}
@pull_issue = Issue.new(issue_params)
if @pull_issue.save!
local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id))
if local_requests.save
gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call
if gitea_request[:status] == :success && local_requests.update_attributes(gpid: gitea_request["body"]["number"])
local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
end
end
end
end
end
end

View File

@ -0,0 +1,852 @@
# Projects
## 申请加入项目
申请加入项目
> 示例:
```shell
curl -X POST http://localhost:3000/api/applied_projects.json
```
```javascript
await octokit.request('POST /api/appliedr_projects.json')
```
### HTTP 请求
`POST /api/applied_projects.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|applied_project.code |是| |string |邀请码 |
|applied_project.role |否| |string |项目权限reporter: 报告者, developer: 开发者manager管理员 |
> 请求的JSON示例
```json
{
"applied_project": {
"code": "1una34",
"role": "developer"
}
}
```
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |申请id |
|status |string |申请状态canceled:取消,common:正在申请, accept:已接受,refuse:已拒绝|
|time_ago |string |项目申请创建的时间 |
|project.id |int |申请项目的id |
|project.identifier |string |申请项目的标识 |
|project.name |string |申请项目的名称 |
|project.description |string |申请项目的描述 |
|project.is_public |bool |申请项目是否公开 |
|project.owner.id |bool |申请项目拥有者id |
|project.owner.type |string |申请项目拥有者类型 |
|project.owner.name |string |申请项目拥有者昵称 |
|project.owner.login |string |申请项目拥有者标识 |
|project.owner.image_url |string |申请项目拥有者头像 |
|user.id |int |申请创建者的id |
|user.type |string |申请创建者的类型 |
|user.name |string |申请创建者的名称 |
|user.login |string |申请创建者的标识 |
|user.image_url |string |申请创建者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 74,
"identifier": "hehuisssjssjjsjs",
"name": "hehuisssjssjjsjs",
"description": "wwww",
"is_public": false,
"owner": {
"id": 10,
"type": "User",
"name": "testforge1",
"login": "testforge1",
"image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png"
}
},
"user": {
"id": 6,
"type": "User",
"name": "何慧",
"login": "yystopf",
"image_url": "images/avatars/User/6?t=1622513134"
},
"id": 7,
"status": "common",
"created_at": "2021-06-09 16:41",
"time_ago": "1分钟前"
}
```
## 获取项目列表
获取项目列表,也可以更加相关条件过滤搜素
> 示例:
```shell
curl -X GET \
-d "page=1" \
-d "limit=5" \
http://localhost:3000/api/projects | jq
```
```javascript
await octokit.request('GET /api/projects')
```
### HTTP 请求
`GET api/projects`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
page | false | 1 | string | 页数,第几页
limit | false | 15 | string | 每页多少条数据默认15条
sort_by | false | | string | 排序类型, 取值updated_on、created_on、forked_count、praises_count; updated_on: 更新时间排序created_on: 创建时间排序forked_count: fork数据排序praises_count: 点赞数量排序默认为updated_on更新时间排序
sort_direction| false | | string | 排序方式,取值为: desc、asc; desc: 降序排序, asc: 升序排序, 默认为desc
search | false | | string | 按照项目名称搜索
category_id | false | | int | 项目类别id
language_id | false | | int | 项目语言id
project_type | false | | string | 项目类型, 取值为common、mirror; common:开源托管项目, mirror:开源镜像项目
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count | int | 项目总条数
id | string | 项目id
name | string | 项目名称
description | string | 项目简介
visits | int | 流量数
forked_count | int | 被fork的数量
praises_count | int | star数量
is_public | boolean | 是否公开, true:公开false:未公开
mirror_url | string | 镜像url
last_update_time| int | 最后更新时间为UNIX格式的时间戳
author | object | 项目创建者
-- name | string | 用户名,也是用户标识
category | object | 项目类别
-- id | int | 项目类型id
-- name | string | 项目类型名称
language | object | 项目语言
-- id | int | 项目语言id
-- name | string | 项目语言名称
> 返回的JSON示例:
```json
{
"total_count": 3096,
"projects": [
{
"id": 1400794,
"repo_id": 1402452,
"identifier": "cscw_2021_sponsor",
"name": "Sponsor机制下的开源贡献",
"description": "CSCW 2021 sponsor机制研究",
"visits": 5,
"praises_count": 0,
"forked_count": 0,
"is_public": true,
"mirror_url": null,
"type": 0,
"last_update_time": 1611971671,
"time_ago": "2天前",
"forked_from_project_id": null,
"open_devops": false,
"platform": "forge",
"author": {
"name": "张迅晖",
"login": "Nigel",
"image_url": "images/avatars/User/3675?t=1611832880"
},
"category": {
"id": 13,
"name": "云计算和大数据"
},
"language": {
"id": 34,
"name": "Python3.6"
}
}
]
}
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 推荐项目
获取推荐项目列表
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/projects/recommend | jq
```
```javascript
await octokit.request('GET /api/projects/recommend.json')
```
### HTTP 请求
`GET api/projects/recommend`
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
total_count |int |项目总条数
id |string |项目id
name |string|项目名称
description |string|项目简介
visits |int|流量数
forked_count |int|被fork的数量
praises_count |int|star数量
is_public |boolean|是否公开, true:公开false:未公开
mirror_url |string|镜像url
last_update_time|int|最后更新时间为UNIX格式的时间戳
author |object|项目创建者
-- name |string|用户名,也是用户标识
category |object|项目类别
-- id |int|项目类型id
-- name |string|项目类型名称
language |object|项目语言
-- id |int|项目语言id
-- name |string|项目语言名称
> 返回的JSON示例:
```json
[
{
"id": 20,
"repo_id": 2,
"identifier": "PNAekinmH",
"name": "FNILL",
"visits": 13567,
"author": {
"name": "王一达",
"login": "wangyida",
"image_url": "avatars/User/b"
},
"category": {
"id": 8,
"name": "其他"
}
}
]
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目导航
获取项目导航信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/menu_list | jq
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/menu_list')
```
### HTTP 请求
`GET api/:owner/:repo/menu_list`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
menu_name |string|导航名称, home:主页,code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置
> 返回的JSON示例:
```json
[
{
"menu_name": "home"
},
{
"menu_name": "code"
},
{
"menu_name": "pulls"
},
{
"menu_name": "activity"
}
]
```
## 项目主页
获取项目主页信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/jasder/forgeplus/about | jq
```
```javascript
await octokit.request('GET /api/jasder/forgeplus/about')
```
### HTTP 请求
`GET api/:owner/:repo/about`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
identifier |string|project's identifier
content |string|主页内容
attachments |array|附件
> 返回的JSON示例:
```json
{
"content": "",
"identifier": "forgeplus",
attachments: []
}
```
<aside class="success">
Remember — a happy kitten is an authenticated kitten!
</aside>
## 项目模块信息
项目模块信息
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('GET /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`GET /api/yystopf/ceshi/project_units`
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|type |string|模块名称 |
> 返回的JSON示例:
```json
[
{
"type": "code"
},
{
"type": "pulls"
},
{
"type": "issues"
}
]
```
## 更改项目模块展示
更改项目模块展示
> 示例:
```shell
curl -X POST \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d "{ \"unit_typs\": [\"code\", \"pulls\"]}" \
http://localhost:3000/api/yystopf/ceshi/project_units.json
```
```javascript
await octokit.request('POST /api/yystopf/ceshi/project_units')
```
### HTTP 请求
`POST /api/yystopf/ceshi/project_units`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|unit_types |是| |array | 项目模块内容, 支持以下参数:code:代码库,issues:疑修,pulls:合并请求,devops:工作流,versions:里程碑 |
### 返回字段说明:
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int|返回状态, 0: 表示操作成功 |
|message |string|返回信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## 创建项目
创建项目
> 示例:
```shell
curl -X POST \
-d "user_id=36401" \
-d "name=hnfl_demo" \
-d "description=my first project" \
-d "repository_name=hnfl_demo" \
-d "project_category_id=1" \
-d "project_language_id=2" \
-d "ignore_id=2" \
-d "license_id=1" \
http://localhost:3000/api/projects.json
```
```javascript
await octokit.request('GET /api/projects.json')
```
### HTTP 请求
`POST api/projects`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|user_id |是| |int |用户id或者组织id |
|name |是| |string |项目名称 |
|description |是| |string |项目描述 |
|repository_name |是| |string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
|project_category_id|是| |int |项目类别id |
|project_language_id|是| |int |项目语言id |
|ignore_id |否| |int |gitignore相关id |
|license_id |否| |int |开源许可证id |
|private |否| |boolean|项目是否私有, true为私有false: 公开,默认为公开 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
> 返回的JSON示例:
```json
{
"id": 3240,
"name": "好项目"
}
```
## 创建镜像项目
创建镜像项目
> 示例:
```shell
curl -X POST \
-d "user_id=36408" \
-d "clone_addr=https://gitea.com/mx8090alex/golden.git" \
-d "name=golden_mirror1" \
-d "description=golden_mirror" \
-d "project_category_id=1" \
-d "project_language_id=2" \
http://localhost:3000/api/projects/migrate.json
```
```javascript
await octokit.request('GET /api/projects/migrate.json')
```
### HTTP 请求
`POST api/projects/migrate.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|user_id |是| |int |用户id或者组织id |
|name |是| |string |项目名称 |
|clone_addr |是| |string |镜像项目clone地址 |
|description |否| |string |项目描述 |
|repository_name |是| |string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 |
|project_category_id|是| |int |项目类别id |
|project_language_id|是| |int |项目语言id |
|is_mirror |否| |boolean|是否设置为镜像, true false默认为否 |
|auth_username |否| |string|镜像源仓库的登录用户名 |
|auth_password |否| |string|镜像源仓库的登录秘密 |
|private |否| |boolean|项目是否私有, true为私有false: 非私有,默认为公开 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |id |
|name |string|项目名称|
> 返回的JSON示例:
```json
{
"id": 3241,
"name": "这是一个镜像项目"
}
```
## 同步镜像
手动同步镜像
> 示例:
```shell
curl -X POST http://localhost:3000/api/repositories/1244/sync_mirror.json
```
```javascript
await octokit.request('POST /api/repositories/1244/sync_mirror.json')
```
### HTTP 请求
`POST api/repositories/:id/sync_mirror.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|id |是| |int |仓库id |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|status |int |状态码, 0:标识请求成功 |
|message |string|服务端返回的信息说明|
> 返回的JSON示例:
```json
{
"status": 0,
"message": "success"
}
```
## Fork项目
fork项目
> 示例:
```shell
curl -X POST http://localhost:3000/api/jasder/forgeplus/forks.json
```
```javascript
await octokit.request('POST /api/jaser/jasder_test/forks.json')
```
### HTTP 请求
`POST api/:owner/:repo/forks.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |项目id |
|identifier |string|项目标识|
> 返回的JSON示例:
```json
{
"id": 3290,
"identifier": "newadm"
}
```
## 用户管理的组织列表
用户管理的组织列表
> 示例:
```shell
curl -X GET \
http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/organizations.json | jq
```
```javascript
await octokit.request('GET /api/:owner/:repo/applied_transfer_projects/organizations')
```
### HTTP 请求
`GET api/:owner/:repo/applied_transfer_projects/organizations`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
owner |是| |string |用户登录名
repo |是| |string |项目标识identifier
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
name |string|组织标识
nickname |string|组织名称
description|string|组织描述
avatar_url|string组织头像
> 返回的JSON示例:
```json
{
"total_count": 3,
"organizations": [
{
"id": 9,
"name": "ceshi_org",
"nickname": "测试组织",
"description": "测试组织",
"avatar_url": "images/avatars/Organization/9?t=1612706073"
},
{
"id": 51,
"name": "ceshi",
"nickname": "测试组织哈哈哈",
"description": "23212312",
"avatar_url": "images/avatars/Organization/51?t=1618800723"
},
{
"id": 52,
"name": "ceshi1",
"nickname": "身份卡手动阀",
"description": "1231手动阀是的",
"avatar_url": "images/avatars/Organization/52?t=1618805056"
}
]
}
```
## 迁移项目
迁移项目edit接口is_transfering为true表示正在迁移
> 示例:
```shell
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects.json
```
```javascript
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects.json')
```
### HTTP 请求
`POST /api/:owner/:repo/applied_transfer_projects.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
|owner_name|是| |string |迁移对象标识 |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |项目id |
|status |string |项目迁移状态canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
|time_ago |string |项目迁移创建的时间 |
|project.id |int |迁移项目的id |
|project.identifier |string |迁移项目的标识 |
|project.name |string |迁移项目的名称 |
|project.description |string |迁移项目的描述 |
|project.is_public |bool |迁移项目是否公开 |
|project.owner.id |bool |迁移项目拥有者id |
|project.owner.type |string |迁移项目拥有者类型 |
|project.owner.name |string |迁移项目拥有者昵称 |
|project.owner.login |string |迁移项目拥有者标识 |
|project.owner.image_url |string |迁移项目拥有者头像 |
|user.id |int |迁移创建者的id |
|user.type |string |迁移创建者的类型 |
|user.name |string |迁移创建者的名称 |
|user.login |string |迁移创建者的标识 |
|user.image_url |string |迁移创建者头像 |
|owner.id |int |迁移接受者的id |
|owner.type |string |迁移接受者的类型 |
|owner.name |string |迁移接受者的名称 |
|owner.login |string |迁移接受者的标识 |
|owner.image_url |string |迁移接受者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 86,
"identifier": "ceshi_repo1",
"name": "测试项目啊1",
"description": "二十多",
"is_public": true,
"owner": {
"id": 52,
"type": "Organization",
"name": "身份卡手动阀",
"login": "ceshi1",
"image_url": "images/avatars/Organization/52?t=1618805056"
}
},
"user": {
"id": 6,
"type": "User",
"name": "yystopf",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
},
"owner": {
"id": 9,
"type": "Organization",
"name": "测试组织",
"login": "ceshi_org",
"image_url": "images/avatars/Organization/9?t=1612706073"
},
"id": 4,
"status": "common",
"created_at": "2021-04-26 09:54",
"time_ago": "1分钟前"
}
```
## 取消迁移项目
迁移项目edit接口is_transfering为true表示正在迁移
> 示例:
```shell
curl -X POST http://localhost:3000/api/ceshi1/ceshi_repo1/applied_transfer_projects/cancel.json
```
```javascript
await octokit.request('POST /api/:owner/:repo/applied_transfer_projects/cancel.json')
```
### HTTP 请求
`POST /api/:owner/:repo/applied_transfer_projects/cancel.json`
### 请求参数
参数 | 必选 | 默认 | 类型 | 字段说明
--------- | ------- | ------- | -------- | ----------
|owner |是| |string |用户登录名 |
|repo |是| |string |项目标识identifier |
### 返回字段说明
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|id |int |迁移id |
|status |string |迁移状态canceled:取消,common:正在迁移, accept:已接受,refuse:已拒绝|
|time_ago |string |迁移创建的时间 |
|project.id |int |迁移项目的id |
|project.identifier |string |迁移项目的标识 |
|project.name |string |迁移项目的名称 |
|project.description |string |迁移项目的描述 |
|project.is_public |bool |迁移项目是否公开 |
|project.owner.id |bool |迁移项目拥有者id |
|project.owner.type |string |迁移项目拥有者类型 |
|project.owner.name |string |迁移项目拥有者昵称 |
|project.owner.login |string |迁移项目拥有者标识 |
|project.owner.image_url |string |迁移项目拥有者头像 |
|user.id |int |迁移创建者的id |
|user.type |string |迁移创建者的类型 |
|user.name |string |迁移创建者的名称 |
|user.login |string |迁移创建者的标识 |
|user.image_url |string |迁移创建者头像 |
|owner.id |int |迁移接受者的id |
|owner.type |string |迁移接受者的类型 |
|owner.name |string |迁移接受者的名称 |
|owner.login |string |迁移接受者的标识 |
|owner.image_url |string |迁移接受者头像 |
> 返回的JSON示例:
```json
{
"project": {
"id": 86,
"identifier": "ceshi_repo1",
"name": "测试项目啊1",
"description": "二十多",
"is_public": true,
"owner": {
"id": 52,
"type": "Organization",
"name": "身份卡手动阀",
"login": "ceshi1",
"image_url": "images/avatars/Organization/52?t=1618805056"
}
},
"user": {
"id": 6,
"type": "User",
"name": "yystopf",
"login": "yystopf",
"image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png"
},
"owner": {
"id": 9,
"type": "Organization",
"name": "测试组织",
"login": "ceshi_org",
"image_url": "images/avatars/Organization/9?t=1612706073"
},
"id": 4,
"status": "common",
"created_at": "2021-04-26 09:54",
"time_ago": "1分钟前"
}
```

View File

@ -1240,11 +1240,11 @@ await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json')
|delete|分支或标签删除|
|fork|仓库被fork|
|push|git仓库推送|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|pull_request|合并请求|
|pull_request_assign|合并请求被指派|
|pull_request_label|合并请求被贴上标签|
@ -1337,11 +1337,11 @@ await octokit.request('POST /api/yystopf/ceshi/webhooks.json')
|delete|分支或标签删除|
|fork|仓库被fork|
|push|git仓库推送|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|pull_request|合并请求|
|pull_request_assign|合并请求被指派|
|pull_request_label|合并请求被贴上标签|
@ -1440,11 +1440,11 @@ await octokit.request('PATCH /api/yystopf/ceshi/webhooks/7.json')
|delete|分支或标签删除|
|fork|仓库被fork|
|push|git仓库推送|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|issue|修已打开、已关闭、已重新打开或编辑|
|issue_assign|修被指派|
|issue_label|修标签被更新或删除|
|issue_milestone|修被收入里程碑|
|issue_comment|修评论|
|pull_request|合并请求|
|pull_request_assign|合并请求被指派|
|pull_request_label|合并请求被贴上标签|

View File

@ -90,13 +90,13 @@ await octokit.request('GET /api/users/:login/messages.json')
#### 消息来源source字段说明
类型|说明
--------- | -----------
|IssueAssigned | 有新指派给我的修 |
|IssueAssignerExpire | 我负责的修截止日期到达最后一天 |
|IssueAtme | 在修中@我 |
|IssueChanged | 我创建或负责的修状态变更 |
|IssueCreatorExpire | 我创建的修截止日期到达最后一天 |
|IssueDeleted | 我创建或负责的修删除 |
|IssueJournal | 我创建或负责的修有新的评论 |
|IssueAssigned | 有新指派给我的修 |
|IssueAssignerExpire | 我负责的修截止日期到达最后一天 |
|IssueAtme | 在修中@我 |
|IssueChanged | 我创建或负责的修状态变更 |
|IssueCreatorExpire | 我创建的修截止日期到达最后一天 |
|IssueDeleted | 我创建或负责的修删除 |
|IssueJournal | 我创建或负责的修有新的评论 |
|LoginIpTip | 登录异常提示 |
|OrganizationJoined | 账号被拉入组织 |
|OrganizationLeft | 账号被移出组织 |
@ -104,7 +104,7 @@ await octokit.request('GET /api/users/:login/messages.json')
|ProjectDeleted | 我关注的仓库被删除 |
|ProjectFollowed | 我管理的仓库被关注 |
|ProjectForked | 我管理的仓库被复刻 |
|ProjectIssue | 我管理/关注的仓库有新的修 |
|ProjectIssue | 我管理/关注的仓库有新的修 |
|ProjectJoined | 账号被拉入项目 |
|ProjectLeft | 账号被移出项目 |
|ProjectMemberJoined | 我管理的仓库有成员加入 |
@ -250,7 +250,7 @@ await octokit.request('POST /api/users/:login/messages.json')
--------- | ----------- | -----------
|type | string | 消息类型 |
|receivers_login | array | 需要发送消息的用户名数组|
|atmeable_type | string | atme消息对象是从哪里@我的比如评论Journal、Issue、合并请求PullRequest |
|atmeable_type | string | atme消息对象是从哪里@我的比如评论Journal、Issue、合并请求PullRequest |
|atmeable_id | integer | atme消息对象id |
> 请求的JSON示例:
@ -469,7 +469,7 @@ await octokit.request('GET /api/template_message_settings.json')
"total_settings_count": 4,
"settings": [
{
"name": "修被指派",
"name": "修被指派",
"key": "IssueAssigned",
"notification_disabled": true,
"email_disabled": false
@ -488,7 +488,7 @@ await octokit.request('GET /api/template_message_settings.json')
"total_settings_count": 4,
"settings": [
{
"name": "有新的修",
"name": "有新的修",
"key": "Issue",
"notification_disabled": true,
"email_disabled": false
@ -875,7 +875,7 @@ await octokit.request('GET /api/users/:login/statistics/activity.json')
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|dates |array |时间 |
|issues_count |array |修数量|
|issues_count |array |修数量|
|pull_requests_count |array |合并请求数量|
|commtis_count |array |贡献数量|
@ -1084,7 +1084,7 @@ await octokit.request('GET /api/users/:login/project_trends.json')
参数 | 类型 | 字段说明
--------- | ----------- | -----------
|total_count |int |所选时间内的总动态数 |
|project_trends.trend_type |string|动态类型IssueVersionRelease版本发布PullRequest合并请求|
|project_trends.trend_type |string|动态类型IssueVersionRelease版本发布PullRequest合并请求|
|project_trends.action_type |string|操作类型|
|project_trends.trend_id |integer|动态id|
|project_trends.user_name |string|用户名称|

View File

@ -1,6 +1,6 @@
class Organizations::CreateForm < BaseForm
NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾
attr_accessor :name, :description, :website, :location, :repo_admin_change_team_access, :visibility, :max_repo_creation, :nickname
attr_accessor :name, :description, :website, :location, :repo_admin_change_team_access, :visibility, :max_repo_creation, :nickname, :original_name
validates :name, :nickname, :visibility, presence: true
validates :name, :nickname, length: { maximum: 100 }
@ -9,7 +9,7 @@ class Organizations::CreateForm < BaseForm
validates :name, format: { with: NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" }
validate do
check_name(name) unless name.blank?
check_name(name) unless name.blank? || name == original_name
end
def check_name(name)

View File

@ -4,5 +4,5 @@ class Users::LoginForm
attr_accessor :password, :login
validates :login, presence: true
validates :password, presence: true, length: { minimum: 8, maximum: 16 }, format: { with: CustomRegexp::PASSWORD, message: "8~16位支持字母数字和符号" }
validates :password, presence: true, length: { minimum: 6, maximum: 16 }, format: { with: CustomRegexp::LOGIN_PASSWORD, message: "6~16位支持字母数字和符号" }
end

View File

@ -45,7 +45,7 @@ module RepositoriesHelper
end
end
def readme_render_decode64_content(str, path)
def readme_render_decode64_content(str, owner, repo, ref)
return nil if str.blank?
begin
content = Base64.decode64(str).force_encoding('UTF-8')
@ -63,13 +63,16 @@ module RepositoriesHelper
if remove_title.length > 0
r_content = r_content.gsub(/#{remove_title[0]}/, "").strip
end
if r_content.include?("?")
new_r_content = r_content + "&raw=true"
else
new_r_content = r_content + "?raw=true"
end
# if r_content.include?("?")
# new_r_content = r_content + "&raw=true"
# else
# new_r_content = r_content + "?raw=true"
# end
new_r_content = r_content
unless r_content.include?("http://") || r_content.include?("https://") || r_content.include?("mailto:")
new_r_content = "#{path}" + new_r_content
# new_r_content = "#{path}" + new_r_content
new_r_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw?filepath=#{r_content}&ref=#{ref}"].join
end
content = content.gsub(/#{r_content}/, new_r_content)
end
@ -94,7 +97,7 @@ module RepositoriesHelper
def decode64_content(entry, owner, repo, ref, path=nil)
if is_readme?(entry['type'], entry['name'])
content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content']
readme_render_decode64_content(content, path)
readme_render_decode64_content(content, owner, repo, ref)
else
file_type = File.extname(entry['name'].to_s)[1..-1]
if image_type?(file_type)

View File

@ -26,8 +26,12 @@ module TagChosenHelper
end
def render_branches(project)
branches = Gitea::Repository::Branches::ListService.call(project&.owner, project.identifier)
branches.collect{|i| i["name"] if i.is_a?(Hash)}
if project.educoder?
return ['master']
else
branches = Gitea::Repository::Branches::ListService.call(project&.owner, project.identifier)
branches.collect{|i| i["name"] if i.is_a?(Hash)}
end
end
def render_cache_trackers

View File

@ -37,6 +37,7 @@ class SendTemplateMessageJob < ApplicationJob
issue = Issue.find_by_id(issue_id)
return unless operator.present? && issue.present?
# receivers = receivers.where.not(id: operator&.id)
receivers = User.where(id: receivers)
receivers_string, content, notification_url = MessageTemplate::IssueAtme.get_message_content(receivers, operator, issue)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2, operator_id)
when 'IssueChanged'
@ -235,6 +236,7 @@ class SendTemplateMessageJob < ApplicationJob
pull_request = PullRequest.find_by_id(pull_request_id)
return unless operator.present? && pull_request.present?
# receivers = receivers.where.not(id: operator&.id)
receivers = User.where(id: receivers)
receivers_string, content, notification_url = MessageTemplate::PullRequestAtme.get_message_content(receivers, operator, pull_request)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, pull_request_id: pull_request.id}, 2, operator_id)
when 'PullRequestChanged'

View File

@ -5,6 +5,7 @@ module CustomRegexp
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/
LOGIN_PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{6,16}\z/
URL = /\Ahttps?:\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#\/%=~_|]\z/
IP = /^((\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/

21
app/models/claim.rb Normal file
View File

@ -0,0 +1,21 @@
# == Schema Information
#
# Table name: claims
#
# id :integer not null, primary key
# issue_id :integer
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
# note :text(65535)
#
# Indexes
#
# index_claims_on_issue_id (issue_id)
# index_claims_on_user_id (user_id)
#
class Claim < ApplicationRecord
belongs_to :user, foreign_key: :user_id
scope :claim_includes, ->{includes(:user)}
end

View File

@ -64,6 +64,7 @@ class Issue < ApplicationRecord
# has_many :memos
has_many :journals, :as => :journalized, :dependent => :destroy
has_many :journal_details, through: :journals
has_many :claims, :dependent => :destroy
has_many :issue_tags_relates, dependent: :destroy
has_many :issue_tags, through: :issue_tags_relates
has_many :issue_times, dependent: :destroy
@ -154,7 +155,7 @@ class Issue < ApplicationRecord
end
def is_collaborators?
self.assigned_to_id.present? ? self.project.members.where(user_id: self.assigned_to_id).present? : false
self.assigned_to_id.present? ? self.project.member?(self.assigned_to_id) : false
end
def get_issue_tags_name

View File

@ -17,15 +17,15 @@ class MessageTemplate < ApplicationRecord
def self.build_init_data
self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '<b>{nickname}</b> 关注了你', notification_url: '{baseurl}/{login}')
email_html = File.read("#{email_template_html_dir}/issue_assigned.html")
self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 指派给你一个修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 指派给你一个修')
self.create(type: 'MessageTemplate::IssueAssignerExpire', sys_notice: '您负责的修 <b>{title}</b> 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueAtme', sys_notice: '<b>{nickname}</b> 在修 <b>{title}</b> 中@我', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 指派给你一个修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 指派给你一个修')
self.create(type: 'MessageTemplate::IssueAssignerExpire', sys_notice: '您负责的修 <b>{title}</b> 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueAtme', sys_notice: '<b>{nickname}</b> 在修 <b>{title}</b> 中@我', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
email_html = File.read("#{email_template_html_dir}/issue_changed.html")
self.create(type: 'MessageTemplate::IssueChanged', sys_notice: '在项目 <b>{nickname2}/{repository}</b> 的修 <b>{title}</b> 中:{ifassigner}{nickname1}将负责人从 <b>{assigner1}</b> 修改为 <b>{assigner2}</b> {endassigner}{ifstatus}{nickname1}将状态从 <b>{status1}</b> 修改为 <b>{status2}</b> {endstatus}{iftracker}{nickname1}将类型从 <b>{tracker1}</b> 修改为 <b>{tracker2}</b> {endtracker}{ifpriority}{nickname1}将优先级从 <b>{priority1}</b> 修改为 <b>{priority2}</b> {endpriority}{ifmilestone}{nickname1}将里程碑从 <b>{milestone1}</b> 修改为 <b>{milestone2}</b> {endmilestone}{iftag}{nickname1}将标记从 <b>{tag1}</b> 修改为 <b>{tag2}</b> {endtag}{ifdoneratio}{nickname1}将完成度从 <b>{doneratio1}</b> 修改为 <b>{doneratio2}</b> {enddoneratio}{ifbranch}{nickname1}将指定分支从 <b>{branch1}</b> 修改为 <b>{branch2}</b> {endbranch}{ifstartdate}{nickname1}将开始日期从 <b>{startdate1}</b> 修改为 <b>{startdate2}</b> {endstartdate}{ifduedate}{nickname1}将结束日期从 <b>{duedate1}</b> 修改为 <b>{duedate2}</b> {endduedate}', email: email_html, email_title: 'GitLink: 修 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueCreatorExpire', sys_notice: '您发布的修 <b>{title}</b> 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueChanged', sys_notice: '在项目 <b>{nickname2}/{repository}</b> 的修 <b>{title}</b> 中:{ifassigner}{nickname1}将负责人从 <b>{assigner1}</b> 修改为 <b>{assigner2}</b> {endassigner}{ifstatus}{nickname1}将状态从 <b>{status1}</b> 修改为 <b>{status2}</b> {endstatus}{iftracker}{nickname1}将类型从 <b>{tracker1}</b> 修改为 <b>{tracker2}</b> {endtracker}{ifpriority}{nickname1}将优先级从 <b>{priority1}</b> 修改为 <b>{priority2}</b> {endpriority}{ifmilestone}{nickname1}将里程碑从 <b>{milestone1}</b> 修改为 <b>{milestone2}</b> {endmilestone}{iftag}{nickname1}将标记从 <b>{tag1}</b> 修改为 <b>{tag2}</b> {endtag}{ifdoneratio}{nickname1}将完成度从 <b>{doneratio1}</b> 修改为 <b>{doneratio2}</b> {enddoneratio}{ifbranch}{nickname1}将指定分支从 <b>{branch1}</b> 修改为 <b>{branch2}</b> {endbranch}{ifstartdate}{nickname1}将开始日期从 <b>{startdate1}</b> 修改为 <b>{startdate2}</b> {endstartdate}{ifduedate}{nickname1}将结束日期从 <b>{duedate1}</b> 修改为 <b>{duedate2}</b> {endduedate}', email: email_html, email_title: 'GitLink: 修 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueCreatorExpire', sys_notice: '您发布的修 <b>{title}</b> 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
email_html = File.read("#{email_template_html_dir}/issue_deleted.html")
self.create(type: 'MessageTemplate::IssueDeleted', sys_notice: '{nickname}已将修 <b>{title}</b> 删除', email: email_html, email_title: 'GitLink: 修 {title} 有状态变更', notification_url: '')
self.create(type: 'MessageTemplate::IssueJournal', sys_notice: '{nickname}评论修{title}<b>{notes}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::IssueDeleted', sys_notice: '{nickname}已将修 <b>{title}</b> 删除', email: email_html, email_title: 'GitLink: 修 {title} 有状态变更', notification_url: '')
self.create(type: 'MessageTemplate::IssueJournal', sys_notice: '{nickname}评论修{title}<b>{notes}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
self.create(type: 'MessageTemplate::LoginIpTip', sys_notice: '您的账号{nickname}于{login_time)在非常用的IP地址{ip}登录,如非本人操作,请立即修改密码', notification_url: '')
email_html = File.read("#{email_template_html_dir}/organization_joined.html")
self.create(type: 'MessageTemplate::OrganizationJoined', sys_notice: '你已加入 <b>{organization}</b> 组织', notification_url: '{baseurl}/{login}', email: email_html, email_title: 'GitLink: 你已加入 {organization} 组织')
@ -37,7 +37,7 @@ class MessageTemplate < ApplicationRecord
self.create(type: 'MessageTemplate::ProjectFollowed', sys_notice: '<b>{nickname}</b> 关注了你管理的仓库', notification_url: '{baseurl}/{login}')
self.create(type: 'MessageTemplate::ProjectForked', sys_notice: '<b>{nickname1}</b> 复刻了你管理的仓库{nickname1}/{repository1}到{nickname2}/{repository2}', notification_url: '{baseurl}/{owner}/{identifier}')
email_html = File.read("#{email_template_html_dir}/project_issue.html")
self.create(type: 'MessageTemplate::ProjectIssue', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 新建修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 新建了一个修')
self.create(type: 'MessageTemplate::ProjectIssue', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 新建修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: 'GitLink: {nickname1} 在 {nickname2}/{repository} 新建了一个修')
email_html = File.read("#{email_template_html_dir}/project_joined.html")
self.create(type: 'MessageTemplate::ProjectJoined', sys_notice: '你已加入 <b>{repository}</b> 项目', notification_url: '{baseurl}/{owner}/{identifier}', email: email_html, email_title: 'GitLink: 你已加入 {repository} 项目')
email_html = File.read("#{email_template_html_dir}/project_left.html")

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 有新指派给我的
# 有新指派给我的
class MessageTemplate::IssueAssigned < MessageTemplate
# MessageTemplate::IssueAssigned.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last)

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 我负责的修截止日期到达最后一天
# 我负责的修截止日期到达最后一天
class MessageTemplate::IssueAssignerExpire < MessageTemplate
# MessageTemplate::IssueAssignerExpire.get_message_content(User.where(login: 'yystopf'), Issue.last)

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 在修中@我
# 在修中@我
class MessageTemplate::IssueAtme < MessageTemplate
# MessageTemplate::IssueAtme.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last)

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 我创建或负责的修状态变更
# 我创建或负责的修状态变更
class MessageTemplate::IssueChanged < MessageTemplate
# MessageTemplate::IssueChanged.get_message_content(User.where(login: 'yystopf'), User.last, Issue.last, {status_id: [1, 2], assigned_to_id: [nil, 203], tracker_id: [4, 3], priority_id: [2, 4], fixed_version_id: [nil, 5], due_date: ['', '2021-09-11'], done_ratio: [0, 40], issue_tags_value: ["", "7"], branch_name: ["", "master"]})
@ -29,7 +29,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
content = MessageTemplate::IssueChanged.sys_notice.gsub('{nickname1}', operator&.real_name).gsub('{nickname2}', owner&.real_name).gsub('{repository}', project&.name).gsub('{title}', issue&.subject)
url = notification_url.gsub('{owner}', owner&.login).gsub('{identifier}', project&.identifier).gsub('{id}', issue&.id.to_s)
change_count = change_params.keys.size
# 修负责人修改
# 修负责人修改
if change_params[:assigned_to_id].present?
assigner1 = User.find_by_id(change_params[:assigned_to_id][0])
assigner2 = User.find_by_id(change_params[:assigned_to_id][1])
@ -44,7 +44,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
end
# 修状态修改
# 修状态修改
if change_params[:status_id].present?
status1 = IssueStatus.find_by_id(change_params[:status_id][0])
status2 = IssueStatus.find_by_id(change_params[:status_id][1])
@ -59,7 +59,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifstatus})(.*)({endstatus})/, '')
end
# 修类型修改
# 修类型修改
if change_params[:tracker_id].present?
tracker1 = Tracker.find_by_id(change_params[:tracker_id][0])
tracker2 = Tracker.find_by_id(change_params[:tracker_id][1])
@ -74,7 +74,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({iftracker})(.*)({endtracker})/, '')
end
# 修里程碑修改
# 修里程碑修改
if change_params[:fixed_version_id].present?
fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
@ -89,7 +89,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
end
# 修标记修改
# 修标记修改
if change_params[:issue_tags_value].present?
issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
@ -106,7 +106,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({iftag})(.*)({endtag})()/, '')
end
# 修优先级修改
# 修优先级修改
if change_params[:priority_id].present?
priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
@ -121,7 +121,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
end
# 修完成度修改
# 修完成度修改
if change_params[:done_ratio].present?
doneratio1 = change_params[:done_ratio][0]
doneratio2 = change_params[:done_ratio][1]
@ -136,7 +136,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifdoneratio})(.*)({enddoneratio})/, '')
end
# 修指定分支修改
# 修指定分支修改
if change_params[:branch_name].present?
branch1 = change_params[:branch_name][0].blank? ? '分支未指定' : change_params[:branch_name][0]
branch2 = change_params[:branch_name][1].blank? ? '分支未指定' : change_params[:branch_name][1]
@ -151,7 +151,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifbranch})(.*)({endbranch})/, '')
end
# 修开始日期修改
# 修开始日期修改
if change_params[:start_date].present?
startdate1 = change_params[:start_date][0].blank? ? "未选择开始日期" : change_params[:start_date][0]
startdate2 = change_params[:start_date][1].blank? ? "未选择开始日期" : change_params[:start_date][1]
@ -166,7 +166,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifstartdate})(.*)({endstartdate})/, '')
end
# 修结束日期修改
# 修结束日期修改
if change_params[:due_date].present?
duedate1 = change_params[:due_date][0].blank? ? '未选择结束日期' : change_params[:due_date][0]
duedate2 = change_params[:due_date][1].blank? ? '未选择结束日期' : change_params[:due_date][1]
@ -207,7 +207,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
content.gsub!('{title}', issue&.subject)
content.gsub!('{id}', issue&.id.to_s)
change_count = change_params.keys.size
# 修负责人修改
# 修负责人修改
if change_params[:assigned_to_id].present?
assigner1 = User.find_by_id(change_params[:assigned_to_id][0])
assigner2 = User.find_by_id(change_params[:assigned_to_id][1])
@ -222,7 +222,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifassigner})(.*)({endassigner})/, '')
end
# 修状态修改
# 修状态修改
if change_params[:status_id].present?
status1 = IssueStatus.find_by_id(change_params[:status_id][0])
status2 = IssueStatus.find_by_id(change_params[:status_id][1])
@ -237,7 +237,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifstatus})(.*)({endstatus})/, '')
end
# 修类型修改
# 修类型修改
if change_params[:tracker_id].present?
tracker1 = Tracker.find_by_id(change_params[:tracker_id][0])
tracker2 = Tracker.find_by_id(change_params[:tracker_id][1])
@ -252,7 +252,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({iftracker})(.*)({endtracker})/, '')
end
# 修里程碑修改
# 修里程碑修改
if change_params[:fixed_version_id].present?
fix_version1 = Version.find_by_id(change_params[:fixed_version_id][0])
fix_version2 = Version.find_by_id(change_params[:fixed_version_id][1])
@ -267,7 +267,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifmilestone})(.*)({endmilestone})/, '')
end
# 修标记修改
# 修标记修改
if change_params[:issue_tags_value].present?
issue_tags1 = IssueTag.where(id: change_params[:issue_tags_value][0]).distinct
issue_tags2 = IssueTag.where(id: change_params[:issue_tags_value][1]).distinct
@ -284,7 +284,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({iftag})(.*)({endtag})()/, '')
end
# 修优先级修改
# 修优先级修改
if change_params[:priority_id].present?
priority1 = IssuePriority.find_by_id(change_params[:priority_id][0])
priority2 = IssuePriority.find_by_id(change_params[:priority_id][1])
@ -299,7 +299,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifpriority})(.*)({endpriority})/, '')
end
# 修完成度修改
# 修完成度修改
if change_params[:done_ratio].present?
doneratio1 = change_params[:done_ratio][0]
doneratio2 = change_params[:done_ratio][1]
@ -314,7 +314,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifdoneratio})(.*)({enddoneratio})/, '')
end
# 修指定分支修改
# 修指定分支修改
if change_params[:branch_name].present?
branch1 = change_params[:branch_name][0].blank? ? '分支未指定' : change_params[:branch_name][0]
branch2 = change_params[:branch_name][1].blank? ? '分支未指定' : change_params[:branch_name][1]
@ -329,7 +329,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifbranch})(.*)({endbranch})/, '')
end
# 修开始日期修改
# 修开始日期修改
if change_params[:start_date].present?
startdate1 = change_params[:start_date][0].blank? ? "未选择开始日期" : change_params[:start_date][0]
startdate2 = change_params[:start_date][1].blank? ? "未选择开始日期" : change_params[:start_date][1]
@ -344,7 +344,7 @@ class MessageTemplate::IssueChanged < MessageTemplate
else
content.gsub!(/({ifstartdate})(.*)({endstartdate})/, '')
end
# 修结束日期修改
# 修结束日期修改
if change_params[:due_date].present?
duedate1 = change_params[:due_date][0].blank? ? '未选择结束日期' : change_params[:due_date][0]
duedate2 = change_params[:due_date][1].blank? ? '未选择结束日期' : change_params[:due_date][1]

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 我创建的修截止日期到达最后一天
# 我创建的修截止日期到达最后一天
class MessageTemplate::IssueCreatorExpire < MessageTemplate
# MessageTemplate::IssueCreatorExpire.get_message_content(User.where(login: 'yystopf'), Issue.last)

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# 我创建或负责的修删除
# 我创建或负责的修删除
class MessageTemplate::IssueDeleted < MessageTemplate
# MessageTemplate::IssueDeleted.get_message_content(User.where(login: 'yystopf'), User.last, "hahah")

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# TODO 我创建或负责的修有新的评论
# TODO 我创建或负责的修有新的评论
class MessageTemplate::IssueJournal < MessageTemplate
# MessageTemplate::IssueJournal.get_message_content(User.where(login: 'yystopf'))

View File

@ -12,7 +12,7 @@
# email_title :string(255)
#
# TODO 我管理/关注的仓库有新的
# TODO 我管理/关注的仓库有新的
class MessageTemplate::ProjectIssue < MessageTemplate
# MessageTemplate::ProjectIssue.get_message_content(User.where(login: 'yystopf'), User.where(login: 'forgetest1'), User.last, Issue.last)

View File

@ -15,7 +15,7 @@
# 我管理的仓库项目设置被更改
class MessageTemplate::ProjectSettingChanged < MessageTemplate
# MessageTemplate::ProjectSettingChanged.get_message_content(User.where(login: 'yystopf'), User.last, Project.last, {description: '测试修改项目简介', category: '大数据', language: 'Ruby', permission: '公有', navbar: '修, 合并请求'})
# MessageTemplate::ProjectSettingChanged.get_message_content(User.where(login: 'yystopf'), User.last, Project.last, {description: '测试修改项目简介', category: '大数据', language: 'Ruby', permission: '公有', navbar: '修, 合并请求'})
def self.get_message_content(receivers, operator, project, change_params)
receivers.each do |receiver|
if receiver.user_template_message_setting.present?
@ -135,7 +135,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
unit_types.unshift('主页')
unit_types.append('动态')
navbar = unit_types.join('')
navbar.gsub!('issues', '修')
navbar.gsub!('issues', '修')
navbar.gsub!('pulls', '合并请求')
navbar.gsub!('wiki', 'Wiki')
navbar.gsub!('devops', '工作流')
@ -284,7 +284,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
unit_types.unshift('主页')
unit_types.append('动态')
navbar = unit_types.join('')
navbar.gsub!('issues', '修')
navbar.gsub!('issues', '修')
navbar.gsub!('pulls', '合并请求')
navbar.gsub!('wiki', 'Wiki')
navbar.gsub!('devops', '工作流')

View File

@ -25,7 +25,7 @@ class TemplateMessageSetting::CreateOrAssign < TemplateMessageSetting
end
def self.build_init_data
self.find_or_create_by(name: "修状态变更", key: "IssueChanged")
self.find_or_create_by(name: "修状态变更", key: "IssueChanged")
self.find_or_create_by(name: "合并请求状态变更", key: "PullRequestChanged")
end
end

View File

@ -25,7 +25,7 @@ class TemplateMessageSetting::ManageProject < TemplateMessageSetting
end
def self.build_init_data
self.find_or_create_by(name: "有新的", key: "Issue")
self.find_or_create_by(name: "有新的", key: "Issue")
self.find_or_create_by(name: "有新的合并请求", key: "PullRequest")
self.find_or_create_by(name: "有成员变动", key: "Member")
self.find_or_create_by(name: "仓库设置被更改", key: "SettingChanged")

View File

@ -27,7 +27,7 @@ class TemplateMessageSetting::Normal < TemplateMessageSetting
self.find_or_create_by(name: "账号有权限变更", key: "Permission")
self.find_or_create_by(name: "被拉入或移出组织", key: "Organization")
self.find_or_create_by(name: "被拉入或移出项目", key: "Project")
self.find_or_create_by(name: "有新的修指派给我", key: "IssueAssigned")
self.find_or_create_by(name: "有新的修指派给我", key: "IssueAssigned")
self.find_or_create_by(name: "有新的合并请求指派给我", key: "PullRequestAssigned")
end
end

View File

@ -32,6 +32,6 @@ class AtmeService < ApplicationService
else
return
end
SendTemplateMessageJob.perform_now(message_source, receivers, user.id, @atmeable.id) if Site.has_notice_menu?
SendTemplateMessageJob.perform_later(message_source, receivers.pluck(:id), user.id, @atmeable.id) if Site.has_notice_menu?
end
end

View File

@ -16,6 +16,7 @@ class Gitea::Organization::UpdateService < Gitea::ClientService
def request_params
update_params = {
name: org.login,
full_name: org.nickname,
description: org.description,
location: org.location,
repo_admin_change_team_access: org.repo_admin_change_team_access,

View File

@ -59,6 +59,8 @@ class Notice::Write::ClientService < ApplicationService
req.request :url_encoded
req.headers['Content-Type'] = 'application/json'
req.adapter Faraday.default_adapter
req.options.timeout = 100 # open/read timeout in seconds
req.options.open_timeout = 10 # connection open timeout in seconds
end
end

View File

@ -148,6 +148,7 @@ class PullRequests::CreateService < ApplicationService
def validate!
raise "title参数不能为空" if @params[:title].blank?
raise "title不能超过255个字符" if @params[:title].length > 255
raise "head参数不能为空" if @params[:head].blank?
raise "base参数不能为空" if @params[:base].blank?
raise "fork_project_id参数错误" if is_original && !@project.forked_projects.pluck(:id).include?(@params[:fork_project_id].to_i)

View File

@ -10,20 +10,12 @@ class Repositories::DetailService < ApplicationService
def call
return {
repo: repo_suitable,
contributor: contributor_suitable,
language: language_suitable,
branch_tag_total_count: branch_tag_total_count
}
rescue
return {
repo: {},
release: [],
branch: [],
branch_type: [],
tag: [],
contributor: [],
language: {},
readme: {}
branch_tag_total_count: {}
}
end
@ -35,14 +27,4 @@ class Repositories::DetailService < ApplicationService
def repo_suitable
Gitea::Repository::GetService.call(@owner, @repo.identifier)
end
def contributor_suitable
contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repo.identifier)
contributors.is_a?(Hash) && contributors.key?(:status) ? [] : contributors
end
def language_suitable
result = Gitea::Repository::Languages::ListService.call(@owner.login, @repo.identifier, @user&.gitea_token)
result[:status] === :success ? hash_transform_precentagable(result[:body]) : nil
end
end

View File

@ -0,0 +1,7 @@
json.user_name claimer.user.try(:show_real_name)
json.user_login claimer.user.try(:login)
json.user_picture url_to_avatar(claimer.user)
json.created_at time_from_now(claimer.created_at)
json.note_body claimer.note
json.claim_id claimer.id
json.visible false

View File

@ -0,0 +1,7 @@
json.partial! "commons/success"
json.currentUserclaimed @user_claimed
json.claimers do
json.array! @claims do |claimer|
json.partial! "claims/claim_item", claimer: claimer
end
end

View File

@ -3,15 +3,26 @@ owner_common = $redis_cache.hgetall("v2-owner-common:#{project_common["owner_id"
json.id item[0]
json.score item[1]
json.name project_common["name"]
json.identifier project_common["identifier"]
json.description project_common["description"]
json.owner do
json.id project_common["owner_id"]
json.type owner_common["type"]
json.name owner_common["name"]
json.login owner_common["login"]
json.avatar_url owner_common["avatar_url"]
if project_common['identifier'].include?("/")
json.identifier project_common["identifier"].split('/')[1]
json.owner do
json.id nil
json.type 'User'
json.name project_common["identifier"].split('/')[0]
json.login project_common["identifier"].split('/')[0]
json.avatar_url User::Avatar.get_letter_avatar_url(project_common["identifier"].split('/')[0])
end
else
json.identifier project_common["identifier"]
json.owner do
json.id project_common["owner_id"]
json.type owner_common["type"]
json.name owner_common["name"]
json.login owner_common["login"]
json.avatar_url owner_common["avatar_url"]
end
end
json.description project_common["description"]
json.visits project_common["visits"]
json.forks project_common["forks"]
json.watchers project_common["watchers"]

View File

@ -7,11 +7,22 @@ if @project.educoder?
json.timestamp 0
json.time_from_now commit[0]['time']
end
json.author do
{}
# json.partial! '/projects/author', user: render_commit_author(commit['author'])
end
json.committer {}
if commit[0]['author'].present?
json.author do
json.id nil
json.login commit[0]['author']['username']
json.name commit[0]['author']['username']
json.type nil
json.image_url commit[0]['author']['image_url']
end
json.committer do
json.id nil
json.login commit[0]['author']['username']
json.name commit[0]['author']['username']
json.type nil
json.image_url commit[0]['author']['image_url']
end
end
end
if @project.forge?

View File

@ -32,6 +32,8 @@ end
if @project.educoder?
file_path = params[:filepath].present? ? [params[:filepath], entry['name']].join('/') : entry['name']
file_type = File.extname(entry['name'].to_s)[1..-1]
image_type = image_type?(file_type)
json.name entry['name']
json.sha nil
@ -42,7 +44,7 @@ if @project.educoder?
json.target nil
json.download_url nil
json.direct_download false
json.image_type false
json.image_type image_type
json.is_readme_file false
json.commit do
json.message entry['title']

View File

@ -1,6 +1,26 @@
if @hash_commit.blank? #如果有状态值,则表示报错了
if @hash_commit.blank? || @project.educoder? #如果有状态值,则表示报错了
json.total_count 0
json.commits []
json.commits do
json.array! @commits do |commit|
json.sha commit['id']
json.message commit['title']
json.time_from_now commit['time']
json.author do
json.id nil
json.login commit['author']['username']
json.name commit['author']['username']
json.type nil
json.image_url commit['author']['image_url']
end
json.committer do
json.id nil
json.login commit['author']['username']
json.name commit['author']['username']
json.type nil
json.image_url commit['author']['image_url']
end
end
end
else
json.total_count @hash_commit[:total_count]
json.commits do

View File

@ -1,5 +1,5 @@
total_count = @contributors.size
json.contributors @contributors.each do |contributor|
json.list @contributors.each do |contributor|
json.partial! 'contributor', locals: { contributor: contributor }
end
json.total_count total_count

View File

@ -44,7 +44,7 @@ end
if @result[:repo]
json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024))
json.ssh_url @result[:repo]['ssh_url']
json.clone_url @result[:repo]['clone_url']
json.clone_url @project.educoder? ? "#{Rails.application.config_for(:configuration)['educoder']['git_site']}/#{@project&.project_educoder&.repo_name}.git" : @result[:repo]['clone_url']
json.default_branch @project.educoder? ? "master" : @result[:repo]['default_branch']
json.empty @result[:repo]['empty']
json.full_name @result[:repo]['full_name']
@ -53,13 +53,13 @@ end
json.license_name @project.license_name
json.branches_count @result[:branch_tag_total_count].present? ? (@result[:branch_tag_total_count]['branch_count'] || 0) : 0
json.tags_count @result[:branch_tag_total_count].present? ? (@result[:branch_tag_total_count]['tag_count'] || 0) : 0
json.contributors do
total_count = @result[:contributor].size
json.list @result[:contributor].each do |contributor|
json.partial! 'contributor', locals: { contributor: contributor }
end
json.total_count total_count
end
json.languages @result[:language].blank? ? nil : @result[:language]
# json.contributors do
# total_count = @result[:contributor].size
# json.list @result[:contributor].each do |contributor|
# json.partial! 'contributor', locals: { contributor: contributor }
# end
# json.total_count total_count
# end
# json.languages @result[:language].blank? ? nil : @result[:language]
json.partial! 'author', locals: { user: @project.owner }

View File

@ -7,7 +7,7 @@ if @project.educoder?
end
end
json.commits_count @entries['commit_count']
json.zip_url @entries['git_url']
json.zip_url ''
json.tar_url ''
json.entries do
json.array! @entries['trees'] do |entry|

View File

@ -24,8 +24,12 @@ if @project.educoder?
end
end
json.entries do
json.array! @sub_entries['trees'] do |entry|
json.partial! 'repositories/simple_entry', locals: { entry: entry }
if @sub_entries['trees'].is_a?(Array)
json.array! @sub_entries['trees'] do |entry|
json.partial! 'repositories/simple_entry', locals: { entry: entry }
end
elsif @sub_entries['trees'].is_a?(Hash)
json.partial! 'repositories/simple_entry', locals: { entry: @sub_entries['trees'] }
end
end
end

View File

@ -15,7 +15,7 @@ zh-CN:
'refused': '已拒绝'
'agreed': '已同意'
trend:
Issue: 修(Issue)
Issue: 修(Issue)
PullRequest: 合并请求(PR)
VersionRelease: 版本发布
create: 创建了

View File

@ -9,6 +9,7 @@ Rails.application.routes.draw do
# Serve websocket cable requests in-process
mount ActionCable.server => '/cable'
get 'attachments/entries/get_file', to: 'attachments#get_file'
get 'attachments/download/:id', to: 'attachments#show'
get 'attachments/download/:id/:filename', to: 'attachments#show'
@ -146,6 +147,16 @@ Rails.application.routes.draw do
get :get_children_journals
end
end
resources :claims, only: [:index] do
collection do
post :create
delete :destroy
get :index
put :update
end
end
resources :issue_times, only: [:create] do
collection do
post :end_work

View File

@ -0,0 +1,11 @@
class CreateClaims < ActiveRecord::Migration[5.2]
def change
create_table :claims do |t|
t.integer :issue_id
t.integer :user_id
t.timestamps
t.index :issue_id
t.index :user_id
end
end
end

View File

@ -0,0 +1,5 @@
class AddNoteToClaim < ActiveRecord::Migration[5.2]
def change
add_column :claims, :note, :text
end
end

2
dir.md
View File

@ -87,7 +87,7 @@ forgeplus
│   │   │   │   └── tags(标签)
│   │   │   ├── user(用户)
│   │   │   └── versions(发行版)
│   │   ├── issues(修)
│   │   ├── issues(修)
│   │   ├── libraries
│   │   ├── oauth
│   │   ├── organizations(组织)

View File

@ -1138,31 +1138,31 @@ Success — a happy kitten is an authenticated kitten!
</thead><tbody>
<tr>
<td>IssueAssigned</td>
<td>有新指派给我的</td>
<td>有新指派给我的</td>
</tr>
<tr>
<td>IssueAssignerExpire</td>
<td>我负责的修截止日期到达最后一天</td>
<td>我负责的修截止日期到达最后一天</td>
</tr>
<tr>
<td>IssueAtme</td>
<td>修中@我</td>
<td>修中@我</td>
</tr>
<tr>
<td>IssueChanged</td>
<td>我创建或负责的修状态变更</td>
<td>我创建或负责的修状态变更</td>
</tr>
<tr>
<td>IssueCreatorExpire</td>
<td>我创建的修截止日期到达最后一天</td>
<td>我创建的修截止日期到达最后一天</td>
</tr>
<tr>
<td>IssueDeleted</td>
<td>我创建或负责的修删除</td>
<td>我创建或负责的修删除</td>
</tr>
<tr>
<td>IssueJournal</td>
<td>我创建或负责的修有新的评论</td>
<td>我创建或负责的修有新的评论</td>
</tr>
<tr>
<td>LoginIpTip</td>
@ -1194,7 +1194,7 @@ Success — a happy kitten is an authenticated kitten!
</tr>
<tr>
<td>ProjectIssue</td>
<td>我管理/关注的仓库有新的</td>
<td>我管理/关注的仓库有新的</td>
</tr>
<tr>
<td>ProjectJoined</td>
@ -1403,7 +1403,7 @@ Success — a happy kitten is an authenticated kitten!
<tr>
<td>atmeable_type</td>
<td>string</td>
<td>atme消息对象是从哪里@我的比如评论Journal、Issue、合并请求PullRequest</td>
<td>atme消息对象是从哪里@我的比如评论Journal、Issue、合并请求PullRequest</td>
</tr>
<tr>
<td>atmeable_id</td>
@ -1706,7 +1706,7 @@ Success — a happy kitten is an authenticated kitten!
</span><span class="nl">"total_settings_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
</span><span class="nl">"settings"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"修被指派"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"修被指派"</span><span class="p">,</span><span class="w">
</span><span class="nl">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IssueAssigned"</span><span class="p">,</span><span class="w">
</span><span class="nl">"notification_disabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"email_disabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
@ -1725,7 +1725,7 @@ Success — a happy kitten is an authenticated kitten!
</span><span class="nl">"total_settings_count"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w">
</span><span class="nl">"settings"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"有新的修"</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"有新的修"</span><span class="p">,</span><span class="w">
</span><span class="nl">"key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Issue"</span><span class="p">,</span><span class="w">
</span><span class="nl">"notification_disabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"email_disabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
@ -2245,7 +2245,7 @@ Success — a happy kitten is an authenticated kitten!
<tr>
<td>issues_count</td>
<td>array</td>
<td>修数量</td>
<td>修数量</td>
</tr>
<tr>
<td>pull_requests_count</td>
@ -2493,7 +2493,7 @@ Success — a happy kitten is an authenticated kitten!
<tr>
<td>project_trends.trend_type</td>
<td>string</td>
<td>动态类型IssueVersionRelease版本发布PullRequest合并请求</td>
<td>动态类型IssueVersionRelease版本发布PullRequest合并请求</td>
</tr>
<tr>
<td>project_trends.action_type</td>
@ -4968,7 +4968,7 @@ http://localhost:3000/api/yystopf/ceshi/menu_list | jq
<tr>
<td>menu_name</td>
<td>string</td>
<td>导航名称, home:主页,code:代码库,issues:修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置</td>
<td>导航名称, home:主页,code:代码库,issues:修,pulls:合并请求,devops:工作流,versions:里程碑,activity:动态,setting:仓库设置</td>
</tr>
</tbody></table>
@ -5131,7 +5131,7 @@ http://localhost:3000/api/yystopf/ceshi/project_units.json
<td></td>
<td></td>
<td>array</td>
<td>项目模块内容, 支持以下参数:code:代码库,issues:修,pulls:合并请求,devops:工作流,versions:里程碑</td>
<td>项目模块内容, 支持以下参数:code:代码库,issues:修,pulls:合并请求,devops:工作流,versions:里程碑</td>
</tr>
</tbody></table>
<h3 id='7447e4874e-2'>返回字段说明:</h3>
@ -8346,23 +8346,23 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json
</tr>
<tr>
<td>issue</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
</tr>
<tr>
<td>issue_assign</td>
<td>修被指派</td>
<td>修被指派</td>
</tr>
<tr>
<td>issue_label</td>
<td>修标签被更新或删除</td>
<td>修标签被更新或删除</td>
</tr>
<tr>
<td>issue_milestone</td>
<td>修被收入里程碑</td>
<td>修被收入里程碑</td>
</tr>
<tr>
<td>issue_comment</td>
<td>修评论</td>
<td>修评论</td>
</tr>
<tr>
<td>pull_request</td>
@ -8568,23 +8568,23 @@ http://localhost:3000/api/yystopf/ceshi/webhooks.json
</tr>
<tr>
<td>issue</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
</tr>
<tr>
<td>issue_assign</td>
<td>修被指派</td>
<td>修被指派</td>
</tr>
<tr>
<td>issue_label</td>
<td>修标签被更新或删除</td>
<td>修标签被更新或删除</td>
</tr>
<tr>
<td>issue_milestone</td>
<td>修被收入里程碑</td>
<td>修被收入里程碑</td>
</tr>
<tr>
<td>issue_comment</td>
<td>修评论</td>
<td>修评论</td>
</tr>
<tr>
<td>pull_request</td>
@ -8833,23 +8833,23 @@ http://localhost:3000/api/yystopf/ceshi/webhooks/7.json
</tr>
<tr>
<td>issue</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
<td>修已打开、已关闭、已重新打开或编辑</td>
</tr>
<tr>
<td>issue_assign</td>
<td>修被指派</td>
<td>修被指派</td>
</tr>
<tr>
<td>issue_label</td>
<td>修标签被更新或删除</td>
<td>修标签被更新或删除</td>
</tr>
<tr>
<td>issue_milestone</td>
<td>修被收入里程碑</td>
<td>修被收入里程碑</td>
</tr>
<tr>
<td>issue_comment</td>
<td>修评论</td>
<td>修评论</td>
</tr>
<tr>
<td>pull_request</td>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>有新的修指派给我</title>
<title>有新的修指派给我</title>
<style type="text/css">
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
@ -31,7 +31,7 @@
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:20px; color:#333; line-height: 1.9;">
<p style="font-size: 14px; color:#333;">
{receiver},您好!<br/>
<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>在 {nickname2}/{repository} 指派给你一个修:<a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a><br/>
<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>在 {nickname2}/{repository} 指派给你一个修:<a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a><br/>
</p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>有新的修指派给我</title>
<title>有新的修指派给我</title>
<style type="text/css">
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
@ -31,7 +31,7 @@
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:20px; color:#333; line-height: 1.9;">
<p style="font-size: 14px; color:#333;">
{receiver},您好!<br/>
在项目 <a href="{baseurl}/{login2}/{identifier}" style="font-weight:bold;color:#3b94d6;">{nickname2}/{repository}</a><a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a> 中:
在项目 <a href="{baseurl}/{login2}/{identifier}" style="font-weight:bold;color:#3b94d6;">{nickname2}/{repository}</a><a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a> 中:
{ifassigner}<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>将负责人从 {assigner1} 修改为 {assigner2}{endassigner}
{ifstatus}<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>将状态从 {status1} 修改为 {status2}{endstatus}
{iftracker}<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>将类型从 {tracker1} 修改为 {tracker2}{endtracker}

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>修状态改变</title>
<title>修状态改变</title>
<style type="text/css">
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
@ -31,7 +31,7 @@
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:20px; color:#333; line-height: 1.9;">
<p style="font-size: 14px; color:#333;">
{receiver},您好!<br/>
<a href="{baseurl}/{login}" style="font-weight:bold;color:#3b94d6;">{nickname}</a>已将修 {title} 删除
<a href="{baseurl}/{login}" style="font-weight:bold;color:#3b94d6;">{nickname}</a>已将修 {title} 删除
</p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div>

View File

@ -1,6 +1,6 @@
<html>
<head>
<title>管理的仓库有新的</title>
<title>管理的仓库有新的</title>
<style type="text/css">
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
body,table,input,textarea,select,button { font-family: "微软雅黑","宋体"; font-size:12px;line-height:1.5; background:#eaebec;}
@ -31,7 +31,7 @@
<div style="width: 558px; border-left:1px solid #ddd;border-right:1px solid #ddd; background:#fff; padding:20px; color:#333; line-height: 1.9;">
<p style="font-size: 14px; color:#333;">
{receiver},您好!<br/>
<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>在 {nickname2}/{repository} 新建了一个修:<a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a><br/>
<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>在 {nickname2}/{repository} 新建了一个修:<a href="{baseurl}/{login2}/{identifier}/issues/{id}" style="font-weight:bold;color:#3b94d6;">{title}</a><br/>
</p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div>