Compare commits

...

66 Commits

Author SHA1 Message Date
yystopf 96b0f2d167 Merge branch 'develop' into standalone_develop 2021-12-28 16:10:16 +08:00
yystopf 8e4e6fc0ac Merge pull request '1224里程碑、1231里程碑内容' (#296) from yystopf/forgeplus:develop into develop 2021-12-28 16:09:45 +08:00
yystopf 75f3166ffa fix 2021-12-28 14:45:12 +08:00
yystopf 04bf423fcb fix 2021-12-28 14:42:55 +08:00
yystopf 51e8c9d908 fix: compare use url escape 2021-12-28 14:22:06 +08:00
yystopf 4304a35b80 fix 2021-12-28 11:07:40 +08:00
yystopf 076375633a fix 2021-12-27 17:55:02 +08:00
yystopf dbf5e711a5 fix 2021-12-27 17:44:08 +08:00
yystopf 4432e65683 fix: operate issue permission reset 2021-12-27 17:32:35 +08:00
yystopf dcf44a78f0 fix: unvalid branch name return error 2021-12-27 16:34:54 +08:00
yystopf 12edc620df fix: complete profile condition 2021-12-24 16:15:59 +08:00
yystopf 4ce03145df fix 2021-12-23 14:33:31 +08:00
yystopf 3e04a37ece add: team remove and joined template message 2021-12-23 14:26:40 +08:00
yystopf 78f464ccba fix: escape 2021-12-23 10:37:57 +08:00
yystopf 64423f1999 fix 2021-12-22 18:16:39 +08:00
yystopf 05c3f60dab fix 2021-12-22 18:08:10 +08:00
yystopf 0083ef33cb fix 2021-12-22 16:03:23 +08:00
yystopf 36b68df273 fix: remote register validate 2021-12-22 14:39:47 +08:00
yystopf e1a5918c22 add: project trends project info 2021-12-22 13:57:02 +08:00
yystopf 26ae9b35df fix: change member role and change team user team 2021-12-22 11:32:40 +08:00
yystopf 8e79b43aaf add: member bind team user 2021-12-22 11:16:27 +08:00
yystopf c2bd78d70d Merge branch 'develop' into standalone_develop 2021-12-21 15:17:08 +08:00
yystopf d28eba6423 fix: create org error 2021-12-21 15:16:38 +08:00
yystopf 688be1bb7d fix: return issue version id 2021-12-21 15:16:38 +08:00
yystopf df69dee282 fix: some error for read raw data 2021-12-21 15:14:10 +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 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 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 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 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 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 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 1c19c05fd8 fix 2021-12-08 16:26:06 +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 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
75 changed files with 2057 additions and 599 deletions

View File

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

View File

@ -7,6 +7,7 @@ class AccountsController < ApplicationController
# 其他平台同步注册的用户 # 其他平台同步注册的用户
def remote_register def remote_register
Register::RemoteForm.new(remote_register_params).validate!
username = params[:username]&.gsub(/\s+/, "") username = params[:username]&.gsub(/\s+/, "")
tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username)
email = params[:email]&.gsub(/\s+/, "") email = params[:email]&.gsub(/\s+/, "")
@ -375,5 +376,9 @@ class AccountsController < ApplicationController
phone_or_mail = strip(reset_password_params[:login]) phone_or_mail = strip(reset_password_params[:login])
User.where("phone = :search OR mail = :search", search: phone_or_mail).last User.where("phone = :search OR mail = :search", search: phone_or_mail).last
end end
def remote_register_params
params.permit(:username, :email, :password, :platform)
end
end end

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

@ -42,12 +42,14 @@ class CompareController < ApplicationController
end end
def load_compare_params def load_compare_params
@base = Addressable::URI.unescape(params[:base]) # @base = Addressable::URI.unescape(params[:base])
@base = Base64.decode64(params[:base])
@head = params[:head].include?('.json') ? params[:head][0..-6] : params[:head] @head = params[:head].include?('.json') ? params[:head][0..-6] : params[:head]
# @head = Addressable::URI.unescape(@head)
@head = Base64.decode64(@head)
end end
def gitea_compare(base, head) def gitea_compare(base, head)
Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head, current_user.gitea_token) Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, CGI.escape(base), CGI.escape(head), current_user.gitea_token)
end end
end end

View File

@ -486,7 +486,8 @@ class IssuesController < ApplicationController
end end
def operate_issue_permission def operate_issue_permission
return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || @project.is_public?) @issue = Issue.find_by_id(params[:id]) unless @issue.present?
return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || (@project.is_public && @issue.nil?) || (@project.is_public && @issue.present? && @issue.author_id == current_user.id))
end end
def export_issues(issues) def export_issues(issues)

View File

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

View File

