Browse Source

Merge remote-tracking branch 'origin/develop' into develop

tags/v4.0.0^2
xiaoxiaoqiong 4 years ago
parent
commit
98d8ee1fb6
45 changed files with 540 additions and 118 deletions
  1. +5
    -0
      app/controllers/accounts_controller.rb
  2. +8
    -2
      app/controllers/application_controller.rb
  3. +5
    -3
      app/controllers/compare_controller.rb
  4. +3
    -2
      app/controllers/concerns/ci/cloud_account_manageable.rb
  5. +2
    -1
      app/controllers/issues_controller.rb
  6. +1
    -1
      app/controllers/organizations/organizations_controller.rb
  7. +2
    -1
      app/controllers/organizations/team_users_controller.rb
  8. +23
    -10
      app/controllers/organizations/teams_controller.rb
  9. +1
    -1
      app/controllers/project_trends_controller.rb
  10. +19
    -13
      app/controllers/projects_controller.rb
  11. +4
    -4
      app/controllers/pull_requests_controller.rb
  12. +3
    -3
      app/controllers/repositories_controller.rb
  13. +13
    -2
      app/controllers/users_controller.rb
  14. +16
    -0
      app/forms/register/remote_form.rb
  15. +2
    -2
      app/helpers/repositories_helper.rb
  16. +3
    -2
      app/helpers/tag_chosen_helper.rb
  17. +1
    -1
      app/jobs/migrate_remote_repository_job.rb
  18. +26
    -12
      app/jobs/send_template_message_job.rb
  19. +1
    -0
      app/libs/util.rb
  20. +47
    -1
      app/models/concerns/project_operable.rb
  21. +6
    -1
      app/models/gitea/webhook_task.rb
  22. +14
    -11
      app/models/member.rb
  23. +4
    -2
      app/models/message_template.rb
  24. +58
    -0
      app/models/message_template/team_joined.rb
  25. +58
    -0
      app/models/message_template/team_left.rb
  26. +3
    -3
      app/models/team.rb
  27. +8
    -0
      app/models/team_user.rb
  28. +1
    -1
      app/models/user.rb
  29. +22
    -0
      app/services/gitea/repository/branches/list_name_service.rb
  30. +6
    -1
      app/services/gitea/repository/entries/create_service.rb
  31. +8
    -3
      app/services/pull_requests/create_service.rb
  32. +1
    -0
      app/views/issues/show.json.jbuilder
  33. +3
    -0
      app/views/organizations/teams/_simple_detail.json.jbuilder
  34. +6
    -2
      app/views/organizations/teams/index.json.jbuilder
  35. +7
    -0
      app/views/project_trends/_detail.json.jbuilder
  36. +19
    -18
      app/views/projects/branches.json.jbuilder
  37. +1
    -1
      app/views/projects/webhooks/_detail.json.jbuilder
  38. +1
    -1
      app/views/projects/webhooks/edit.json.jbuilder
  39. +2
    -1
      app/views/projects/webhooks/tasks.json.jbuilder
  40. +26
    -11
      app/views/pull_requests/_commit.json.jbuilder
  41. +1
    -1
      app/views/repositories/_simple_entry.json.jbuilder
  42. +1
    -0
      config/routes.rb
  43. +5
    -0
      db/migrate/20211222015006_add_team_user_to_members.rb
  44. +47
    -0
      public/message_template/team_joined.html
  45. +47
    -0
      public/message_template/team_left.html

+ 5
- 0
app/controllers/accounts_controller.rb View File

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

+ 8
- 2
app/controllers/application_controller.rb View File

@@ -709,14 +709,20 @@ class ApplicationController < ActionController::Base
Rails.application.config_for(:configuration)['platform_url'] || request.base_url
end

def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end

def convert_image!
@image = params[:image]
@image = @image.nil? && params[:user].present? ? params[:user][:image] : @image
return unless @image.present?
max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M
if @image.class == ActionDispatch::Http::UploadedFile
render_error('请上传文件') if @image.size.zero?
render_error('文件大小超过限制') if @image.size > max_size.to_i
return render_error('请上传文件') if @image.size.zero?
return render_error('文件大小超过限制') if @image.size > max_size.to_i
return render_error('头像格式不正确!') unless image_type?(File.extname(@image.original_filename.to_s)[1..-1])
else
image = @image.to_s.strip
return render_error('请上传正确的图片') if image.blank?


