Merge branch 'standalone_develop' into pre_trustie_server

This commit is contained in:
xiaoxiaoqiong 2022-06-02 16:41:27 +08:00
commit 6f10d4ca24
11 changed files with 553 additions and 422 deletions

View File

@ -2,8 +2,24 @@ class Admins::MessageTemplatesController < Admins::BaseController
before_action :get_template, only: [:edit, :update, :destroy]
def index
message_templates = MessageTemplate.group(:type).count.keys
@message_templates = kaminari_array_paginate(message_templates)
message_templates = MessageTemplate.ransack(sys_notice_or_email_or_email_title_cont: params[:search]).result
@message_templates = kaminari_paginate(message_templates)
end
def new
@message_template = MessageTemplate::CustomTip.new
end
def create
@message_template = MessageTemplate::CustomTip.new(ignore_params)
if @message_template.save!
redirect_to admins_message_templates_path
flash[:success] = "创建消息模板成功"
else
render :new
flash[:danger] = "创建消息模板失败"
end
end
def edit

View File

@ -1,7 +1,7 @@
class NoticesController < ApplicationController
def create
tip_exception("参数有误") if params["source"].blank?
return tip_exception("参数有误") if params["source"].blank?
user_id = params[:user_id]
if params["source"] == "CompetitionBegin"
@ -13,9 +13,21 @@ class NoticesController < ApplicationController
elsif params["source"] == "CompetitionReview"
competition_id = params[:competition_id]
SendTemplateMessageJob.perform_later('CompetitionReview', user_id, competition_id)
elsif params["source"] == "CustomTip"
users_id = params[:users_id]
props = params[:props].to_unsafe_hash
return tip_exception("参数有误") unless props.is_a?(Hash) && users_id.is_a?(Array)
template_id = params[:template_id]
SendTemplateMessageJob.perform_later('CustomTip', users_id, template_id, props)
else
tip_exception("#{params["source"]}未配置")
end
render_ok
end
private
def params_props
params.require(:notice).permit(:props)
end
end

View File

@ -4,6 +4,24 @@ class SendTemplateMessageJob < ApplicationJob
def perform(source, *args)
Rails.logger.info "SendTemplateMessageJob [args] #{args}"
case source
when 'CustomTip'
receivers_id, template_id, props = args[0], args[1], args[2]
template = MessageTemplate.find_by_id(template_id)
return unless template.present?
receivers = User.where(id: receivers_id).or(User.where(mail: receivers_id))
not_exists_receivers = receivers_id - receivers.pluck(:id) - receivers.pluck(:mail)
receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props)
Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props})
receivers.find_each do |receiver|
receivers_email_string, email_title, email_content = MessageTemplate::CustomTip.get_email_message_content(receiver, template, props)
Notice::Write::EmailCreateService.call(receivers_email_string, email_title, email_content)
end
not_exists_receivers.each do |mail|
valid_email_regex = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/i
next unless (mail =~ valid_email_regex)
email_title, email_content = MessageTemplate::CustomTip.get_email_content(template, props)
Notice::Write::EmailCreateService.call(mail, email_title, email_content)
end
when 'FollowTip'
watcher_id = args[0]
watcher = Watcher.find_by_id(watcher_id)

View File

@ -16,8 +16,7 @@ class MessageTemplate < ApplicationRecord
PLATFORM = 'GitLink'
def self.build_init_data
MessageTemplate::IssueAssignerExpire.destroy_all
MessageTemplate::IssueCreatorExpire.destroy_all
MessageTemplate.where.not(type: 'MessageTemplate::CustomTip').destroy_all
self.create(type: 'MessageTemplate::FollowedTip', sys_notice: '<b>{nickname}</b> 关注了你', notification_url: '{baseurl}/{login}')
email_html = File.read("#{email_template_html_dir}/issue_assigned.html")
self.create(type: 'MessageTemplate::IssueAssigned', sys_notice: '{nickname1}在 <b>{nickname2}/{repository}</b> 指派给你一个疑修:<b>{title}</b>', notification_url: '{baseurl}/{owner}/{identifier}/issues/{id}', email: email_html, email_title: "#{PLATFORM}: {nickname1} 在 {nickname2}/{repository} 指派给你一个疑修")

View File