@ -18,7 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id) @team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id)
@organization_user = OrganizationUser.build(@organization.id, @operate_user.id) @organization_user = OrganizationUser.build(@organization.id, @operate_user.id)
SendTemplateMessageJob.perform_later('OrganizationRole', @operate_user.id, @organization.id, @team.authorize_name) if Site.has_notice_menu? SendTemplateMessageJob.perform_later('TeamJoined', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu?
Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login) Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
end end
rescue Exception => e rescue Exception => e
@ -31,6 +31,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@team_user.destroy! @team_user.destroy!
Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login) Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login)
SendTemplateMessageJob.perform_later('TeamLeft', @operate_user.id, @organization.id, @team.id) if Site.has_notice_menu?
org_team_users = @organization.team_users.where(user_id: @operate_user.id) org_team_users = @organization.team_users.where(user_id: @operate_user.id)
unless org_team_users.present? unless org_team_users.present?
@organization.organization_users.find_by(user_id: @operate_user.id).destroy! @organization.organization_users.find_by(user_id: @operate_user.id).destroy!

View File

@ -3,7 +3,7 @@ class ProjectTrendsController < ApplicationController
before_action :check_project_public before_action :check_project_public
def index def index
project_trends = @project.project_trends.preload(:user, trend: :user) project_trends = @project.project_trends.preload(:user, trend: :user, project: :owner)
check_time = params[:time] #时间的筛选 check_time = params[:time] #时间的筛选
check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version

View File

@ -16,13 +16,13 @@ class ProjectsController < ApplicationController
menu.append(menu_hash_by_name("home")) 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("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("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("pulls")) if @project.has_menu_permission("pulls") && @project.forge?
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") && @project.forge?
menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops")
menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions") 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("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 render json: menu
end end

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,16 @@
module Register
class RemoteForm < Register::BaseForm
# login 登陆方式,支持邮箱、登陆、手机号等
attr_accessor :username, :email, :password, :platform
validates :username, :email, :password, presence: true
validate :check!
def check!
Rails.logger.info "Register::RemoteForm params: username: #{username}; email: #{email}; password: #{password}; platform: #{platform}"
check_login(username)
check_mail(email)
check_password(password)
end
end
end

View File

@ -4,5 +4,5 @@ class Users::LoginForm
attr_accessor :password, :login attr_accessor :password, :login
validates :login, presence: true 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 end

View File

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

View File

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

View File

@ -37,6 +37,7 @@ class SendTemplateMessageJob < ApplicationJob
issue = Issue.find_by_id(issue_id) issue = Issue.find_by_id(issue_id)
return unless operator.present? && issue.present? return unless operator.present? && issue.present?
# receivers = receivers.where.not(id: operator&.id) # 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) 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) Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {operator_id: operator.id, issue_id: issue.id}, 2, operator_id)
when 'IssueChanged' when 'IssueChanged'
@ -93,18 +94,6 @@ class SendTemplateMessageJob < ApplicationJob
receivers_email_string, email_title, email_content = MessageTemplate::OrganizationLeft.get_email_message_content(receiver, organization) receivers_email_string, email_title, email_content = MessageTemplate::OrganizationLeft.get_email_message_content(receiver, organization)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end end
when 'OrganizationRole'
user_id, organization_id, role = args[0], args[1], args[2]
user = User.find_by_id(user_id)
organization = Organization.find_by_id(organization_id)
return unless user.present? && organization.present?
receivers = User.where(id: user.id)
receivers_string, content, notification_url = MessageTemplate::OrganizationRole.get_message_content(receivers, organization, role)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, role: role})
receivers.find_each do |receiver|
receivers_email_string, email_title, email_content = MessageTemplate::OrganizationRole.get_email_message_content(receiver, organization, role)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end
when 'ProjectIssue' when 'ProjectIssue'
operator_id, issue_id = args[0], args[1] operator_id, issue_id = args[0], args[1]
operator = User.find_by_id(operator_id) operator = User.find_by_id(operator_id)
@ -235,6 +224,7 @@ class SendTemplateMessageJob < ApplicationJob
pull_request = PullRequest.find_by_id(pull_request_id) pull_request = PullRequest.find_by_id(pull_request_id)
return unless operator.present? && pull_request.present? return unless operator.present? && pull_request.present?
# receivers = receivers.where.not(id: operator&.id) # 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) 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) 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' when 'PullRequestChanged'
@ -274,6 +264,32 @@ class SendTemplateMessageJob < ApplicationJob
receivers_email_string, email_title, email_content = MessageTemplate::PullRequestMerged.get_email_message_content(receiver, operator, pull_request) receivers_email_string, email_title, email_content = MessageTemplate::PullRequestMerged.get_email_message_content(receiver, operator, pull_request)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content) Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end end
when 'TeamJoined'
user_id, organization_id, team_id = args[0], args[1], args[2]
user = User.find_by_id(user_id)
organization = Organization.find_by_id(organization_id)
team = Team.find_by_id(team_id)
return unless user.present? && organization.present? && team.present?
receivers = User.where(id: user.id)
receivers_string, content, notification_url = MessageTemplate::TeamJoined.get_message_content(receivers, organization, team)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, team_id: team.id})
receivers.find_each do |receiver|
receivers_email_string, email_title, email_content = MessageTemplate::TeamJoined.get_email_message_content(receiver, organization, team)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end
when 'TeamLeft'
user_id, organization_id, team_id = args[0], args[1], args[2]
user = User.find_by_id(user_id)
organization = Organization.find_by_id(organization_id)
team = Team.find_by_id(team_id)
return unless user.present? && organization.present? && team.present?
receivers = User.where(id: user.id)
receivers_string, content, notification_url = MessageTemplate::TeamLeft.get_message_content(receivers, organization, team)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {user_id: user.id, organization_id: organization.id, team_id: team.id})
receivers.find_each do |receiver|
receivers_email_string, email_title, email_content = MessageTemplate::TeamLeft.get_email_message_content(receiver, organization, team)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end
end end
end end
end end