+ 5
- 3
app/controllers/compare_controller.rb View File

@@ -42,12 +42,14 @@ class CompareController < ApplicationController
end

def load_compare_params
@base = Addressable::URI.unescape(params[:base])
# @base = Addressable::URI.unescape(params[:base])
@base = params[:base].include?(":") ? Addressable::URI.unescape(params[:base].split(":")[0]) + ':' + Base64.decode64(params[:base].split(":")[1]) : Base64.decode64(params[:base])
@head = params[:head].include?('.json') ? params[:head][0..-6] : params[:head]

# @head = Addressable::URI.unescape(@head)
@head = @head.include?(":") ? Addressable::URI.unescape(@head.split(":")[0]) + ':' + Base64.decode64(@head.split(":")[1]) : Base64.decode64(@head)
end

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

+ 3
- 2
app/controllers/concerns/ci/cloud_account_manageable.rb View File

@@ -179,7 +179,7 @@ module Ci::CloudAccountManageable

def drone_oauth_user!(url, state)
logger.info "[drone] drone_oauth_user url: #{url}"
conn = Faraday.new(url: url) do |req|
conn = Faraday.new(url: "#{Gitea.gitea_config[:domain]}#{url}") do |req|
req.request :url_encoded
req.adapter Faraday.default_adapter
req.headers["cookie"] = "_session_=#{SecureRandom.hex(28)}; _oauth_state_=#{state}"
@@ -188,7 +188,8 @@ module Ci::CloudAccountManageable
response = conn.get
logger.info "[drone] response headers: #{response.headers}"

response.headers['location'].include?('error') ? false : true
true
# response.headers['location'].include?('error') ? false : true
end

private


+ 2
- 1
app/controllers/issues_controller.rb View File

@@ -486,7 +486,8 @@ class IssuesController < ApplicationController
end

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

def export_issues(issues)


+ 1
- 1
app/controllers/organizations/organizations_controller.rb View File

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


+ 2
- 1
app/controllers/organizations/team_users_controller.rb View File

@@ -18,7 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do
@team_user = TeamUser.build(@organization.id, @operate_user.id, @team.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)
end
rescue Exception => e
@@ -31,6 +31,7 @@ class Organizations::TeamUsersController < Organizations::BaseController
ActiveRecord::Base.transaction do
@team_user.destroy!
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)
unless org_team_users.present?
@organization.organization_users.find_by(user_id: @operate_user.id).destroy!


+ 23
- 10
app/controllers/organizations/teams_controller.rb View File

@@ -4,15 +4,24 @@ class Organizations::TeamsController < Organizations::BaseController
before_action :check_user_can_edit_org, only: [:create, :update, :destroy]

def index
#if @organization.is_owner?(current_user) || current_user.admin?
@teams = @organization.teams
#else
# @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
#end
@is_admin = can_edit_org?
@teams = @teams.includes(:team_units, :team_users)

@teams = kaminari_paginate(@teams)
if params[:is_full].present?
if can_edit_org?
@teams = @organization.teams
else
@teams = []
end
else
#if @organization.is_owner?(current_user) || current_user.admin?
@teams = @organization.teams
#else
# @teams = @organization.teams.joins(:team_users).where(team_users: {user_id: current_user.id})
#end
@is_admin = can_edit_org?
@teams = @teams.includes(:team_units, :team_users)
@teams = kaminari_paginate(@teams)
end
end

def search
@@ -34,8 +43,12 @@ class Organizations::TeamsController < Organizations::BaseController

def create
ActiveRecord::Base.transaction do
Organizations::CreateTeamForm.new(team_params).validate!
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
if @organization.teams.count >= 50
return tip_exception("组织的团队数量已超过限制!")
else
Organizations::CreateTeamForm.new(team_params).validate!
@team = Organizations::Teams::CreateService.call(current_user, @organization, team_params)
end
end
rescue Exception => e
uid_logger_error(e.message)


+ 1
- 1
app/controllers/project_trends_controller.rb View File

@@ -3,7 +3,7 @@ class ProjectTrendsController < ApplicationController
before_action :check_project_public

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_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version


+ 19
- 13
app/controllers/projects_controller.rb View File

@@ -59,18 +59,23 @@ class ProjectsController < ApplicationController
Projects::MigrateForm.new(mirror_params).validate!