@ -0,0 +1,70 @@
# == 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::CustomTip < MessageTemplate
# MessageTemplate::CustomTip.get_message_content(User.where(login: 'yystopf'), "hahah")
def self.get_message_content(receivers, template, props={})
return '', '', '' if receivers.blank? || template.blank?
content = template.sys_notice
notification_url = template.notification_url
props.each do |k, v|
content.gsub!("{#{k}}", v)
notification_url.gsub!("{#{k}}", v)
end
notification_url.gsub!('{baseurl}', base_url)
return receivers_string(receivers), content, notification_url
rescue => e
Rails.logger.info("MessageTemplate::CustomTip.get_message_content [ERROR] #{e}")
return '', '', ''
end
def self.get_email_message_content(receiver, template, props={})
return '', '', '' if receiver.blank? || template.blank?
title = template.email_title
content = template.email
props.each do |k, v|
title.gsub!("{#{k}}", v)
content.gsub!("{#{k}}", v)
end
content.gsub!('{receiver}', receiver&.real_name)
title.gsub!('{platform}', PLATFORM)
content.gsub!('{platform}', PLATFORM)
content.gsub!('{baseurl}', base_url)
return receiver&.mail, title, content
rescue => e
Rails.logger.info("MessageTemplate::CustomTip.get_email_message_content [ERROR] #{e}")
return '', '', ''
end
def self.get_email_content(template, props = {})
return '', '', '' if template.blank?
title = template.email_title
content = template.email
props.each do |k, v|
title.gsub!("{#{k}}", v)
content.gsub!("{#{k}}", v)
end
title.gsub!('{platform}', PLATFORM)
content.gsub!('{platform}', PLATFORM)
return title, content
rescue => e
Rails.logger.info("MessageTemplate::CustomTip.get_email_content [ERROR] #{e}")
return '', ''
end
end

View File