View File

@ -5,6 +5,7 @@ module CustomRegexp
LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/
NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/
PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\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/ 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])$/ 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

@ -21,13 +21,37 @@ module ProjectOperable
end end
def add_member!(user_id, role_name='Developer') def add_member!(user_id, role_name='Developer')
member = members.create!(user_id: user_id) if self.owner.is_a?(Organization)
case role_name
when 'Manager'
team = self.owner.teams.admin.take
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = TeamUser.build(self.user_id, user_id, team.id)
when 'Developer'
team = self.owner.teams.write.take
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = TeamUser.build(self.user_id, user_id, team.id)
when 'Reporter'
team = self.owner.teams.read.take
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = TeamUser.build(self.user_id, user_id, team.id)
end
end
member = members.create!(user_id: user_id, team_user_id: team_user&.id)
set_developer_role(member, role_name) set_developer_role(member, role_name)
end end
def remove_member!(user_id) def remove_member!(user_id)
member = members.find_by(user_id: user_id) member = members.find_by(user_id: user_id)
member.destroy! if member && self.user_id != user_id member.destroy! if member && self.user_id != user_id
team_user = TeamUser.find_by_id(member&.team_user_id)
team_user.destroy! if team_user
end end
def member?(user_id) def member?(user_id)
@ -47,6 +71,28 @@ module ProjectOperable
def change_member_role!(user_id, role) def change_member_role!(user_id, role)
member = self.member(user_id) member = self.member(user_id)
if self.owner.is_a?(Organization) && member.team_user.present?
case role&.name
when 'Manager'
team = self.owner.teams.admin.take
team = team.nil? ? Team.build(self.user_id, 'admin', '管理员', '', 'admin', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
when 'Developer'
team = self.owner.teams.write.take
team = team.nil? ? Team.build(self.user_id, 'developer', '开发者', '', 'write', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
when 'Reporter'
team = self.owner.teams.read.take
team = team.nil? ? Team.build(self.user_id, 'reporter', '报告者', '', 'read', false, false) : team
TeamProject.build(self.user_id, team.id, self.id)
OrganizationUser.build(self.user_id, user_id)
team_user = member.team_user.update(team_id: team&.id)
end
end
member.member_roles.last.update_attributes!(role: role) member.member_roles.last.update_attributes!(role: role)
end end

View File

@ -64,6 +64,7 @@ class Issue < ApplicationRecord
# has_many :memos # has_many :memos
has_many :journals, :as => :journalized, :dependent => :destroy has_many :journals, :as => :journalized, :dependent => :destroy
has_many :journal_details, through: :journals has_many :journal_details, through: :journals
has_many :claims, :dependent => :destroy
has_many :issue_tags_relates, dependent: :destroy has_many :issue_tags_relates, dependent: :destroy
has_many :issue_tags, through: :issue_tags_relates has_many :issue_tags, through: :issue_tags_relates
has_many :issue_times, dependent: :destroy has_many :issue_times, dependent: :destroy
@ -154,7 +155,7 @@ class Issue < ApplicationRecord
end end
def is_collaborators? 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 end
def get_issue_tags_name def get_issue_tags_name

View File

@ -11,23 +11,26 @@
# course_group_id :integer default("0") # course_group_id :integer default("0")
# is_collect :integer default("1") # is_collect :integer default("1")
# graduation_group_id :integer default("0") # graduation_group_id :integer default("0")
# team_user_id :integer
# #
# Indexes # Indexes
# #
# index_members_on_course_id (course_id) # index_members_on_course_id (course_id)
# index_members_on_project_id (project_id) # index_members_on_project_id (project_id)
# index_members_on_team_user_id (team_user_id)
# index_members_on_user_id (user_id) # index_members_on_user_id (user_id)
# index_members_on_user_id_and_project_id (user_id,project_id,course_id) UNIQUE # index_members_on_user_id_and_project_id (user_id,project_id,course_id) UNIQUE
# #
class Member < ApplicationRecord class Member < ApplicationRecord
belongs_to :user belongs_to :user
# belongs_to :course, optional: true # belongs_to :course, optional: true
belongs_to :project, optional: true belongs_to :project, optional: true
belongs_to :team_user, optional: true
has_many :member_roles, dependent: :destroy
has_many :roles, through: :member_roles has_many :member_roles, dependent: :destroy
has_many :roles, through: :member_roles
validates :user_id, :project_id, presence: true
validates :user_id, :project_id, presence: true
end
end

View File

@ -17,27 +17,25 @@ class MessageTemplate < ApplicationRecord
def self.build_init_data def self.build_init_data
self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '<b>{nickname}</b> 关注了你', notification_url: '{baseurl}/{login}') 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") 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::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::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::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") 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::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::IssueCreatorExpire', sys_notice: '您发布的修 <b>{title}</b> 已临近截止日期,请尽快处理', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}')
email_html = File.read("#{email_template_html_dir}/issue_deleted.html") 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::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::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: '') 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") 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} 组织') self.create(type: 'MessageTemplate::OrganizationJoined', sys_notice: '你已加入 <b>{organization}</b> 组织', notification_url: '{baseurl}/{login}', email: email_html, email_title: 'GitLink: 你已加入 {organization} 组织')
email_html = File.read("#{email_template_html_dir}/organization_left.html") email_html = File.read("#{email_template_html_dir}/organization_left.html")
self.create(type: 'MessageTemplate::OrganizationLeft', sys_notice: '你已被移出 <b>{organization}</b> 组织', notification_url: '', email: email_html, email_title: 'GitLink: 你已被移出 {organization} 组织') self.create(type: 'MessageTemplate::OrganizationLeft', sys_notice: '你已被移出 <b>{organization}</b> 组织', notification_url: '', email: email_html, email_title: 'GitLink: 你已被移出 {organization} 组织')
email_html = File.read("#{email_template_html_dir}/organization_role.html")
self.create(type: 'MessageTemplate::OrganizationRole', sys_notice: '组织 <b>{organization}</b> 已把你的角色改为 <b>{role}</b>', email: email_html, email_title: 'GitLink: 在 {organization} 组织你的账号有权限变更', notification_url: '{baseurl}/{login}')
self.create(type: 'MessageTemplate::ProjectDeleted', sys_notice: '你关注的仓库{nickname}/{repository}已被删除', notification_url: '') self.create(type: 'MessageTemplate::ProjectDeleted', sys_notice: '你关注的仓库{nickname}/{repository}已被删除', notification_url: '')
self.create(type: 'MessageTemplate::ProjectFollowed', sys_notice: '<b>{nickname}</b> 关注了你管理的仓库', notification_url: '{baseurl}/{login}') 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}') 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") 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") 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} 项目') 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") email_html = File.read("#{email_template_html_dir}/project_left.html")
@ -66,6 +64,10 @@ class MessageTemplate < ApplicationRecord
self.create(type: 'MessageTemplate::PullRequestJournal', sys_notice: '{nickname}评论合并请求{title}<b>{notes}</b>', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}') self.create(type: 'MessageTemplate::PullRequestJournal', sys_notice: '{nickname}评论合并请求{title}<b>{notes}</b>', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
email_html = File.read("#{email_template_html_dir}/pull_request_merged.html") email_html = File.read("#{email_template_html_dir}/pull_request_merged.html")
self.create(type: 'MessageTemplate::PullRequestMerged', sys_notice: '你提交的合并请求:{title} <b>已通过</b>', email: email_html, email_title: 'GitLink: 合并请求 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}') self.create(type: 'MessageTemplate::PullRequestMerged', sys_notice: '你提交的合并请求:{title} <b>已通过</b>', email: email_html, email_title: 'GitLink: 合并请求 {title} 有状态变更', notification_url: '{baseurl}/{owner}/{identifier}/pulls/{id}')
email_html = File.read("#{email_template_html_dir}/team_joined.html")
self.create(type: 'MessageTemplate::TeamJoined', sys_notice: '你已被拉入组织 <b>{organization}</b> 的 <b>{team}</b> 团队,拥有<b>{role}</b>权限', email: email_html, email_title: 'GitLink: 在 {organization} 组织你的账号有权限变更', notification_url: '{baseurl}/{login}')
email_html = File.read("#{email_template_html_dir}/team_left.html")
self.create(type: 'MessageTemplate::TeamLeft', sys_notice: '你已被移出组织 <b>{organization}</b> 的 <b>{team}</b> 团队', email: email_html, email_title: 'GitLink: 在 {organization} 组织你的账号有权限变更', notification_url: '{baseurl}/{login}')
end end
def self.sys_notice def self.sys_notice

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@
# email_title :string(255) # email_title :string(255)
# #
# TODO 我管理/关注的仓库有新的 # TODO 我管理/关注的仓库有新的
class MessageTemplate::ProjectIssue < MessageTemplate class MessageTemplate::ProjectIssue < MessageTemplate
# MessageTemplate::ProjectIssue.get_message_content(User.where(login: 'yystopf'), User.where(login: 'forgetest1'), User.last, Issue.last) # 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 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) def self.get_message_content(receivers, operator, project, change_params)
receivers.each do |receiver| receivers.each do |receiver|
if receiver.user_template_message_setting.present? if receiver.user_template_message_setting.present?
@ -135,7 +135,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
unit_types.unshift('主页') unit_types.unshift('主页')
unit_types.append('动态') unit_types.append('动态')
navbar = unit_types.join('') navbar = unit_types.join('')
navbar.gsub!('issues', '修') navbar.gsub!('issues', '修')
navbar.gsub!('pulls', '合并请求') navbar.gsub!('pulls', '合并请求')
navbar.gsub!('wiki', 'Wiki') navbar.gsub!('wiki', 'Wiki')
navbar.gsub!('devops', '工作流') navbar.gsub!('devops', '工作流')
@ -284,7 +284,7 @@ class MessageTemplate::ProjectSettingChanged < MessageTemplate
unit_types.unshift('主页') unit_types.unshift('主页')
unit_types.append('动态') unit_types.append('动态')
navbar = unit_types.join('') navbar = unit_types.join('')
navbar.gsub!('issues', '修') navbar.gsub!('issues', '修')
navbar.gsub!('pulls', '合并请求') navbar.gsub!('pulls', '合并请求')
navbar.gsub!('wiki', 'Wiki') navbar.gsub!('wiki', 'Wiki')
navbar.gsub!('devops', '工作流') navbar.gsub!('devops', '工作流')

