| @@ -51,6 +51,51 @@ http://localhost:3000/api/accounts/remote_register | jq | |||
| |-- token |string|用户token| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success", | |||
| "user": { | |||
| "id": 36400, | |||
| "token": "8c87a80d9cfacc92fcb2451845104f35119eda96" | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| #### 独立注册接口 | |||
| ``` | |||
| POST accounts/register | |||
| ``` | |||
| *示例* | |||
| ```bash | |||
| curl -X POST \ | |||
| -d "login=2456233122@qq.com" \ | |||
| -d "password=djs_D_00001" \ | |||
| -d "namespace=16895620" \ | |||
| -d "code=forge" \ | |||
| http://localhost:3000/api/accounts/remote_register | jq | |||
| ``` | |||
| *请求参数说明:* | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |login |是|string |邮箱或者手机号 | | |||
| |namespace |是|string |登录名 | | |||
| |password |是|string |密码 | | |||
| |code |是|string |验证码 | | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |user|json object |返回数据| | |||
| |-- id |int |用户id | | |||
| |-- token |string|用户token| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| @@ -1,4 +1,5 @@ | |||
| class AccountsController < ApplicationController | |||
| include ApplicationHelper | |||
| #skip_before_action :check_account, :only => [:logout] | |||
| @@ -9,7 +10,7 @@ class AccountsController < ApplicationController | |||
| # 其他平台同步注册的用户 | |||
| def remote_register | |||
| username = params[:username]&.gsub(/\s+/, "") | |||
| tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.is_reversed(username).present? | |||
| tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) | |||
| email = params[:email]&.gsub(/\s+/, "") | |||
| password = params[:password] | |||
| platform = (params[:platform] || 'forge')&.gsub(/\s+/, "") | |||
| @@ -109,67 +110,46 @@ class AccountsController < ApplicationController | |||
| # 用户注册 | |||
| # 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用 | |||
| # params[:login] 邮箱或者手机号 | |||
| # params[:namespace] 登录名 | |||
| # params[:code] 验证码 | |||
| # code_type 1:注册手机验证码 8:邮箱注册验证码 | |||
| # 本地forge注册入口 | |||
| # 本地forge注册入口需要重新更改逻辑 | |||
| def register | |||
| # type只可能是1或者8 | |||
| user = nil | |||
| begin | |||
| # 查询验证码是否正确;type只可能是1或者8 | |||
| type = phone_mail_type(params[:login].strip) | |||
| # code = params[:code].strip | |||
| if type == 1 | |||
| uid_logger("start register by phone: type is #{type}") | |||
| pre = 'p' | |||
| email = nil | |||
| phone = params[:login] | |||
| # verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last | |||
| # TODO: 暂时限定邮箱注册 | |||
| return normal_status(-1, '只支持邮箱注册') | |||
| else | |||
| uid_logger("start register by email: type is #{type}") | |||
| pre = 'm' | |||
| email = params[:login] | |||
| phone = nil | |||
| return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login]) | |||
| return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL | |||
| # verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last | |||
| end | |||
| # uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}") | |||
| # check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60) | |||
| # todo 上线前请删除万能验证码"513231" | |||
| return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD | |||
| code = generate_identifier User, 8, pre | |||
| login = pre + code | |||
| is_admin = !User.exists?(type: 'User') | |||
| @user = User.new(admin: is_admin, login: login, mail: email, phone: phone, type: "User") | |||
| @user.password = params[:password] | |||
| # 现在因为是验证码,所以在注册的时候就可以激活 | |||
| @user.activate | |||
| # 必须要用save操作,密码的保存是在users中 | |||
| interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]}) | |||
| Register::Form.new(register_params).validate! | |||
| user = Users::RegisterService.call(register_params) | |||
| password = register_params[:password].strip | |||
| # gitea用户注册, email, username, password | |||
| interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) | |||
| if interactor.success? | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.new(login, params[:password]).call | |||
| @user.gitea_token = result['sha1'] | |||
| @user.gitea_uid = gitea_user[:body]['id'] | |||
| if @user.save! | |||
| # set user for admin role | |||
| if @user.admin? | |||
| sync_params = { email: @user.mail, admin: true } | |||
| Gitea::User::UpdateInteractor.call(@user.login, sync_params) | |||
| end | |||
| UserExtension.create!(user_id: @user.id) | |||
| successful_authentication(@user) | |||
| normal_status("注册成功") | |||
| result = Gitea::User::GenerateTokenService.call(user.login, password) | |||
| user.gitea_token = result['sha1'] | |||
| user.gitea_uid = gitea_user[:body]['id'] | |||
| if user.save! | |||
| UserExtension.create!(user_id: user.id) | |||
| successful_authentication(user) | |||
| render_ok | |||
| end | |||
| else | |||
| tip_exception(-1, interactor.error) | |||
| end | |||
| rescue Register::BaseForm::EmailError => e | |||
| render_error(-2, e.message) | |||
| rescue Register::BaseForm::LoginError => e | |||
| render_error(-3, e.message) | |||
| rescue Register::BaseForm::PhoneError => e | |||
| render_error(-4, e.message) | |||
| rescue Register::BaseForm::PasswordFormatError => e | |||
| render_error(-5, e.message) | |||
| rescue Register::BaseForm::VerifiCodeError => e | |||
| render_error(-6, e.message) | |||
| rescue Exception => e | |||
| Gitea::User::DeleteService.call(user.login) unless user.nil? | |||
| uid_logger_error(e.message) | |||
| tip_exception(-1, e.message) | |||
| end | |||
| @@ -304,7 +284,7 @@ class AccountsController < ApplicationController | |||
| # 发送验证码 | |||
| # params[:login] 手机号或者邮箱号 | |||
| # params[:type]为事件通知类型 1:用户注册注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 | |||
| # params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 | |||
| # 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱 | |||
| # 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效 | |||
| def get_verification_code | |||
| @@ -318,19 +298,22 @@ class AccountsController < ApplicationController | |||
| sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}") | |||
| tip_exception(501, "请求不合理") if sign != params[:smscode] | |||
| logger.info "########### 验证码:#{verification_code}" | |||
| logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ") | |||
| # 记录验证码 | |||
| check_verification_code(verification_code, send_type, value) | |||
| sucess_status | |||
| render_ok | |||
| end | |||
| # 1 手机类型;0 邮箱类型 | |||
| # 注意新版的login是自动名生成的 | |||
| def phone_mail_type value | |||
| value =~ /^1\d{10}$/ ? 1 : 0 | |||
| # check user's login or email or phone is used | |||
| # params[:value] 手机号或者邮箱号或者登录名 | |||
| # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) | |||
| def check | |||
| Register::CheckColumnsForm.new(check_params).validate! | |||
| render_ok | |||
| end | |||
| private | |||
| # type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 | |||
| @@ -376,4 +359,13 @@ class AccountsController < ApplicationController | |||
| def account_params | |||
| params.require(:account).permit(:login, :password) | |||
| end | |||
| def check_params | |||
| params.permit(:type, :value) | |||
| end | |||
| def register_params | |||
| params.permit(:login, :namespace, :password, :code) | |||
| end | |||
| end | |||
| @@ -10,6 +10,10 @@ class Admins::SystemNotificationsController < Admins::BaseController | |||
| @notifications = paginate(notifications) | |||
| end | |||
| def history | |||
| @users = @notification.users | |||
| end | |||
| def new | |||
| @notification = SystemNotification.new | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::ActivityForumsController < Admins::Topic::BaseController | |||
| before_action :find_activity_forum, only: [:edit, :update, :destroy] | |||
| def index | |||
| q = ::Topic::ActivityForum.ransack(title_cont: params[:search]) | |||
| activity_forums = q.result(distinct: true) | |||
| @activity_forums = paginate(activity_forums) | |||
| end | |||
| def new | |||
| @activity_forum = ::Topic::ActivityForum.new | |||
| end | |||
| def create | |||
| @activity_forum = ::Topic::ActivityForum.new(activity_forum_params) | |||
| if @activity_forum.save | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:success] = "新增平台动态成功" | |||
| else | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:danger] = "新增平台动态失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @activity_forum.attributes = activity_forum_params | |||
| if @activity_forum.save | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:success] = "更新平台动态成功" | |||
| else | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:danger] = "更新平台动态失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @activity_forum.destroy | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:success] = "删除平台动态成功" | |||
| else | |||
| redirect_to admins_topic_activity_forums_path | |||
| flash[:danger] = "删除平台动态失败" | |||
| end | |||
| end | |||
| private | |||
| def find_activity_forum | |||
| @activity_forum = ::Topic::ActivityForum.find_by_id(params[:id]) | |||
| end | |||
| def activity_forum_params | |||
| params.require(:topic_activity_forum).permit(:title, :uuid, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::BannersController < Admins::Topic::BaseController | |||
| before_action :find_banner, only: [:edit, :update, :destroy] | |||
| def index | |||
| @banners = paginate(::Topic::Banner) | |||
| end | |||
| def new | |||
| @banner = ::Topic::Banner.new | |||
| end | |||
| def create | |||
| @banner = ::Topic::Banner.new(banner_params) | |||
| if @banner.save | |||
| save_image_file(params[:image], @banner) | |||
| redirect_to admins_topic_banners_path | |||
| flash[:success] = "新增banner成功" | |||
| else | |||
| redirect_to admins_topic_banners_path | |||
| flash[:danger] = "新增banner失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @banner.attributes = banner_params | |||
| if @banner.save | |||
| save_image_file(params[:image], @banner) | |||
| redirect_to admins_topic_banners_path | |||
| flash[:success] = "更新banner成功" | |||
| else | |||
| redirect_to admins_topic_banners_path | |||
| flash[:danger] = "更新banner失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @banner.destroy | |||
| redirect_to admins_topic_banners_path | |||
| flash[:success] = "删除banner成功" | |||
| else | |||
| redirect_to admins_topic_banners_path | |||
| flash[:danger] = "删除banner失败" | |||
| end | |||
| end | |||
| private | |||
| def find_banner | |||
| @banner = ::Topic::Banner.find_by_id(params[:id]) | |||
| end | |||
| def banner_params | |||
| params.require(:topic_banner).permit(:title, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,11 @@ | |||
| class Admins::Topic::BaseController < Admins::BaseController | |||
| protected | |||
| def save_image_file(file, topic) | |||
| return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile) | |||
| file_path = Util::FileManage.source_disk_filename(topic, 'image') | |||
| File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 | |||
| Util.write_file(file, file_path) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::CardsController < Admins::Topic::BaseController | |||
| before_action :find_card, only: [:edit, :update, :destroy] | |||
| def index | |||
| q = ::Topic::Card.ransack(title_cont: params[:search]) | |||
| cards = q.result(distinct: true) | |||
| @cards = paginate(cards) | |||
| end | |||
| def new | |||
| @card = ::Topic::Card.new | |||
| end | |||
| def create | |||
| @card = ::Topic::Card.new(card_params) | |||
| if @card.save | |||
| redirect_to admins_topic_cards_path | |||
| flash[:success] = "新增合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cards_path | |||
| flash[:danger] = "新增合作单位失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @card.attributes = card_params | |||
| if @card.save | |||
| redirect_to admins_topic_cards_path | |||
| flash[:success] = "更新合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cards_path | |||
| flash[:danger] = "更新合作单位失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @card.destroy | |||
| redirect_to admins_topic_cards_path | |||
| flash[:success] = "删除合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cards_path | |||
| flash[:danger] = "删除合作单位失败" | |||
| end | |||
| end | |||
| private | |||
| def find_card | |||
| @card = ::Topic::Card.find_by_id(params[:id]) | |||
| end | |||
| def card_params | |||
| params.require(:topic_card).permit(:title, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::CooperatorsController < Admins::Topic::BaseController | |||
| before_action :find_cooperator, only: [:edit, :update, :destroy] | |||
| def index | |||
| @cooperators = paginate(::Topic::Cooperator) | |||
| end | |||
| def new | |||
| @cooperator = ::Topic::Cooperator.new | |||
| end | |||
| def create | |||
| @cooperator = ::Topic::Cooperator.new(cooperator_params) | |||
| if @cooperator.save | |||
| save_image_file(params[:image], @cooperator) | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:success] = "新增合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:danger] = "新增合作单位失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @cooperator.attributes = cooperator_params | |||
| if @cooperator.save | |||
| save_image_file(params[:image], @cooperator) | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:success] = "更新合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:danger] = "更新合作单位失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @cooperator.destroy | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:success] = "删除合作单位成功" | |||
| else | |||
| redirect_to admins_topic_cooperators_path | |||
| flash[:danger] = "删除合作单位失败" | |||
| end | |||
| end | |||
| private | |||
| def find_cooperator | |||
| @cooperator = ::Topic::Cooperator.find_by_id(params[:id]) | |||
| end | |||
| def cooperator_params | |||
| params.require(:topic_cooperator).permit(:title, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::ExcellentProjectsController < Admins::Topic::BaseController | |||
| before_action :find_excellent_project, only: [:edit, :update, :destroy] | |||
| def index | |||
| q = ::Topic::ExcellentProject.ransack(title_cont: params[:search]) | |||
| excellent_projects = q.result(distinct: true) | |||
| @excellent_projects = paginate(excellent_projects) | |||
| end | |||
| def new | |||
| @excellent_project = ::Topic::ExcellentProject.new | |||
| end | |||
| def create | |||
| @excellent_project = ::Topic::ExcellentProject.new(excellent_project_params) | |||
| if @excellent_project.save | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:success] = "新增优秀仓库成功" | |||
| else | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:danger] = "新增优秀仓库失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @excellent_project.attributes = excellent_project_params | |||
| if @excellent_project.save | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:success] = "更新优秀仓库成功" | |||
| else | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:danger] = "更新优秀仓库失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @excellent_project.destroy | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:success] = "删除优秀仓库成功" | |||
| else | |||
| redirect_to admins_topic_excellent_projects_path | |||
| flash[:danger] = "删除优秀仓库失败" | |||
| end | |||
| end | |||
| private | |||
| def find_excellent_project | |||
| @excellent_project = ::Topic::ExcellentProject.find_by_id(params[:id]) | |||
| end | |||
| def excellent_project_params | |||
| params.require(:topic_excellent_project).permit(:title, :uuid, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::ExperienceForumsController < Admins::Topic::BaseController | |||
| before_action :find_experience_forum, only: [:edit, :update, :destroy] | |||
| def index | |||
| q = ::Topic::ExperienceForum.ransack(title_cont: params[:search]) | |||
| experience_forums = q.result(distinct: true) | |||
| @experience_forums = paginate(experience_forums) | |||
| end | |||
| def new | |||
| @experience_forum = ::Topic::ExperienceForum.new | |||
| end | |||
| def create | |||
| @experience_forum = ::Topic::ExperienceForum.new(experience_forum_params) | |||
| if @experience_forum.save | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:success] = "新增经验分享成功" | |||
| else | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:danger] = "新增经验分享失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @experience_forum.attributes = experience_forum_params | |||
| if @experience_forum.save | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:success] = "更新经验分享成功" | |||
| else | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:danger] = "更新经验分享失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @experience_forum.destroy | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:success] = "删除经验分享成功" | |||
| else | |||
| redirect_to admins_topic_experience_forums_path | |||
| flash[:danger] = "删除经验分享失败" | |||
| end | |||
| end | |||
| private | |||
| def find_experience_forum | |||
| @experience_forum = ::Topic::ExperienceForum.find_by_id(params[:id]) | |||
| end | |||
| def experience_forum_params | |||
| params.require(:topic_experience_forum).permit(:title, :uuid, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,57 @@ | |||
| class Admins::Topic::PinnedForumsController < Admins::Topic::BaseController | |||
| before_action :find_pinned_forum, only: [:edit, :update, :destroy] | |||
| def index | |||
| q = ::Topic::PinnedForum.ransack(title_cont: params[:search]) | |||
| pinned_forums = q.result(distinct: true) | |||
| @pinned_forums = paginate(pinned_forums) | |||
| end | |||
| def new | |||
| @pinned_forum = ::Topic::PinnedForum.new | |||
| end | |||
| def create | |||
| @pinned_forum = ::Topic::PinnedForum.new(pinned_forum_params) | |||
| if @pinned_forum.save | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:success] = "新增精选文章成功" | |||
| else | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:danger] = "新增精选文章失败" | |||
| end | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| @pinned_forum.attributes = pinned_forum_params | |||
| if @pinned_forum.save | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:success] = "更新精选文章成功" | |||
| else | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:danger] = "更新精选文章失败" | |||
| end | |||
| end | |||
| def destroy | |||
| if @pinned_forum.destroy | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:success] = "删除精选文章成功" | |||
| else | |||
| redirect_to admins_topic_pinned_forums_path | |||
| flash[:danger] = "删除精选文章失败" | |||
| end | |||
| end | |||
| private | |||
| def find_pinned_forum | |||
| @pinned_forum = ::Topic::PinnedForum.find_by_id(params[:id]) | |||
| end | |||
| def pinned_forum_params | |||
| params.require(:topic_pinned_forum).permit(:title, :uuid, :url, :order_index) | |||
| end | |||
| end | |||
| @@ -1,4 +1,6 @@ | |||
| class Admins::UsersController < Admins::BaseController | |||
| before_action :finder_user, except: [:index] | |||
| def index | |||
| params[:sort_by] = params[:sort_by].presence || 'created_on' | |||
| params[:sort_direction] = params[:sort_direction].presence || 'desc' | |||
| @@ -8,12 +10,9 @@ class Admins::UsersController < Admins::BaseController | |||
| end | |||
| def edit | |||
| @user = User.find(params[:id]) | |||
| end | |||
| def update | |||
| @user = User.find(params[:id]) | |||
| Admins::UpdateUserService.call(@user, update_params) | |||
| flash[:success] = '保存成功' | |||
| redirect_to edit_admins_user_path(@user) | |||
| @@ -26,43 +25,47 @@ class Admins::UsersController < Admins::BaseController | |||
| end | |||
| def destroy | |||
| User.find(params[:id]).destroy! | |||
| @user.destroy! | |||
| Gitea::User::DeleteService.call(@user.login) | |||
| render_delete_success | |||
| end | |||
| def lock | |||
| User.find(params[:id]).lock! | |||
| @user.lock! | |||
| render_ok | |||
| end | |||
| def unlock | |||
| User.find(params[:id]).activate! | |||
| @user.activate! | |||
| render_ok | |||
| end | |||
| def reward_grade | |||
| user = User.find(params[:user_id]) | |||
| return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0 | |||
| RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true) | |||
| RewardGradeService.call(@user, container_id: @user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true) | |||
| render_ok(grade: user.grade) | |||
| render_ok(grade: @user.grade) | |||
| end | |||
| def reset_login_times | |||
| User.find(params[:id]).reset_login_times! | |||
| @user.reset_login_times! | |||
| render_ok | |||
| end | |||
| private | |||
| def finder_user | |||
| @user = User.find(params[:id]) | |||
| end | |||
| def update_params | |||
| params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker | |||
| mail phone location location_city school_id department_id admin business is_test | |||
| password professional_certification authentication]) | |||
| password professional_certification authentication login]) | |||
| end | |||
| end | |||
| @@ -70,49 +70,11 @@ class ApplicationController < ActionController::Base | |||
| (current_user.professional_certification && (ue.teacher? || ue.professional?)) | |||
| end | |||
| def shixun_marker | |||
| unless current_user.is_shixun_marker? || current_user.admin_or_business? | |||
| tip_exception(403, "..") | |||
| end | |||
| end | |||
| # 实训的访问权限 | |||
| def shixun_access_allowed | |||
| if !current_user.shixun_permission(@shixun) | |||
| tip_exception(403, "..") | |||
| end | |||
| end | |||
| def admin_or_business? | |||
| User.current.admin? || User.current.business? | |||
| end | |||
| # 访问课堂时没权限直接弹加入课堂的弹框 :409 | |||
| def user_course_identity | |||
| @user_course_identity = current_user.course_identity(@course) | |||
| if @user_course_identity > Course::STUDENT && @course.is_public == 0 | |||
| tip_exception(401, "..") unless User.current.logged? | |||
| check_account | |||
| tip_exception(@course.excellent ? 410 : 409, "您没有权限进入") | |||
| end | |||
| if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id | |||
| # 实名认证和职业认证的身份判断 | |||
| tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication && | |||
| @course.professional_certification && (!current_user.authentication && !current_user.professional_certification) | |||
| tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication | |||
| tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification | |||
| end | |||
| uid_logger("###############user_course_identity:#{@user_course_identity}") | |||
| end | |||
| # 题库的访问权限 | |||
| def bank_visit_auth | |||
| tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public | |||
| tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? || | |||
| (current_user.certification_teacher? && @bank.is_public) | |||
| end | |||
| # 判断用户的邮箱或者手机是否可用 | |||
| # params[:type] 1: 注册;2:忘记密码;3:绑定 | |||
| def check_mail_and_phone_valid login, type | |||
| @@ -120,16 +82,16 @@ class ApplicationController < ActionController::Base | |||
| login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/ | |||
| tip_exception(-2, "请输入正确的手机号或邮箱") | |||
| end | |||
| # 考虑到安全参数问题,多一次查询,去掉Union | |||
| user = User.where(phone: login).first || User.where(mail: login).first | |||
| if type.to_i == 1 && !user.nil? | |||
| user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login) | |||
| if user_exist && type.to_i == 1 | |||
| tip_exception(-2, "该手机号码或邮箱已被注册") | |||
| elsif type.to_i == 2 && user.nil? | |||
| elsif type.to_i == 2 && !user_exist | |||
| tip_exception(-2, "该手机号码或邮箱未注册") | |||
| elsif type.to_i == 3 && user.present? | |||
| elsif type.to_i == 3 && user_exist | |||
| tip_exception(-2, "该手机号码或邮箱已绑定") | |||
| end | |||
| sucess_status | |||
| render_ok | |||
| end | |||
| # 发送及记录激活码 | |||
| @@ -186,26 +148,6 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| end | |||
| def find_course | |||
| return normal_status(2, '缺少course_id参数!') if params[:course_id].blank? | |||
| @course = Course.find(params[:course_id]) | |||
| tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business? | |||
| rescue Exception => e | |||
| tip_exception(e.message) | |||
| end | |||
| def course_manager | |||
| return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR | |||
| end | |||
| def find_board | |||
| return normal_status(2, "缺少board_id参数") if params[:board_id].blank? | |||
| @board = Board.find(params[:board_id]) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def validate_type(object_type) | |||
| normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip) | |||
| end | |||
| @@ -215,21 +157,6 @@ class ApplicationController < ActionController::Base | |||
| @page_size = params[:page_size] || 15 | |||
| end | |||
| # 课堂教师权限 | |||
| def teacher_allowed | |||
| logger.info("#####identity: #{current_user.course_identity(@course)}") | |||
| unless current_user.course_identity(@course) < Course::STUDENT | |||
| normal_status(403, "") | |||
| end | |||
| end | |||
| # 课堂教师、课堂管理员、超级管理员的权限(不包含助教) | |||
| def teacher_or_admin_allowed | |||
| unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR | |||
| normal_status(403, "") | |||
| end | |||
| end | |||
| def require_admin | |||
| normal_status(403, "") unless User.current.admin? | |||
| end | |||
| @@ -681,7 +608,7 @@ class ApplicationController < ActionController::Base | |||
| def kaminari_paginate(relation) | |||
| limit = params[:limit] || params[:per_page] | |||
| limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i | |||
| limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i | |||
| page = params[:page].to_i.zero? ? 1 : params[:page].to_i | |||
| relation.page(page).per(limit) | |||
| @@ -689,7 +616,7 @@ class ApplicationController < ActionController::Base | |||
| def kaminari_array_paginate(relation) | |||
| limit = params[:limit] || params[:per_page] | |||
| limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i | |||
| limit = (limit.to_i.zero? || limit.to_i > 20) ? 20 : limit.to_i | |||
| page = params[:page].to_i.zero? ? 1 : params[:page].to_i | |||
| Kaminari.paginate_array(relation).page(page).per(limit) | |||
| @@ -3,8 +3,8 @@ module RenderHelper | |||
| render json: { status: 0, message: 'success' }.merge(data) | |||
| end | |||
| def render_error(message = '') | |||
| render json: { status: -1, message: message } | |||
| def render_error(status = -1, message = '') | |||
| render json: { status: status, message: message } | |||
| end | |||
| def render_not_acceptable(message = '请求已拒绝') | |||
| @@ -27,7 +27,7 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.is_reversed(organization_params[:name]).present? | |||
| tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name]) | |||
| Organizations::CreateForm.new(organization_params).validate! | |||
| @organization = Organizations::CreateService.call(current_user, organization_params) | |||
| Util.write_file(@image, avatar_path(@organization)) if params[:image].present? | |||
| @@ -0,0 +1,9 @@ | |||
| class TopicsController < ApplicationController | |||
| def index | |||
| return render_not_found("请输入正确的数据类型") unless params[:topic_type].present? | |||
| scope = Topic.with_single_type(params[:topic_type]) | |||
| @topics = kaminari_paginate(scope) | |||
| end | |||
| end | |||
| @@ -0,0 +1,15 @@ | |||
| class Users::SystemNotificationHistoriesController < Users::BaseController | |||
| before_action :private_user_resources!, only: [:create] | |||
| def create | |||
| @history = observed_user.system_notification_histories.new(system_notification_id: params[:system_notification_id]) | |||
| if @history.save | |||
| render_ok | |||
| else | |||
| Rails.logger.info @history.errors.as_json | |||
| render_error(@history.errors.full_messages.join(",")) | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| end | |||
| @@ -199,6 +199,36 @@ await octokit.request('GET /api/users/:login/messages.json') | |||
| Success Data. | |||
| </aside> | |||
| ## 用户阅读系统通知 | |||
| 用户阅读系统通知 | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/users/:login/system_notification_histories.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST /api/users/:login/system_notification_histories.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |system_notification_id |integer |阅读的系统通知id | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| ## 发送消息 | |||
| 发送消息, 目前只支持atme | |||
| @@ -23,7 +23,11 @@ class BaseForm | |||
| end | |||
| def check_reversed_keyword(repository_name) | |||
| raise "项目标识已被占用." if ReversedKeyword.is_reversed(repository_name).exists? | |||
| raise "项目标识已被占用." if ReversedKeyword.check_exists?(repository_name) | |||
| end | |||
| private | |||
| def strip(str) | |||
| str.to_s.strip.presence | |||
| end | |||
| end | |||
| @@ -0,0 +1,51 @@ | |||
| module Register | |||
| class BaseForm < ::BaseForm | |||
| include ActiveModel::Model | |||
| Error = Class.new(StandardError) | |||
| EmailError = Class.new(Error) | |||
| LoginError = Class.new(Error) | |||
| PhoneError = Class.new(Error) | |||
| PasswordFormatError = Class.new(Error) | |||
| VerifiCodeError = Class.new(Error) | |||
| private | |||
| def check_login(login) | |||
| login = strip(login) | |||
| raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN | |||
| login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login) | |||
| raise LoginError, '登录名已被使用' if login_exist | |||
| end | |||
| def check_mail(mail) | |||
| mail = strip(mail) | |||
| raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL | |||
| mail_exist = Owner.exists?(mail: mail) | |||
| raise EmailError, '邮箱已被使用' if mail_exist | |||
| end | |||
| def check_phone(phone) | |||
| phone = strip(phone) | |||
| raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE | |||
| phone_exist = Owner.exists?(phone: phone) | |||
| raise PhoneError, '手机号已被使用' if phone_exist | |||
| end | |||
| def check_password(password) | |||
| password = strip(password) | |||
| raise PasswordFormatError, "8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD | |||
| end | |||
| def check_verifi_code(verifi_code, code) | |||
| code = strip(code) | |||
| # return if code == "123123" # TODO 万能验证码,用于测试 | |||
| raise VerifiCodeError, "验证码不正确" if verifi_code&.code != code | |||
| raise VerifiCodeError, "验证码已失效" if !verifi_code&.effective? | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| module Register | |||
| class CheckColumnsForm < Register::BaseForm | |||
| attr_accessor :type, :value | |||
| validates :type, presence: true, numericality: true | |||
| validates :value, presence: true | |||
| validate :check! | |||
| def check! | |||
| # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) | |||
| case strip(type).to_i | |||
| when 1 then check_login(strip(value)) | |||
| when 2 then check_mail(strip(value)) | |||
| when 3 then check_phone(strip(value)) | |||
| else raise("type值无效") | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| module Register | |||
| class Form < Register::BaseForm | |||
| # login 登陆方式,支持邮箱、登陆、手机号等 | |||
| # namespace 用户空间地址 | |||
| # type: 1:手机号注册;2:邮箱注册 | |||
| attr_accessor :login, :namespace, :password, :code, :type | |||
| validates :login, :code, :password, :namespace, presence: true | |||
| validate :check! | |||
| def check! | |||
| Rails.logger.info "Register::Form params: code: #{code}; login: #{login}; namespace: #{namespace}; password: #{password}; type: #{type}" | |||
| db_verifi_code = | |||
| if type == 1 | |||
| check_phone(login) | |||
| VerificationCode.where(phone: login, code: code, code_type: 1).last | |||
| elsif type == 0 | |||
| check_mail(login) | |||
| VerificationCode.where(email: login, code: code, code_type: 8).last | |||
| end | |||
| check_login(namespace) | |||
| check_verifi_code(db_verifi_code, code) | |||
| check_password(password) | |||
| end | |||
| end | |||
| end | |||
| @@ -463,5 +463,11 @@ module ApplicationHelper | |||
| sidebar_item(url, "数据统计", icon: 'bar-chart', controller: 'root') | |||
| end | |||
| end | |||
| # 1 手机类型;0 邮箱类型 | |||
| # 注意新版的login是自动名生成的 | |||
| def phone_mail_type value | |||
| value =~ /^1\d{10}$/ ? 1 : 0 | |||
| end | |||
| end | |||
| @@ -1,6 +1,7 @@ | |||
| module CustomRegexp | |||
| PHONE = /1\d{10}/ | |||
| EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/ | |||
| LOGIN = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ | |||
| NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ | |||
| PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ | |||
| @@ -0,0 +1,20 @@ | |||
| module Forum | |||
| class << self | |||
| def forum_config | |||
| forum_config = {} | |||
| begin | |||
| config = Rails.application.config_for(:configuration).symbolize_keys! | |||
| forum_config = config[:forum].symbolize_keys! | |||
| raise 'forum config missing' if forum_config.blank? | |||
| rescue => ex | |||
| raise ex if Rails.env.production? | |||
| puts %Q{\033[33m [warning] forum config or configuration.yml missing, | |||
| please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m} | |||
| forum_config = {} | |||
| end | |||
| forum_config | |||
| end | |||
| end | |||
| end | |||
| @@ -1,17 +1,11 @@ | |||
| class UserMailer < ApplicationMailer | |||
| # 注意:这个地方一定要和你的邮箱服务域名一致 | |||
| default from: 'educoder@trustie.org' | |||
| default from: 'notification@trustie.org' | |||
| # 用户注册验证码 | |||
| def register_email(mail, code) | |||
| @code = code | |||
| mail(to: mail, subject: '验证你的电子邮件') | |||
| mail(to: mail, subject: 'Gitink | 注册验证码') | |||
| end | |||
| # 课堂讨论区的邮件通知 | |||
| def course_message_email(mail, message_id) | |||
| @message = Message.find_by(id: message_id) | |||
| @course = @message&.board&.course | |||
| mail(to: mail, subject: '课堂发布了新的帖子') if @message.present? && @course.present? | |||
| end | |||
| end | |||
| @@ -18,6 +18,10 @@ class ReversedKeyword < ApplicationRecord | |||
| before_validation :set_identifier | |||
| def self.check_exists?(identifier) | |||
| self.is_reversed(identifier).exists? | |||
| end | |||
| private | |||
| def set_identifier | |||
| @@ -15,6 +15,13 @@ class SystemNotification < ApplicationRecord | |||
| default_scope { order(created_at: :desc)} | |||
| has_many :system_notification_histories | |||
| has_many :users, through: :system_notification_histories | |||
| scope :is_top, lambda { where(is_top: true) } | |||
| def read_member?(user_id) | |||
| self.system_notification_histories.where(user_id: user_id).present? ? true : false | |||
| end | |||
| end | |||
| @@ -0,0 +1,23 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: system_notification_histories | |||
| # | |||
| # id :integer not null, primary key | |||
| # system_message_id :integer | |||
| # user_id :integer | |||
| # created_at :datetime not null | |||
| # updated_at :datetime not null | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_system_notification_histories_on_system_message_id (system_message_id) | |||
| # index_system_notification_histories_on_user_id (user_id) | |||
| # | |||
| class SystemNotificationHistory < ApplicationRecord | |||
| belongs_to :system_notification | |||
| belongs_to :user | |||
| validates :system_notification_id, uniqueness: { scope: :user_id, message: '只能阅读一次'} | |||
| end | |||
| @@ -0,0 +1,50 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| class Topic < ApplicationRecord | |||
| default_scope { order(order_index: :desc)} | |||
| scope :with_single_type, ->(type){where(type: trans_simpletype_to_classtype(type))} | |||
| def image | |||
| image_url('image') | |||
| end | |||
| def self.trans_simpletype_to_classtype(type) | |||
| case type | |||
| when 'activity_forum' | |||
| 'Topic::ActivityForum' | |||
| when 'banner' | |||
| 'Topic::Banner' | |||
| when 'card' | |||
| 'Topic::Card' | |||
| when 'cooperator' | |||
| 'Topic::Cooperator' | |||
| when 'excellent_project' | |||
| 'Topic::ExcellentProject' | |||
| when 'experience_forum' | |||
| 'Topic::ExperienceForum' | |||
| when 'pinned_forum' | |||
| 'Topic::PinnedForum' | |||
| end | |||
| end | |||
| private | |||
| def image_url(type) | |||
| return nil unless Util::FileManage.exists?(self, type) | |||
| Util::FileManage.source_disk_file_url(self, type) | |||
| end | |||
| end | |||
| @@ -0,0 +1,17 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页平台动态 | |||
| class Topic::ActivityForum < Topic | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页banner | |||
| class Topic::Banner < Topic | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页卡片内容 | |||
| class Topic::Card < Topic | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页合作伙伴 | |||
| class Topic::Cooperator < Topic | |||
| end | |||
| @@ -0,0 +1,17 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页优秀项目 | |||
| class Topic::ExcellentProject < Topic | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页经验分享 | |||
| class Topic::ExperienceForum < Topic | |||
| end | |||
| @@ -0,0 +1,16 @@ | |||
| # == Schema Information | |||
| # | |||
| # Table name: topics | |||
| # | |||
| # id :integer not null, primary key | |||
| # type :string(255) | |||
| # title :string(255) | |||
| # uuid :integer | |||
| # image_url :string(255) | |||
| # url :string(255) | |||
| # order_index :integer | |||
| # | |||
| # 首页精选文章 | |||
| class Topic::PinnedForum < Topic | |||
| end | |||
| @@ -117,8 +117,6 @@ class User < Owner | |||
| enumerize :platform, in: [:forge, :educoder, :trustie, :military], default: :forge, scope: :shallow | |||
| belongs_to :laboratory, optional: true | |||
| has_many :composes, dependent: :destroy | |||
| has_many :compose_users, dependent: :destroy | |||
| has_one :user_extension, dependent: :destroy | |||
| has_many :open_users, dependent: :destroy | |||
| has_one :wechat_open_user, class_name: 'OpenUsers::Wechat' | |||
| @@ -174,6 +172,9 @@ class User < Owner | |||
| has_one :user_template_message_setting, dependent: :destroy | |||
| has_many :system_notification_histories | |||
| has_many :system_notifications, through: :system_notification_histories | |||
| # Groups and active users | |||
| scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } | |||
| scope :like, lambda { |keywords| | |||
| @@ -440,6 +441,7 @@ class User < Owner | |||
| def activate! | |||
| update_attribute(:status, STATUS_ACTIVE) | |||
| prohibit_gitea_user_login!(false) | |||
| end | |||
| def register! | |||
| @@ -448,6 +450,12 @@ class User < Owner | |||
| def lock! | |||
| update_attribute(:status, STATUS_LOCKED) | |||
| prohibit_gitea_user_login! | |||
| end | |||
| def prohibit_gitea_user_login!(prohibit_login = true) | |||
| Gitea::User::UpdateInteractor.call(self.login, | |||
| {email: self.mail, prohibit_login: prohibit_login}) | |||
| end | |||
| def need_edit_info! | |||
| @@ -11,20 +11,45 @@ class Projects::ListQuery < ApplicationQuery | |||
| end | |||
| def call | |||
| q = params[:pinned].present? ? Project.pinned : Project | |||
| q = q.visible.by_name_or_identifier(params[:search]) | |||
| scope = q | |||
| .with_project_type(params[:project_type]) | |||
| .with_project_category(params[:category_id]) | |||
| .with_project_language(params[:language_id]) | |||
| collection = Project.all | |||
| collection = filter_projects(collection) | |||
| sort = params[:sort_by] || "updated_on" | |||
| sort_direction = params[:sort_direction] || "desc" | |||
| custom_sort(scope, sort, sort_direction) | |||
| custom_sort(collection, sort, sort_direction) | |||
| # scope = scope.reorder("projects.#{sort} #{sort_direction}") | |||
| # scope | |||
| end | |||
| def filter_projects(collection) | |||
| collection = by_pinned(collection) | |||
| collection = by_search(collection) | |||
| collection = by_project_type(collection) | |||
| collection = by_project_category(collection) | |||
| collection = by_project_language(collection) | |||
| collection | |||
| end | |||
| def by_search(items) | |||
| items.visible.by_name_or_identifier(params[:search]) | |||
| end | |||
| def by_project_type(items) | |||
| items.with_project_type(params[:project_type]) | |||
| end | |||
| def by_project_category(items) | |||
| items.with_project_category(params[:category_id]) | |||
| end | |||
| def by_project_language(items) | |||
| items.with_project_language(params[:language_id]) | |||
| end | |||
| def by_pinned(items) | |||
| (params[:pinned].present? && params[:category_id].present?) ? items.pinned : items | |||
| end | |||
| end | |||
| @@ -15,19 +15,13 @@ class Admins::UpdateUserService < ApplicationService | |||
| user.firstname = '' | |||
| user.password = params[:password] if params[:password].present? | |||
| if params[:identity].to_s == 'student' | |||
| params[:technical_title] = nil | |||
| else | |||
| params[:student_id] = nil | |||
| end | |||
| user.user_extension.assign_attributes(user_extension_attributes) | |||
| old_login = user.login | |||
| ActiveRecord::Base.transaction do | |||
| user.save! | |||
| user.user_extension.save! | |||
| user.update!(is_shixun_marker: true) if user.is_certification_teacher | |||
| update_gitlab_password if params[:password].present? | |||
| update_gitea_user(old_login) | |||
| end | |||
| user | |||
| @@ -36,7 +30,7 @@ class Admins::UpdateUserService < ApplicationService | |||
| private | |||
| def user_attributes | |||
| params.slice(*%i[lastname nickname mail phone admin business is_test | |||
| params.slice(*%i[lastname nickname mail phone admin business is_test login | |||
| professional_certification authentication is_shixun_marker]) | |||
| end | |||
| @@ -44,10 +38,29 @@ class Admins::UpdateUserService < ApplicationService | |||
| params.slice(*%i[gender identity technical_title student_id location location_city school_id department_id]) | |||
| end | |||
| def update_gitlab_password | |||
| return if user.gid.blank? | |||
| # 同步修改gitlab密码 | |||
| Gitlab.client.edit_user(user.gid, password: params[:password]) | |||
| def gitea_user_params | |||
| hash = { | |||
| password: params[:password].to_s.presence, | |||
| email: user.mail, | |||
| login_name: params[:login].to_s.presence, | |||
| admin: boolean_admin | |||
| }.compact | |||
| hash.delete_if {|_,v| v.to_s.strip == ''} | |||
| end | |||
| def boolean_admin | |||
| admin = params[:admin].to_s.presence | |||
| case admin | |||
| when "0" then false | |||
| when "1" then true | |||
| end | |||
| end | |||
| def update_gitea_user(old_login) | |||
| return if user.gitea_uid.blank? | |||
| Gitea::User::UpdateInteractor.call(old_login, gitea_user_params) | |||
| rescue Exception => ex | |||
| Util.logger_error(ex) | |||
| raise Error, '保存失败' | |||
| @@ -0,0 +1,94 @@ | |||
| class Forum::ClientService < ApplicationService | |||
| attr_reader :url, :params | |||
| PAGINATE_DEFAULT_PAGE = 1 | |||
| PAGINATE_DEFAULT_LIMIT = 20 | |||
| def initialize(options={}) | |||
| @url = options[:url] | |||
| @params = options[:params] | |||
| end | |||
| def get(url, params={}) | |||
| conn(params).get do |req| | |||
| req.url full_url(url, 'get') | |||
| params.except(:token).each_pair do |key, value| | |||
| req.params["#{key}"] = value | |||
| end | |||
| end | |||
| # response.headers.each do |k,v| | |||
| # puts "#{k}:#{v}" | |||
| # end #=> 响应头 | |||
| end | |||
| private | |||
| def conn(auth={}) | |||
| @client ||= begin | |||
| Faraday.new(url: domain) do |req| | |||
| req.request :url_encoded | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| end | |||
| end | |||
| @client | |||
| end | |||
| def base_url | |||
| Forum.forum_config[:base_url] | |||
| end | |||
| def domain | |||
| Forum.forum_config[:domain] | |||
| end | |||
| def api_url | |||
| [domain, base_url].join('') | |||
| end | |||
| def full_url(api_rest, action='post') | |||
| url = [api_url, api_rest].join('').freeze | |||
| url = action === 'get' ? url : URI.escape(url) | |||
| url = URI.escape(url) unless url.ascii_only? | |||
| puts "[forum] request url: #{url}" | |||
| return url | |||
| end | |||
| def render_response(response) | |||
| status = response.status | |||
| body = response&.body | |||
| # log_error(status, body) | |||
| body, message = get_body_by_status(status, body) | |||
| [status, message, body] | |||
| end | |||
| def get_body_by_status(status, body) | |||
| body, message = | |||
| case status | |||
| when 401 then [nil, "401"] | |||
| when 404 then [nil, "404"] | |||
| when 403 then [nil, "403"] | |||
| when 500 then [nil, "500"] | |||
| else | |||
| if body.present? | |||
| body = JSON.parse(body) | |||
| fix_body(body) | |||
| else | |||
| nil | |||
| end | |||
| end | |||
| [body, message] | |||
| end | |||
| def fix_body(body) | |||
| return [body, nil] if body.is_a?(Array) || body.is_a?(Hash) | |||
| body['message'].blank? ? [body, nil] : [nil, body['message']] | |||
| end | |||
| end | |||
| @@ -0,0 +1,21 @@ | |||
| class Forum::Memos::GetService < Forum::ClientService | |||
| attr_reader :memo_id | |||
| def initialize(memo_id) | |||
| @memo_id = memo_id | |||
| end | |||
| def call | |||
| response = get(url) | |||
| code, message, body = render_response(response) | |||
| if code == 200 && body["status"] == 0 | |||
| return body | |||
| else | |||
| return nil | |||
| end | |||
| end | |||
| def url | |||
| "/memos/#{memo_id}.json".freeze | |||
| end | |||
| end | |||
| @@ -82,6 +82,8 @@ class Gitea::ClientService < ApplicationService | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.response :logger # 显示日志 | |||
| req.adapter Faraday.default_adapter | |||
| req.options.timeout = 100 # open/read timeout in seconds | |||
| req.options.open_timeout = 10 # connection open timeout in seconds | |||
| if token.blank? | |||
| req.basic_auth(username, secret) | |||
| else | |||
| @@ -0,0 +1,31 @@ | |||
| class Gitea::User::DeleteService < Gitea::ClientService | |||
| attr_reader :username | |||
| def initialize(username) | |||
| @username = username | |||
| end | |||
| def call | |||
| response = delete(request_url, params) | |||
| render_status(response) | |||
| end | |||
| private | |||
| def token | |||
| { | |||
| username: Gitea.gitea_config[:access_key_id], | |||
| password: Gitea.gitea_config[:access_key_secret] | |||
| } | |||
| end | |||
| def request_url | |||
| "/admin/users/#{username}" | |||
| end | |||
| def params | |||
| Hash.new.merge(token: token) | |||
| end | |||
| end | |||
| @@ -0,0 +1,58 @@ | |||
| class Users::RegisterService < ApplicationService | |||
| def initialize(params) | |||
| @login = params[:login] | |||
| @namespace = params[:namespace] | |||
| @password = params[:password] | |||
| @code = params[:code] | |||
| end | |||
| def call | |||
| code = strip(@code) | |||
| login = strip(@login) | |||
| namespace = strip(@namespace) | |||
| password = strip(@password) | |||
| Rails.logger.info "Users::RegisterService params: ##### #{params} " | |||
| email, phone = | |||
| if register_type == 1 | |||
| phone_register(login, code) | |||
| elsif register_type == 0 | |||
| mail_register(login, code) | |||
| end | |||
| user = User.new(admin: false, login: namespace, mail: email, phone: phone, type: "User") | |||
| user.password = password | |||
| user.activate # 现在因为是验证码,所以在注册的时候就可以激活 | |||
| user | |||
| end | |||
| private | |||
| # 手机注册 | |||
| def phone_register(login, code) | |||
| Rails.logger.info("start register by phone: phone is #{login}") | |||
| email = nil | |||
| phone = login | |||
| [email, phone] | |||
| end | |||
| # 邮箱注册 | |||
| def mail_register(login, code) | |||
| Rails.logger.info("start register by email: email is #{login}") | |||
| email = login | |||
| phone = nil | |||
| [email, phone] | |||
| end | |||
| def register_type | |||
| phone_mail_type(@login) | |||
| end | |||
| def phone_mail_type value | |||
| value =~ /^1\d{10}$/ ? 1 : 0 | |||
| end | |||
| end | |||
| @@ -31,17 +31,30 @@ | |||
| <li><%= sidebar_item(admins_reversed_keywords_path, '系统保留关键词', icon: 'key', controller: 'admins-reversed_keywords') %></li> | |||
| <li><%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %></li> | |||
| <!-- <li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-index', '首页配置', icon: 'file') do %> | |||
| <li><%= sidebar_item(admins_topic_banners_path, 'banner管理', icon: 'image', controller: 'admins-topic-banners') %></li> | |||
| <li><%= sidebar_item(admins_topic_cards_path, '卡片管理', icon: 'archive', controller: 'admins-topic-cards') %></li> | |||
| <li><%= sidebar_item(admins_topic_activity_forums_path, '平台动态管理', icon: 'bell', controller: 'admins-topic-activity_forums') %></li> | |||
| <li><%= sidebar_item(admins_topic_excellent_projects_path, '优秀仓库管理', icon: 'git', controller: 'admins-topic-excellent_projects') %></li> | |||
| <li><%= sidebar_item(admins_topic_pinned_forums_path, '精选文章管理', icon: 'edit', controller: 'admins-topic-pinned_forums') %></li> | |||
| <li><%= sidebar_item(admins_topic_experience_forums_path, '经验分享管理', icon: 'edit', controller: 'admins-topic-experience_forums') %></li> | |||
| <li><%= sidebar_item(admins_topic_cooperators_path, '合作伙伴管理', icon: 'user', controller: 'admins-topic-cooperators') %></li> | |||
| <% end %> | |||
| </li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-submenu', '网站建设', icon: 'cogs') do %> | |||
| <!-- > | |||
| <li><%= sidebar_item(edit_admins_about_path, '关于我们', icon: 'smile-o', controller: 'admins-abouts') %></li> | |||
| <li><%= sidebar_item(edit_admins_contact_us_path, '联系我们', icon: 'commenting-o', controller: 'admins-contact_us') %></li> | |||
| <li><%= sidebar_item(admins_cooperatives_path, '合作伙伴', icon: 'handshake-o', controller: 'admins-cooperatives') %></li> | |||
| <li><%= sidebar_item(edit_admins_agreement_path, '服务协议', icon: 'file-text-o', controller: 'admins-agreements') %></li> | |||
| <li><%= sidebar_item(edit_admins_help_center_path, '帮助中心', icon: 'question-circle-o', controller: 'admins-help_centers') %></li> | |||
| --> | |||
| <li><%= sidebar_item(admins_faqs_path, 'FAQ', icon: 'question-circle', controller: 'admins-faqs') %></li> | |||
| <% end %> | |||
| </li> --> | |||
| </li> | |||
| <li> | |||
| <%= sidebar_item_group('#setting-system', '系统配置', icon: 'wrench') do %> | |||
| <li><%= sidebar_item(admins_sites_path, 'setting接口配置', icon: 'deaf', controller: 'admins-sites') %></li> | |||
| @@ -0,0 +1,44 @@ | |||
| <div class="modal fade activity-forum-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @activity_forum, url: {controller: "topic/activity_forums", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 帖子ID <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :uuid,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,33 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="10%">帖子ID</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if activity_forums.present? %> | |||
| <% activity_forums.each_with_index do |activity_forum, index| %> | |||
| <tr class="activity-forum-item-<%= activity_forum.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= activity_forum.title %></td> | |||
| <td><%= activity_forum.url %></td> | |||
| <td><%= activity_forum.uuid %></td> | |||
| <td><%= activity_forum.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_activity_forum_path(activity_forum), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_activity_forum_path(activity_forum), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: activity_forums } %> | |||
| @@ -0,0 +1,2 @@ | |||
| $("#activity-forum-modals").html("<%= j render(partial: 'admins/topic/activity_forums/form_modal', locals: {type: 'update'}) %>") | |||
| $(".activity-forum-change-modal").modal('show'); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('平台动态管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_activity_forums_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 "新增", new_admins_topic_activity_forum_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container activity-forum-list-container"> | |||
| <%= render partial: 'admins/topic/activity_forums/list', locals: { activity_forums: @activity_forums } %> | |||
| </div> | |||
| <div id="activity-forum-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.activity-forum-list-container').html("<%= j( render partial: 'admins/topic/activity_forums/list', locals: { activity_forums: @activity_forums } ) %>"); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#activity-forum-modals").html("<%= j render(partial: 'admins/topic/activity_forums/form_modal', locals: {type: 'create'}) %>") | |||
| $(".activity-forum-change-modal").modal('show'); | |||
| @@ -0,0 +1,45 @@ | |||
| <div class="modal fade banner-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @banner, url: {controller: "topic/banners", action: "#{type}"}, html: { enctype: 'multipart/form-data' } do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| <div class="logo-item"> | |||
| <% logo_img = @banner.image %> | |||
| <div class="logo-item-left mr-3 <%= logo_img ? 'has-img' : '' %>"> | |||
| <img class="logo-item-img nav-logo-img" src="<%= logo_img %>" style="<%= logo_img.present? ? '' : 'display: none' %>"/> | |||
| <%= file_field_tag(:image, accept: 'image/png,image/jpg,image/jpeg',style: "display: none", value: params[:image]) %> | |||
| <label for="image" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label> | |||
| </div> | |||
| <div class="logo-item-right"> | |||
| <div class="logo-item-title flex-1">logo</div> | |||
| <div>格式:PNG、JPG</div> | |||
| <div>尺寸:高度38px以内,宽等比例缩放</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,31 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">图片</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if banners.present? %> | |||
| <% banners.each_with_index do |banner, index| %> | |||
| <tr class="banner-item-<%= banner.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= banner.title %></td> | |||
| <td><img style="width:150px" src="<%= banner.image %>" /></td> | |||
| <td><%= banner.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_banner_path(banner), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_banner_path(banner), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: banners } %> | |||
| @@ -0,0 +1,18 @@ | |||
| $("#banner-modals").html("<%= j render(partial: 'admins/topic/banners/form_modal', locals: {type: 'update'}) %>") | |||
| $(".banner-change-modal").modal('show'); | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('卡片管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_banners_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 "新增", new_admins_topic_banner_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container banner-list-container"> | |||
| <%= render partial: 'admins/topic/banners/list', locals: { banners: @banners } %> | |||
| </div> | |||
| <div id="banner-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.banner-list-container').html("<%= j( render partial: 'admins/topic/banners/list', locals: { banners: @banners } ) %>"); | |||
| @@ -0,0 +1,18 @@ | |||
| $("#banner-modals").html("<%= j render(partial: 'admins/topic/banners/form_modal', locals: {type: 'create'}) %>") | |||
| $(".banner-change-modal").modal('show'); | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| @@ -0,0 +1,38 @@ | |||
| <div class="modal fade card-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @card, url: {controller: "topic/cards", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,31 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if cards.present? %> | |||
| <% cards.each_with_index do |card, index| %> | |||
| <tr class="card-item-<%= card.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= card.title %></td> | |||
| <td><%= card.url %></td> | |||
| <td><%= card.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_card_path(card), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_card_path(card), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: cards } %> | |||
| @@ -0,0 +1,2 @@ | |||
| $("#card-modals").html("<%= j render(partial: 'admins/topic/cards/form_modal', locals: {type: 'update'}) %>") | |||
| $(".card-change-modal").modal('show'); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('卡片管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_cards_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 "新增", new_admins_topic_card_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container card-list-container"> | |||
| <%= render partial: 'admins/topic/cards/list', locals: { cards: @cards } %> | |||
| </div> | |||
| <div id="card-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.card-list-container').html("<%= j( render partial: 'admins/topic/cards/list', locals: { cards: @cards } ) %>"); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#card-modals").html("<%= j render(partial: 'admins/topic/cards/form_modal', locals: {type: 'create'}) %>") | |||
| $(".card-change-modal").modal('show'); | |||
| @@ -0,0 +1,51 @@ | |||
| <div class="modal fade cooperator-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @cooperator, url: {controller: "topic/cooperators", action: "#{type}"}, html: { enctype: 'multipart/form-data' } do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| <div class="logo-item"> | |||
| <% logo_img = @cooperator.image %> | |||
| <div class="logo-item-left mr-3 <%= logo_img ? 'has-img' : '' %>"> | |||
| <img class="logo-item-img nav-logo-img" src="<%= logo_img %>" style="<%= logo_img.present? ? '' : 'display: none' %>"/> | |||
| <%= file_field_tag(:image, accept: 'image/png,image/jpg,image/jpeg',style: "display: none", value: params[:image]) %> | |||
| <label for="image" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label> | |||
| </div> | |||
| <div class="logo-item-right"> | |||
| <div class="logo-item-title flex-1">logo</div> | |||
| <div>格式:PNG、JPG</div> | |||
| <div>尺寸:高度38px以内,宽等比例缩放</div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,33 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="20%">图片</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if cooperators.present? %> | |||
| <% cooperators.each_with_index do |cooperator, index| %> | |||
| <tr class="cooperator-item-<%= cooperator.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= cooperator.title %></td> | |||
| <td><%= cooperator.url %></td> | |||
| <td><img style="width:150px" src="<%= cooperator.image %>" /></td> | |||
| <td><%= cooperator.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_cooperator_path(cooperator), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_cooperator_path(cooperator), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: cooperators } %> | |||
| @@ -0,0 +1,18 @@ | |||
| $("#cooperator-modals").html("<%= j render(partial: 'admins/topic/cooperators/form_modal', locals: {type: 'update'}) %>") | |||
| $(".cooperator-change-modal").modal('show'); | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('卡片管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_cooperators_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 "新增", new_admins_topic_cooperator_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container cooperator-list-container"> | |||
| <%= render partial: 'admins/topic/cooperators/list', locals: { cooperators: @cooperators } %> | |||
| </div> | |||
| <div id="cooperator-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.cooperator-list-container').html("<%= j( render partial: 'admins/topic/cooperators/list', locals: { cooperators: @cooperators } ) %>"); | |||
| @@ -0,0 +1,18 @@ | |||
| $("#cooperator-modals").html("<%= j render(partial: 'admins/topic/cooperators/form_modal', locals: {type: 'create'}) %>") | |||
| $(".cooperator-change-modal").modal('show'); | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| @@ -0,0 +1,44 @@ | |||
| <div class="modal fade excellent-project-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @excellent_project, url: {controller: "topic/excellent_projects", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 项目ID <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.number_field :uuid,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,33 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="10%">项目ID</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if excellent_projects.present? %> | |||
| <% excellent_projects.each_with_index do |excellent_project, index| %> | |||
| <tr class="excellent-project-item-<%= excellent_project.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= excellent_project.title %></td> | |||
| <td><%= excellent_project.url %></td> | |||
| <td><%= excellent_project.uuid %></td> | |||
| <td><%= excellent_project.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_excellent_project_path(excellent_project), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_excellent_project_path(excellent_project), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: excellent_projects } %> | |||
| @@ -0,0 +1,2 @@ | |||
| $("#excellent-project-modals").html("<%= j render(partial: 'admins/topic/excellent_projects/form_modal', locals: {type: 'update'}) %>") | |||
| $(".excellent-project-change-modal").modal('show'); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('优秀仓库管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_excellent_projects_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 "新增", new_admins_topic_excellent_project_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container excellent-project-list-container"> | |||
| <%= render partial: 'admins/topic/excellent_projects/list', locals: { excellent_projects: @excellent_projects } %> | |||
| </div> | |||
| <div id="excellent-project-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.excellent-project-list-container').html("<%= j( render partial: 'admins/topic/excellent_projects/list', locals: { excellent_projects: @excellent_projects } ) %>"); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#excellent-project-modals").html("<%= j render(partial: 'admins/topic/excellent_projects/form_modal', locals: {type: 'create'}) %>") | |||
| $(".excellent-project-change-modal").modal('show'); | |||
| @@ -0,0 +1,44 @@ | |||
| <div class="modal fade experience-forum-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @experience_forum, url: {controller: "topic/experience_forums", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 帖子ID <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.number_field :uuid,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,33 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="10%">帖子ID</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if experience_forums.present? %> | |||
| <% experience_forums.each_with_index do |experience_forum, index| %> | |||
| <tr class="experience-forum-item-<%= experience_forum.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= experience_forum.title %></td> | |||
| <td><%= experience_forum.url %></td> | |||
| <td><%= experience_forum.uuid %></td> | |||
| <td><%= experience_forum.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_experience_forum_path(experience_forum), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_experience_forum_path(experience_forum), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: experience_forums } %> | |||
| @@ -0,0 +1,2 @@ | |||
| $("#experience-forum-modals").html("<%= j render(partial: 'admins/topic/experience_forums/form_modal', locals: {type: 'update'}) %>") | |||
| $(".experience-forum-change-modal").modal('show'); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('经验分享管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_experience_forums_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 "新增", new_admins_topic_experience_forum_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container experience-forum-list-container"> | |||
| <%= render partial: 'admins/topic/experience_forums/list', locals: { experience_forums: @experience_forums } %> | |||
| </div> | |||
| <div id="experience-forum-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.experience-forum-list-container').html("<%= j( render partial: 'admins/topic/experience_forums/list', locals: { experience_forums: @experience_forums } ) %>"); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#experience-forum-modals").html("<%= j render(partial: 'admins/topic/experience_forums/form_modal', locals: {type: 'create'}) %>") | |||
| $(".experience-forum-change-modal").modal('show'); | |||
| @@ -0,0 +1,44 @@ | |||
| <div class="modal fade pinned-forum-change-modal" tabindex="-1" role="dialog" aria-hidden="true"> | |||
| <div class="modal-dialog modal-dialog-centered" role="document"> | |||
| <div class="modal-content"> | |||
| <div class="modal-header"> | |||
| <h5 class="modal-title"><%= type == "create" ? "新增" : "编辑" %></h5> | |||
| <button type="button" class="close" data-dismiss="modal" aria-label="Close"> | |||
| <span aria-hidden="true">×</span> | |||
| </button> | |||
| </div> | |||
| <%= form_for @pinned_forum, url: {controller: "topic/pinned_forums", action: "#{type}"} do |p| %> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label> | |||
| 标题 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :title,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 跳转地址 <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.text_field :url,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 帖子ID <span class="ml10 color-orange mr20">*</span> | |||
| </label> | |||
| <%= p.number_field :uuid,class: "form-control input-lg",required: true%> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label> | |||
| 排序等级 | |||
| </label> | |||
| <%= p.number_field :order_index, class: "form-control",placeholder: ""%> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button> | |||
| <%= p.submit "确认", class: "btn btn-primary submit-btn" %> | |||
| </div> | |||
| <% end %> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,33 @@ | |||
| <table class="table table-hover text-center subject-list-table"> | |||
| <thead class="thead-light"> | |||
| <tr> | |||
| <th width="5%">序号</th> | |||
| <th width="20%">标题</th> | |||
| <th width="20%">跳转地址</th> | |||
| <th width="10%">帖子ID</th> | |||
| <th width="20%">排序等级</th> | |||
| <th width="25%">操作</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| <% if pinned_forums.present? %> | |||
| <% pinned_forums.each_with_index do |pinned_forum, index| %> | |||
| <tr class="pinned-forum-item-<%= pinned_forum.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td><%= pinned_forum.title %></td> | |||
| <td><%= pinned_forum.url %></td> | |||
| <td><%= pinned_forum.uuid %></td> | |||
| <td><%= pinned_forum.order_index %></td> | |||
| <td class="action-container"> | |||
| <%= link_to "编辑", edit_admins_topic_pinned_forum_path(pinned_forum), remote: true, class: "action" %> | |||
| <%= link_to "删除", admins_topic_pinned_forum_path(pinned_forum), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> | |||
| </td> | |||
| </tr> | |||
| <% end %> | |||
| <% else %> | |||
| <%= render 'admins/shared/no_data_for_table' %> | |||
| <% end %> | |||
| </tbody> | |||
| </table> | |||
| <%= render partial: 'admins/shared/paginate', locals: { objects: pinned_forums } %> | |||
| @@ -0,0 +1,2 @@ | |||
| $("#pinned-forum-modals").html("<%= j render(partial: 'admins/topic/pinned_forums/form_modal', locals: {type: 'update'}) %>") | |||
| $(".pinned-forum-change-modal").modal('show'); | |||
| @@ -0,0 +1,18 @@ | |||
| <% define_admin_breadcrumbs do %> | |||
| <% add_admin_breadcrumb('精选文章管理') %> | |||
| <% end %> | |||
| <div class="box search-form-container project-list-form"> | |||
| <%= form_tag(admins_topic_pinned_forums_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 "新增", new_admins_topic_pinned_forum_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> | |||
| </div> | |||
| <div class="box admin-list-container pinned-forum-list-container"> | |||
| <%= render partial: 'admins/topic/pinned_forums/list', locals: { pinned_forums: @pinned_forums } %> | |||
| </div> | |||
| <div id="pinned-forum-modals"> | |||
| </div> | |||
| @@ -0,0 +1 @@ | |||
| $('.pinned-forum-list-container').html("<%= j( render partial: 'admins/topic/pinned_forums/list', locals: { pinned_forums: @pinned_forums } ) %>"); | |||
| @@ -0,0 +1,2 @@ | |||
| $("#pinned-forum-modals").html("<%= j render(partial: 'admins/topic/pinned_forums/form_modal', locals: {type: 'create'}) %>") | |||
| $(".pinned-forum-change-modal").modal('show'); | |||
| @@ -7,8 +7,8 @@ | |||
| <div class="box user-edit-container"> | |||
| <div class="user-info mb-4 row"> | |||
| <%= link_to "/users/#{@user.login}", class: 'user-info-avatar col-md-1', target: '_blank', data: { toggle: 'tooltip', title: '个人中心' } do %> | |||
| <img src="/images/<%= url_to_avatar(@user) %>" class="rounded-circle" width="80" height="80" /> | |||
| <%= link_to "/#{@user.login}", class: 'user-info-avatar col-md-1', target: '_blank', data: { toggle: 'tooltip', title: '个人中心' } do %> | |||
| <img src="/<%= url_to_avatar(@user) %>" class="rounded-circle" width="80" height="80" /> | |||
| <% end %> | |||
| <div class="d-flex flex-column justify-content-between col-md-3 user-info-content"> | |||
| <div class="user-info-name flex"><%= @user.real_name %> | <%= @user.id %> | <%= @user.login %></div> | |||
| @@ -50,6 +50,10 @@ | |||
| <div><h6>基本信息</h6></div> | |||
| <div class="form-group px-2"> | |||
| <div class="form-row"> | |||
| <%= f.input :login, label: '登录名', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-11', value: @user.login } %> | |||
| </div> | |||
| <div class="form-row"> | |||
| <%= f.input :lastname, label: '姓名', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-md-11', value: @user.only_real_name } %> | |||
| </div> | |||
| @@ -109,7 +113,6 @@ | |||
| <div class="d-flex"> | |||
| <%= f.input :professional_certification, as: :boolean, label: '职业认证', checked_value: 1, unchecked_value: 0 %> | |||
| <%= f.input :authentication, as: :boolean, label: '实名认证', wrapper_html: { class: 'ml-3' }, checked_value: 1, unchecked_value: 0 %> | |||
| <%= f.input :is_shixun_marker, as: :boolean, label: '实训制作', wrapper_html: { class: 'ml-3' }, checked_value: 1, unchecked_value: 0 %> | |||
| </div> | |||
| </div> | |||
| @@ -18,7 +18,7 @@ | |||
| <tr class="user-item-<%= user.id %>"> | |||
| <td><%= list_index_no((params[:page] || 1).to_i, index) %></td> | |||
| <td class="text-left"> | |||
| <%= link_to "/users/#{user.login}", target: '_blank' do %> | |||
| <%= link_to "/#{user.login}", target: '_blank' do %> | |||
| <%= overflow_hidden_span user.real_name, width: 100 %> | |||
| <% end %> | |||
| </td> | |||
| @@ -44,7 +44,6 @@ | |||
| <div class="d-inline"> | |||
| <%= javascript_void_link('更多', class: 'action dropdown-toggle', 'data-toggle': 'dropdown', 'aria-haspopup': true, 'aria-expanded': false) %> | |||
| <div class="dropdown-menu more-action-dropdown"> | |||
| <%= javascript_void_link('奖励', class: 'dropdown-item reward-grade-action', data: { toggle: 'modal', target: '.admin-users-reward-grade-modal', id: user.id }) %> | |||
| <%= javascript_void_link '恢复禁密账号', class: 'dropdown-item reset-login-times-action', data: { id: user.id } %> | |||
| @@ -61,6 +61,7 @@ json.setting do | |||
| if @top_system_notification.present? | |||
| json.system_notification do | |||
| json.(@top_system_notification, :id, :subject, :sub_subject, :content) | |||
| json.is_read @top_system_notification.read_member?(current_user&.id) | |||
| end | |||
| else | |||
| json.system_notification nil | |||
| @@ -0,0 +1,4 @@ | |||
| json.(activity_forum, :id, :title, :url) | |||
| request_memo = Forum::Memos::GetService.call(activity_forum&.uuid) | |||
| json.visits request_memo.nil? ? 0 : request_memo["memo"]["viewed_count"] | |||
| json.created_time request_memo.nil? ? format_time(Time.now) : request_memo["memo"]["published_time"] | |||
| @@ -0,0 +1 @@ | |||
| json.(banner, :id, :title, :image) | |||
| @@ -0,0 +1 @@ | |||
| json.(card, :id, :title, :url) | |||
| @@ -0,0 +1 @@ | |||
| json.(cooperator, :id, :title, :image, :url) | |||
| @@ -0,0 +1,3 @@ | |||
| json.(excellent_project, :id, :title, :url) | |||
| project_common = $redis_cache.hgetall("v2-project-common:#{excellent_project&.uuid}") | |||
| json.visits (project_common['visits'] || 0).to_i | |||
| @@ -0,0 +1,4 @@ | |||
| json.(experience_forum, :id, :title, :url) | |||
| request_memo = Forum::Memos::GetService.call(experience_forum&.uuid) | |||
| json.visits request_memo.nil? ? 0 : request_memo["memo"]["viewed_count"] | |||
| json.created_time request_memo.nil? ? format_time(Time.now) : request_memo["memo"]["published_time"] | |||
| @@ -0,0 +1,4 @@ | |||
| json.(pinned_forum, :id, :title, :url) | |||
| request_memo = Forum::Memos::GetService.call(pinned_forum&.uuid) | |||
| json.visits request_memo.nil? ? 0 : request_memo["memo"]["viewed_count"] | |||
| json.created_time request_memo.nil? ? format_time(Time.now) : request_memo["memo"]["published_time"] | |||
| @@ -0,0 +1,49 @@ | |||
| json.partial! "commons/success" | |||
| json.total_count @topics.total_count | |||
| json.topics do | |||
| if params[:group_size].present? | |||
| json.array! @topics.to_a.each_slice(params[:group_size].to_i).to_a.each do |group| | |||
| json.array! group.each do |topic| | |||
| case topic.type | |||
| when "Topic::ActivityForum" | |||
| json.partial! "activity_forum", locals: {activity_forum: topic} | |||
| when "Topic::Banner" | |||
| json.partial! "banner", locals: {banner: topic} | |||
| when "Topic::Card" | |||
| json.partial! "card", locals: {card: topic} | |||
| when "Topic::Cooperator" | |||
| json.partial! "cooperator", locals: {cooperator: topic} | |||
| when "Topic::ExcellentProject" | |||
| json.partial! "excellent_project", locals: {excellent_project: topic} | |||
| when "Topic::ExperienceForum" | |||
| json.partial! "experience_forum", locals: {experience_forum: topic} | |||
| when "Topic::PinnedForum" | |||
| json.partial! "pinned_forum", locals: {pinned_forum: topic} | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| end | |||
| else | |||
| json.array! @topics.each do |topic| | |||
| case topic.type | |||
| when "Topic::ActivityForum" | |||
| json.partial! "activity_forum", locals: {activity_forum: topic} | |||
| when "Topic::Banner" | |||
| json.partial! "banner", locals: {banner: topic} | |||
| when "Topic::Card" | |||
| json.partial! "card", locals: {card: topic} | |||
| when "Topic::Cooperator" | |||
| json.partial! "cooperator", locals: {cooperator: topic} | |||
| when "Topic::ExcellentProject" | |||
| json.partial! "excellent_project", locals: {excellent_project: topic} | |||
| when "Topic::ExperienceForum" | |||
| json.partial! "experience_forum", locals: {experience_forum: topic} | |||
| when "Topic::PinnedForum" | |||
| json.partial! "pinned_forum", locals: {pinned_forum: topic} | |||
| else | |||
| json.nil! | |||
| end | |||
| end | |||
| end | |||
| end | |||