@project =
if enable_accelerator?(mirror_params[:clone_addr])
# if enable_accelerator?(mirror_params[:clone_addr])
# source_clone_url = mirror_params[:clone_addr]
# uid_logger("########## 已动加速器 ##########")
# result = Gitea::Accelerator::MigrateService.call(mirror_params)
# if result[:status] == :success
# Rails.logger.info "########## 加速镜像成功 ########## "
# Projects::MigrateService.call(current_user,
# mirror_params.merge(source_clone_url: source_clone_url,
# clone_addr: accelerator_url(mirror_params[:repository_name])))
# else
# Projects::MigrateService.call(current_user, mirror_params)
# end
if mirror_params[:clone_addr].include?("github.com")
source_clone_url = mirror_params[:clone_addr]
uid_logger("########## 已动加速器 ##########")
result = Gitea::Accelerator::MigrateService.call(mirror_params)
if result[:status] == :success
Rails.logger.info "########## 加速镜像成功 ########## "
Projects::MigrateService.call(current_user,
mirror_params.merge(source_clone_url: source_clone_url,
clone_addr: accelerator_url(mirror_params[:repository_name])))
else
Projects::MigrateService.call(current_user, mirror_params)
end
clone_url = source_clone_url.gsub('github.com', 'github.com.cnpmjs.org')
uid_logger("########## 更改clone_addr ##########")
Projects::MigrateService.call(current_user, mirror_params.merge(source_clone_url: source_clone_url, clone_addr: clone_url))
else
Projects::MigrateService.call(current_user, mirror_params)
end
@@ -82,8 +87,9 @@ class ProjectsController < ApplicationController
def branches
return @branches = [] unless @project.forge?

result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier)
@branches = result.is_a?(Hash) && result.key?(:status) ? [] : result
# result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier)
result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier)
@branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result["branch_name"]) : result
end

def branches_slice


+ 4
- 4
app/controllers/pull_requests_controller.rb View File

@@ -274,12 +274,12 @@ class PullRequestsController < ApplicationController
base: params[:base], #目标分支
milestone: 0, #里程碑,未与本地的里程碑关联
}
assignee_login = User.find_by_id(params[:assigned_to_id])&.login
@requests_params = @local_params.merge({
assignee: current_user.try(:login),
# assignees: ["#{params[:assigned_login].to_s}"],
assignees: ["#{current_user.try(:login).to_s}"],
labels: params[:issue_tag_ids],
due_date: Time.now
assignees: ["#{assignee_login.to_s}"],
labels: params[:issue_tag_ids]
# due_date: Time.now
})
@issue_params = {
author_id: current_user.id,


+ 3
- 3
app/controllers/repositories_controller.rb View File

@@ -238,7 +238,7 @@ class RepositoriesController < ApplicationController
def archive
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{URI.escape(params[:archive])}"
archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{CGI.escape(params[:archive])}"
file_path = [domain, api_url, archive_url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden?
@@ -252,11 +252,11 @@ class RepositoriesController < ApplicationController
domain = Gitea.gitea_config[:domain]
api_url = Gitea.gitea_config[:base_url]
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{params[:filepath]}?ref=#{params[:ref]}"
url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{CGI.escape(params[:filepath])}?ref=#{CGI.escape(params[:ref])}"
file_path = [domain, api_url, url].join
file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&")
redirect_to URI.escape(file_path)
redirect_to file_path
end
private


+ 13
- 2
app/controllers/users_controller.rb View File

@@ -6,7 +6,7 @@ class UsersController < ApplicationController
before_action :check_user_exist, only: [:show, :homepage_info,:projects, :watch_users, :fan_users, :hovercard]
before_action :require_login, only: %i[me list sync_user_info]
before_action :connect_to_ci_db, only: [:get_user_info]
before_action :convert_image!, only: [:update]
before_action :convert_image!, only: [:update, :update_image]
skip_before_action :check_sign, only: [:attachment_show]
def connect_to_ci_db(options={})
@@ -82,10 +82,21 @@ class UsersController < ApplicationController
Util.write_file(@image, avatar_path(@user)) if user_params[:image].present?
@user.attributes = user_params.except(:image)
unless @user.save
render_error(@user.errors.full_messages.join(", "))
render_error(-1, @user.errors.full_messages.join(", "))
end
end
def update_image
return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id])
return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id)
Util.write_file(@image, avatar_path(@user))
return render_ok({message: '头像修改成功'})
rescue Exception => e
uid_logger_error(e.message)
render_error(-1, '头像修改失败!')
end
def me
@user = current_user
end