View File

@ -0,0 +1,58 @@
# == Schema Information
#
# Table name: message_templates
#
# id :integer not null, primary key
# type :string(255)
# sys_notice :text(65535)
# email :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
# notification_url :string(255)
# email_title :string(255)
#
# 账号被拉入组织团队
class MessageTemplate::TeamJoined < MessageTemplate
# MessageTemplate::TeamJoined.get_message_content(User.where(login: 'yystopf'), Organization.last, Organization.last.teams.take)
def self.get_message_content(receivers, organization, team)
receivers.each do |receiver|
if receiver.user_template_message_setting.present?
receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
end
end
return '', '', '' if receivers.blank?
content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{team}', team&.nickname).gsub('{role}', team&.authorize_name)
url = notification_url.gsub('{login}', organization&.login)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::TeamJoined.get_message_content [ERROR] #{e}")
return '', '', ''
end
def self.get_email_message_content(receiver, organization, role)
if receiver.user_template_message_setting.present?
return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
title = email_title
title.gsub!('{organization}', organization&.real_name)
title.gsub!('{team}', team&.nickname)
title.gsub!('{role}', team&.authorize_name)
content = email
content.gsub!('{receiver}', receiver&.real_name)
content.gsub!('{baseurl}', base_url)
content.gsub!('{login}', organization&.login)
content.gsub!('{organization}', organization&.real_name)
content.gsub!('{team}', team&.nickname)
content.gsub!('{role}', team&.authorize_name)
return receiver&.mail, title, content
else
return '', '', ''
end
rescue => e
Rails.logger.info("MessageTemplate::TeamJoined.get_email_message_content [ERROR] #{e}")
return '', '', ''
end
end