@ -1,412 +1,420 @@
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :text(4294967295)
# homepage :string(255) default("")
# is_public :boolean default("1"), not null
# parent_id :integer
# created_on :datetime
# updated_on :datetime
# identifier :string(255)
# status :integer default("1"), not null
# lft :integer
# rgt :integer
# inherit_members :boolean default("0"), not null
# project_type :integer default("0")
# hidden_repo :boolean default("0"), not null
# attachmenttype :integer default("1")
# user_id :integer
# dts_test :integer default("0")
# enterprise_name :string(255)
# organization_id :integer
# project_new_type :integer
# gpid :integer
# forked_from_project_id :integer
# forked_count :integer default("0")
# publish_resource :integer default("0")
# visits :integer default("0")
# hot :integer default("0")
# invite_code :string(255)
# qrcode :string(255)
# qrcode_expiretime :integer default("0")
# script :text(65535)
# training_status :integer default("0")
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
# pull_requests_count :integer default("0")
# language :string(255)
# versions_count :integer default("0")
# issue_tags_count :integer default("0")
# closed_issues_count :integer default("0")
# open_devops :boolean default("0")
# gitea_webhook_id :integer
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# license_id :integer
# ignore_id :integer
# default_branch :string(255) default("master")
# website :string(255)
# lesson_url :string(255)
# is_pinned :boolean default("0")
# recommend_index :integer default("0")
#
# Indexes
#
# index_projects_on_forked_from_project_id (forked_from_project_id)
# index_projects_on_identifier (identifier)
# index_projects_on_invite_code (invite_code)
# index_projects_on_is_public (is_public)
# index_projects_on_lft (lft)
# index_projects_on_license_id (license_id)
# index_projects_on_name (name)
# index_projects_on_platform (platform)
# index_projects_on_project_category_id (project_category_id)
# index_projects_on_project_language_id (project_language_id)
# index_projects_on_project_type (project_type)
# index_projects_on_recommend (recommend)
# index_projects_on_rgt (rgt)
# index_projects_on_status (status)
# index_projects_on_updated_on (updated_on)
#
class Project < ApplicationRecord
include Matchable
include Publicable
include Watchable
include ProjectOperable
include Dcodes
# common:开源托管项目
# mirror:普通镜像项目,没有定时同步功能
# sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作
#
enum project_type: { sync_mirror: 2, mirror: 1, common: 0 }
# forge: trustie平台项目 educoder: educoder平台项目 默认为forge平台
enum platform: { forge: 0, educoder: 1 }
belongs_to :ignore, optional: true
belongs_to :license, optional: true
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
belongs_to :project_category, optional: true , :counter_cache => true
belongs_to :project_language, optional: true , :counter_cache => true
belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id
has_many :project_trends, dependent: :destroy
has_many :watchers, as: :watchable, dependent: :destroy
has_many :fork_users, dependent: :destroy
has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy
has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id
has_one :project_educoder, dependent: :destroy
has_one :project_score, dependent: :destroy
has_one :repository, dependent: :destroy
has_many :pull_requests, dependent: :destroy
has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy
has_many :issues, dependent: :destroy
# has_many :user_grades, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
has_one :project_score, dependent: :destroy
has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_one :project_detail, dependent: :destroy
has_many :project_units, dependent: :destroy
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
has_many :user_trace_tasks, dependent: :destroy
after_create :incre_user_statistic, :incre_platform_statistic
after_save :check_project_members
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data
before_destroy :decre_project_common, :decre_forked_from_project_count
after_destroy :decre_user_statistic, :decre_platform_statistic
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
scope :pinned, -> {where(is_pinned: true)}
delegate :content, to: :project_detail, allow_nil: true
delegate :name, to: :license, prefix: true, allow_nil: true
def self.all_visible(user_id=nil)
user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql
if user_id.present?
org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql
org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible
else
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible
end
end
def reset_cache_data
CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id)
if changes[:user_id].present?
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last)
end
if changes[:project_language_id].present?
first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first)
last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id)
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1})
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1})
end
if changes[:is_public].present?
if changes[:is_public][0] && !changes[:is_public][1]
CacheAsyncClearJob.perform_later('project_rank_service', self.id)
end
if !changes[:is_public][0] && changes[:is_public][1]
$redis_cache.srem("v2-project-rank-deleted", self.id)
end
end
end
def decre_project_common
CacheAsyncClearJob.perform_later('project_common_service', self.id)
end
def decre_forked_from_project_count
forked_project = self.forked_from_project
if forked_project.present?
forked_project.decrement(:forked_count, 1)
forked_project.save
end
end
def incre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id)
end
def decre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id)
end
def incre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1})
end
def decre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1})
end
def is_full_public
owner = self.owner
if owner.is_a?(Organization)
return self.is_public && owner&.visibility == "common"
else
return self.is_public
end
end
def reset_unmember_followed
if changes[:is_public].present? && changes[:is_public] == [true, false]
self.watchers.where.not(user_id: self.all_collaborators).destroy_all
end
end
def set_invite_code
if self.invite_code.nil?
self.invite_code= self.generate_dcode('invite_code', 6)
end
end
def set_recommend_and_is_pinned
self.recommend = self.recommend_index.zero? ? false : true
# 私有项目不允许设置精选和推荐
unless self.is_public
self.recommend = false
self.recommend_index = 0
self.is_pinned = false
end
end
def self.search_project(search)
ransack(name_or_identifier_cont: search)
end
# 创建者
def creator
User.find(user_id).full_name
end
def members_user_infos
members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User")
# members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname")
# .pluck("users.id", "users.login","users.lastname", "users.firstname")
end
def to_param
self.identifier.parameterize
end
def get_issues_count(status_id)
if status_id.present?
self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size
else
self&.issues.issue_issue.select(:id)&.pluck(:id).size
end
end
def get_pull_requests_count(status_id)
if status_id.present?
self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size
else
self&.pull_requests.select(:id)&.pluck(:id).size
end
end
#创建项目管理员
def check_project_members
return if owner.is_a?(Organization)
unless members.present? && members.exists?(user_id: self.user_id)
member_params = {
user_id: self.user_id,
project_id: self.id
}
user_member = Member.new(member_params)
if user_member.save
role_id = Role.select(:id,:position).where(position: 3)&.first&.id
MemberRole.create!(member_id: user_member.id ,role_id: role_id)
end
end
end
def self.init_bluck_repository
Project.includes(:repository).find_each do |project|
puts project.id
next if project.owner.blank?
if project.repository.blank?
puts "########### start create repositoy #############"
Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id)
end
end
end
def self.generate_identifier
str_arr = (("a".."z").to_a + ("A".."Z").to_a)
str = str_arr.shuffle[0..8].join
while Repository.exists?(identifier: str)
str = str_arr.shuffle[0..8].join
end
str
end
def self.list_user_projects(user_id)
projects = Project.is_private.select(:id,:user_id)
user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq
user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq
Project.where.not(id: (user_not_show_1 - user_show_2).uniq)
end
def members_count
members.select(:id).size
end
def can_visited?
is_public? || User.current.admin? || member?(User.current)
end
def releases_size(current_user_id, type)
if current_user_id == self.user_id && type.to_s == "all"
self.repository.version_releases_count
else
self.repository.version_releases.releases_size
end
end
def contributor_users
self.pull_requests.select(:user_id).pluck(:user_id).uniq.size
end
def open_issues_count
issues_count - closed_issues_count
end
def numerical_for_project_type
self.class.name.constantize.project_types["#{self.project_type}"]
end
def watched_by? user
watchers.pluck(:user_id).include? user&.id
end
def praised_by? user
praise_treads.pluck(:user_id).include? user&.id
end
def get_premission user
return "Owner" if owner?(user)
return "Manager" if manager?(user)
return "Developer" if develper?(user)
return "Reporter" if reporter?(user)
return ""
end
def fork_project
Project.find_by(id: self.forked_from_project_id)
end
def self.members_projects(member_user_id)
joins(:members).where(members: { user_id: member_user_id})
end
def self.find_with_namespace(namespace_path, identifier)
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
user = Owner.find_by_login namespace_path
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
return nil if project.blank?
[project, user]
end
def ci_reactivate?
open_devops_count > 0
end
def ci_reactivate!(ci_repo)
ci_repo.update_column(:repo_active, 1)
update_column(:open_devops, true)
increment!(:open_devops_count)
end
def self.sync_educoder_shixun(url, private_token, page, per_page)
SyncEducoderShixunJob.perform_later(url, private_token, page, per_page)
end
def self.update_common_projects_count!
ps = ProjectStatistic.first
ps.increment!(:common_projects_count) unless ps.blank?
end
def self.update_mirror_projects_count!
ps = ProjectStatistic.first
ps.increment!(:mirror_projects_count) unless ps.blank?
end
def set_updated_on(time)
return if time.blank?
update_column(:updated_on, time)
end
def is_transfering
applied_transfer_project&.common? ? true : false
end
end
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255) default(""), not null
# description :text(4294967295)
# homepage :string(255) default("")
# is_public :boolean default("1"), not null
# parent_id :integer
# created_on :datetime
# updated_on :datetime
# identifier :string(255)
# status :integer default("1"), not null
# lft :integer
# rgt :integer
# inherit_members :boolean default("0"), not null
# project_type :integer default("0")
# hidden_repo :boolean default("0"), not null
# attachmenttype :integer default("1")
# user_id :integer
# dts_test :integer default("0")
# enterprise_name :string(255)
# organization_id :integer
# project_new_type :integer
# gpid :integer
# forked_from_project_id :integer
# forked_count :integer default("0")
# publish_resource :integer default("0")
# visits :integer default("0")
# hot :integer default("0")
# invite_code :string(255)
# qrcode :string(255)
# qrcode_expiretime :integer default("0")
# script :text(65535)
# training_status :integer default("0")
# rep_identifier :string(255)
# project_category_id :integer
# project_language_id :integer
# license_id :integer
# ignore_id :integer
# praises_count :integer default("0")
# watchers_count :integer default("0")
# issues_count :integer default("0")
# pull_requests_count :integer default("0")
# language :string(255)
# versions_count :integer default("0")
# issue_tags_count :integer default("0")
# closed_issues_count :integer default("0")
# open_devops :boolean default("0")
# gitea_webhook_id :integer
# open_devops_count :integer default("0")
# recommend :boolean default("0")
# platform :integer default("0")
# default_branch :string(255) default("master")
# website :string(255)
# lesson_url :string(255)
# is_pinned :boolean default("0")
# recommend_index :integer default("0")
#
# Indexes
#
# index_projects_on_forked_from_project_id (forked_from_project_id)
# index_projects_on_identifier (identifier)
# index_projects_on_invite_code (invite_code)
# index_projects_on_is_public (is_public)
# index_projects_on_lft (lft)
# index_projects_on_license_id (license_id)
# index_projects_on_name (name)
# index_projects_on_platform (platform)
# index_projects_on_project_category_id (project_category_id)
# index_projects_on_project_language_id (project_language_id)
# index_projects_on_project_type (project_type)
# index_projects_on_recommend (recommend)
# index_projects_on_rgt (rgt)
# index_projects_on_status (status)
# index_projects_on_updated_on (updated_on)
#
class Project < ApplicationRecord
include Matchable
include Publicable
include Watchable
include ProjectOperable
include Dcodes
# common:开源托管项目
# mirror:普通镜像项目,没有定时同步功能
# sync_mirror:同步镜像项目,有系统定时同步功能,且用户可手动同步操作
#
enum project_type: { sync_mirror: 2, mirror: 1, common: 0 }
# forge: trustie平台项目 educoder: educoder平台项目 默认为forge平台
enum platform: { forge: 0, educoder: 1 }
belongs_to :ignore, optional: true
belongs_to :license, optional: true
belongs_to :owner, class_name: 'Owner', foreign_key: :user_id, optional: true
belongs_to :organization_extension, foreign_key: :user_id, primary_key: :organization_id, optional: true, counter_cache: :num_projects
belongs_to :project_category, optional: true , :counter_cache => true
belongs_to :project_language, optional: true , :counter_cache => true
belongs_to :forked_from_project, class_name: 'Project', optional: true, foreign_key: :forked_from_project_id
has_many :project_trends, dependent: :destroy
has_many :watchers, as: :watchable, dependent: :destroy
has_many :fork_users, dependent: :destroy
has_many :forked_users, class_name: 'ForkUser', foreign_key: :fork_project_id, dependent: :destroy
has_many :forked_projects, class_name: 'Project', foreign_key: :forked_from_project_id
has_one :project_educoder, dependent: :destroy
has_one :project_score, dependent: :destroy
has_one :repository, dependent: :destroy
has_many :pull_requests, dependent: :destroy
has_many :issue_tags, -> { order("issue_tags.created_at DESC") }, dependent: :destroy
has_many :issues, dependent: :destroy
# has_many :user_grades, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
has_one :project_score, dependent: :destroy
has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position"
has_one :project_detail, dependent: :destroy
has_many :project_units, dependent: :destroy
has_one :applied_transfer_project,-> { order created_at: :desc }, dependent: :destroy
has_many :pinned_projects, dependent: :destroy
has_many :has_pinned_users, through: :pinned_projects, source: :user
has_many :webhooks, class_name: "Gitea::Webhook", primary_key: :gpid, foreign_key: :repo_id
has_many :user_trace_tasks, dependent: :destroy
after_create :incre_user_statistic, :incre_platform_statistic
after_save :check_project_members
before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data
before_destroy :decre_project_common, :decre_forked_from_project_count
after_destroy :decre_user_statistic, :decre_platform_statistic
scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :description, :visits, :project_category_id, :project_language_id, :license_id, :ignore_id, :watchers_count, :created_on)}
scope :no_anomory_projects, -> {where("projects.user_id is not null and projects.user_id != ?", 2)}
scope :recommend, -> { visible.project_statics_select.where(recommend: true) }
scope :pinned, -> {where(is_pinned: true)}
delegate :content, to: :project_detail, allow_nil: true
delegate :name, to: :license, prefix: true, allow_nil: true
validate :validate_sensitive_string
def self.all_visible(user_id=nil)
user_projects_sql = Project.joins(:owner).where(users: {type: 'User'}).to_sql
org_public_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'common'})).to_sql
if user_id.present?
org_limit_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension).where(organization_extensions: {visibility: 'limited'})).to_sql
org_privacy_projects_sql = Project.joins(:owner).merge(Organization.joins(:organization_extension, :organization_users).where(organization_extensions: {visibility: 'privacy'}, organization_users: {user_id: user_id})).to_sql
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } UNION #{ org_limit_projects_sql } UNION #{org_privacy_projects_sql} ) AS projects").visible
else
return Project.from("( #{ user_projects_sql } UNION #{ org_public_projects_sql } ) AS projects").visible
end
end
def reset_cache_data
CacheAsyncResetJob.set(wait: 5.seconds).perform_later("project_common_service", self.id)
if changes[:user_id].present?
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1}, changes[:user_id].first)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1}, changes[:user_id].last)
end
if changes[:project_language_id].present?
first_language = ProjectLanguage.find_by_id(changes[:project_language_id].first)
last_language = ProjectLanguage.find_by_id(changes[:project_language_id].last)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1}, self.user_id)
CacheAsyncSetJob.perform_later("user_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1}, self.user_id)
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: first_language&.name, project_language_count: -1})
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_language_count_key: last_language&.name, project_language_count: 1})
end
if changes[:is_public].present?
if changes[:is_public][0] && !changes[:is_public][1]
CacheAsyncClearJob.perform_later('project_rank_service', self.id)
end
if !changes[:is_public][0] && changes[:is_public][1]
$redis_cache.srem("v2-project-rank-deleted", self.id)
end
end
end
def decre_project_common
CacheAsyncClearJob.perform_later('project_common_service', self.id)
end
def decre_forked_from_project_count
forked_project = self.forked_from_project
if forked_project.present?
forked_project.decrement(:forked_count, 1)
forked_project.save
end
end
def incre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1}, self.user_id)
end
def decre_user_statistic
CacheAsyncSetJob.perform_later("user_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1}, self.user_id)
end
def incre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: 1, project_language_count_key: self.project_language&.name, project_language_count: 1})
end
def decre_platform_statistic
CacheAsyncSetJob.perform_later("platform_statistic_service", {project_count: -1, project_language_count_key: self.project_language&.name, project_language_count: -1})
end
def is_full_public
owner = self.owner
if owner.is_a?(Organization)
return self.is_public && owner&.visibility == "common"
else
return self.is_public
end
end
def reset_unmember_followed
if changes[:is_public].present? && changes[:is_public] == [true, false]
self.watchers.where.not(user_id: self.all_collaborators).destroy_all
end
end
def set_invite_code
if self.invite_code.nil?
self.invite_code= self.generate_dcode('invite_code', 6)
end
end
def set_recommend_and_is_pinned
self.recommend = self.recommend_index.zero? ? false : true
# 私有项目不允许设置精选和推荐
unless self.is_public
self.recommend = false
self.recommend_index = 0
self.is_pinned = false
end
end
def self.search_project(search)
ransack(name_or_identifier_cont: search)
end
# 创建者
def creator
User.find(user_id).full_name
end
def members_user_infos
members.joins(:roles).where("roles.name in ('Manager', 'Developer', 'Reporter')").joins("left join users on members.user_id = users.id ").includes(:user).where("users.type = ?", "User")
# members.joins("left join users on members.user_id = users.id").select("users.id", "users.login","users.firstname","users.lastname")
# .pluck("users.id", "users.login","users.lastname", "users.firstname")
end
def to_param
self.identifier.parameterize
end
def get_issues_count(status_id)
if status_id.present?
self&.issues.issue_issue.select(:id, :status_id).where(status_id: status_id)&.pluck(:id).size
else
self&.issues.issue_issue.select(:id)&.pluck(:id).size
end
end
def get_pull_requests_count(status_id)
if status_id.present?
self&.pull_requests.select(:id, :status).where(status: status_id)&.pluck(:id).size
else
self&.pull_requests.select(:id)&.pluck(:id).size
end
end
#创建项目管理员
def check_project_members
return if owner.is_a?(Organization)
unless members.present? && members.exists?(user_id: self.user_id)
member_params = {
user_id: self.user_id,
project_id: self.id
}
user_member = Member.new(member_params)
if user_member.save
role_id = Role.select(:id,:position).where(position: 3)&.first&.id
MemberRole.create!(member_id: user_member.id ,role_id: role_id)
end
end
end
def self.init_bluck_repository
Project.includes(:repository).find_each do |project|
puts project.id
next if project.owner.blank?
if project.repository.blank?
puts "########### start create repositoy #############"
Repository.create!(project_id: project.id, identifier: Project.generate_identifier, user_id: project&.owner&.id)
end
end
end
def self.generate_identifier
str_arr = (("a".."z").to_a + ("A".."Z").to_a)
str = str_arr.shuffle[0..8].join
while Repository.exists?(identifier: str)
str = str_arr.shuffle[0..8].join
end
str
end
def self.list_user_projects(user_id)
projects = Project.is_private.select(:id,:user_id)
user_not_show_1 = projects.where("user_id != ?",user_id).pluck(:id).uniq
user_show_2 = projects.joins(:members).where("members.user_id = ?", user_id).pluck(:id).uniq
Project.where.not(id: (user_not_show_1 - user_show_2).uniq)
end
def members_count
members.select(:id).size
end
def can_visited?
is_public? || User.current.admin? || member?(User.current)
end
def releases_size(current_user_id, type)
if current_user_id == self.user_id && type.to_s == "all"
self.repository.version_releases_count
else
self.repository.version_releases.releases_size
end
end
def contributor_users
self.pull_requests.select(:user_id).pluck(:user_id).uniq.size
end
def open_issues_count
issues_count - closed_issues_count
end
def numerical_for_project_type
self.class.name.constantize.project_types["#{self.project_type}"]
end
def watched_by? user
watchers.pluck(:user_id).include? user&.id
end
def praised_by? user
praise_treads.pluck(:user_id).include? user&.id
end
def get_premission user
return "Owner" if owner?(user)
return "Manager" if manager?(user)
return "Developer" if develper?(user)
return "Reporter" if reporter?(user)
return ""
end
def fork_project
Project.find_by(id: self.forked_from_project_id)
end
def self.members_projects(member_user_id)
joins(:members).where(members: { user_id: member_user_id})
end
def self.find_with_namespace(namespace_path, identifier)
logger.info "########namespace_path: #{namespace_path} ########identifier: #{identifier} "
user = Owner.find_by_login namespace_path
user = Owner.new(login: namespace_path) if user.nil?
project = user&.projects&.find_by(identifier: identifier) || Project.find_by(identifier: "#{namespace_path}/#{identifier}")
return nil if project.blank?
[project, user]
end
def ci_reactivate?
open_devops_count > 0
end
def ci_reactivate!(ci_repo)
ci_repo.update_column(:repo_active, 1)
update_column(:open_devops, true)
increment!(:open_devops_count)
end
def self.sync_educoder_shixun(url, private_token, page, per_page)
SyncEducoderShixunJob.perform_later(url, private_token, page, per_page)
end
def self.update_common_projects_count!
ps = ProjectStatistic.first
ps.increment!(:common_projects_count) unless ps.blank?
end
def self.update_mirror_projects_count!
ps = ProjectStatistic.first
ps.increment!(:mirror_projects_count) unless ps.blank?
end
def set_updated_on(time)
return if time.blank?
update_column(:updated_on, time)
end
def is_transfering
applied_transfer_project&.common? ? true : false
end
def validate_sensitive_string
raise("项目名称包含敏感词汇,请重新输入") if name && !HarmoniousDictionary.clean?(name)
raise("项目描述包含敏感词汇,请重新输入") if description && !HarmoniousDictionary.clean?(description)
end
end