+ 16
- 0
app/forms/register/remote_form.rb 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

+ 2
- 2
app/helpers/repositories_helper.rb View File

@@ -11,11 +11,11 @@ module RepositoriesHelper
def download_type(str)
default_type = %w(xlsx xls ppt pptx pdf zip 7z rar exe pdb obj idb RData rdata doc docx mpp vsdx dot otf eot ttf woff woff2 mp4 mov wmv flv mpeg avi avchd webm mkv)
default_type.include?(str&.downcase)
default_type.include?(str&.downcase) || str.blank?
end
def image_type?(str)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg)
default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd)
default_type.include?(str&.downcase)
end


+ 3
- 2
app/helpers/tag_chosen_helper.rb View File

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



+ 1
- 1
app/jobs/migrate_remote_repository_job.rb View File

@@ -6,7 +6,7 @@ class MigrateRemoteRepositoryJob < ApplicationJob
return if repo.blank?

puts "############ MigrateRemoteRepositoryJob starting ... ############"
params.except!(:auth_password, :auth_username) if params[:auth_username].nil?
gitea_repository = Gitea::Repository::MigrateService.new(token, params).call
puts "#gitea_repository#{gitea_repository}"
if gitea_repository[0]==201


+ 26
- 12
app/jobs/send_template_message_job.rb View File

@@ -94,18 +94,6 @@ class SendTemplateMessageJob < ApplicationJob
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)
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'
operator_id, issue_id = args[0], args[1]
operator = User.find_by_id(operator_id)
@@ -276,6 +264,32 @@ class SendTemplateMessageJob < ApplicationJob
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)
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

+ 1
- 0
app/libs/util.rb View File

@@ -29,6 +29,7 @@ module Util
file.write(io)
end
end
true
end
def download_file(url, save_path)


+ 47
- 1
app/models/concerns/project_operable.rb View File

@@ -21,13 +21,37 @@ module ProjectOperable
end

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)
end

def remove_member!(user_id)
member = members.find_by(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

def member?(user_id)
@@ -47,6 +71,28 @@ module ProjectOperable

def change_member_role!(user_id, role)
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)
end



+ 6
- 1
app/models/gitea/webhook_task.rb View File

@@ -1,7 +1,6 @@
class Gitea::WebhookTask < Gitea::Base
serialize :payload_content, JSON
serialize :request_content, JSON
serialize :response_content, JSON

self.inheritance_column = nil

@@ -10,4 +9,10 @@ class Gitea::WebhookTask < Gitea::Base
belongs_to :webhook, class_name: "Gitea::Webhook", foreign_key: :hook_id

enum type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9}

def response_content_json
JSON.parse(response_content)
rescue
{}
end
end

+ 14
- 11
app/models/member.rb View File

@@ -11,23 +11,26 @@
# course_group_id :integer default("0")
# is_collect :integer default("1")
# graduation_group_id :integer default("0")
# team_user_id :integer
#
# Indexes
#
# index_members_on_course_id (course_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_and_project_id (user_id,project_id,course_id) UNIQUE
#

class Member < ApplicationRecord
belongs_to :user
# belongs_to :course, optional: true
belongs_to :project, optional: true
has_many :member_roles, dependent: :destroy
has_many :roles, through: :member_roles
validates :user_id, :project_id, presence: true
end
class Member < ApplicationRecord
belongs_to :user
# belongs_to :course, 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

validates :user_id, :project_id, presence: true

end

+ 4
- 2
app/models/message_template.rb View File

@@ -31,8 +31,6 @@ class MessageTemplate < ApplicationRecord
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")
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::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}')
@@ -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}')
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}')
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

def self.sys_notice


+ 58
- 0
app/models/message_template/team_joined.rb 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

+ 58
- 0
app/models/message_template/team_left.rb 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

+ 3
- 3
app/models/team.rb View File

@@ -56,10 +56,10 @@ class Team < ApplicationRecord

def authorize_name
case self.authorize
when 'read' then '报告者'
when 'write' then '开发者'
when 'read' then '读取'
when 'write' then '写入'
when 'admin' then '管理员'
when 'owner' then '拥有者'
when 'owner' then '管理员'
else
''
end