View File

@ -0,0 +1,58 @@
# == Schema Information
#
# Table name: message_templates
#
# id :integer not null, primary key
# type :string(255)
# sys_notice :text(65535)
# email :text(65535)
# created_at :datetime not null
# updated_at :datetime not null
# notification_url :string(255)
# email_title :string(255)
#
# 账号被移出组织团队
class MessageTemplate::TeamLeft < MessageTemplate
# MessageTemplate::TeamLeft.get_message_content(User.where(login: 'yystopf'), Organization.last, Organization.last.teams.take)
def self.get_message_content(receivers, organization, team)
receivers.each do |receiver|
if receiver.user_template_message_setting.present?
receivers = receivers.where.not(id: receiver.id) unless receiver.user_template_message_setting.notification_body["Normal::Permission"]
end
end
return '', '', '' if receivers.blank?
content = sys_notice.gsub('{organization}', organization&.real_name).gsub('{team}', team&.nickname).gsub('{role}', team&.authorize_name)
url = notification_url.gsub('{login}', organization&.login)
return receivers_string(receivers), content, url
rescue => e
Rails.logger.info("MessageTemplate::TeamLeft.get_message_content [ERROR] #{e}")
return '', '', ''
end
def self.get_email_message_content(receiver, organization, team)
if receiver.user_template_message_setting.present?
return '', '', '' unless receiver.user_template_message_setting.email_body["Normal::Permission"]
title = email_title
title.gsub!('{organization}', organization&.real_name)
title.gsub!('{team}', team&.nickname)
title.gsub!('{role}', team&.authorize_name)
content = email
content.gsub!('{receiver}', receiver&.real_name)
content.gsub!('{baseurl}', base_url)
content.gsub!('{login}', organization&.login)
content.gsub!('{organization}', organization&.real_name)
content.gsub!('{team}', team&.nickname)
content.gsub!('{role}', team&.authorize_name)
return receiver&.mail, title, content
else
return '', '', ''
end
rescue => e
Rails.logger.info("MessageTemplate::TeamLeft.get_email_message_content [ERROR] #{e}")
return '', '', ''
end
end

