| @@ -1,6 +1,21 @@ | |||
| # Changelog | |||
| ## [v3.1.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 | |||
| * ENHANCEMENTS | |||
| * ADD 用户活动统计图表功能 | |||
| * ADD 用户精选项目功能 | |||
| * ADD 用户贡献度统计图表功能 | |||
| * ADD 用户开发能力数据统计工 | |||
| * ADD 用户角色定位展示功能 | |||
| * ADD 用户专业定位标签展示功能 | |||
| * ADD 修改用户基本资料功能 | |||
| * ADD 更改密码功能 | |||
| * ADD 用户个人主页基本现在展示可配置功能 | |||
| * BUGFIXES | |||
| * Fix 解决一些bug | |||
| * Fix 优化美化页面 | |||
| ## [v3.0.4](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-05-24 | |||
| * BUGFIXES | |||
| * Fix 在线修改文件,页面文件显不及时的问题(46049) | |||
| @@ -32,7 +32,7 @@ class Admins::AuthSchoolsController < Admins::BaseController | |||
| def search_manager | |||
| school = School.find_by(id: params[:school_id]) | |||
| user_ids = school&.ec_school_users&.pluck(:user_id) | |||
| @users = User.where.not(id: user_ids).where("CONCAT_WS(lastname, firstname, nickname) like ?", "%#{params[:name].strip.to_s}%").limit(10) | |||
| @users = User.where.not(id: user_ids).where("CONCAT(lastname, firstname) like ? OR nickname like ?", "%#{params[:name].strip.to_s}%", "%#{params[:name].strip.to_s}%").limit(10) | |||
| end | |||
| # 添加认证学校管理员 | |||
| @@ -2,8 +2,8 @@ class Admins::FaqsController < Admins::BaseController | |||
| before_action :find_faq, only: [:edit,:update, :destroy] | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'updated_at' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = Faq.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| keyword = params[:keyword].to_s.strip | |||
| collection = Faq.search_question(keyword).order("#{sort_by} #{sort_direction}") | |||
| @@ -32,8 +32,8 @@ class Admins::LaboratoriesController < Admins::BaseController | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword '\ | |||
| 'OR mirror_repositories.name LIKE :keyword' | |||
| like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\ | |||
| 'users.nickname LIKE :keyword OR mirror_repositories.name LIKE :keyword' | |||
| shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -48,7 +48,7 @@ class Admins::LaboratoriesController < Admins::BaseController | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' | |||
| like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' | |||
| subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -3,8 +3,8 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| before_action :validate_names, only: [:create, :update] | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'created_at' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = ProjectCategory.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = ProjectCategory.ransack(name_cont: params[:name]) | |||
| project_categories = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @project_categories = paginate(project_categories) | |||
| @@ -3,8 +3,8 @@ class Admins::ProjectIgnoresController < Admins::BaseController | |||
| before_action :validate_params, only: [:create, :update] | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'created_at' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = Ignore.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = Ignore.ransack(name_cont: params[:search]) | |||
| project_ignores = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @project_ignores = paginate(project_ignores) | |||
| @@ -3,8 +3,8 @@ class Admins::ProjectLanguagesController < Admins::BaseController | |||
| before_action :validate_names, only: [:create, :update] | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'created_at' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = ProjectLanguage.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = ProjectLanguage.ransack(name_cont: params[:search]) | |||
| project_languages = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @project_languages = paginate(project_languages) | |||
| @@ -3,8 +3,8 @@ class Admins::ProjectLicensesController < Admins::BaseController | |||
| before_action :validate_params, only: [:create, :update] | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'created_at' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = License.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = License.ransack(name_cont: params[:search]) | |||
| project_licenses = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @project_licenses = paginate(project_licenses) | |||
| @@ -1,9 +1,8 @@ | |||
| class Admins::ProjectsController < Admins::BaseController | |||
| def index | |||
| sort_by = params[:sort_by] ||= 'created_on' | |||
| sort_direction = params[:sort_direction] ||= 'desc' | |||
| sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| search = params[:search].to_s.strip | |||
| projects = Project.where("name like ?", "%#{search}%").order("#{sort_by} #{sort_direction}") | |||
| @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score) | |||
| @@ -615,8 +615,8 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| # 排序 | |||
| rorder = option[:order] || "updated_at" | |||
| b_order = option[:b_order] || "desc" | |||
| rorder = UserExtension.column_names.include?(option[:order]) ? option[:order] : "updated_at" | |||
| b_order = %w(desc asc).include?(option[:b_order]) ? option[:b_order] : "desc" | |||
| if rorder == "created_at" || rorder == "work_score" | |||
| work_list = work_list.order("graduation_works.#{rorder} #{b_order}") | |||
| elsif rorder == "student_id" | |||
| @@ -0,0 +1,13 @@ | |||
| class AppliedProjectsController < ApplicationController | |||
| before_action :require_login | |||
| def create | |||
| @applied_project = Projects::ApplyJoinService.call(current_user, applied_params) | |||
| rescue Projects::ApplyJoinService::Error => ex | |||
| render_error(ex.message) | |||
| end | |||
| private | |||
| def applied_params | |||
| params.require(:applied_project).permit(:code, :role) | |||
| end | |||
| end | |||
| @@ -3,13 +3,12 @@ class ComposesController < ApplicationController | |||
| before_action :find_compose, except: [:index, :new,:create] | |||
| def index | |||
| @order_type = params[:order] || "created_at" | |||
| @search_name = params[:search] | |||
| composes = Compose.compose_includes | |||
| if @search_name.present? | |||
| composes = composes.where("title like ?", "%#{@search_name}%") | |||
| end | |||
| composes = composes.order("#{@order_type} desc") | |||
| composes = composes.order("#{order_type} desc") | |||
| @page = params[:page] || 1 | |||
| @limit = params[:limit] || 15 | |||
| @composes_size = composes.size | |||
| @@ -96,4 +95,8 @@ class ComposesController < ApplicationController | |||
| end | |||
| end | |||
| def order_type | |||
| Compose.column_names.include?(params[:order_type]) ? params[:order_type] : 'created_at' | |||
| end | |||
| end | |||
| @@ -7,9 +7,6 @@ class IssueTagsController < ApplicationController | |||
| def index | |||
| order_name = params[:order_name] || "created_at" | |||
| order_type = params[:order_type] || "desc" | |||
| issue_tags = @project.issue_tags.order("#{order_name} #{order_type}") | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| @page = params[:page] || 1 | |||
| @@ -138,4 +135,14 @@ class IssueTagsController < ApplicationController | |||
| end | |||
| end | |||
| private | |||
| def order_name | |||
| IssueTag.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_at' | |||
| end | |||
| def order_type | |||
| %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' | |||
| end | |||
| end | |||
| @@ -17,11 +17,15 @@ class IssuesController < ApplicationController | |||
| issues = @project.issues.issue_issue.issue_index_includes | |||
| issues = issues.where(is_private: false) unless @user_admin_or_member | |||
| @all_issues_size = issues.size | |||
| @open_issues_size = issues.where.not(status_id: 5).size | |||
| @close_issues_size = issues.where(status_id: 5).size | |||
| @assign_to_me_size = issues.where(assigned_to_id: current_user&.id).size | |||
| @my_published_size = issues.where(author_id: current_user&.id).size | |||
| @all_issues = issues | |||
| @filter_issues = @all_issues | |||
| @filter_issues = @filter_issues.where.not(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::ADD | |||
| @filter_issues = @filter_issues.where(status_id: IssueStatus::CLOSED) if params[:status_type].to_i == IssueStatus::SOLVING | |||
| @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? | |||
| @open_issues = @all_issues.where.not(status_id: IssueStatus::CLOSED) | |||
| @close_issues = @all_issues.where(status_id: IssueStatus::CLOSED) | |||
| @assign_to_me = @filter_issues.where(assigned_to_id: current_user&.id) | |||
| @my_published = @filter_issues.where(author_id: current_user&.id) | |||
| scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "Issue") | |||
| @issues_size = scopes.size | |||
| @issues = paginate(scopes) | |||
| @@ -18,7 +18,7 @@ class MembersController < ApplicationController | |||
| scope = @project.members.includes(:roles, user: :user_extension) | |||
| search = params[:search].to_s.downcase | |||
| role = params[:role].to_s | |||
| scope = scope.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? | |||
| scope = scope.joins(:user).merge(User.like(search)) | |||
| scope = scope.joins(:roles).where("roles.name LIKE ?", "%#{role}%") if role.present? | |||
| @total_count = scope.size | |||
| @@ -5,7 +5,7 @@ class Organizations::OrganizationUsersController < Organizations::BaseController | |||
| def index | |||
| @organization_users = @organization.organization_users.includes(:user) | |||
| search = params[:search].to_s.downcase | |||
| @organization_users = @organization_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? | |||
| @organization_users = @organization_users.joins(:user).merge(User.like(search)) | |||
| @organization_users = kaminari_paginate(@organization_users) | |||
| end | |||
| @@ -88,11 +88,11 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| end | |||
| def sort_by | |||
| params.fetch(:sort_by, "created_at") | |||
| OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| end | |||
| def sort_direction | |||
| params.fetch(:sort_direction, "desc") | |||
| %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| end | |||
| end | |||
| @@ -36,10 +36,10 @@ class Organizations::ProjectsController < Organizations::BaseController | |||
| end | |||
| def sort | |||
| params.fetch(:sort_by, "updated_on") | |||
| Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'updated_on' | |||
| end | |||
| def sort_direction | |||
| params.fetch(:sort_direction, "desc") | |||
| %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| end | |||
| end | |||
| @@ -8,7 +8,7 @@ class Organizations::TeamUsersController < Organizations::BaseController | |||
| @team_users = @team.team_users.includes(:user) | |||
| search = params[:search].to_s.downcase | |||
| @team_users = @team_users.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.mail, users.nickname)) LIKE ?", "%#{search.split(" ").join('|')}%") if search.present? | |||
| @team_users = @team_users.joins(:user).merge(User.like(search)) | |||
| @team_users = kaminari_paginate(@team_users) | |||
| end | |||
| @@ -12,10 +12,12 @@ class PullRequestsController < ApplicationController | |||
| # @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取 | |||
| issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user) | |||
| issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user)) | |||
| @all_issues_size = issues.size | |||
| @open_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 0}).size | |||
| @close_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 2}).size | |||
| @merged_issues_size = issues.joins(:pull_request).where(pull_requests: {status: 1}).size | |||
| @all_issues = issues.distinct | |||
| @filter_issues = @all_issues | |||
| @filter_issues = @filter_issues.where("subject LIKE ? OR description LIKE ? ", "%#{params[:search]}%", "%#{params[:search]}%") if params[:search].present? | |||
| @open_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::OPEN}) | |||
| @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED}) | |||
| @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED}) | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "PullRequest") | |||
| @@ -56,9 +56,7 @@ class RepositoriesController < ApplicationController | |||
| # TODO | |||
| # 临时处理readme文件问题 | |||
| admin = current_user.blank? ? User.where(admin: true).last : current_user | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, admin&.gitea_token) | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, @owner&.gitea_token) | |||
| @readme = | |||
| if result[:status] == :success | |||
| result[:body] | |||
| @@ -9,7 +9,7 @@ class Users::AppliedMessagesController < Users::BaseController | |||
| private | |||
| def check_auth | |||
| return render_forbidden unless observed_logged_user? | |||
| return render_forbidden unless current_user.admin? || observed_logged_user? | |||
| end | |||
| def view_messages | |||
| @@ -0,0 +1,39 @@ | |||
| class Users::AppliedProjectsController < Users::BaseController | |||
| before_action :check_auth | |||
| before_action :find_applied_project, except: [:index] | |||
| before_action :find_project, except: [:index] | |||
| def index | |||
| @applied_projects = AppliedProject.where(project_id: observed_user.full_admin_projects) | |||
| @applied_projects = paginate @applied_projects.order("created_at desc") | |||
| end | |||
| # 接受申请 | |||
| def accept | |||
| @applied_project = Projects::AcceptJoinService.call(current_user, @applied_project) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| # 拒绝申请 | |||
| def refuse | |||
| @applied_project = Projects::RefuseJoinService.call(current_user, @applied_project) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| private | |||
| def check_auth | |||
| return render_forbidden unless current_user.admin? || observed_logged_user? | |||
| end | |||
| def find_applied_project | |||
| @applied_project = AppliedProject.find_by_id params[:id] | |||
| end | |||
| def find_project | |||
| @project = @applied_project.project | |||
| end | |||
| end | |||
| @@ -28,7 +28,7 @@ class Users::AppliedTransferProjectsController < Users::BaseController | |||
| private | |||
| def check_auth | |||
| return render_forbidden unless observed_logged_user? | |||
| return render_forbidden unless current_user.admin? || observed_logged_user? | |||
| end | |||
| def find_applied_transfer_project | |||
| @@ -1,8 +1,8 @@ | |||
| class Users::BanksController < Users::BaseController | |||
| before_action :params_filter | |||
| def index | |||
| order = params[:order] || "updated_at" | |||
| sort = params[:sort] || "desc" | |||
| order = CourseList.column_names.include?(params[:order]) ? params[:order] : "updated_at" | |||
| sort = %w(desc asc).includes?(params[:sort]) ? params[:sort] : "desc" | |||
| @banks = @object_type.classify.constantize.where(@object_filter) | |||
| @course_lists = CourseList.where(id: @banks.pluck(:course_list_id)) | |||
| @banks = @banks.where(course_list_id: params[:tag_id]) unless params[:tag_id].blank? | |||
| @@ -16,10 +16,10 @@ class Users::OrganizationsController < Users::BaseController | |||
| private | |||
| def sort_by | |||
| params.fetch(:sort_by, "created_at") | |||
| OrganizationExtension.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| end | |||
| def sort_direction | |||
| params.fetch(:sort_direction, "desc") | |||
| %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| end | |||
| end | |||
| @@ -28,13 +28,15 @@ class UsersController < ApplicationController | |||
| def show | |||
| #待办事项,现在未做 | |||
| if User.current.login == @user.login | |||
| if User.current.admin? || User.current.login == @user.login | |||
| @waiting_applied_messages = @user.applied_messages.waiting | |||
| @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @user.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @user.id}, teams: {authorize: %w(admin owner)} )).common | |||
| @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size | |||
| @common_applied_projects = AppliedProject.where(project_id: @user.full_admin_projects).common | |||
| @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size | |||
| else | |||
| @waiting_applied_messages = AppliedMessage.none | |||
| @common_applied_transfer_projects = AppliedTransferProject.none | |||
| @common_applied_projects = AppliedProject.none | |||
| @undo_events = 0 | |||
| end | |||
| #用户的组织数量 | |||
| @@ -63,8 +65,7 @@ class UsersController < ApplicationController | |||
| def fan_users | |||
| watchers = @user.watchers.includes(:user).order("watchers.created_at desc") | |||
| watchers = watchers.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.login, users.nickname)) LIKE ?", "%#{params[:search].split(" ").join('|')}%") if params[:search].present? | |||
| watchers = watchers.joins(:user).merge(User.like(params[:search])) | |||
| @watchers_count = watchers.size | |||
| @watchers = paginate(watchers) | |||
| end | |||
| @@ -11,7 +11,7 @@ class UsersForPrivateMessagesController < ApplicationController | |||
| return | |||
| end | |||
| users = users.where('LOWER(CONCAT_WS(lastname, firstname, nickname)) LIKE ?', "%#{keyword}%") | |||
| users = users.like(keyword) | |||
| @users = users.limit(10).includes(:user_extension) | |||
| end | |||
| @@ -7,8 +7,6 @@ class VersionsController < ApplicationController | |||
| def index | |||
| return render_not_found unless @project.has_menu_permission("versions") | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| order_name = params[:order_name] || "created_on" | |||
| order_type = params[:order_type] || "desc" | |||
| status = params[:status] | |||
| versions = @project.versions.version_includes | |||
| @open_versions_size = versions.where(status: "open")&.size | |||
| @@ -27,9 +25,6 @@ class VersionsController < ApplicationController | |||
| end | |||
| def show | |||
| order_name = params[:order_name] || "created_on" | |||
| order_type = params[:order_type] || "desc" | |||
| version_issues = @version.issues.issue_includes | |||
| status_type = params[:status_type] || "1" | |||
| @@ -167,4 +162,12 @@ class VersionsController < ApplicationController | |||
| end | |||
| end | |||
| def order_name | |||
| Version.column_names.include?(params[:order_name]) ? params[:order_name] : 'created_on' | |||
| end | |||
| def order_type | |||
| %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' | |||
| end | |||
| end | |||
| @@ -86,7 +86,7 @@ class Weapps::CoursesController < Weapps::BaseController | |||
| end | |||
| if search.present? | |||
| @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ?", "%#{search}%") | |||
| @teacher_list = @teacher_list.joins(:user).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? OR users.nickname like ?", "%#{search}%", "%#{search}%") | |||
| end | |||
| @teacher_list_size = @teacher_list.size | |||
| @@ -127,8 +127,8 @@ class Weapps::CoursesController < Weapps::BaseController | |||
| @students = CourseMember.students(@course) | |||
| if search.present? | |||
| @students = @students.joins(user: :user_extension).where("LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) like ? or | |||
| user_extensions.student_id like ?", "%#{search}%", "%#{search}%") | |||
| @students = @students.joins(user: :user_extension).where("LOWER(CONCAT(users.lastname, users.firstname)) like ? or users.nickname like ? or | |||
| user_extensions.student_id like ?", "%#{search}%", "%#{search}%", "%#{search}%") | |||
| end | |||
| if course_group_id.present? | |||
| @@ -86,7 +86,7 @@ class ZipsController < ApplicationController | |||
| #搜索 | |||
| if params[:search].present? | |||
| @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%") | |||
| @ex_users = @ex_users.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? OR nickname like ? OR student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%") | |||
| end | |||
| default_ex_users_size = @ex_users&.size | |||
| @@ -130,8 +130,8 @@ class ZipsController < ApplicationController | |||
| end | |||
| unless params[:search].blank? | |||
| @all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT_WS(lastname, firstname, nickname) like ? | |||
| or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%") | |||
| @all_student_works = @all_student_works.joins(user: :user_extension).where("CONCAT(lastname, firstname) like ? or nickname like ? | |||
| or student_id like ?", "%#{params[:search]}%", "%#{params[:search]}%", "%#{params[:search]}%") | |||
| end | |||
| student_work_sizes = @all_student_works&.size | |||
| @@ -438,4 +438,14 @@ module ApplicationHelper | |||
| return nil if str.blank? | |||
| Base64.decode64 str | |||
| end | |||
| def render_admin_statistics_item | |||
| url = Rails.application.config_for(:configuration)["admin_statistics_url"] | |||
| return if url.blank? | |||
| content_tag(:li) do | |||
| sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root') | |||
| end | |||
| end | |||
| end | |||
| @@ -24,8 +24,14 @@ module Gitea | |||
| def run | |||
| Contents::CreateForm.new(valid_params).validate! | |||
| response = Gitea::Repository::Entries::CreateService.new(token, owner, @params[:identifier], @params[:filepath], file_params).call | |||
| render_result(response) | |||
| result = Gitea::Repository::Entries::CreateService.call(token, | |||
| owner, @params[:identifier], @params[:filepath], file_params) | |||
| if result[:status] == :success | |||
| @result = result[:body] | |||
| else | |||
| fail!(result[:message]) | |||
| end | |||
| rescue Exception => exception | |||
| Rails.logger.info "Exception ===========> #{exception.message}" | |||
| fail!(exception.message) | |||
| @@ -56,7 +62,7 @@ module Gitea | |||
| file_params = {} | |||
| file_params = file_params.merge(branch: @params[:branch]) unless @params[:branch].blank? | |||
| file_params = file_params.merge(new_branch: @params[:new_branch]) unless @params[:new_branch].blank? | |||
| file_params = file_params.merge(content: Base64.encode64(@params[:content])) | |||
| file_params = file_params.merge(content: Base64.encode64(@params[:content] || "")) | |||
| file_params = file_params.merge(message: @params[:message]) unless @params[:message].blank? | |||
| file_params = file_params.merge(committer: @params[:committer]) | |||
| file_params | |||
| @@ -23,7 +23,7 @@ module Projects | |||
| ActiveRecord::Base.transaction do | |||
| gitea_result = Gitea::Repository::Members::AddService.new(owner, project.identifier, collaborator.login, permission).call | |||
| if gitea_result.status == 204 | |||
| project.add_member!(collaborator.id) | |||
| project.add_member!(collaborator.id, role_name) | |||
| end | |||
| fail!(nil) | |||
| end | |||
| @@ -38,5 +38,20 @@ module Projects | |||
| @error = error | |||
| end | |||
| def role_name | |||
| case permission | |||
| when 'read' | |||
| 'Reporter' | |||
| when 'write' | |||
| 'Developer' | |||
| when 'admin' | |||
| 'Manager' | |||
| when 'owner' | |||
| 'Manager' | |||
| else | |||
| 'Reporter' | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| class SendJoinProjectAppliedMessageJob < ApplicationJob | |||
| queue_as :default | |||
| def perform(applied_project, applied_user, message_status) | |||
| project = applied_project.project | |||
| return unless project.present? | |||
| return unless applied_user.present? | |||
| return unless applied_project.user.present? | |||
| AppliedMessage.find_or_create_by!(user_id: applied_project.user_id, | |||
| applied: applied_project, | |||
| status: message_status, | |||
| name: build_name(project.name, message_status), | |||
| applied_user_id: applied_user.id, | |||
| project_id: project.id) | |||
| end | |||
| private | |||
| def build_name(repo_name, message_status, applied_name="") | |||
| case message_status | |||
| when 'successed' | |||
| return "已通过你加入【#{repo_name}】仓库的申请。" | |||
| when 'failure' | |||
| return "已拒绝你加入【#{repo_name}】仓库的申请。" | |||
| end | |||
| "" | |||
| end | |||
| end | |||
| @@ -17,6 +17,7 @@ | |||
| # | |||
| class AppliedMessage < ApplicationRecord | |||
| self.table_name = 'forge_applied_messages' | |||
| belongs_to :user | |||
| belongs_to :applied, polymorphic: true | |||
| belongs_to :project | |||
| @@ -7,14 +7,19 @@ | |||
| # user_id :integer not null | |||
| # role :integer default("0") | |||
| # status :integer default("0") | |||
| # created_at :datetime | |||
| # updated_at :datetime | |||
| # | |||
| class AppliedProject < ApplicationRecord | |||
| self.table_name = "forge_applied_projects" | |||
| belongs_to :user | |||
| belongs_to :project | |||
| has_many :applied_messages, as: :applied, dependent: :destroy | |||
| has_many :forge_activities, as: :forge_act, dependent: :destroy | |||
| # has_many :forge_activities, as: :forge_act, dependent: :destroy | |||
| enum role: {manager: 3, developer: 4, reporter: 5} | |||
| enum status: {canceled: -1, common: 0, accepted: 1, refused: 2} # -1 已取消 0 待操作 1 已接收 2 已拒绝 | |||
| scope :pending, -> { where(status: 0) } | |||
| end | |||
| @@ -37,6 +37,7 @@ | |||
| # index_attachments_on_quotes (quotes) | |||
| # | |||
| class Attachment < ApplicationRecord | |||
| include BaseModel | |||
| include Publicable | |||
| @@ -51,7 +52,7 @@ class Attachment < ApplicationRecord | |||
| # 二级目录 | |||
| # belongs_to :course_second_category, optional: true | |||
| scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE :search", | |||
| scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(CONCAT(users.lastname, users.firstname)) LIKE :search OR users.nickname LIKE :search", | |||
| :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? } | |||
| scope :by_keywords, -> (keywords) { where("filename LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank? } | |||
| scope :ordered, -> (opts = {}) { order("#{opts[:sort_type]} #{opts[:sort] == 1 ? 'asc': 'desc'}") } | |||
| @@ -46,6 +46,10 @@ | |||
| # is_sync_pwd :boolean default("1") | |||
| # watchers_count :integer default("0") | |||
| # devops_step :integer default("0") | |||
| # sponsor_certification :integer default("0") | |||
| # sponsor_num :integer default("0") | |||
| # sponsored_num :integer default("0") | |||
| # award_time :datetime | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -0,0 +1,28 @@ | |||
| module Dcodes | |||
| DCODES = %W(1 2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) | |||
| extend ActiveSupport::Concern | |||
| def generate_dcode(field, num, pre='') | |||
| code = DCODES.sample(num).join | |||
| while self.class.exists?("#{field}": pre+code) do | |||
| code = DCODES.sample(num).join | |||
| end | |||
| code | |||
| end | |||
| def init_project_invite_code | |||
| while Project.where(invite_code: nil).size > 0 do | |||
| projects = Project.where(invite_code: nil).limit(1000) | |||
| set_sql = "" | |||
| projects.each do |p| | |||
| set_sql += "WHEN #{p.id} THEN '#{DCODES.sample(6).join}' " | |||
| end | |||
| sql = "UPDATE projects SET invite_code = CASE id "+ set_sql+ "END WHERE id IN(#{projects.ids.join(",")})" | |||
| Project.connection.execute(sql) | |||
| end | |||
| repeat_codes = Project.group(:invite_code).count.select{|k,v| v>1} | |||
| Project.where(invite_code: repeat_code.keys).update_all(invite_code: nil) | |||
| end | |||
| end | |||
| @@ -65,7 +65,7 @@ module ProjectOperable | |||
| if owner.is_a?(User) | |||
| managers.exists?(user_id: user.id) | |||
| elsif owner.is_a?(Organization) | |||
| managers.exists?(user_id: user.id) || owner.is_admin?(user.id) | |||
| managers.exists?(user_id: user.id) || owner.is_only_admin?(user.id) | |||
| else | |||
| false | |||
| end | |||
| @@ -76,7 +76,7 @@ module ProjectOperable | |||
| if owner.is_a?(User) | |||
| developers.exists?(user_id: user.id) | |||
| elsif owner.is_a?(Organization) | |||
| developers.exists?(user_id: user.id) || owner.is_write?(user.id) | |||
| developers.exists?(user_id: user.id) || owner.is_only_write?(user.id) | |||
| else | |||
| false | |||
| end | |||
| @@ -6,7 +6,7 @@ | |||
| # tracker_id :integer not null | |||
| # project_id :integer not null | |||
| # subject :string(255) default(""), not null | |||
| # description :text(65535) | |||
| # description :text(4294967295) | |||
| # due_date :date | |||
| # category_id :integer | |||
| # status_id :integer not null | |||
| @@ -14,7 +14,6 @@ | |||
| # priority_id :integer not null | |||
| # fixed_version_id :integer | |||
| # author_id :integer not null | |||
| # lock_version :integer default("0"), not null | |||
| # created_on :datetime | |||
| # updated_on :datetime | |||
| # start_date :date | |||
| @@ -28,7 +27,7 @@ | |||
| # closed_on :datetime | |||
| # project_issues_index :integer | |||
| # issue_type :string(255) | |||
| # token :string(255) | |||
| # token :integer default("0") | |||
| # issue_tags_value :string(255) | |||
| # is_lock :boolean default("0") | |||
| # issue_classify :string(255) | |||
| @@ -74,7 +73,7 @@ class Issue < ApplicationRecord | |||
| scope :issue_issue, ->{where(issue_classify: [nil,"issue"])} | |||
| scope :issue_pull_request, ->{where(issue_classify: "pull_request")} | |||
| scope :issue_index_includes, ->{includes(:tracker, :priority, :version, :issue_status, :journals,:issue_tags,user: :user_extension)} | |||
| scope :closed, ->{where(status_id: 5)} | |||
| after_update :change_versions_count | |||
| after_save :reset_cache_data | |||
| after_destroy :update_closed_issues_count_in_project!, :reset_cache_data | |||
| @@ -7,6 +7,7 @@ | |||
| # content :text(65535) | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # is_secret :boolean default("0") | |||
| # | |||
| class License < ApplicationRecord | |||
| @@ -11,6 +11,7 @@ | |||
| # course_group_id :integer default("0") | |||
| # is_collect :integer default("1") | |||
| # graduation_group_id :integer default("0") | |||
| # is_apply_signature :boolean default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -46,6 +46,10 @@ | |||
| # is_sync_pwd :boolean default("1") | |||
| # watchers_count :integer default("0") | |||
| # devops_step :integer default("0") | |||
| # sponsor_certification :integer default("0") | |||
| # sponsor_num :integer default("0") | |||
| # sponsored_num :integer default("0") | |||
| # award_time :datetime | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -106,6 +110,14 @@ class Organization < Owner | |||
| team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read write admin owner)}).present? | |||
| end | |||
| def is_only_admin?(user_id) | |||
| team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(admin)}).present? | |||
| end | |||
| def is_only_write?(user_id) | |||
| team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write)}).present? | |||
| end | |||
| def is_only_read?(user_id) | |||
| team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read)}).present? | |||
| end | |||
| @@ -1,19 +1,20 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: praise_treads | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer not null | |||
| # praise_tread_object_id :integer | |||
| # praise_tread_object_type :string(255) | |||
| # praise_or_tread :integer default("1") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # praise_tread (praise_tread_object_id,praise_tread_object_type) | |||
| # | |||
| # == Schema Information | |||
| # | |||
| # Table name: praise_treads | |||
| # | |||
| # id :integer not null, primary key | |||
| # user_id :integer not null | |||
| # praise_tread_object_id :integer | |||
| # praise_tread_object_type :string(255) | |||
| # praise_or_tread :integer default("1") | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # praise_tread (praise_tread_object_id,praise_tread_object_type) | |||
| # | |||
| class PraiseTread < ApplicationRecord | |||
| belongs_to :user | |||
| @@ -1,63 +1,78 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: projects | |||
| # | |||
| # id :integer not null, primary key | |||
| # name :string(255) default(""), not null | |||
| # description :text(65535) | |||
| # 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") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_projects_on_forked_from_project_id (forked_from_project_id) | |||
| # index_projects_on_identifier (identifier) | |||
| # index_projects_on_is_public (is_public) | |||
| # index_projects_on_lft (lft) | |||
| # index_projects_on_name (name) | |||
| # index_projects_on_platform (platform) | |||
| # 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) | |||
| # | |||
| # == 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) | |||
| # order_index :integer default("0") | |||
| # lesson_url :string(255) | |||
| # | |||
| # 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_name (name) | |||
| # index_projects_on_platform (platform) | |||
| # 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) | |||
| # | |||
| @@ -66,6 +81,7 @@ class Project < ApplicationRecord | |||
| include Publicable | |||
| include Watchable | |||
| include ProjectOperable | |||
| include Dcodes | |||
| # common:开源托管项目 | |||
| # mirror:普通镜像项目,没有定时同步功能 | |||
| @@ -106,6 +122,7 @@ class Project < ApplicationRecord | |||
| has_many :has_pinned_users, through: :pinned_projects, source: :user | |||
| after_save :check_project_members, :reset_cache_data | |||
| before_save :set_invite_code | |||
| after_destroy :reset_cache_data | |||
| scope :project_statics_select, -> {select(:id,:name, :is_public, :identifier, :status, :project_type, :user_id, :forked_count, :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)} | |||
| @@ -123,6 +140,12 @@ class Project < ApplicationRecord | |||
| self.reset_user_cache_async_job(self.owner) | |||
| end | |||
| def set_invite_code | |||
| if self.invite_code.nil? | |||
| self.invite_code= self.generate_dcode('invite_code', 6) | |||
| end | |||
| end | |||
| def self.search_project(search) | |||
| ransack(name_or_identifier_cont: search) | |||
| end | |||
| @@ -16,6 +16,11 @@ | |||
| # head :string(255) | |||
| # base :string(255) | |||
| # issue_id :integer | |||
| # fork_project_id :integer | |||
| # is_original :boolean default("0") | |||
| # comments_count :integer default("0") | |||
| # commits_count :integer default("0") | |||
| # files_count :integer default("0") | |||
| # | |||
| class PullRequest < ApplicationRecord | |||
| @@ -33,6 +38,9 @@ class PullRequest < ApplicationRecord | |||
| has_many :project_trends, as: :trend, dependent: :destroy | |||
| has_many :attachments, as: :container, dependent: :destroy | |||
| scope :merged_and_closed, ->{where.not(status: 0)} | |||
| scope :opening, -> {where(status: 0)} | |||
| after_save :reset_cache_data | |||
| after_destroy :reset_cache_data | |||
| @@ -46,6 +46,10 @@ | |||
| # is_sync_pwd :boolean default("1") | |||
| # watchers_count :integer default("0") | |||
| # devops_step :integer default("0") | |||
| # sponsor_certification :integer default("0") | |||
| # sponsor_num :integer default("0") | |||
| # sponsored_num :integer default("0") | |||
| # award_time :datetime | |||
| # | |||
| # Indexes | |||
| # | |||
| @@ -170,7 +174,7 @@ class User < Owner | |||
| # Groups and active users | |||
| scope :active, lambda { where(status: STATUS_ACTIVE) } | |||
| scope :like, lambda { |keywords| | |||
| sql = "CONCAT_WS(lastname, firstname, nickname) LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" | |||
| sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" | |||
| where(sql, :search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? | |||
| } | |||
| @@ -206,6 +210,13 @@ class User < Owner | |||
| return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct | |||
| end | |||
| # 用户管理的所有项目 | |||
| def full_admin_projects | |||
| normal_projects = Project.joins(members: :roles).where(roles: {name: 'Manager'}, members: {user_id: self.id}).to_sql | |||
| org_projects = Project.joins(teams: :team_users).where(teams: {authorize: %w(admin owner)}, team_users: {user_id: self.id}).to_sql | |||
| return Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct | |||
| end | |||
| def name | |||
| login | |||
| end | |||
| @@ -29,6 +29,8 @@ class Version < ApplicationRecord | |||
| belongs_to :user, optional: true | |||
| scope :version_includes, ->{includes(:issues, :user)} | |||
| scope :closed, ->{where(status: 'closed')} | |||
| scope :opening, ->{where(status: 'open')} | |||
| # def open_issues_count | |||
| # issues.select(:id,:status_id).where(status_id: [1,2,3,4,6]).size | |||
| @@ -26,7 +26,7 @@ class Admins::ApplyItemBankQuery < ApplicationQuery | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| applies = applies.joins(user: { user_extension: :school }) | |||
| .where('CONCAT_WS(lastname,firstname, nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") | |||
| .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") | |||
| end | |||
| custom_sort(applies, params[:sort_by], params[:sort_direction]) | |||
| @@ -26,7 +26,7 @@ class Admins::ApplyUserAuthenticationQuery < ApplicationQuery | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| applies = applies.joins(user: { user_extension: :school }) | |||
| .where('CONCAT_WS(lastname,firstname,nickname) LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") | |||
| .where('CONCAT(lastname,firstname) LIKE :keyword OR users.nickname LIKE :keyword OR schools.name LIKE :keyword', keyword: "%#{keyword}%") | |||
| end | |||
| custom_sort(applies, params[:sort_by], params[:sort_direction]) | |||
| @@ -19,7 +19,7 @@ class Admins::CourseListQuery < ApplicationQuery | |||
| case search_type | |||
| when "0" | |||
| course_lists = course_lists.joins(:user) | |||
| .where('CONCAT_WS(lastname, firstname, nickname) like :keyword', keyword: "%#{keyword}%") | |||
| .where('CONCAT(lastname, firstname) like :keyword OR users.nickname like :keyword', keyword: "%#{keyword}%") | |||
| when "1" | |||
| course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%") | |||
| end | |||
| @@ -35,7 +35,7 @@ class Admins::CourseQuery < ApplicationQuery | |||
| # 关键字 | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword | |||
| sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword' | |||
| sql = 'CONCAT(lastname, firstname) LIKE :keyword OR users.nickname LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword' | |||
| courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -11,7 +11,7 @@ class Admins::LaboratoryShixunQuery < ApplicationQuery | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| like_sql = 'shixuns.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' | |||
| like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' | |||
| laboratory_shixuns = laboratory_shixuns.joins(shixun: :user).where(like_sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -11,7 +11,7 @@ class Admins::LaboratorySubjectQuery < ApplicationQuery | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword.present? | |||
| like_sql = 'subjects.name LIKE :keyword OR CONCAT_WS(users.lastname, users.firstname, users.nickname) LIKE :keyword' | |||
| like_sql = 'subjects.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword OR users.nickname LIKE :keyword' | |||
| laboratory_subjects = laboratory_subjects.joins(subject: :user).where(like_sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -40,7 +40,7 @@ class Admins::SubjectQuery < ApplicationQuery | |||
| # 关键字 | |||
| keyword = params[:keyword].to_s.strip | |||
| if keyword | |||
| sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR subjects.name LIKE :keyword' | |||
| sql = 'CONCAT(lastname, firstname) LIKE :keyword OR users.nickname LIKE :keyword OR subjects.name LIKE :keyword' | |||
| subjects = subjects.joins(:user).where(sql, keyword: "%#{keyword}%") | |||
| end | |||
| @@ -30,14 +30,14 @@ class Admins::UserQuery < ApplicationQuery | |||
| # 关键字检索 | |||
| keyword = params[:keyword].to_s.strip.presence | |||
| if keyword | |||
| sql = 'CONCAT_WS(lastname, firstname, nickname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword' | |||
| sql = 'CONCAT(lastname, firstname) LIKE :keyword OR nickname LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword' | |||
| users = users.where(sql, keyword: "%#{keyword}%") | |||
| end | |||
| # 姓名 | |||
| name = params[:name].to_s.strip.presence | |||
| if name.present? | |||
| users = users.where('CONCAT_WS(lastname, firstname, nickname) LIKE :name', name: "%#{name}%") | |||
| users = users.where('CONCAT(lastname, firstname) LIKE :name OR nickname LIKE :name', name: "%#{name}%") | |||
| end | |||
| # 单位ID | |||
| @@ -55,8 +55,8 @@ class Projects::ListMyQuery < ApplicationQuery | |||
| scope = q.result.includes(:project_category, :project_language,:owner, :repository, :has_pinned_users) | |||
| sort = params[:sort_by] || "updated_on" | |||
| sort_direction = params[:sort_direction] || "desc" | |||
| sort = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : "updated_on" | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : "desc" | |||
| if params[:choosed].present? && params[:choosed].is_a?(Array) | |||
| scope.order("FIELD(id, #{params[:choosed].reverse.join(",")}) desc") | |||
| @@ -10,7 +10,7 @@ class UserQuery < ApplicationQuery | |||
| # 真实姓名 | |||
| if name = strip_param(:name) | |||
| users = users.where('LOWER(CONCAT_WS(users.lastname, users.firstname, users.nickname)) LIKE ?', "%#{name.downcase}%") | |||
| users = users.where('LOWER(CONCAT(users.lastname, users.firstname)) LIKE ? OR users.nickname LIKE ?', "%#{name.downcase}%", "%#{name.downcase}%") | |||
| end | |||
| # 单位名称 | |||
| @@ -28,10 +28,10 @@ class Weapps::SubjectQuery < ApplicationQuery | |||
| private | |||
| def order_type | |||
| params[:order] || "updated_at" | |||
| Subject.column_names.include?(params[:order]) ? params[:order] : 'updated_at' | |||
| end | |||
| def sort_type | |||
| params[:sort] || "desc" | |||
| %w(desc asc).include?(params[:sort]) ? params[:sort] : "desc" | |||
| end | |||
| end | |||
| @@ -84,7 +84,7 @@ class Admins::ImportUserService < ApplicationService | |||
| if data.identity.to_i == 1 | |||
| users = users.where(user_extensions: { student_id: data.student_id }) | |||
| else | |||
| users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT_WS(users.lastname,users.firstname,users.nickname) = ?', data.name) | |||
| users = users.where(user_extensions: { technical_title: data.technical_title }).where('CONCAT(users.lastname,users.firstname) = ? OR users.nickname = ?', data.name, data.name) | |||
| end | |||
| users.first | |||
| @@ -214,6 +214,14 @@ class Gitea::ClientService < ApplicationService | |||
| [body, message] | |||
| end | |||
| def json_parse!(body) | |||
| return nil unless body.present? | |||
| body = JSON.parse(body) | |||
| body, message = fix_body(body) | |||
| body | |||
| end | |||
| def log_error(status, body) | |||
| puts "[gitea] status: #{status}" | |||
| puts "[gitea] body: #{body&.force_encoding('UTF-8')}" | |||
| @@ -30,8 +30,7 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService | |||
| def call | |||
| response = post(url, params) | |||
| render_201_response(response) | |||
| response_payload(response) | |||
| end | |||
| private | |||
| @@ -43,4 +42,21 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService | |||
| "/repos/#{owner}/#{repo_name}/contents/#{filepath}".freeze | |||
| end | |||
| def response_payload(response) | |||
| status = response.status | |||
| body = response&.body | |||
| log_error(status, body) | |||
| status_payload(status, body) | |||
| end | |||
| def status_payload(status, body) | |||
| case status | |||
| when 201 then success(json_parse!(body)) | |||
| when 403 then error("你没有权限操作!") | |||
| when 404 then error("你操作的链接不存在!") | |||
| when 422 then error("#{filepath}文件已存在,不能重复创建!") | |||
| else error("系统错误!") | |||
| end | |||
| end | |||
| end | |||
| @@ -45,9 +45,17 @@ class Issues::ListQueryService < ApplicationService | |||
| issues = issues.where(issue_type: params[:issue_type].to_s) if params[:issue_type].present? && params[:issue_type].to_s != "all" | |||
| issues = issues.joins(:issue_tags).where(issue_tags: {id: params[:issue_tag_id].to_i}) if params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all" | |||
| order_type = params[:order_type] || "desc" #或者"asc" | |||
| order_name = params[:order_name] || "updated_on" #或者"updated_on" | |||
| issues.reorder("issues.#{order_name} #{order_type}") | |||
| end | |||
| private | |||
| def order_name | |||
| Issue.column_names.include?(params[:order_name]) ? params[:order_name] : 'updated_on' | |||
| end | |||
| def order_type | |||
| %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' | |||
| end | |||
| end | |||
| @@ -0,0 +1,61 @@ | |||
| class Projects::AcceptJoinService < ApplicationService | |||
| attr_accessor :applied_project, :owner | |||
| attr_reader :user, :project | |||
| def initialize(user, applied_project) | |||
| @user = user | |||
| @project = applied_project.project | |||
| @applied_project = applied_project | |||
| end | |||
| def call | |||
| Rails.logger.info("###### Project accept_join_service begin ######") | |||
| ActiveRecord::Base.transaction do | |||
| validate! | |||
| update_apply | |||
| operate_project_member | |||
| send_apply_message | |||
| end | |||
| Rails.logger.info("##### Project accept_join_service end ######") | |||
| return @applied_project | |||
| end | |||
| private | |||
| def permission | |||
| case @applied_project.role | |||
| when 'manager' | |||
| 'admin' | |||
| when 'developer' | |||
| 'write' | |||
| when 'reporter' | |||
| 'read' | |||
| else | |||
| 'read' | |||
| end | |||
| end | |||
| def validate! | |||
| raise Error, '该申请已经被接受' if @applied_project.accepted? | |||
| raise Error, '该申请不存在' unless @applied_project.present? | |||
| raise Error, '未拥有接受申请权限' unless is_permit_operator | |||
| end | |||
| def is_permit_operator | |||
| return @user.admin? || @project.manager?(@user) | |||
| end | |||
| def update_apply | |||
| @applied_project.update!(status: 'accepted') | |||
| end | |||
| def operate_project_member | |||
| Projects::AddMemberInteractor.call(@project.owner, @project, @applied_project.user, permission) | |||
| end | |||
| def send_apply_message | |||
| SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'successed') | |||
| end | |||
| end | |||
| @@ -9,26 +9,25 @@ class Projects::ApplyJoinService < ApplicationService | |||
| end | |||
| def call | |||
| validate! | |||
| # 项目报告人员直接加入项目 | |||
| if params[:role] == 'reporter' | |||
| # Projects::JoinService.call(project, user, role: 'reporter') | |||
| return project | |||
| end | |||
| # if params[:role] == 'reporter' | |||
| # # Projects::JoinService.call(project, user, role: 'reporter') | |||
| # return project | |||
| # end | |||
| ActiveRecord::Base.transaction do | |||
| validate! | |||
| apply = user.applied_projects.create!(project: project, role: role_value) | |||
| apply | |||
| # apply.forge_activities.find_or_create_by!(user: user, project: project) | |||
| apply.forge_activities.find_or_create_by!(user: user, project: project) | |||
| notify_project_manager!(apply) | |||
| # notify_project_manager!(apply) | |||
| end | |||
| # notify_project_owner | |||
| ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value) | |||
| # ApplyJoinProjectNotifyJob.perform_later(user.id, project.id, role_value) | |||
| project | |||
| end | |||
| private | |||
| @@ -43,7 +42,8 @@ class Projects::ApplyJoinService < ApplicationService | |||
| when 'manager' then 3 | |||
| when 'developer' then 4 | |||
| when 'reporter' then 5 | |||
| else raise Error, '角色无效' | |||
| else | |||
| 5 | |||
| end | |||
| end | |||
| @@ -74,12 +74,18 @@ class Projects::ApplyJoinService < ApplicationService | |||
| def validate! | |||
| # params check | |||
| raise Error, '邀请码不能为空' if params[:code].blank? | |||
| raise Error, '角色不能为空' if params[:role].blank? | |||
| raise Error, '角色无效' unless %w(manager developer reporter).include?(params[:role]) | |||
| raise Error, '请输入6位项目邀请码' unless valid_invite_code( params[:code]) | |||
| # logical check | |||
| raise Error, '邀请码无效' if project.blank? | |||
| raise Error, '您已在该项目中' if project.member?(user) | |||
| raise Error, '您已经提交过申请' if user.applied_projects.pending.exists?(project: project) | |||
| raise Error, '您已是项目成员' if project.member?(user) | |||
| raise Error, '您已经提交过申请' if user.applied_projects.common.exists?(project: project) | |||
| end | |||
| def valid_invite_code(str) | |||
| if (str =~ /^[A-Za-z0-9]{6}+$/) | |||
| return true | |||
| end | |||
| return false | |||
| end | |||
| end | |||
| @@ -0,0 +1,39 @@ | |||
| class Projects::RefuseJoinService < ApplicationService | |||
| attr_accessor :applied_project, :owner | |||
| attr_reader :user, :project | |||
| def initialize(user, applied_project) | |||
| @user = user | |||
| @project = applied_project.project | |||
| @applied_project = applied_project | |||
| end | |||
| def call | |||
| Rails.logger.info("###### Project refuse_join_service begin ######") | |||
| validate! | |||
| update_apply | |||
| send_apply_message | |||
| Rails.logger.info("###### Project refuse_join_service end ######") | |||
| return @applied_project | |||
| end | |||
| private | |||
| def validate! | |||
| raise Error, '该申请已被拒绝' if @applied_project.refused? | |||
| raise Error, '该申请不存在' unless @applied_project.present? | |||
| raise Error, '未拥有接受申请权限' unless is_permit_operator | |||
| end | |||
| def is_permit_operator | |||
| return @user.admin? || @project.manager?(@user) | |||
| end | |||
| def update_apply | |||
| @applied_project.update!(status: 'refused') | |||
| end | |||
| def send_apply_message | |||
| SendJoinProjectAppliedMessageJob.perform_now(@applied_project, @user, 'failure') | |||
| end | |||
| end | |||
| @@ -42,8 +42,11 @@ | |||
| <% end %> | |||
| </li> --> | |||
| <li> | |||
| <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> | |||
| <%= sidebar_item('/admins/sidekiq', '定时任务', icon: 'bell', controller: 'root') %> | |||
| </li> | |||
| <%= render_admin_statistics_item %> | |||
| <li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li> | |||
| </ul> | |||
| </nav> | |||
| @@ -0,0 +1,19 @@ | |||
| project = object.project | |||
| json.project do | |||
| json.id project.id | |||
| json.identifier project.identifier | |||
| json.name project.name | |||
| json.description project.description | |||
| json.is_public project.is_public | |||
| json.owner do | |||
| json.partial! "/users/user_simple", locals: {user: project.owner} | |||
| end | |||
| end | |||
| json.user do | |||
| json.partial! "/users/user_simple", locals: {user: object.user} | |||
| end | |||
| json.id object.id | |||
| json.status object.status | |||
| json.role object.role | |||
| json.created_at format_time(object.created_at) | |||
| json.time_ago time_from_now(object.created_at) | |||
| @@ -0,0 +1 @@ | |||
| json.partial! "detail", locals: {object: @applied_project} | |||
| @@ -1,9 +1,9 @@ | |||
| json.partial! "commons/success" | |||
| json.all_count @all_issues_size | |||
| json.open_count @open_issues_size | |||
| json.close_count @close_issues_size | |||
| json.assign_me_count @assign_to_me_size | |||
| json.my_published_count @my_published_size | |||
| json.all_count @all_issues.size | |||
| json.open_count @open_issues.size | |||
| json.close_count @close_issues.size | |||
| json.assign_me_count @assign_to_me.size | |||
| json.my_published_count @my_published.size | |||
| json.search_count @issues_size | |||
| json.limit @limit | |||
| json.user_admin_or_member @user_admin_or_member | |||
| @@ -1,8 +1,7 @@ | |||
| json.partial! "commons/success" | |||
| json.all_count @all_issues_size | |||
| json.open_count @open_issues_size | |||
| json.close_count @close_issues_size | |||
| json.merged_issues_size @merged_issues_size | |||
| json.open_count @open_issues.size | |||
| json.close_count @close_issues.size | |||
| json.merged_issues_size @merged_issues.size | |||
| json.search_count @issues_size | |||
| json.limit @limit | |||
| json.user_admin_or_member @user_admin_or_member | |||
| @@ -7,17 +7,18 @@ else | |||
| json.readme @result[:readme].merge(content: readme_render_decode64_content(@result[:readme]["content"], nil)) | |||
| end | |||
| json.identifier render_identifier(@project) | |||
| json.invite_code @project.invite_code | |||
| json.name @project.name | |||
| json.description @project.description | |||
| json.project_id @project.id | |||
| json.repo_id @repository.id | |||
| json.issues_count @project.issues_count.to_i - @project.pull_requests_count.to_i | |||
| json.pull_requests_count @project.pull_requests_count | |||
| json.issues_count @project.issues.issue_issue.size - @project.issues.issue_issue.closed.size | |||
| json.pull_requests_count @project.pull_requests.opening.size | |||
| json.project_identifier render_identifier(@project) | |||
| json.praises_count @project.praises_count.to_i | |||
| json.forked_count @project.forked_count.to_i | |||
| json.watchers_count @project.watchers_count.to_i | |||
| json.versions_count @project.versions_count #里程碑数量 | |||
| json.versions_count @project.versions.opening.size #里程碑数量 | |||
| json.version_releases_count @project.releases_size(@user.try(:id), "all") | |||
| json.version_releasesed_count @project.releases_size(@user.try(:id), "released") #已发行的版本 | |||
| json.permission render_permission(@user, @project) | |||
| @@ -42,6 +43,7 @@ json.fork_info do | |||
| json.fork_project_user_login @fork_project_user.try(:login) | |||
| json.fork_project_identifier @fork_project.identifier | |||
| json.fork_project_user_name @fork_project_user.try(:show_real_name) | |||
| json.fork_project_user_type @fork_project_user.try(:type) | |||
| end | |||
| end | |||
| if @result[:repo] | |||
| @@ -13,7 +13,12 @@ | |||
| # json.partial! "/users/user_simple", locals: {user: object.user} | |||
| # end | |||
| json.applied do | |||
| json.partial! "/projects/applied_transfer_projects/detail", locals: {object: object.applied} | |||
| case object.applied_type | |||
| when 'AppliedTransferProject' | |||
| json.partial! "/projects/applied_transfer_projects/detail", locals: {object: object.applied} | |||
| when 'AppliedProject' | |||
| json.partial! "/applied_projects/detail", locals: {object: object.applied} | |||
| end | |||
| end | |||
| json.applied_user do | |||
| json.partial! "/users/user_simple", locals: {user: object.applied_user} | |||
| @@ -0,0 +1 @@ | |||
| json.partial! "/applied_projects/detail", locals: {object: @applied_project} | |||
| @@ -0,0 +1,4 @@ | |||
| json.total_count @applied_projects.total_count | |||
| json.applied_projects @applied_projects do |apply| | |||
| json.partial! "/applied_projects/detail", locals: {object: apply} | |||
| end | |||
| @@ -0,0 +1 @@ | |||
| json.partial! "/applied_projects/detail", locals: {object: @applied_project} | |||
| @@ -1,6 +1,7 @@ | |||
| json.partial! 'users/user', locals: { user: @user } | |||
| json.undo_messages @waiting_applied_messages.size | |||
| json.undo_transfer_projects @common_applied_transfer_projects.size | |||
| json.undo_join_projects @common_applied_projects.size | |||
| json.undo_events @undo_events | |||
| json.user_composes_count @user_composes_count | |||
| json.user_org_count @user_org_count | |||
| @@ -148,6 +148,8 @@ Rails.application.routes.draw do | |||
| resources :issue_depends, only: [:create, :destroy] | |||
| end | |||
| resources :applied_projects, only: [:create] | |||
| resources :project_categories, only: [:index, :show] do | |||
| get :group_list, on: :collection | |||
| end | |||
| @@ -266,6 +268,12 @@ Rails.application.routes.draw do | |||
| post :refuse | |||
| end | |||
| end | |||
| resources :applied_projects, only: [:index] do | |||
| member do | |||
| post :accept | |||
| post :refuse | |||
| end | |||
| end | |||
| resources :headmaps, only: [:index] | |||
| resources :is_pinned_projects, only: [:index, :update] do | |||
| collection do | |||
| @@ -0,0 +1,5 @@ | |||
| class AddTimestampToAppliedProjects < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_timestamps(:applied_projects, null: true) | |||
| end | |||
| end | |||
| @@ -0,0 +1,8 @@ | |||
| class AddInviteCodeIndexToProjects < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_index :projects, :invite_code | |||
| execute <<-SQL | |||
| ALTER TABLE projects MODIFY COLUMN invite_code VARCHAR(255) BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL; | |||
| SQL | |||
| end | |||
| end | |||
| @@ -0,0 +1,25 @@ | |||
| class CreateForgeRepeatTables < ActiveRecord::Migration[5.2] | |||
| def change | |||
| create_table :forge_applied_messages do |t| | |||
| t.references :user | |||
| t.references :applied, polymorphic: true | |||
| t.integer :viewed, default: 0 | |||
| t.integer :status , default: 0 | |||
| t.string :name | |||
| t.references :applied_user | |||
| t.integer :role | |||
| t.references :project | |||
| t.timestamps | |||
| end | |||
| create_table :forge_applied_projects do |t| | |||
| t.references :project | |||
| t.references :user | |||
| t.integer :role, default: 0 | |||
| t.integer :status, default: 0 | |||
| t.timestamps | |||
| end | |||
| end | |||
| end | |||