+ 8
- 0
app/models/team_user.rb View File

@@ -22,9 +22,17 @@ class TeamUser < ApplicationRecord
belongs_to :team, counter_cache: :num_users
belongs_to :user

has_one :member

validates :user_id, uniqueness: {scope: [:organization_id, :team_id]}

before_destroy :remove_project_member

def self.build(organization_id, user_id, team_id)
self.create!(organization_id: organization_id, user_id: user_id, team_id: team_id)
end

def remove_project_member
member.destroy if member.present?
end
end

+ 1
- 1
app/models/user.rb View File

@@ -773,7 +773,7 @@ class User < Owner
end

def profile_is_completed?
self.nickname.present? && self.gender.present? && self.mail.present? && self.custom_department.present?
self.nickname.present? && self.mail.present?
end

protected


+ 22
- 0
app/services/gitea/repository/branches/list_name_service.rb View File

@@ -0,0 +1,22 @@
class Gitea::Repository::Branches::ListNameService < Gitea::ClientService
attr_reader :user, :repo

def initialize(user, repo)
@user = user
@repo = repo
end

def call
response = get(url, params)
render_200_response(response)
end

private
def params
Hash.new.merge(token: user.gitea_token)
end

def url
"/repos/#{user.login}/#{repo}/branch_name_set".freeze
end
end

+ 6
- 1
app/services/gitea/repository/entries/create_service.rb View File

@@ -55,7 +55,12 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService
when 201 then success(json_parse!(body))
when 403 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("系统错误!")
end
end


+ 8
- 3
app/services/pull_requests/create_service.rb View File

@@ -125,12 +125,17 @@ class PullRequests::CreateService < ApplicationService
end

def gitea_pull_request_params
merge_original_pull_params.except(:milestone)
assignee_login = User.find_by_id(params[:assigned_to_id])&.login
merge_original_pull_params.except(:milestone).merge(
# assignees: ["#{params[:assigned_login].to_s}"],
assignees: ["#{assignee_login.to_s}"])
end

def merge_original_pull_params
base_pull_params[:head] = CGI.escape(base_pull_params[:head])
base_pull_params[:base] = CGI.escape(base_pull_params[:base])
if pull_request.is_original && @params[:merge_user_login]
base_pull_params.merge(head: "#{@params[:merge_user_login]}:#{@params[:head]}")
base_pull_params.merge(head: "#{@params[:merge_user_login]}:#{base_pull_params[:head]}")
else
base_pull_params
end
@@ -160,7 +165,7 @@ class PullRequests::CreateService < ApplicationService

def compare_head_base!
head = pull_request.is_original && @params[:merge_user_login] ? "#{@params[:merge_user_login]}/#{@project.identifier}:#{@params[:head]}" : @params[:head]
compare_result = Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, @params[:base], head, @current_user.gitea_token)
compare_result = Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, CGI.escape(@params[:base]), CGI.escape(head), @current_user.gitea_token)
raise '分支内容相同,无需创建合并请求' if compare_result["Commits"].blank? && compare_result["Diff"].blank?
end



+ 1
- 0
app/views/issues/show.json.jbuilder View File

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


+ 3
- 0
app/views/organizations/teams/_simple_detail.json.jbuilder View File

@@ -0,0 +1,3 @@
json.id team.id
json.name team.name
json.nickname team.nickname.blank? ? team.name : team.nickname

+ 6
- 2
app/views/organizations/teams/index.json.jbuilder View File

@@ -1,4 +1,8 @@
json.total_count @teams.total_count
json.total_count params[:is_full].present? ? @teams.count : @teams.total_count
json.teams @teams do |team|
json.partial! "detail", team: team, organization: @organization
if params[:is_full].present?
json.partial! "simple_detail", team: team, organization: @organization
else
json.partial! "detail", team: team, organization: @organization
end
end

+ 7
- 0
app/views/project_trends/_detail.json.jbuilder View File

@@ -6,6 +6,13 @@ json.user_name trend.user.try(:show_real_name)
json.user_login trend.user.login
json.user_avatar url_to_avatar(trend.user)
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"
json.partial! "issues/simple_issue_item", locals: {issue: trend.trend}


+ 19
- 18
app/views/projects/branches.json.jbuilder View File