View File

@ -56,10 +56,10 @@ class Team < ApplicationRecord
def authorize_name def authorize_name
case self.authorize case self.authorize
when 'read' then '报告者' when 'read' then '读取'
when 'write' then '开发者' when 'write' then '写入'
when 'admin' then '管理员' when 'admin' then '管理员'
when 'owner' then '拥有者' when 'owner' then '管理员'
else else
'' ''
end end

View File

@ -22,9 +22,17 @@ class TeamUser < ApplicationRecord
belongs_to :team, counter_cache: :num_users belongs_to :team, counter_cache: :num_users
belongs_to :user belongs_to :user
has_one :member
validates :user_id, uniqueness: {scope: [:organization_id, :team_id]} validates :user_id, uniqueness: {scope: [:organization_id, :team_id]}
before_destroy :remove_project_member
def self.build(organization_id, user_id, team_id) def self.build(organization_id, user_id, team_id)
self.create!(organization_id: organization_id, user_id: user_id, team_id: team_id) self.create!(organization_id: organization_id, user_id: user_id, team_id: team_id)
end end
def remove_project_member
member.destroy if member.present?
end
end end

View File

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

View File

@ -25,7 +25,7 @@ class TemplateMessageSetting::ManageProject < TemplateMessageSetting
end end
def self.build_init_data 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: "PullRequest")
self.find_or_create_by(name: "有成员变动", key: "Member") self.find_or_create_by(name: "有成员变动", key: "Member")
self.find_or_create_by(name: "仓库设置被更改", key: "SettingChanged") 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: "Permission")
self.find_or_create_by(name: "被拉入或移出组织", key: "Organization") self.find_or_create_by(name: "被拉入或移出组织", key: "Organization")
self.find_or_create_by(name: "被拉入或移出项目", key: "Project") 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") self.find_or_create_by(name: "有新的合并请求指派给我", key: "PullRequestAssigned")
end end
end end

View File

@ -786,7 +786,7 @@ class User < Owner
end end
def profile_is_completed? def profile_is_completed?
self.nickname.present? && self.gender.present? && self.mail.present? && self.custom_department.present? self.nickname.present? && self.mail.present?
end end
protected protected

View File

@ -32,6 +32,6 @@ class AtmeService < ApplicationService
else else
return return
end 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
end end

View File

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

View File

@ -55,7 +55,12 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService
when 201 then success(json_parse!(body)) when 201 then success(json_parse!(body))
when 403 then error("你没有权限操作!") when 403 then error("你没有权限操作!")
when 404 then error("你操作的链接不存在!") when 404 then error("你操作的链接不存在!")
when 422 then error("#{filepath}文件已存在,不能重复创建!") when 422
if @body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*')
error("不合法的分支名称!")
else
error("#{filepath}文件已存在,不能重复创建!")
end
else error("系统错误!") else error("系统错误!")
end end
end end

View File

@ -59,6 +59,8 @@ class Notice::Write::ClientService < ApplicationService
req.request :url_encoded req.request :url_encoded
req.headers['Content-Type'] = 'application/json' req.headers['Content-Type'] = 'application/json'
req.adapter Faraday.default_adapter 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
end end

View File

@ -148,6 +148,7 @@ class PullRequests::CreateService < ApplicationService
def validate! def validate!
raise "title参数不能为空" if @params[:title].blank? raise "title参数不能为空" if @params[:title].blank?
raise "title不能超过255个字符" if @params[:title].length > 255
raise "head参数不能为空" if @params[:head].blank? raise "head参数不能为空" if @params[:head].blank?
raise "base参数不能为空" if @params[:base].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) 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 def call
return { return {
repo: repo_suitable, repo: repo_suitable,
contributor: contributor_suitable,
language: language_suitable,
branch_tag_total_count: branch_tag_total_count branch_tag_total_count: branch_tag_total_count
} }
rescue rescue
return { return {
repo: {}, repo: {},
release: [], branch_tag_total_count: {}
branch: [],
branch_type: [],
tag: [],
contributor: [],
language: {},
readme: {}
} }
end end
@ -35,14 +27,4 @@ class Repositories::DetailService < ApplicationService
def repo_suitable def repo_suitable
Gitea::Repository::GetService.call(@owner, @repo.identifier) Gitea::Repository::GetService.call(@owner, @repo.identifier)
end 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 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