View File

@ -11,8 +11,8 @@
</thead>
<tbody>
<% if message_templates.present? %>
<% message_templates.each_with_index do |message_template_type, index| %>
<% message_template = message_template_type.constantize.last%>
<% message_templates.each_with_index do |message_template, index| %>
<%# message_template = message_template_type.constantize.last%>
<tr class="project-language-item-<%= message_template.id %>">
<td><%= list_index_no((params[:page] || 1).to_i, index) %></td>
<td><%= message_template.simple_type %></td>

View File

@ -3,7 +3,13 @@
<% end %>
<div id="admins-message-templates-content">
<div class="box search-form-container project-list-form">
<%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary pull-right", "data-disabled-with":"...初始化数据" %>
<%= form_tag(admins_message_templates_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<input type="reset" class="btn btn-secondary clear-btn" value="清空"/>
<% end %>
<%= link_to "初始化数据", init_data_admins_message_templates_path, class: "btn btn-primary mr-3 pull-right", "data-disabled-with":"...初始化数据" %>
<%= link_to "新增", new_admins_message_template_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %>
</div>
<div class="box admin-list-container message-templates-list-container">
<%= render partial: 'admins/message_templates/list', locals: { message_templates: @message_templates } %>

View File

@ -0,0 +1,2 @@
$("#admins-message-templates-content").html("<%= j render partial: 'admins/message_templates/form', locals:{type: 'create'} %>")
createMDEditor('message-template-email-editor', { height: 500, placeholder: '请输入邮件模版' });

View File

@ -1,6 +1,6 @@
json.total_count @projects.total_count
json.projects @projects.each do |project|
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id)
json.(project, :id, :name, :identifier, :description, :forked_count, :praises_count, :forked_from_project_id, :is_public)
json.mirror_url project.repository&.mirror_url
json.type project.numerical_for_project_type
json.praised project.praised_by?(current_user)

View File

@ -729,7 +729,7 @@ Rails.application.routes.draw do
get :history
end
end
resources :message_templates, only: [:index, :edit, :update] do
resources :message_templates, only: [:index, :new, :create, :edit, :update] do
collection do
get :init_data
end