@@ -1,21 +1,22 @@
json.array! @branches do |branch|
json.name branch['name']
json.user_can_push branch['user_can_push']
json.user_can_merge branch['user_can_merge']
json.protected branch['protected']
branch_name = branch.is_a?(Hash) ? branch['name'] : branch
json.name branch_name
# json.user_can_push branch['user_can_push']
# json.user_can_merge branch['user_can_merge']
# json.protected branch['protected']
json.http_url render_http_url(@project)
json.zip_url render_zip_url(@owner, @repository, branch['name'])
json.tar_url render_tar_url(@owner, @repository, branch['name'])
json.last_commit do
json.sha branch['commit']['id']
json.message branch['commit']['message']
json.timestamp render_unix_time(branch['commit']['timestamp'])
json.time_from_now time_from_now(branch['commit']['timestamp'])
json.author do
json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['author']), name: branch['commit']['author']['name']
end
json.committer do
json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name']
end
end
json.zip_url render_zip_url(@owner, @repository, branch_name)
json.tar_url render_tar_url(@owner, @repository, branch_name)
# json.last_commit do
# json.sha branch['commit']['id']
# json.message branch['commit']['message']
# json.timestamp render_unix_time(branch['commit']['timestamp'])
# json.time_from_now time_from_now(branch['commit']['timestamp'])
# json.author do
# json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['author']), name: branch['commit']['author']['name']
# end
# json.committer do
# json.partial! 'repositories/commit_author', user: render_cache_commit_author(branch['commit']['committer']), name: branch['commit']['committer']['name']
# end
# end
end

+ 1
- 1
app/views/projects/webhooks/_detail.json.jbuilder View File

@@ -1,4 +1,4 @@
json.(webhook, :id, :url, :http_method, :is_active)
json.type webhook.hook_task_type
json.type webhook.type
json.last_status webhook.last_status
json.create_time Time.at(webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S")

+ 1
- 1
app/views/projects/webhooks/edit.json.jbuilder View File

@@ -1,6 +1,6 @@
json.id @webhook.id
json.(@webhook, :id, :http_method, :content_type, :url, :secret, :last_status, :is_active)
json.type @webhook.hook_task_type
json.type @webhook.type
json.create_time Time.at(@webhook.created_unix).strftime("%Y-%m-%d %H:%M:%S")
event = @webhook.events
json.branch_filter event["branch_filter"]


+ 2
- 1
app/views/projects/webhooks/tasks.json.jbuilder View File

@@ -1,5 +1,6 @@
json.total_count @tasks.total_count
json.tasks @tasks.each do |task|
json.(task, :id, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content, :response_content)
json.(task, :id, :event_type, :type, :uuid, :is_succeed, :is_delivered, :payload_content, :request_content)
json.response_content task.response_content_json
json.delivered_time Time.at(task.delivered*10**-9).strftime("%Y-%m-%d %H:%M:%S")
end

+ 26
- 11
app/views/pull_requests/_commit.json.jbuilder View File

@@ -1,12 +1,27 @@
json.author do
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Author']), name: commit['Author']['Name'] }
end
if commit['Status'].present?
json.author do
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.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']
json.committer do
json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['commit']['committer']), name: commit['commit']['committer']['name'] }
end
json.timestamp render_unix_time(commit['commit']['committer']['date'])
json.time_from_now time_from_now(commit['commit']['committer']['date'])
json.created_at render_format_time_with_date(commit['commit']['committer']['date'])
json.message commit['commit']['message']
json.sha commit['sha']
end

+ 1
- 1
app/views/repositories/_simple_entry.json.jbuilder View File

@@ -9,7 +9,7 @@ if @project.forge?
json.type entry['type']
json.size entry['size']

json.content decode64_content(entry, @owner, @repository, @ref, @path)
json.content direct_download ? nil : decode64_content(entry, @owner, @repository, @ref, @path)
json.target entry['target']
download_url =


+ 1
- 0
config/routes.rb View File

@@ -213,6 +213,7 @@ Rails.application.routes.draw do
get :watch_users
get :fan_users
get :hovercard
put :update_image
end
collection do
post :following


+ 5
- 0
db/migrate/20211222015006_add_team_user_to_members.rb View File

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

+ 47
- 0
public/message_template/team_joined.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> 团队,拥有{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>

+ 47
- 0
public/message_template/team_left.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>

Loading…
Cancel
Save