@ -13,6 +13,7 @@ json.tracker @issue.tracker.try(:name)
json.issue_status @issue.issue_status.try(:name) json.issue_status @issue.issue_status.try(:name)
json.priority @issue.priority.try(:name) json.priority @issue.priority.try(:name)
json.version @issue.version.try(:name) json.version @issue.version.try(:name)
json.version_id @issue.version.try(:id)
json.issue_tags @issue.get_issue_tags json.issue_tags @issue.get_issue_tags
json.done_ratio @issue.done_ratio.to_s + "%" json.done_ratio @issue.done_ratio.to_s + "%"
json.issue_type @issue.issue_type json.issue_type @issue.issue_type

View File

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

View File

@ -6,6 +6,13 @@ json.user_name trend.user.try(:show_real_name)
json.user_login trend.user.login json.user_login trend.user.login
json.user_avatar url_to_avatar(trend.user) json.user_avatar url_to_avatar(trend.user)
json.action_time time_from_now(trend.created_at) json.action_time time_from_now(trend.created_at)
json.project do
json.owner do
json.partial! 'users/user_simple', locals: {user: trend&.project&.owner}
end
json.identifier trend&.project&.identifier
json.description trend&.project&.description
end
if trend.trend_type == "Issue" if trend.trend_type == "Issue"
json.partial! "issues/simple_issue_item", locals: {issue: trend.trend} json.partial! "issues/simple_issue_item", locals: {issue: trend.trend}

View File

@ -1,12 +1,27 @@
json.author do if commit['Status'].present?
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Author']), name: commit['Author']['Name'] } json.author do
end json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Author']), name: commit['Author']['Name'] }
end
json.committer do
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] }
end
json.timestamp render_unix_time(commit['Committer']['When'])
json.time_from_now time_from_now(commit['Committer']['When'])
json.created_at render_format_time_with_date(commit['Committer']['When'])
json.message commit['CommitMessage']
json.sha commit['Sha']
else
json.author do
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['commit']['author']), name: commit['commit']['author']['name'] }
end
json.committer do json.committer do
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] }
end end
json.timestamp render_unix_time(commit['Committer']['When']) json.timestamp render_unix_time(commit['commit']['committer']['date'])
json.time_from_now time_from_now(commit['Committer']['When']) json.time_from_now time_from_now(commit['commit']['committer']['date'])
json.created_at render_format_time_with_date(commit['Committer']['When']) json.created_at render_format_time_with_date(commit['commit']['committer']['date'])
json.message commit['CommitMessage'] json.message commit['commit']['message']
json.sha commit['Sha'] json.sha commit['sha']
end

View File

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

View File

@ -32,6 +32,8 @@ end
if @project.educoder? if @project.educoder?
file_path = params[:filepath].present? ? [params[:filepath], entry['name']].join('/') : entry['name'] 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.name entry['name']
json.sha nil json.sha nil
@ -42,7 +44,7 @@ if @project.educoder?
json.target nil json.target nil
json.download_url nil json.download_url nil
json.direct_download false json.direct_download false
json.image_type false json.image_type image_type
json.is_readme_file false json.is_readme_file false
json.commit do json.commit do
json.message entry['title'] 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.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 else
json.total_count @hash_commit[:total_count] json.total_count @hash_commit[:total_count]
json.commits do json.commits do

View File

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

View File

@ -44,7 +44,7 @@ end
if @result[:repo] if @result[:repo]
json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024)) json.size replace_bytes_to_b(number_to_human_size(@result[:repo]['size'].to_i*1024))
json.ssh_url @result[:repo]['ssh_url'] 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.default_branch @project.educoder? ? "master" : @result[:repo]['default_branch']
json.empty @result[:repo]['empty'] json.empty @result[:repo]['empty']
json.full_name @result[:repo]['full_name'] json.full_name @result[:repo]['full_name']
@ -53,13 +53,13 @@ end
json.license_name @project.license_name 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.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.tags_count @result[:branch_tag_total_count].present? ? (@result[:branch_tag_total_count]['tag_count'] || 0) : 0
json.contributors do # json.contributors do
total_count = @result[:contributor].size # total_count = @result[:contributor].size
json.list @result[:contributor].each do |contributor| # json.list @result[:contributor].each do |contributor|
json.partial! 'contributor', locals: { contributor: contributor } # json.partial! 'contributor', locals: { contributor: contributor }
end # end
json.total_count total_count # json.total_count total_count
end # end
json.languages @result[:language].blank? ? nil : @result[:language] # json.languages @result[:language].blank? ? nil : @result[:language]
json.partial! 'author', locals: { user: @project.owner } json.partial! 'author', locals: { user: @project.owner }

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
class AddTeamUserToMembers < ActiveRecord::Migration[5.2]
def change
add_reference :members, :team_user
end
end

