diff --git a/api_document.md b/api_document.md index 50a45573..2cbb2431 100644 --- a/api_document.md +++ b/api_document.md @@ -286,6 +286,7 @@ http://localhost:3000/api/licenses/ | jq |licenses|array |返回数据| |-- id |int |id | |-- name |string|开源许可证名称| +|-- is_secret |string|是否为特殊许可证| 返回值 @@ -294,23 +295,33 @@ http://localhost:3000/api/licenses/ | jq "licenses": [ { "id": 57, - "name": "AFL-1.2" + "name": "AFL-1.2", + "is_secret": false }, { "id": 76, - "name": "AFL-3.0" + "name": "AFL-3.0", + "is_secret": false }, { "id": 214, - "name": "AFL-1.1" + "name": "AFL-1.1", + "is_secret": false }, { "id": 326, - "name": "AFL-2.1" + "name": "AFL-2.1", + "is_secret": false }, { "id": 350, - "name": "AFL-2.0" + "name": "AFL-2.0", + "is_secret": false + }, + { + "id": 359, + "name": "PHengLEI", + "is_secret": true } ] } @@ -555,6 +566,8 @@ curl -X GET http://localhost:3000/api/repositories/:id/edit.json | jq |project_category_id |int|项目类别id| |project_language_id |int|项目语言id| |private |boolean|项目是否私有, true:为私有,false: 公开 | +|is_secret |boolean|项目是否为特殊协议项目, true:是,false: 否 | + 返回值 @@ -567,7 +580,8 @@ curl -X GET http://localhost:3000/api/repositories/:id/edit.json | jq "project_description": "my first project mirror_demo", "project_category_id": 1, "project_language_id": 2, - "private": false + "private": false, + "is_secret": true } ``` --- @@ -1146,6 +1160,7 @@ http://localhost:3000/api/projects | jq |forked_count |int|被fork的数量| |praises_count |int|star数量| |is_public |boolean|是否公开, true:公开,false:未公开| +|is_secret |boolean|是否为特殊许可证项目, true:是,false:否| |mirror_url |string|镜像url| |last_update_time|int|最后更新时间,为UNIX格式的时间戳| |author |object|项目创建者| @@ -1156,6 +1171,10 @@ http://localhost:3000/api/projects | jq |language |object|项目语言| |-- id |int|项目语言id| |-- name |string|项目语言名称| +|user_apply_signatures |object|用户创建的特殊许可申请| +|-- id |int|用户创建的特殊许可申请id| +|-- status |int|用户创建的特殊许可申请审核状态, 'unpassed': 审核未通过,'waiting': 等待审核 'passed':审核通过| + 返回值 @@ -1171,6 +1190,7 @@ http://localhost:3000/api/projects | jq "praises_count": 0, "forked_count": 0, "is_public": true, + "is_secret": true, "mirror_url": null, "last_update_time": 1577697461, "author": { @@ -1184,7 +1204,13 @@ http://localhost:3000/api/projects | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[ + { + "id": 1, + "status": "waiting" + } + ] }, { "id": 2, @@ -1194,6 +1220,7 @@ http://localhost:3000/api/projects | jq "praises_count": 0, "forked_count": 0, "is_public": true, + "is_secret": false, "mirror_url": null, "last_update_time": 1577697403, "author": { @@ -1207,7 +1234,8 @@ http://localhost:3000/api/projects | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[] }, { "id": 3, @@ -1217,6 +1245,7 @@ http://localhost:3000/api/projects | jq "praises_count": 0, "forked_count": 0, "is_public": true, + "is_secret": true, "mirror_url": null, "last_update_time": 1577415173, "author": { @@ -1230,7 +1259,8 @@ http://localhost:3000/api/projects | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[] }, { "id": 5, @@ -1240,6 +1270,7 @@ http://localhost:3000/api/projects | jq "praises_count": 0, "forked_count": 0, "is_public": false, + "is_secret": true, "mirror_url": "https://gitea.com/CasperVector/slew.git", "last_update_time": 1577346228, "author": { @@ -1253,7 +1284,8 @@ http://localhost:3000/api/projects | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[] }, { "id": 7, @@ -1263,6 +1295,7 @@ http://localhost:3000/api/projects | jq "praises_count": 0, "forked_count": 0, "is_public": true, + "is_secret": true, "mirror_url": null, "last_update_time": 1577341572, "author": { @@ -1276,7 +1309,8 @@ http://localhost:3000/api/projects | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[] } ] } @@ -3970,6 +4004,7 @@ http://localhost:3000/api/users/Jason/projects.json | jq |forked_count |int|被fork的数量| |praises_count |int|star数量| |is_public |boolean|是否公开, true:公开,false:未公开| +|is_secret |boolean|是否为特殊许可证项目, true:是,false:否| |mirror_url |string|镜像url| |last_update_time|int|最后更新时间,为UNIX格式的时间戳| |author |object|项目创建者| @@ -3980,7 +4015,9 @@ http://localhost:3000/api/users/Jason/projects.json | jq |language |object|项目语言| |-- id |int|项目语言id| |-- name |string|项目语言名称| - +|user_apply_signatures |object|用户创建的特殊许可申请| +|-- id |int|用户创建的特殊许可申请id| +|-- status |int|用户创建的特殊许可申请审核状态, 'unpassed': 审核未通过,'waiting': 等待审核 'passed':审核通过| 返回值 ```json @@ -3995,6 +4032,7 @@ http://localhost:3000/api/users/Jason/projects.json | jq "praises_count": 0, "forked_count": 0, "is_public": true, + "is_secret": false, "mirror_url": null, "last_update_time": 1577697461, "author": { @@ -4008,9 +4046,53 @@ http://localhost:3000/api/users/Jason/projects.json | jq "language": { "id": 2, "name": "C" - } + }, + "user_apply_signatures":[ + { + "id": 1, + "status": "waiting" + } + ] } ] } ``` --- +#### 特殊许可证项目用户创建申请 +``` +POST /api/apply_signatures +``` + +*示例* +```bash +curl -X POST \ +-d "project_id=8" \ +-d "attachment_id=4" \ +http://localhost:3000/api/apply_signatures.json | jq +``` + +*请求参数说明:* + +|参数名|必选|类型|说明| +|-|-|-|-| +|project_id |是|int |项目id | +|attachment_id |是|int |上传的文件id | + +*返回参数说明:* + +|参数名|类型|说明| +|-|-|-| +|id |int|特殊许可证项目用户创建申请的id| +|attachment |object|上传的文件| +|--filename |string|上传的文件的文件名| + +返回值 +```json +{ + "id": 5, + "attachment": { + "filename": "timg.jpeg" + } +} +``` +--- \ No newline at end of file diff --git a/app/controllers/admins/apply_signatures_controller.rb b/app/controllers/admins/apply_signatures_controller.rb new file mode 100644 index 00000000..c6b0f4c0 --- /dev/null +++ b/app/controllers/admins/apply_signatures_controller.rb @@ -0,0 +1,30 @@ +class Admins::ApplySignaturesController < Admins::BaseController + + def index + sort_by = params[:sort_by] ||= 'created_on' + sort_direction = params[:sort_direction] ||= 'desc' + + @apply_signatures = paginate ApplySignature.waiting.includes(:attachments) + end + + def update + ActiveRecord::Base.transaction do + begin + apply_signature = ApplySignature.find_by!(id: params[:id]) + apply_signature.update_attributes!(apply_signatures_params) + Projects::AddMemberInteractor.call(apply_signature.project.owner, apply_signature.project, apply_signature.user, "write", true) + redirect_to admins_apply_signatures_path + flash[:success] = "更新成功" + rescue => e + raise ActiveRecord::Rollback + redirect_to admins_apply_signatures_path + flash[:danger] = "更新失败" + end + end + end + + private + def apply_signatures_params + params.permit(:status) + end + end \ No newline at end of file diff --git a/app/controllers/apply_signatures_controller.rb b/app/controllers/apply_signatures_controller.rb new file mode 100644 index 00000000..54f64aa6 --- /dev/null +++ b/app/controllers/apply_signatures_controller.rb @@ -0,0 +1,25 @@ +class ApplySignaturesController < ApplicationController + include ApplicationHelper + + def template_file + license = License.find_by_name("PHengLEI") + file = license.attachments.take + normal_status(-1, "文件不存在") if file.blank? + send_file(absolute_path(local_path(file)), filename: file.title,stream:false, type: file.content_type.presence || 'application/octet-stream') + end + + def create + ActiveRecord::Base.transaction do + begin + @signature = current_user.apply_signatures.create!(project_id: params[:project_id]) + @attachment = Attachment.find_by_id(params[:attachment_id]) + @attachment.container = @signature + @attachment.save! + rescue Exception => e + tip_exception("#{e}") + raise ActiveRecord::Rollback + end + render_json + end + end +end \ No newline at end of file diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 31bb0475..c2486949 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -11,7 +11,7 @@ class ProjectsController < ApplicationController scope = Projects::ListQuery.call(params) # @projects = kaminari_paginate(scope) - @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, owner: :user_extension) + @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :apply_signatures, owner: :user_extension) category_id = params[:category_id] @total_count = @@ -53,7 +53,7 @@ class ProjectsController < ApplicationController # else # projects = Project.visible # end - projects = Project.no_anomory_projects.visible + projects = Project.no_anomory_projects.secret_and_visible language_lists = projects.joins(:project_language).group("project_languages.name", "project_languages.id").size.keys.sort.to_h @project_group_list = language_lists.delete_if { |k, v| k.blank? } end @@ -153,7 +153,7 @@ class ProjectsController < ApplicationController private def project_params params.permit(:user_id, :name, :description, :repository_name, - :project_category_id, :project_language_id, :license_id, :ignore_id) + :project_category_id, :project_language_id, :license_id, :ignore_id, :private) end def mirror_params diff --git a/app/interactors/projects/add_member_interactor.rb b/app/interactors/projects/add_member_interactor.rb index a3fe9e33..1e17dc45 100644 --- a/app/interactors/projects/add_member_interactor.rb +++ b/app/interactors/projects/add_member_interactor.rb @@ -1,18 +1,19 @@ module Projects class AddMemberInteractor - def self.call(owner, project, collaborator, permission="write") - interactor = new(owner, project, collaborator, permission) + def self.call(owner, project, collaborator, permission="write", is_apply_signature=false) + interactor = new(owner, project, collaborator, permission, is_apply_signature) interactor.run interactor end attr_reader :error, :result - def initialize(owner, project, collaborator, permission) - @owner = owner - @project = project - @collaborator = collaborator - @permission = permission + def initialize(owner, project, collaborator, permission, is_apply_signature) + @owner = owner + @project = project + @collaborator = collaborator + @permission = permission + @is_apply_signature = is_apply_signature end def success? @@ -23,7 +24,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, 'Developer', is_apply_signature) end fail!(nil) end @@ -32,7 +33,7 @@ module Projects end private - attr_reader :owner, :project, :collaborator, :permission + attr_reader :owner, :project, :collaborator, :permission, :is_apply_signature def fail!(error) @error = error diff --git a/app/models/apply_signature.rb b/app/models/apply_signature.rb new file mode 100644 index 00000000..64a9f1f5 --- /dev/null +++ b/app/models/apply_signature.rb @@ -0,0 +1,10 @@ +class ApplySignature < ApplicationRecord + belongs_to :user + belongs_to :project + + has_many :attachments, as: :container, dependent: :destroy + + scope :with_user_id, -> (user_id) {where(user_id: user_id)} + + enum status: {unpassed: -1, waiting: 0, passed: 1} +end diff --git a/app/models/concerns/project_operable.rb b/app/models/concerns/project_operable.rb index f05ed164..8bb11ffd 100644 --- a/app/models/concerns/project_operable.rb +++ b/app/models/concerns/project_operable.rb @@ -10,8 +10,8 @@ module ProjectOperable has_many :writable_members, -> { joins(:roles).where.not(roles: {name: 'Reporter'}) }, class_name: 'Member' end - def add_member!(user_id, role_name='Developer') - member = members.create!(user_id: user_id) + def add_member!(user_id, role_name='Developer', is_apply_signature=false) + member = members.create!(user_id: user_id, is_apply_signature: is_apply_signature) set_developer_role(member) end diff --git a/app/models/license.rb b/app/models/license.rb index 0a14fb85..2baebb4f 100644 --- a/app/models/license.rb +++ b/app/models/license.rb @@ -11,4 +11,6 @@ class License < ApplicationRecord include Projectable + has_many :attachments, as: :container, dependent: :destroy + end diff --git a/app/models/project.rb b/app/models/project.rb index 887a6952..659e2c3b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,73 +1,73 @@ -# == 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") -# -# 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") +# +# 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) +# + class Project < ApplicationRecord include Matchable include Publicable @@ -106,12 +106,16 @@ class Project < ApplicationRecord 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 :apply_signatures, dependent: :destroy after_save :check_project_members 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)} scope :recommend, -> { visible.project_statics_select.where(recommend: true) } + scope :secret_and_visible, -> {joins(:license).where("licenses.is_secret = TRUE OR projects.is_public = TRUE")} + + delegate :is_secret, to: :license, allow_nil: true def self.search_project(search) diff --git a/app/models/user.rb b/app/models/user.rb index 6aef70b5..38014ad5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -161,6 +161,7 @@ class User < ApplicationRecord # has_many :libraries, dependent: :destroy has_many :project_trends, dependent: :destroy has_many :oauths , dependent: :destroy + has_many :apply_signatures, dependent: :destroy # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } diff --git a/app/queries/projects/list_query.rb b/app/queries/projects/list_query.rb index 04f1d168..7e175cc4 100644 --- a/app/queries/projects/list_query.rb +++ b/app/queries/projects/list_query.rb @@ -10,7 +10,7 @@ class Projects::ListQuery < ApplicationQuery end def call - q = Project.visible.by_name_or_identifier(params[:search]) + q = Project.secret_and_visible.by_name_or_identifier(params[:search]) scope = q .with_project_type(params[:project_type]) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index dc14d10d..8d6aecb1 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -53,6 +53,11 @@ class Projects::CreateService < ApplicationService # end def repo_is_public - params[:private].blank? ? true : !params[:private] + license = License.find_by_id(params[:license_id]) + if license.is_secret + false + else + params[:private].blank? ? true : !params[:private] + end end end diff --git a/app/views/admins/apply_signatures/index.html.erb b/app/views/admins/apply_signatures/index.html.erb new file mode 100644 index 00000000..cf63c95d --- /dev/null +++ b/app/views/admins/apply_signatures/index.html.erb @@ -0,0 +1,7 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('特殊许可申请列表') %> +<% end %> + +
序号 | +项目名称 | +申请人 | +申请材料 | +操作 | +
---|---|---|---|---|
<%= list_index_no((params[:page] || 1).to_i, index) %> | ++ <%= link_to(signature&.project&.name, "/projects/#{signature&.project&.owner.login}/#{signature&.project&.identifier}", target: '_blank') %> + | ++ <%= link_to(signature&.user&.real_name, "/users/#{signature&.user&.login}", target: '_blank') %> + | ++ <% if signature.attachments.present? %> + <% signature.attachments.each do |attachment|%> + <%= image_tag("/api/attachments/#{attachment.id}", width: 40, height: 40, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <% end %> + <% end %> + + | ++ <%= link_to "同意", admins_apply_signature_path(signature.id, status: 1), method: :patch, class: "delete-project-action" %> + <%= link_to "拒绝", admins_apply_signature_path(signature.id, status: -1), method: :patch, class: "delete-project-action" %> + | +