2
dir.md
View File

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

View File

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

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>有新的修指派给我</title> <title>有新的修指派给我</title>
<style type="text/css"> <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,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;} 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;"> <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;"> <p style="font-size: 14px; color:#333;">
{receiver},您好!<br/> {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> </p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div> <div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div> </div>

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>有新的修指派给我</title> <title>有新的修指派给我</title>
<style type="text/css"> <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,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;} 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;"> <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;"> <p style="font-size: 14px; color:#333;">
{receiver},您好!<br/> {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} {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} {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} {iftracker}<a href="{baseurl}/{login1}" style="font-weight:bold;color:#3b94d6;">{nickname1}</a>将类型从 {tracker1} 修改为 {tracker2}{endtracker}

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>修状态改变</title> <title>修状态改变</title>
<style type="text/css"> <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,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;} 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;"> <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;"> <p style="font-size: 14px; color:#333;">
{receiver},您好!<br/> {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> </p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div> <div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div> </div>

View File

@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>管理的仓库有新的</title> <title>管理的仓库有新的</title>
<style type="text/css"> <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,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;} 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;"> <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;"> <p style="font-size: 14px; color:#333;">
{receiver},您好!<br/> {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> </p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div> <div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div> </div>

View File

@ -0,0 +1,47 @@
<html>
<head>
<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;}
div,img,tr,td,table{ border:0;}
table,tr,td{border:0;}
ol,ul,li{ list-style-type:none}
.new_content{ background:#fff; width: 100%;}
.email-page-link{ }
.email-link-top{ }
.c_white{ color:#fff;}
.email-link-con{ }
.email-link-line{ }
.email-link-footer{ padding:15px; color:#333; line-height: 1.9; }
.c_grey02{ color: #888;}
.fb{ font-weight: normal;}
.f14{ }
</style>
</head>
<body style="background:#fff;">
<div class="new_content">
<div style="width: 598px; background:#fff; margin:20px auto;">
<div style="height:50px; width: 578px; background:#3b94d6; padding:9px 10px 6px;border:1px solid #ddd; border-bottom:none;">
<a href="{baseurl}"><img src="{baseurl}/images/email_logo.png" height="45" ></a>
<p style="color:#fff; float:right; margin-top:15px;">确实开源,协同创新</p>
<div style="clear:both; overflow:hidden;"></div>
</div>
<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;">{organization}</a><a href="{baseurl}/{login}" style="font-weight:bold;color:#3b94d6;">{team}</a> 团队,拥有{role}权限
</p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div>
<div style="padding:20px; color:#333; line-height: 1.9;background: #eee;border:1px solid #ddd; border-top:none; width: 558px;">
<p style="color:#888; float:left;">如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见<br/>
QQ群1071514693</p>
<p style="color:#888; float:right;font-weight: bold;font-size: 16px;">GitLink团队</p>
<div style="clear:both; overflow:hidden;"></div>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,47 @@
<html>
<head>
<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;}
div,img,tr,td,table{ border:0;}
table,tr,td{border:0;}
ol,ul,li{ list-style-type:none}
.new_content{ background:#fff; width: 100%;}
.email-page-link{ }
.email-link-top{ }
.c_white{ color:#fff;}
.email-link-con{ }
.email-link-line{ }
.email-link-footer{ padding:15px; color:#333; line-height: 1.9; }
.c_grey02{ color: #888;}
.fb{ font-weight: normal;}
.f14{ }
</style>
</head>
<body style="background:#fff;">
<div class="new_content">
<div style="width: 598px; background:#fff; margin:20px auto;">
<div style="height:50px; width: 578px; background:#3b94d6; padding:9px 10px 6px;border:1px solid #ddd; border-bottom:none;">
<a href="{baseurl}"><img src="{baseurl}/images/email_logo.png" height="45" ></a>
<p style="color:#fff; float:right; margin-top:15px;">确实开源,协同创新</p>
<div style="clear:both; overflow:hidden;"></div>
</div>
<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;">{organization}</a><a href="{baseurl}/{login}" style="font-weight:bold;color:#3b94d6;">{team}</a> 团队
</p>
<div style="width: 100%; border-top: 1px solid #ddd; margin:10px 0;"></div>
</div>
<div style="padding:20px; color:#333; line-height: 1.9;background: #eee;border:1px solid #ddd; border-top:none; width: 558px;">
<p style="color:#888; float:left;">如果您在使用中有任何的疑问和建议,欢迎您给我们反馈意见<br/>
QQ群1071514693</p>
<p style="color:#888; float:right;font-weight: bold;font-size: 16px;">GitLink团队</p>
<div style="clear:both; overflow:hidden;"></div>
</div>
</div>
</div>
</body>
</html>