diff --git a/Gemfile.lock b/Gemfile.lock index bc10c4825..b7f060b2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,47 +1,47 @@ GEM remote: https://mirrors.cloud.tencent.com/rubygems/ specs: - aasm (5.5.0) + aasm (5.0.6) concurrent-ruby (~> 1.0) - actioncable (5.2.8.1) - actionpack (= 5.2.8.1) + actioncable (5.2.4.1) + actionpack (= 5.2.4.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) + actionmailer (5.2.4.1) + actionpack (= 5.2.4.1) + actionview (= 5.2.4.1) + activejob (= 5.2.4.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.8.1) - actionview (= 5.2.8.1) - activesupport (= 5.2.8.1) + actionpack (5.2.4.1) + actionview (= 5.2.4.1) + activesupport (= 5.2.4.1) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.8.1) - activesupport (= 5.2.8.1) + actionview (5.2.4.1) + activesupport (= 5.2.4.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - active_decorator (1.4.0) + active_decorator (1.3.2) activesupport - activejob (5.2.8.1) - activesupport (= 5.2.8.1) + activejob (5.2.4.1) + activesupport (= 5.2.4.1) globalid (>= 0.3.6) - activemodel (5.2.8.1) - activesupport (= 5.2.8.1) - activerecord (5.2.8.1) - activemodel (= 5.2.8.1) - activesupport (= 5.2.8.1) + activemodel (5.2.4.1) + activesupport (= 5.2.4.1) + activerecord (5.2.4.1) + activemodel (= 5.2.4.1) + activesupport (= 5.2.4.1) arel (>= 9.0) - activestorage (5.2.8.1) - actionpack (= 5.2.8.1) - activerecord (= 5.2.8.1) - marcel (~> 1.0.0) - activesupport (5.2.8.1) + activestorage (5.2.4.1) + actionpack (= 5.2.4.1) + activerecord (= 5.2.4.1) + marcel (~> 0.3.1) + activesupport (5.2.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -50,20 +50,20 @@ GEM activerecord (>= 5.0, < 6.1) acts_as_list (0.9.19) activerecord (>= 3.0) - addressable (2.8.4) - public_suffix (>= 2.0.2, < 6.0) - ancestry (4.0.0) - activerecord (>= 5.2.4.5) - annotate (2.6.7) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + ancestry (3.0.7) + activerecord (>= 3.2.0) + annotate (2.6.5) activerecord (>= 2.3.0) - rake (~> 10.4.2, >= 10.4.2) + rake (>= 0.8.7) archive-zip (0.12.0) io-like (~> 0.3.0) arel (9.0.0) - ast (2.4.2) - autoprefixer-rails (10.4.13.0) - execjs (~> 2) - awesome_print (1.9.2) + ast (2.4.0) + autoprefixer-rails (9.7.4) + execjs + awesome_print (1.8.0) axlsx (3.0.0.pre) htmlentities (~> 4.3, >= 4.3.4) mimemagic (~> 0.3) @@ -72,40 +72,40 @@ GEM axlsx_rails (0.5.2) actionpack (>= 3.1) axlsx (>= 2.0.1) - backport (1.2.0) - benchmark (0.2.1) + backport (1.1.2) + benchmark (0.1.0) bindex (0.8.1) - bootsnap (1.12.0) - msgpack (~> 1.2) + bootsnap (1.4.6) + msgpack (~> 1.0) bootstrap (4.3.1) autoprefixer-rails (>= 9.1.0) popper_js (>= 1.14.3, < 2) sassc-rails (>= 2.0.0) builder (3.2.4) - bulk_insert (1.9.0) + bulk_insert (1.7.0) activerecord (>= 3.2.0) - capybara (3.32.2) + capybara (3.15.1) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) rack (>= 1.6.0) rack-test (>= 0.6.3) - regexp_parser (~> 1.5) + regexp_parser (~> 1.2) xpath (~> 3.2) - chartkick (3.4.2) + chartkick (3.3.1) childprocess (3.0.0) - chinese_pinyin (1.1.0) + chinese_pinyin (1.0.2) chromedriver-helper (2.1.1) archive-zip (~> 0.10) nokogiri (~> 1.8) - chunky_png (1.4.0) - concurrent-ruby (1.2.2) - connection_pool (2.2.5) + chunky_png (1.3.11) + concurrent-ruby (1.1.6) + connection_pool (2.2.2) crass (1.0.6) deep_cloneable (3.0.0) activerecord (>= 3.1.0, < 7) - diff-lcs (1.5.0) - diffy (3.4.2) + diff-lcs (1.3) + diffy (3.3.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) doorkeeper (5.5.1) @@ -121,19 +121,19 @@ GEM elasticsearch-transport (7.5.0) faraday (>= 0.14, < 1) multi_json - enumerize (2.5.0) + enumerize (2.3.1) activesupport (>= 3.2) - erubi (1.12.0) - et-orbi (1.2.7) + erubi (1.9.0) + et-orbi (1.2.4) tzinfo - execjs (2.8.1) + execjs (2.7.0) faraday (0.15.4) multipart-post (>= 1.2, < 3) - ffi (1.15.5) + ffi (1.12.2) font-awesome-sass (4.7.0) sass (>= 3.2) - fugit (1.8.1) - et-orbi (~> 1, >= 1.2.7) + fugit (1.4.1) + et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) gitea-client (1.4.2) rest-client (~> 2.1.0) @@ -145,78 +145,78 @@ GEM groupdate (4.1.2) activesupport (>= 4.2) harmonious_dictionary (0.0.1) - hashie (5.0.0) + hashie (3.6.0) htmlentities (4.3.4) http-accept (1.7.0) http-cookie (1.0.5) domain_name (~> 0.5) - i18n (1.13.0) + i18n (1.8.2) concurrent-ruby (~> 1.0) io-like (0.3.1) - jaro_winkler (1.5.5) - jbuilder (2.11.5) - actionview (>= 5.0.0) + jaro_winkler (1.5.4) + jbuilder (2.10.0) activesupport (>= 5.0.0) - jquery-rails (4.5.1) + jquery-rails (4.3.5) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jwt (2.3.0) - kaminari (1.2.2) + jwt (2.2.1) + kaminari (1.2.0) activesupport (>= 4.1.0) - kaminari-actionview (= 1.2.2) - kaminari-activerecord (= 1.2.2) - kaminari-core (= 1.2.2) - kaminari-actionview (1.2.2) + kaminari-actionview (= 1.2.0) + kaminari-activerecord (= 1.2.0) + kaminari-core (= 1.2.0) + kaminari-actionview (1.2.0) actionview - kaminari-core (= 1.2.2) - kaminari-activerecord (1.2.2) + kaminari-core (= 1.2.0) + kaminari-activerecord (1.2.0) activerecord - kaminari-core (= 1.2.2) - kaminari-core (1.2.2) - letter_avatar (0.3.9) + kaminari-core (= 1.2.0) + kaminari-core (1.2.0) + letter_avatar (0.3.8) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.20.0) + loofah (2.4.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (1.0.2) + marcel (0.3.3) + mimemagic (~> 0.3.2) maruku (0.7.3) - method_source (1.0.0) + method_source (0.9.2) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2023.0218.1) - mimemagic (0.4.3) + mimemagic (0.3.10) nokogiri (~> 1) rake - mini_mime (1.1.2) + mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.15.0) - msgpack (1.6.1) - multi_json (1.15.0) + minitest (5.14.0) + msgpack (1.3.3) + multi_json (1.14.1) multi_xml (0.6.0) - multipart-post (2.3.0) - mustermann (1.1.2) + multipart-post (2.1.1) + mustermann (1.1.1) ruby2_keywords (~> 0.0.1) - mysql2 (0.5.5) + mysql2 (0.5.3) netrc (0.11.0) - nio4r (2.5.9) - nokogiri (1.10.10) + nio4r (2.5.2) + nokogiri (1.10.8) mini_portile2 (~> 2.4.0) - oauth2 (1.4.8) - faraday (>= 0.8, < 3.0) + oauth2 (1.4.4) + faraday (>= 0.8, < 2.0) jwt (>= 1.0, < 3.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - omniauth (1.9.2) - hashie (>= 3.4.6) + omniauth (1.9.0) + hashie (>= 3.4.6, < 3.7.0) rack (>= 1.6.2, < 3) - omniauth-cas (2.0.0) + omniauth-cas (1.1.1) addressable (~> 2.3) nokogiri (~> 1.5) omniauth (~> 1.2) @@ -235,81 +235,85 @@ GEM omniauth-wechat-oauth2 (0.2.2) omniauth (>= 1.3.2) omniauth-oauth2 (>= 1.1.1) - parallel (1.20.1) - parser (2.7.2.0) - ast (~> 2.4.1) - pdfkit (0.8.4.3.2) - popper_js (1.16.1) - powerpack (0.1.3) - prettier (2.1.0) - public_suffix (4.0.7) - puma (5.6.5) - nio4r (~> 2.0) + parallel (1.19.1) + parser (2.7.1.1) + ast (~> 2.4.0) + pdfkit (0.8.4.1) + polyamorous (2.3.2) + activerecord (>= 5.2.1) + popper_js (1.16.0) + powerpack (0.1.2) + prettier (0.18.2) + public_suffix (4.0.3) + puma (3.12.2) raabro (1.4.0) - rack (2.0.9.3) - rack-cors (2.0.1) + rack (2.0.9) + rack-cors (1.1.1) rack (>= 2.0.0) + rack-mini-profiler (2.0.1) + rack (>= 1.2.0) rack-protection (2.0.8.1) rack - rack-test (2.1.0) - rack (>= 1.3) - rails (5.2.8.1) - actioncable (= 5.2.8.1) - actionmailer (= 5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) - activemodel (= 5.2.8.1) - activerecord (= 5.2.8.1) - activestorage (= 5.2.8.1) - activesupport (= 5.2.8.1) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.2.4.1) + actioncable (= 5.2.4.1) + actionmailer (= 5.2.4.1) + actionpack (= 5.2.4.1) + actionview (= 5.2.4.1) + activejob (= 5.2.4.1) + activemodel (= 5.2.4.1) + activerecord (= 5.2.4.1) + activestorage (= 5.2.4.1) + activesupport (= 5.2.4.1) bundler (>= 1.3.0) - railties (= 5.2.8.1) + railties (= 5.2.4.1) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) - loofah (~> 2.19, >= 2.19.1) + rails-html-sanitizer (1.3.0) + loofah (~> 2.3) rails-i18n (5.1.3) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.2.8.1) - actionpack (= 5.2.8.1) - activesupport (= 5.2.8.1) + railties (5.2.4.1) + actionpack (= 5.2.4.1) + activesupport (= 5.2.4.1) method_source rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) - rainbow (3.1.1) - rake (10.4.2) - ransack (2.4.1) - activerecord (>= 5.2.4) - activesupport (>= 5.2.4) + rainbow (3.0.0) + rake (13.0.1) + ransack (2.3.2) + activerecord (>= 5.2.1) + activesupport (>= 5.2.1) i18n - rb-fsevent (0.11.2) + polyamorous (= 2.3.2) + rb-fsevent (0.10.3) rb-inotify (0.10.1) ffi (~> 1.0) rchardet (1.8.0) - redcarpet (3.6.0) - redis (4.8.1) - redis-actionpack (5.3.0) - actionpack (>= 5, < 8) + redcarpet (3.5.0) + redis (4.1.3) + redis-actionpack (5.2.0) + actionpack (>= 5, < 7) redis-rack (>= 2.1.0, < 3) redis-store (>= 1.1.0, < 2) - redis-activesupport (5.3.0) - activesupport (>= 3, < 8) + redis-activesupport (5.2.0) + activesupport (>= 3, < 7) redis-store (>= 1.3, < 2) - redis-rack (2.1.4) + redis-rack (2.1.2) rack (>= 2.0.8, < 3) redis-store (>= 1.2, < 2) redis-rails (5.0.2) redis-actionpack (>= 5.0, < 6) redis-activesupport (>= 5.0, < 6) redis-store (>= 1.2, < 2) - redis-store (1.9.2) - redis (>= 4, < 6) - regexp_parser (1.8.2) - request_store (1.5.1) + redis-store (1.8.2) + redis (>= 4, < 5) + regexp_parser (1.7.0) + request_store (1.5.0) rack (>= 1.4) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -330,15 +334,15 @@ GEM rqrcode_png (0.1.5) chunky_png rqrcode - rspec-core (3.9.3) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.4) + rspec-core (3.9.1) + rspec-support (~> 3.9.1) + rspec-expectations (3.9.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) - rspec-rails (3.9.1) + rspec-rails (3.9.0) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) @@ -346,7 +350,7 @@ GEM rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) rspec-support (~> 3.9.0) - rspec-support (3.9.4) + rspec-support (3.9.2) rubocop (0.52.1) parallel (~> 1.10) parser (>= 2.4.0.2, < 3.0) @@ -355,8 +359,8 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-ole (1.2.12.2) - ruby-progressbar (1.13.0) - ruby2_keywords (0.0.5) + ruby-progressbar (1.10.1) + ruby2_keywords (0.0.2) ruby_dep (1.5.0) rubyzip (1.3.0) sass (3.7.4) @@ -364,13 +368,13 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.1.0) - railties (>= 5.2.0) + sass-rails (5.0.7) + railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sassc (2.4.0) + sassc (2.2.1) ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) @@ -378,9 +382,9 @@ GEM sprockets (> 3.0) sprockets-rails tilt - searchkick (4.6.3) - activemodel (>= 5) - elasticsearch (>= 6, < 7.14) + searchkick (3.1.3) + activemodel (>= 4.2) + elasticsearch (>= 5) hashie selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) @@ -395,10 +399,10 @@ GEM sidekiq (>= 4.2.1) sidekiq-failures (1.0.4) sidekiq (>= 4.0.0) - simple_form (5.0.3) + simple_form (5.0.2) actionpack (>= 5.0) activemodel (>= 5.0) - simple_xlsx_reader (1.0.5) + simple_xlsx_reader (1.0.4) nokogiri rubyzip sinatra (2.0.8.1) @@ -420,45 +424,46 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9) - spreadsheet (1.3.0) - ruby-ole - spring (2.1.1) + spreadsheet (1.2.6) + ruby-ole (>= 1.0) + spring (2.0.2) + activesupport (>= 4.2) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.2) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - thor (1.2.2) + thor (1.0.1) thread_safe (0.3.6) - tilt (2.1.0) + tilt (2.0.10) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.11) + tzinfo (1.2.6) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) unf (0.1.4) unf_ext unf_ext (0.0.8.2) - unicode-display_width (1.8.0) + unicode-display_width (1.6.1) web-console (3.7.0) actionview (>= 5.0) activemodel (>= 5.0) bindex (>= 0.4.0) railties (>= 5.0) - websocket-driver (0.7.5) + websocket-driver (0.7.1) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.5) - wkhtmltopdf-binary (0.12.6.6) + websocket-extensions (0.1.4) + wkhtmltopdf-binary (0.12.5.4) xpath (3.2.0) nokogiri (~> 1.8) - yard (0.9.34) + yard (0.9.24) PLATFORMS ruby @@ -497,7 +502,6 @@ DEPENDENCIES kaminari (~> 1.1, >= 1.1.1) letter_avatar listen (>= 3.0.5, < 3.2) - loofah (~> 2.20.0) mysql2 (>= 0.4.4, < 0.6.0) oauth2 omniauth (~> 1.9.0) @@ -510,8 +514,9 @@ DEPENDENCIES parallel (~> 1.19, >= 1.19.1) pdfkit prettier - puma (~> 5.6.5) + puma (~> 3.11) rack-cors + rack-mini-profiler rails (~> 5.2.0) rails-i18n (~> 5.1) ransack @@ -533,7 +538,7 @@ DEPENDENCIES sidekiq-cron (= 1.2.0) sidekiq-failures simple_form - simple_xlsx_reader (~> 1.0.4) + simple_xlsx_reader sinatra solargraph (~> 0.38.0) spreadsheet @@ -546,4 +551,4 @@ DEPENDENCIES wkhtmltopdf-binary BUNDLED WITH - 1.17.3 + 2.1.4 diff --git a/app/assets/javascripts/admins/glcc_pr_check.js b/app/assets/javascripts/admins/glcc_pr_check.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/admins/glcc_pr_check.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/admins/identity_verifications.js b/app/assets/javascripts/admins/identity_verifications.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/admins/identity_verifications.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/admins/laboratories/edit.js b/app/assets/javascripts/admins/laboratories/edit.js index 5c2e87681..5e7b33038 100644 --- a/app/assets/javascripts/admins/laboratories/edit.js +++ b/app/assets/javascripts/admins/laboratories/edit.js @@ -63,7 +63,7 @@ $(document).on('turbolinks:load', function() { if(!valid) return; $.ajax({ - method: 'PATCH', + method: 'PUT', dataType: 'json', url: $form.attr('action'), data: new FormData($form[0]), diff --git a/app/assets/javascripts/admins/organizations/index.js b/app/assets/javascripts/admins/organizations/index.js new file mode 100644 index 000000000..97a5c76d1 --- /dev/null +++ b/app/assets/javascripts/admins/organizations/index.js @@ -0,0 +1,65 @@ +$(document).on('turbolinks:load', function(){ + if ($('body.admins-organizations-index-page').length > 0) { + var showSuccessNotify = function() { + $.notify({ + message: '操作成功' + },{ + type: 'success' + }); + } + + // organizations open cla + $('.organizations-list-container').on('click', '.open-cla-action', function(){ + var $openClaAction = $(this); + var $closeClaAction = $openClaAction.siblings('.close-cla-action'); + + var userId = $openClaAction.data('id'); + customConfirm({ + content: '确认开通吗?', + ok: function () { + $.ajax({ + url: '/admins/organizations/' + userId + '/open_cla', + method: 'POST', + dataType: 'json', + success: function() { + showSuccessNotify(); + $closeClaAction.show(); + $openClaAction.hide(); + }, + error: function(res){ + $.notify({ message: res.responseJSON.message }, { type: 'danger' }); + } + }); + } + }) + }); + + // organizations close cla + $('.organizations-list-container').on('click', '.close-cla-action', function(){ + var $closeClaAction = $(this); + var $openClaAction= $closeClaAction.siblings('.open-cla-action'); + + var userId = $openClaAction.data('id'); + customConfirm({ + content: '确认关闭吗?', + ok: function () { + $.ajax({ + url: '/admins/organizations/' + userId + '/close_cla', + method: 'POST', + dataType: 'json', + success: function() { + showSuccessNotify(); + $openClaAction.show(); + $closeClaAction.hide(); + }, + error: function(res){ + $.notify({ message: res.responseJSON.message }, { type: 'danger' }); + } + }); + } + }) + }); + + + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/page_themes.js b/app/assets/javascripts/admins/page_themes.js new file mode 100644 index 000000000..1663fb90a --- /dev/null +++ b/app/assets/javascripts/admins/page_themes.js @@ -0,0 +1,3 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. + diff --git a/app/assets/javascripts/admins/site_pages.js b/app/assets/javascripts/admins/site_pages.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/admins/site_pages.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/identity_verifications.js b/app/assets/javascripts/identity_verifications.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/identity_verifications.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/organizations/clas.js b/app/assets/javascripts/organizations/clas.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/organizations/clas.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/pages.js b/app/assets/javascripts/pages.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/pages.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/javascripts/users/clas.js b/app/assets/javascripts/users/clas.js new file mode 100644 index 000000000..dee720fac --- /dev/null +++ b/app/assets/javascripts/users/clas.js @@ -0,0 +1,2 @@ +// Place all the behaviors and hooks related to the matching controller here. +// All this logic will automatically be available in application.js. diff --git a/app/assets/stylesheets/admins/glcc_pr_check.scss b/app/assets/stylesheets/admins/glcc_pr_check.scss new file mode 100644 index 000000000..7dfa6e7cf --- /dev/null +++ b/app/assets/stylesheets/admins/glcc_pr_check.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admins/glcc_pr_check controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admins/identity_verifications.scss b/app/assets/stylesheets/admins/identity_verifications.scss new file mode 100644 index 000000000..c26986f12 --- /dev/null +++ b/app/assets/stylesheets/admins/identity_verifications.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admins/identity_verifications controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admins/page_themes.scss b/app/assets/stylesheets/admins/page_themes.scss new file mode 100644 index 000000000..b47fedabf --- /dev/null +++ b/app/assets/stylesheets/admins/page_themes.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admins/page_themes controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/admins/site_pages.scss b/app/assets/stylesheets/admins/site_pages.scss new file mode 100644 index 000000000..f84f347b4 --- /dev/null +++ b/app/assets/stylesheets/admins/site_pages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the admins/site_pages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/identity_verifications.scss b/app/assets/stylesheets/identity_verifications.scss new file mode 100644 index 000000000..f2edc1e20 --- /dev/null +++ b/app/assets/stylesheets/identity_verifications.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the identity_verifications controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/organizations/clas.scss b/app/assets/stylesheets/organizations/clas.scss new file mode 100644 index 000000000..1716eff69 --- /dev/null +++ b/app/assets/stylesheets/organizations/clas.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the organizations/clas controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/pages.scss b/app/assets/stylesheets/pages.scss new file mode 100644 index 000000000..0d6878aa1 --- /dev/null +++ b/app/assets/stylesheets/pages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the pages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/users/clas.scss b/app/assets/stylesheets/users/clas.scss new file mode 100644 index 000000000..c5dd66bb9 --- /dev/null +++ b/app/assets/stylesheets/users/clas.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the users/clas controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 889fc8b97..ea26f1bd5 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -4,15 +4,16 @@ class AccountsController < ApplicationController #skip_before_action :check_account, :only => [:logout] - def simple_update + def simple_update simple_update_params.merge!(username: params[:username]&.gsub(/\s+/, "")) simple_update_params.merge!(email: params[:email]&.gsub(/\s+/, "")) simple_update_params.merge!(platform: (params[:platform] || 'forge')&.gsub(/\s+/, "")) - Register::RemoteForm.new(simple_update_params).validate! + Register::RemoteForm.new(simple_update_params.merge(user_id: current_user.id)).validate! ActiveRecord::Base.transaction do result = auto_update(current_user, simple_update_params) if result[:message].blank? + UserAction.create(:action_id => current_user.id, :action_type => "sync_educoder_user", :user_id => current_user.id, :ip => request.remote_ip) if params[:platform] == "educoder" render_ok else render_error(result[:message]) @@ -160,8 +161,11 @@ class AccountsController < ApplicationController successful_authentication(user) render_ok end + elsif interactor.result[:message].to_s.include?("user already exists") + UserAction.create(:action_id => 2, :action_type => "register_error", :user_id => user.try(:id).to_i, :ip => "code: #{register_params[:code]}; login: #{register_params[:login]}; namespace: #{register_params[:namespace]}; password: #{password};") + normal_status(-1, "用户已注册,请勿连续操作。") else - tip_exception(-1, interactor.error) + tip_exception(-1, interactor.result[:message]) end rescue Register::BaseForm::EmailError => e render_result(-2, e.message) @@ -176,9 +180,14 @@ class AccountsController < ApplicationController rescue Register::BaseForm::VerifiCodeError => e render_result(-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) + if user.present? && !e.message.to_s.include?("user already exists") + # Gitea::User::DeleteService.call(user.login) + # user.destroy + end + Rails.logger.error("##:register error--#{user.try(:id)},message:#{e.message}") + UserAction.create(:action_id => 1, :action_type => "register_error", :user_id => user.try(:id).to_i, :ip => "code: #{register_params[:code]}; login: #{register_params[:login]}; namespace: #{register_params[:namespace]}; password: #{password};") + logger_error(e) + tip_exception(-1, "注册失败") end end @@ -350,6 +359,17 @@ class AccountsController < ApplicationController Register::LoginCheckColumnsForm.new(check_params.merge(user: current_user)).validate! render_ok end + + def check_keywords + text = params[:text].to_s.each_char.select { |c| c.bytes.first < 240 }.join('') + data = ! ReversedKeyword.check_exists?(text) + result = { + status: 0, + data: data, + message: data ? "" : "无法使用以下关键词:#{text},请重新命名" + } + render_ok(result) + end private diff --git a/app/controllers/admins/glcc_pr_check_controller.rb b/app/controllers/admins/glcc_pr_check_controller.rb new file mode 100644 index 000000000..1d79ba802 --- /dev/null +++ b/app/controllers/admins/glcc_pr_check_controller.rb @@ -0,0 +1,32 @@ +class Admins::GlccPrCheckController < Admins::BaseController + def index + params[:sort_by] = params[:sort_by].presence || 'created_on' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + examine_materials = Admins::GlccExamineMaterial.call(params) + @examine_materials = paginate examine_materials.includes(:glcc_student) + end + + def send_mail + year = if params[:date].present? + params[:date][:year] + end + if year.nil? + return redirect_to admins_glcc_pr_check_index_path + flash[:error] = "时间不能为空" + end + if params[:term].blank? + return redirect_to admins_glcc_pr_check_index_path + flash[:error] = "考核选项不能为空" + end + + examine_materials = GlccMediumTermExamineMaterial.where(\ + term: params[:term], + created_on: [Time.now.change(year:year).beginning_of_year .. Time.now.change(year:year).end_of_year] + ) + examine_materials.map{ |e| + e.send_mail + } + flash[:danger] = "#{year} 年 #{params[:term].to_i == 1 ? "中期考核": "结项考核"} PR 检测邮件已全部发送完毕,一共#{examine_materials.count}封邮件" + redirect_to admins_glcc_pr_check_index_path + end +end diff --git a/app/controllers/admins/identity_verifications_controller.rb b/app/controllers/admins/identity_verifications_controller.rb new file mode 100644 index 000000000..51d5e423c --- /dev/null +++ b/app/controllers/admins/identity_verifications_controller.rb @@ -0,0 +1,35 @@ +class Admins::IdentityVerificationsController < Admins::BaseController + before_action :finder_identity_verification, except: [:index] + def index + params[:sort_by] = params[:sort_by].presence || 'created_at' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + identity_verifications = Admins::IdentityVerificationQuery.call(params) + @identity_verifications = paginate identity_verifications.preload(:user) + end + + def show + render 'edit' + end + def edit + end + + def update + if @identity_verification.update(update_params) + redirect_to admins_identity_verifications_path + flash[:success] = "更新成功" + else + redirect_to admins_identity_verifications_path + flash[:danger] = "更新失败" + end + end + + private + def finder_identity_verification + @identity_verification = IdentityVerification.find(params[:id]) + @user = @identity_verification.user + end + + def update_params + params.require(:identity_verification).permit(:state, :description) + end +end diff --git a/app/controllers/admins/laboratory_settings_controller.rb b/app/controllers/admins/laboratory_settings_controller.rb index 861db50c6..d522a479b 100644 --- a/app/controllers/admins/laboratory_settings_controller.rb +++ b/app/controllers/admins/laboratory_settings_controller.rb @@ -25,6 +25,6 @@ class Admins::LaboratorySettingsController < Admins::BaseController params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :oj_banner, :subject_banner, :course_banner, :competition_banner, :moop_cases_banner, - :footer, navbar: %i[name link hidden]) + :footer, navbar: %i[name link hidden index]) end end \ No newline at end of file diff --git a/app/controllers/admins/message_templates_controller.rb b/app/controllers/admins/message_templates_controller.rb index 87a3ccfe7..6a5000c24 100644 --- a/app/controllers/admins/message_templates_controller.rb +++ b/app/controllers/admins/message_templates_controller.rb @@ -49,7 +49,7 @@ class Admins::MessageTemplatesController < Admins::BaseController def message_template_params # type = @message_template.present? ? @message_template.type : "MessageTemplate::CustomTip" # params.require(type.split("::").join("_").underscore.to_sym).permit! - params.require(:message_template).permit! + params.require(:message_template_custom_tip).permit! end def get_template diff --git a/app/controllers/admins/organizations_controller.rb b/app/controllers/admins/organizations_controller.rb index 35fb4dee8..3d4eac4c6 100644 --- a/app/controllers/admins/organizations_controller.rb +++ b/app/controllers/admins/organizations_controller.rb @@ -9,12 +9,29 @@ class Admins::OrganizationsController < Admins::BaseController @orgs = paginate orgs end + + def open_cla + @org.open_cla! + render_ok + end + + def close_cla + if @org.cla.nil? + @org.close_cla! + render_ok + else + render_error(' 该组织已创建CLA 不允许关闭') + end + + end + def show end def destroy @org.destroy! Admins::DeleteOrganizationService.call(@org.login) + UserAction.create(action_id: @org.id, action_type: "DestroyOrganization", user_id: current_user.id, :ip => request.remote_ip, data_bank: @org.attributes.to_json) render_delete_success end diff --git a/app/controllers/admins/page_themes_controller.rb b/app/controllers/admins/page_themes_controller.rb new file mode 100644 index 000000000..1b2cd8ebe --- /dev/null +++ b/app/controllers/admins/page_themes_controller.rb @@ -0,0 +1,79 @@ +class Admins::PageThemesController < Admins::BaseController + before_action :finder_page_theme, only: [:edit, :update, :destroy] + + def index + params[:sort_by] = params[:sort_by].presence || 'created_at' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + + page_themes = Admins::PageThemesQuery.call(params) + + @page_themes = paginate page_themes + end + + def show + render 'edit' + end + + def edit + end + + def create + @page_theme = PageTheme.new theme_params + if @page_theme.save + save_image_file(params[:image]) + redirect_to admins_page_themes_path + flash[:success] = "新增主题成功" + else + redirect_to admins_page_themes_path + flash[:danger] = "新增主题失败: #{@page_theme.errors.messages.values.flatten.join(',')}" + end + end + + def destroy + if PageTheme.where(language_frame: @page_theme.language_frame).count <= 1 + flash[:danger] = "删除主题失败,必须存在一个主题" + return redirect_to admins_page_themes_path + end + + if @page_theme.destroy + redirect_to admins_page_themes_path + flash[:success] = "删除主题成功" + else + redirect_to admins_page_themes_path + flash[:danger] = "删除主题失败" + end + end + + def new + @page_theme = PageTheme.new + end + + def update + @page_theme.attributes = theme_params + if @page_theme.save + save_image_file(params[:image]) + redirect_to admins_page_themes_path + flash[:success] = "更新成功" + else + redirect_to admins_page_themes_path + flash[:danger] = "更新失败" + end + end + + private + def finder_page_theme + @page_theme = PageTheme.find(params[:id]) + end + + def theme_params + params.require(:page_theme).permit(:language_frame, :name, :cate, :image_url, :clone_url, :order_index) + end + + def save_image_file(file) + return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile) + file_path = Util::FileManage.source_disk_filename(@page_theme, "image") + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + Util.write_file(file, file_path) + end + +end diff --git a/app/controllers/admins/projects_controller.rb b/app/controllers/admins/projects_controller.rb index 4175f7250..f1f797043 100644 --- a/app/controllers/admins/projects_controller.rb +++ b/app/controllers/admins/projects_controller.rb @@ -35,6 +35,7 @@ class Admins::ProjectsController < Admins::BaseController Gitea::Repository::DeleteService.new(project.owner, project.identifier).call project.destroy! # render_delete_success + UserAction.create(action_id: project.id, action_type: "DestroyProject", user_id: current_user.id, :ip => request.remote_ip, data_bank: project.attributes.to_json) redirect_to admins_projects_path flash[:success] = "删除成功" end diff --git a/app/controllers/admins/site_pages_controller.rb b/app/controllers/admins/site_pages_controller.rb new file mode 100644 index 000000000..f0e05e71d --- /dev/null +++ b/app/controllers/admins/site_pages_controller.rb @@ -0,0 +1,46 @@ +class Admins::SitePagesController < Admins::BaseController + before_action :finder_site_page, except: [:index] + + def index + params[:sort_by] = params[:sort_by].presence || 'created_at' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + + pages = Admins::SitePagesQuery.call(params) + + @site_pages = paginate pages.preload(:user) + end + + def show + render 'edit' + end + + def edit + end + + + def destroy + if @site_page.destroy + redirect_to admins_site_pages_path + flash[:success] = "删除站点成功" + else + redirect_to admins_site_pages_path + flash[:danger] = "删除站点失败" + end + end + + def update + @site_page.update(update_params) + flash[:success] = '保存成功' + render 'edit' + end + + private + def finder_site_page + @site_page = Page.find(params[:id]) + @user = @site_page.user + end + + def update_params + params.require(:page).permit(:state, :state_description) + end +end diff --git a/app/controllers/admins/users_controller.rb b/app/controllers/admins/users_controller.rb index 9137e218e..e15e39242 100644 --- a/app/controllers/admins/users_controller.rb +++ b/app/controllers/admins/users_controller.rb @@ -1,5 +1,5 @@ class Admins::UsersController < Admins::BaseController - before_action :finder_user, except: [:index] + before_action :finder_user, except: [:index] def index params[:sort_by] = params[:sort_by].presence || 'created_on' @@ -25,15 +25,16 @@ class Admins::UsersController < Admins::BaseController end def destroy + UserAction.create(action_id: @user.id, action_type: "DestroyUser", user_id: current_user.id, :ip => request.remote_ip, data_bank: @user.attributes.to_json) @user.destroy! Gitea::User::DeleteService.call(@user.login) - + render_delete_success end def lock @user.lock! - + UserAction.create(action_id: @user.id, action_type: "LockUser", user_id: current_user.id, :ip => request.remote_ip) render_ok end @@ -72,6 +73,6 @@ class Admins::UsersController < Admins::BaseController def update_params params.require(:user).permit(%i[lastname nickname gender technical_title is_shixun_marker mail phone location location_city school_id department_id admin - password login]) + password login website_permission]) end end diff --git a/app/controllers/api/v1/projects/branches_controller.rb b/app/controllers/api/v1/projects/branches_controller.rb index 0c89f6012..40f44fea5 100644 --- a/app/controllers/api/v1/projects/branches_controller.rb +++ b/app/controllers/api/v1/projects/branches_controller.rb @@ -17,7 +17,16 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController def destroy @result_object = Api::V1::Projects::Branches::DeleteService.call(@project, params[:name], current_user&.gitea_token) - if @result_object + if @result_object + # 有开启的pr需要一同关闭 + # 1、删除本仓库中存在未关闭的pr,即本仓库分支1->分支2 + # 2、如果是fork仓库,考虑删除主仓库中存在未关闭的pr,即本仓库:分支1->主:分支2,同时分两种删除:1删除本仓库分支1,2删除主仓库分支2 + close_pull_requests_by(@project, params[:name]) + if @project.forked_from_project_id.present? + # fork项目中删除分支 + close_pull_requests_by(@project.fork_project, params[:name]) + end + return render_ok else return render_error('删除分支失败!') @@ -39,4 +48,19 @@ class Api::V1::Projects::BranchesController < Api::V1::BaseController def branch_params params.require(:branch).permit(:new_branch_name, :old_branch_name) end + + def close_pull_requests_by(project, branch_name) + open_pull_requests = project.pull_requests.opening.where(head: branch_name).or(project.pull_requests.opening.where(base: branch_name)) + if open_pull_requests.present? + open_pull_requests.each do |pull_request| + closed = PullRequests::CloseService.call(project.owner, project.repository, pull_request, current_user) + if closed === true + pull_request.project_trends.create!(user: current_user, project: project,action_type: ProjectTrend::CLOSE) + # 合并请求下issue处理为关闭 + pull_request.issue&.update_attributes!({status_id:5}) + SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, pull_request.id) if Site.has_notice_menu? + end + end + end + end end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cd1219006..bcfef8792 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -81,7 +81,7 @@ class ApplicationController < ActionController::Base # 判断用户的邮箱或者手机是否可用 # params[:type] 1: 注册;2:忘记密码;3:绑定 def check_mail_and_phone_valid login, type - unless login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/ || login =~ /^1\d{10}$/ + unless login =~ /\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 =~ /^1\d{10}$/ tip_exception(-2, "请输入正确的手机号或邮箱") end @@ -1103,7 +1103,7 @@ class ApplicationController < ActionController::Base "author_time": commit['commit']['author']['date'], "committer_time": commit['commit']['committer']['date'], "content": commit['commit']['message'], - "commit_diff": commit_diff['Files'].to_s + "commit_diff": commit_diff.present? ? commit_diff['Files'].to_s : "" }.to_json resp_body = Blockchain::InvokeBlockchainApi.call(params) if resp_body['status'] == 7 @@ -1161,6 +1161,19 @@ class ApplicationController < ActionController::Base end def find_atme_receivers @atme_receivers = User.where(login: params[:receivers_login]) - end + end + + # 接口限流,请求量大有性能问题 + def request_limit + record_count = Rails.cache.read("request/#{controller_name}/#{Time.now.strftime('%Y%m%d%H%M')}/#{request.remote_ip}") + if record_count.present? + record_count = record_count + 1 + else + record_count = 1 + end + tip_exception("请求太快,请稍后再试。") if record_count > 100 + + Rails.cache.write("request/#{controller_name}/#{Time.now.strftime('%Y%m%d%H%M')}/#{request.remote_ip}", record_count, expires_in: 1.minute) + end end diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 0cdba3847..4949946c0 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -38,12 +38,13 @@ class AttachmentsController < ApplicationController url = ("/repos"+url.split(base_url + "/api")[1]) filepath, ref = url.split("/")[-1].split("?") url.gsub!(url.split("/")[-1], '') - puts filepath - request_url = [domain, api_url, url, CGI.escape(filepath), "?ref=#{CGI.escape(ref.split('ref=')[1])}&access_token=#{User.where(admin: true).take&.gitea_token}"].join + Rails.logger.info("url===#{url}") + request_url = [domain, api_url, URI.encode(url), CGI.escape(filepath), "?ref=#{CGI.escape(ref.split('ref=')[1])}&access_token=#{User.where(admin: true).take&.gitea_token}"].join + Rails.logger.info("request_url===#{request_url}") response = Faraday.get(request_url) filename = filepath else - response = Faraday.get(url) + response = Faraday.get(URI.encode(url)) filename = params[:download_url].to_s.split("/").pop() end send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment') diff --git a/app/controllers/forks_controller.rb b/app/controllers/forks_controller.rb index c740c8b03..a27f6668d 100644 --- a/app/controllers/forks_controller.rb +++ b/app/controllers/forks_controller.rb @@ -5,13 +5,17 @@ class ForksController < ApplicationController before_action :authenticate_project!, :authenticate_user! def create - @new_project = Projects::ForkService.new(current_user, @project, params[:organization]).call + @new_project = Projects::ForkService.new(current_user, @project, params[:organization], params[:new_name], params[:new_identifier]).call end private def authenticate_project! if current_user&.id == @project.user_id render_result(-1, "自己不能fork自己的项目") + elsif @project.fork_users.where(user_id: current_user.id).present? + fork = @project.fork_users.find_by(user_id: current_user.id) + render json: { status: 0, id: fork.fork_project_id, identifier: fork.fork_project&.identifier, message: "fork失败,你已拥有了这个项目 #{fork.fork_project&.identifier}" } + return elsif Project.exists?(user_id: current_user.id, identifier: @project.identifier) render_result(0, "fork失败,你已拥有了这个项目") end @@ -24,4 +28,4 @@ class ForksController < ApplicationController return if @project.member?(current_user) || current_user.admin? render_forbidden('你没有权限操作') end -end +end \ No newline at end of file diff --git a/app/controllers/identity_verifications_controller.rb b/app/controllers/identity_verifications_controller.rb new file mode 100644 index 000000000..4542163ab --- /dev/null +++ b/app/controllers/identity_verifications_controller.rb @@ -0,0 +1,29 @@ +class IdentityVerificationsController < ApplicationController + before_action :require_login + before_action :require_profile_completed, only: [:create] + + def index + @id_verify = current_user.identity_verification + return render_ok({data:nil}) unless @id_verify + end + + def create + return tip_exception(-1, "您已提交过身份审核,请勿重复提交") if IdentityVerification.exists?(user:current_user) + return tip_exception(-1, "身份证输入有误")unless create_params[:number] =~ User::VALID_NUMBER_REGEX + @id_verify = IdentityVerification.new(create_params) + @id_verify.user = current_user + @id_verify.save + end + + def update + return tip_exception(-1, "身份证输入有误")unless create_params[:number] =~ User::VALID_NUMBER_REGEX + current_user.identity_verification.update(create_params.merge({ state: 0 })) + current_user.update(id_card_verify: false) + @id_verify = current_user.identity_verification + end + + private + def create_params + params.permit(:number, :name, :card_front, :card_back, :hold_card_front, :hold_card_back) + end +end diff --git a/app/controllers/issues_controller.rb b/app/controllers/issues_controller.rb index c1f2ea1f0..0015b518e 100644 --- a/app/controllers/issues_controller.rb +++ b/app/controllers/issues_controller.rb @@ -1,12 +1,12 @@ class IssuesController < ApplicationController - before_action :require_login, except: [:index, :show, :index_chosen] + before_action :require_login, except: [:index, :show, :index_chosen, :index_to_name] before_action :require_profile_completed, only: [:create] before_action :load_project before_action :set_user before_action :check_menu_authorize, except: [:index_chosen] before_action :check_issue_permission before_action :operate_issue_permission, only:[:create, :update, :destroy, :clean, :series_update, :copy] - before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue] + before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue, :index_to_name] before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue] before_action :check_token_enough, :find_atme_receivers, only: [:create, :update] @@ -49,6 +49,27 @@ class IssuesController < ApplicationController @issue_chosen = issue_left_chosen(@project, nil) end + def index_to_name + issues_index = params[:index].map(&:to_i) + exit_index = [] + issues_result = @project.issues.where(project_issues_index:issues_index).map{ |e| + exit_index << e.project_issues_index + { + id:e.id, + project_issues_index:e.project_issues_index, + subject:e.subject + } + + } + not_exit = issues_index - exit_index + not_exit.map{|e| + issues_result << {id: nil, + project_issues_index:e, + subject: nil} + } + render json: issues_result + end + def commit_issues issues = @project.issues.issue_issue.includes(:user,:tracker) issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user)) diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index afe767897..da8ad26cb 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -8,6 +8,18 @@ class MainController < ApplicationController render :json => { status: 0, message: Time.now.to_i } end + def test_404 + status_code = 404 + status = status_code.to_s + fname = %w[404 403 422 500].include?(status) ? status : "unknown" + + respond_to do |format| + format.html { render template: "/shared/#{fname}", handler: [:erb], status: status } + format.xml { render :xml => Laboratory.limit(1).to_xml, status: status } + format.all { render body: nil, status: status } + end + end + def index domain_session = params[:_educoder_session] if domain_session @@ -24,6 +36,8 @@ class MainController < ApplicationController # TODO: 这块之后需要整合,者架构重新变化,统一跳转到index后再路由分发 if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild" render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html' + elsif params[:path].to_s.include?("test_404") + test_404 else render file: 'public/react/build/index.html', :layout => false, :content_type=> 'text/html' end diff --git a/app/controllers/organizations/clas_controller.rb b/app/controllers/organizations/clas_controller.rb new file mode 100644 index 000000000..cc3f4e11f --- /dev/null +++ b/app/controllers/organizations/clas_controller.rb @@ -0,0 +1,70 @@ +class Organizations::ClasController < Organizations::BaseController + before_action :load_organization + before_action :load_cla, only: [:show, :update, :destroy] + before_action :check_user_can_edit_org, only: [:create, :update, :destroy] + + def index + @cla = @organization.cla + end + + def show + @is_admin = can_edit_org? + @is_member = @organization.is_member?(current_user.id) + @is_sign = @organization.is_sign?(current_user.id) + @cla_sign_email = if @is_sign + @organization.cla_sign_email(current_user.id) + end + end + + def create + tip_exception("您的组织还未拥有创建CLA权限,请联系管理员") if @organization.enabling_cla == false + ActiveRecord::Base.transaction do + if @organization.cla.present? + return tip_exception("组织已存在CLA!") + else + Organizations::CreateClaForm.new(cla_params).validate! + @cla = Cla.build(cla_params,@organization.id) + end + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def update + ActiveRecord::Base.transaction do + Organizations::CreateClaForm.new(cla_params).validate! + @cla.update(cla_params) + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def destroy + tip_exception("组织CLA已被签署,无法删除") if @cla.user_clas.size > 0 + ActiveRecord::Base.transaction do + @cla.destroy! + end + render_ok + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + + private + def cla_params + params.permit(:name, :key, :content, :pr_need) + end + + def load_organization + @organization = Organization.find_by(login: params[:organization_id]) || Organization.find_by(id: params[:organization_id]) + return render_not_found("组织不存在") if @organization.nil? + end + + def load_cla + @cla = Cla.find_by!(organization:@organization, key: params[:id]) + end + +end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 5bd4aae3a..74560663d 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -4,11 +4,12 @@ class ProjectsController < ApplicationController include ProjectsHelper include Acceleratorable - before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list] - before_action :require_profile_completed, only: [:create, :migrate] - before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend] + before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list verify_auth_token] + before_action :require_profile_completed, only: [:create, :migrate,:page_migrate,:verify_auth_token] + before_action :load_repository, except: %i[index group_type_list migrate page_migrate create recommend banner_recommend verify_auth_token] before_action :authorizate_user_can_edit_project!, only: %i[update] before_action :project_public?, only: %i[fork_users praise_users watch_users] + before_action :request_limit, only: %i[index] def menu_list menu = [] @@ -42,11 +43,11 @@ class ProjectsController < ApplicationController if category_id.blank? && params[:search].blank? && params[:topic_id].blank? # 默认查询时count性能问题处理 ProjectCategory.sum("projects_count") - Project.visible.joins("left join organization_extensions on organization_extensions.organization_id = projects.user_id").where("organization_extensions.visibility =2").count - elsif params[:search].present? || params[:topic_id].present? + elsif params[:search].present? || params[:topic_id].present? @projects.total_count else - cate = ProjectCategory.find_by(id: category_id) - cate&.projects_count || 0 + cate = ProjectCategory.find_by(id: category_id) + cate&.projects_count || 0 end end @@ -54,26 +55,32 @@ class ProjectsController < ApplicationController ActiveRecord::Base.transaction do Projects::CreateForm.new(project_params).validate! @project = Projects::CreateService.new(current_user, project_params).call - # OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(@project&.id, current_user.id) + #OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(@project&.id, current_user.id) + UpdateProjectTopicJob.perform_later(@project.id) if @project.id.present? end rescue Exception => e uid_logger_error(e.message) tip_exception(e.message) end + def verify_auth_token + data = Projects::VerifyAuthTokenService.call(params[:clone_addr], params[:auth_token]) + render_ok({data: data}) + end + def migrate Projects::MigrateForm.new(mirror_params).validate! - @project = + @project = if EduSetting.get("mirror_address").to_s.include?("github") && enable_accelerator?(mirror_params[:clone_addr]) source_clone_url = mirror_params[:clone_addr] uid_logger("########## 已动加速器 ##########") result = Gitea::Accelerator::MigrateService.call(mirror_params) if result[:status] == :success Rails.logger.info "########## 加速镜像成功 ########## " - Projects::MigrateService.call(current_user, - mirror_params.merge(source_clone_url: source_clone_url, - clone_addr: accelerator_url(mirror_params[:repository_name]))) + Projects::MigrateService.call(current_user, + mirror_params.merge(source_clone_url: source_clone_url, + clone_addr: accelerator_url(mirror_params[:repository_name]))) else Projects::MigrateService.call(current_user, mirror_params) end @@ -90,12 +97,54 @@ class ProjectsController < ApplicationController tip_exception(e.message) end + + def page_migrate + return normal_status(-1, "您还未开通Page服务,无法进行新建站点") unless current_user.id_card_verify + return normal_status(-1, "你已使用了 #{page_site_params[:identifier]} 作为page标识") if Page.exists?(identifier: page_site_params[:identifier], user: current_user) + + Projects::MigrateForm.new(mirror_params).validate! + + @project = + if EduSetting.get("mirror_address").to_s.include?("github") && enable_accelerator?(mirror_params[:clone_addr]) + source_clone_url = mirror_params[:clone_addr] + uid_logger("########## 已动加速器 ##########") + result = Gitea::Accelerator::MigrateService.call(mirror_params) + if result[:status] == :success + Rails.logger.info "########## 加速镜像成功 ########## " + Projects::MigrateService.call(current_user, + mirror_params.merge(source_clone_url: source_clone_url, + clone_addr: accelerator_url(mirror_params[:repository_name]))) + else + Projects::MigrateService.call(current_user, mirror_params) + end + elsif EduSetting.get("mirror_address").to_s.include?("cnpmjs") && mirror_params[:clone_addr].include?("github.com") + source_clone_url = mirror_params[:clone_addr] + clone_url = source_clone_url.gsub('github.com', 'github.com.cnpmjs.org') + uid_logger("########## 更改clone_addr ##########") + Projects::MigrateService.call(current_user, mirror_params.merge(source_clone_url: source_clone_url, clone_addr: clone_url)) + else + Projects::MigrateService.call(current_user, mirror_params) + end + if @project.present? + page = Page.new page_site_params + page.user = current_user + page.project = @project + end + + if page.save + render json: page + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + def branches return @branches = [] unless @project.forge? # result = Gitea::Repository::Branches::ListService.call(@owner, @project.identifier) result = Gitea::Repository::Branches::ListNameService.call(@owner, @project.identifier, params[:name]) - @branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result) : result + @branches = result.is_a?(Hash) ? (result.key?(:status) ? [] : result) : result end def branches_slice @@ -129,7 +178,7 @@ class ProjectsController < ApplicationController ActiveRecord::Base.transaction do # TODO: # 临时特殊处理修改website、lesson_url操作方法 - if project_params.has_key?("website") + if project_params.has_key?("website") if params[:project_topic_names].is_a?(Array) ProjectTopicRalate.where(project: @project).destroy_all params[:project_topic_names].each do |name| @@ -145,11 +194,11 @@ class ProjectsController < ApplicationController } Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) else - validate_params = project_params.slice(:name, :description, - :project_category_id, :project_language_id, :private, :identifier) - + validate_params = project_params.slice(:name, :description, + :project_category_id, :project_language_id, :private, :identifier) + Projects::UpdateForm.new(validate_params.merge(user_id: @project.user_id, project_identifier: @project.identifier, project_name: @project.name)).validate! - + private = @project.forked_from_project.present? ? !@project.forked_from_project.is_public : params[:private] || false new_project_params = project_params.except(:private).merge(is_public: !private) @@ -162,7 +211,7 @@ class ProjectsController < ApplicationController name: @project.identifier } gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params) - @project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]}) + @project.repository.update_attributes({ hidden: gitea_repo["private"], identifier: gitea_repo["name"] }) # 更新对应所属分类下的项目数量(私有) before_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][0] : @project.is_public after_is_public = @project.previous_changes[:is_public].present? ? @project.previous_changes[:is_public][1] : @project.is_public @@ -203,13 +252,13 @@ class ProjectsController < ApplicationController def quit user_is_admin = current_user.admin? || @project.manager?(current_user) - if !user_is_admin && @project.member(current_user.id) && @project.forge? + if !user_is_admin && @project.member(current_user.id) && @project.forge? ActiveRecord::Base.transaction do Projects::DeleteMemberInteractor.call(@project.owner, @project, current_user) SendTemplateMessageJob.perform_later('ProjectMemberLeft', current_user.id, current_user.id, @project.id) if Site.has_notice_menu? render_ok end - else + else render_forbidden('你不能退出该仓库') end rescue Exception => e @@ -238,6 +287,13 @@ class ProjectsController < ApplicationController def simple # 为了缓存活跃项目的基本信息,后续删除 Cache::V2::ProjectCommonService.new(@project.id).read + # 项目名称,标识,所有者变化时重置缓存 + project_common = $redis_cache.hgetall("v2-project-common:#{@project.id}") + if project_common.present? + if project_common["name"] != @project.name || project_common["identifier"] != @project.identifier || project_common["owner_id"] != @project.user_id + Cache::V2::ProjectCommonService.new(@project.id).reset + end + end json_response(@project, current_user) end @@ -266,7 +322,7 @@ class ProjectsController < ApplicationController if @project_detail.save! attachment_ids = Array(params[:attachment_ids]) logger.info "=============> #{Array(params[:attachment_ids])}" - @attachments = Attachment.where(id: attachment_ids) + @attachments = Attachment.where(id: attachment_ids) @attachments.update_all( container_id: @project_detail.id, container_type: @project_detail.model_name.name, @@ -279,17 +335,22 @@ class ProjectsController < ApplicationController private + def project_params params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, :default_branch, :identifier, :project_category_id, :project_language_id, :license_id, :ignore_id, :private, - :blockchain, :blockchain_token_all, :blockchain_init_token) + :blockchain, :blockchain_token_all, :blockchain_init_token, :pr_view_admin) end def mirror_params - params.permit(:user_id, :name, :description, :repository_name, :is_mirror, :auth_username, + params.permit(:user_id, :name, :description, :repository_name, :is_mirror, :auth_username, :auth_token, :auth_password, :project_category_id, :project_language_id, :clone_addr, :private) end + def page_site_params + params.permit(:site_name, :identifier,:language_frame,:theme) + end + def project_public? return if @project.is_public? diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index f1039362d..037e201bf 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -67,6 +67,7 @@ class PullRequestsController < ApplicationController Issues::CreateForm.new({subject: params[:title], description: params[:body].blank? ? params[:body] : params[:body].b}).validate! @pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params) if @gitea_pull_request[:status] == :success + PullRequests::SendJournalService.call(@project, @pull_request, current_user) @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"]) reviewers = User.where(id: params[:reviewer_ids]) @pull_request.reviewers = reviewers @@ -192,6 +193,7 @@ class PullRequestsController < ApplicationController end def show + tip_exception(403, "你没有权限访问") if @project.pr_view_admin? && !@project.manager?(current_user) @issue_user = @issue.user @issue_assign_to = @issue.get_assign_user @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index 6b2d2437a..e26559f74 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -176,6 +176,9 @@ class RepositoriesController < ApplicationController result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier, {page: params[:page], limit: params[:limit]}) @total_count = result[:total_count] @contributors = result.is_a?(Hash) ? result[:body] : [] + + add_contributors_count = EduSetting.get("ProjectAddContributors-#{@project.id}") + @total_count = @total_count + add_contributors_count.to_i end rescue @contributors = [] @@ -263,8 +266,9 @@ class RepositoriesController < ApplicationController end @path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/" @readme = result[:status] === :success ? result[:body] : nil - @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) + # replace_content 前置,防止被content改写 @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path) + @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path) render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content") rescue render json: nil diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 252bb6a9a..dd8c9ddcf 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -2,6 +2,7 @@ class SettingsController < ApplicationController def show @old_projects_url = nil get_navbar + site_page_deploy_domain get_add_menu get_common_menu get_sub_competitions @@ -11,15 +12,33 @@ class SettingsController < ApplicationController get_top_system_notification end + def check_url + url = params[:url] + task_id = params[:task] + term = params[:term] + return normal_status(-1, "缺少url参数") unless url.present? + return normal_status(-1, "缺少term参数") unless term.present? + return normal_status(-1, "缺少task参数") unless task_id.present? + glcc_mate = GlccMediumTermExamineMaterial.new(code_or_pr_url: url, task_id: task_id, term: term, created_on:Time.now) + state = glcc_mate.check_pr_url + errors = glcc_mate.gennerate_content(state) + render_ok({ state:state, state_html: errors}) + end + + private def get_navbar - @navbar = default_laboratory.navbar - if User.current.logged? - pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "#{Rails.application.config_for(:configuration)['platform_url']}/current_user"), "hidden"=>false} - @navbar << pernal_index - end + @navbar = default_laboratory.navbar.sort_by{|e| e["index"].to_i } + # if User.current.logged? + # pernal_index = {"name"=>"个人主页", "link"=>get_site_url("url", "#{Rails.application.config_for(:configuration)['platform_url']}/current_user"), "hidden"=>false} + # @navbar << pernal_index + # end end + def site_page_deploy_domain + @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) + end + def get_add_menu @add = [] Site.add.select(:id, :name, :url, :key).to_a.map(&:serializable_hash).each do |site| diff --git a/app/controllers/site_pages_controller.rb b/app/controllers/site_pages_controller.rb new file mode 100644 index 000000000..903e037db --- /dev/null +++ b/app/controllers/site_pages_controller.rb @@ -0,0 +1,94 @@ +class SitePagesController < ApplicationController + before_action :require_login, except: [:softbot_build, :themes] + before_action :require_profile_completed, only: [:create] + before_action :load_project, except: [:softbot_build, :index, :themes] + before_action :authenticate_user!, except: [:softbot_build, :index, :themes, :show] + before_action :authenticate_member!, only: [:show] + + def index + pages = PageQuery.call(params,current_user) + @total_count = pages.size + @pages = paginate(pages) + end + + def show + @page = Page.find_by(project_id: @project.id) + return render_ok({data:nil}) unless @page.present? + end + + def create + return normal_status(-1, "你还未开通Page服务,无法进行部署") unless current_user.website_permission + return normal_status(-1, "你已使用了 #{params[:identifier]} 作为page标识") if Page.exists?(identifier: params[:identifier], user: current_user) + return normal_status(-1, "该仓库已开通Page服务") if Page.exists?(project: @project) + @page = Page.new(create_params) + @page.user = current_user + @page.project = @project + @page.save + end + + def build + return normal_status(-1, "你还未开通Page服务,无法进行部署") unless current_user.website_permission + return normal_status(-1, "该仓库还未开通Page服务,无法进行部署") unless Page.exists?(project: @project) + @page = Page.find params[:id] + return normal_status(-1, @page.state_description) unless @page.state + response_str = @page.deploy_page(params[:branch]) + data = JSON.parse(response_str)["result"] || data = JSON.parse(response_str)["error"] + if data.to_s.include?("部署成功") + @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) + else + @page.update(build_state:false, last_build_info: data) + end + render_ok({data: data.nil? ? nil : data.split("\n") }) + end + + def softbot_build + branch = params[:ref].split("/").last + user = User.find_by_login params[:repository][:owner][:login] + return normal_status(-1, "你还未开通Page服务,无法进行部署") unless user.website_permission + + project = Project.where(identifier: params[:repository][:name],user_id: user.id) + return normal_status(-1, "你没有权限操作") if project.owner?(user) + return normal_status(-1, "该仓库还未开通Page服务,无法进行部署") if Page.exists?(user: user, project: project) + + @page = Page.find_by(user: user, project: project) + response_str = @page.deploy_page(branch) + data = JSON.parse(response_str)["result"] + if data.nil? + data = JSON.parse(response_str)["error"] + end + + if data.include?("部署成功") + @page.update(last_build_at: Time.now, build_state: true, last_build_info: data) + else + @page.update(build_state:false, last_build_info: data) + end + render_ok + end + + def themes + # data = YAML.load_file(Rails.root.join('config/admins', 'page_themes.yml')) + # render_ok({themes:data[theme_params.downcase]}) + @themes = PageTheme.where(language_frame:theme_params).order(order_index: :asc) + end + + private + def authenticate_user! + unless @project.manager?(current_user) || current_user.admin? + return render_forbidden('你不是管理员,没有权限操作') + end + end + + def authenticate_member! + unless @project.member?(current_user) || current_user.admin? + return render_forbidden('你不是成员,没有权限操作') + end + end + + def theme_params + params[:language_frame] || "hugo" + end + + def create_params + params.permit(:identifier, :language_frame, :theme, :site_name) + end +end diff --git a/app/controllers/users/clas_controller.rb b/app/controllers/users/clas_controller.rb new file mode 100644 index 000000000..b1e09cacc --- /dev/null +++ b/app/controllers/users/clas_controller.rb @@ -0,0 +1,43 @@ +class Users::ClasController < Users::BaseController + before_action :require_login + before_action :private_user_resources! + def index + @user_clas = UserCla.where(user: current_user) + end + + def show + @user_cla = current_user.user_clas.find params[:id] + end + + def create + @user_cla = current_user.user_clas.find_by(cla_id: params[:cla_id]) + if @user_cla.nil? + ActiveRecord::Base.transaction do + Users::UserClaForm.new(user_cla_params).validate! + @user_cla = UserCla.build(user_cla_params, current_user.id) + + end + elsif @user_cla.state == "failed" + @user_cla.update_by_params(user_cla_params) + elsif @user_cla.state == "signed" + return render_error('协议生效中,请勿重复签署') + end + render_ok + rescue Exception => e + uid_logger_error(e.message) + tip_exception(e.message) + end + + def destroy + @user_cla = current_user.user_clas.find params[:id] + @user_cla.update_attributes(state: 2) + render_ok + end + + private + + def user_cla_params + params.permit(:email, :real_name, :cla_id) + end + +end diff --git a/app/controllers/users/project_trends_controller.rb b/app/controllers/users/project_trends_controller.rb index 9edd56f18..5c636f63b 100644 --- a/app/controllers/users/project_trends_controller.rb +++ b/app/controllers/users/project_trends_controller.rb @@ -6,6 +6,7 @@ class Users::ProjectTrendsController < Users::BaseController else @project_trends = observed_user.project_trends end + @project_trends = @project_trends.left_joins(:project).where("projects.is_public = TRUE") @project_trends = kaminari_paginate(@project_trends.includes(:trend, :project).order(created_at: :desc)) end end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 6cf505a6d..34d617f19 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -212,6 +212,10 @@ class UsersController < ApplicationController def update return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id]) return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id) + if user_params[:nickname].present? + keywords = user_params[:nickname].to_s.each_char.select { |c| c.bytes.first < 240 }.join('') + return normal_status(-1, "昵称中包含关键词:#{keywords},请重新命名") if ReversedKeyword.check_exists?(keywords) + end Util.write_file(@image, avatar_path(@user)) if user_params[:image].present? @user.attributes = user_params.except(:image) unless @user.save @@ -759,10 +763,11 @@ class UsersController < ApplicationController password = "12345678" # 没有用户时,新建用户并登录 - user = User.where("login = ? or phone = ? or mail = ? ", "#{login}", phone, email).first + user = phone.present? ? User.find_by(phone: phone) : nil + user = User.where("login = ? or phone = ? or mail = ? ", "#{login}", phone, email).first if user.nil? if user.present? # 手机号先记录,后续用 - user.update_column(:phone, "#{phone}") if phone.present? + user.update_column(:phone, "#{phone}") if phone.present? && user.phone.blank? else ActiveRecord::Base.transaction do email = "#{login}@gitlink.org.cn" if email.blank? diff --git a/app/forms/organizations/create_cla_form.rb b/app/forms/organizations/create_cla_form.rb new file mode 100644 index 000000000..ce20a23ce --- /dev/null +++ b/app/forms/organizations/create_cla_form.rb @@ -0,0 +1,6 @@ +class Organizations::CreateClaForm < BaseForm + KEY_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 + attr_accessor :name, :key, :content, :pr_need + validates :name , :key, presence: true + validates :key, format: { with: KEY_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } +end \ No newline at end of file diff --git a/app/forms/projects/migrate_form.rb b/app/forms/projects/migrate_form.rb index 12a4ee617..98ebbc7ac 100644 --- a/app/forms/projects/migrate_form.rb +++ b/app/forms/projects/migrate_form.rb @@ -1,5 +1,5 @@ class Projects::MigrateForm < BaseForm - attr_accessor :user_id, :name, :repository_name, :project_category_id, :description, + attr_accessor :user_id, :name, :repository_name, :project_category_id, :description, :auth_token, :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner validates :user_id, :name, :repository_name, :clone_addr, presence: true diff --git a/app/forms/register/remote_form.rb b/app/forms/register/remote_form.rb index 59a5fbc82..c3eda4358 100644 --- a/app/forms/register/remote_form.rb +++ b/app/forms/register/remote_form.rb @@ -1,15 +1,16 @@ module Register class RemoteForm < Register::BaseForm # login 登陆方式,支持邮箱、登陆、手机号等 - attr_accessor :username, :email, :password, :platform + attr_accessor :username, :email, :password, :platform, :user_id validates :username, :email, :password, presence: true validate :check! def check! - Rails.logger.info "Register::RemoteForm params: username: #{username}; email: #{email}; password: #{password}; platform: #{platform}" - check_login(username) - check_mail(email) + user = User.find_by(id: user_id) + Rails.logger.info "Register::RemoteForm params: id: #{user&.id}; username: #{username}; email: #{email}; password: #{password}; platform: #{platform}" + check_login(username, user) + check_mail(email,user) check_password(password) end end diff --git a/app/forms/users/user_cla_form.rb b/app/forms/users/user_cla_form.rb new file mode 100644 index 000000000..24f615591 --- /dev/null +++ b/app/forms/users/user_cla_form.rb @@ -0,0 +1,6 @@ +class Users::UserClaForm + include ActiveModel::Model + attr_accessor :email, :real_name, :cla_id + validates :email, presence: true, format: { with: CustomRegexp::EMAIL } + end + \ No newline at end of file diff --git a/app/helpers/admins/glcc_pr_check_helper.rb b/app/helpers/admins/glcc_pr_check_helper.rb new file mode 100644 index 000000000..9934b83bf --- /dev/null +++ b/app/helpers/admins/glcc_pr_check_helper.rb @@ -0,0 +1,2 @@ +module Admins::GlccPrCheckHelper +end diff --git a/app/helpers/admins/identity_verifications_helper.rb b/app/helpers/admins/identity_verifications_helper.rb new file mode 100644 index 000000000..c44651a7f --- /dev/null +++ b/app/helpers/admins/identity_verifications_helper.rb @@ -0,0 +1,2 @@ +module Admins::IdentityVerificationsHelper +end diff --git a/app/helpers/admins/page_themes_helper.rb b/app/helpers/admins/page_themes_helper.rb new file mode 100644 index 000000000..721d9b78c --- /dev/null +++ b/app/helpers/admins/page_themes_helper.rb @@ -0,0 +1,2 @@ +module Admins::PageThemesHelper +end diff --git a/app/helpers/admins/site_pages_helper.rb b/app/helpers/admins/site_pages_helper.rb new file mode 100644 index 000000000..d21cdefb9 --- /dev/null +++ b/app/helpers/admins/site_pages_helper.rb @@ -0,0 +1,2 @@ +module Admins::SitePagesHelper +end diff --git a/app/helpers/identity_verifications_helper.rb b/app/helpers/identity_verifications_helper.rb new file mode 100644 index 000000000..acd433185 --- /dev/null +++ b/app/helpers/identity_verifications_helper.rb @@ -0,0 +1,2 @@ +module IdentityVerificationsHelper +end diff --git a/app/helpers/organizations/clas_helper.rb b/app/helpers/organizations/clas_helper.rb new file mode 100644 index 000000000..38c1531c3 --- /dev/null +++ b/app/helpers/organizations/clas_helper.rb @@ -0,0 +1,2 @@ +module Organizations::ClasHelper +end diff --git a/app/helpers/pages_helper.rb b/app/helpers/pages_helper.rb new file mode 100644 index 000000000..2c057fd05 --- /dev/null +++ b/app/helpers/pages_helper.rb @@ -0,0 +1,2 @@ +module PagesHelper +end diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb index c9be515d2..f364c11ff 100644 --- a/app/helpers/repositories_helper.rb +++ b/app/helpers/repositories_helper.rb @@ -16,7 +16,11 @@ module RepositoriesHelper def image_type?(str) default_type = %w(png jpg gif tif psd svg bmp webp jpeg ico psd) - default_type.include?(str&.downcase) + default_type.include?(str.to_s.gsub("\r", "").downcase) + end + + def is_text_file?(entry) + entry['is_text_file'] end def is_readme?(type, str) @@ -36,18 +40,26 @@ module RepositoriesHelper end def render_cache_commit_author(author_json) + user = nil if author_json["name"].present? && author_json["email"].present? - return find_user_in_redis_cache(author_json['name'], author_json['email']) + user = find_user_in_redis_cache(author_json['name'], author_json['email']) end if author_json["Name"].present? && author_json["Email"].present? - return find_user_in_redis_cache(author_json['Name'], author_json['Email']) + user = find_user_in_redis_cache(author_json['Name'], author_json['Email']) end + if user.blank? && author_json["email"].present? + user = User.find_by(mail: author_json["email"]) + end + if user.blank? && author_json["Email"].present? + user = User.find_by(mail: author_json["Email"]) + end + user end def readme_render_decode64_content(str, owner, repo, ref, path) return nil if str.blank? begin - content = Base64.decode64(str).force_encoding('UTF-8') + content = Base64.decode64(content).force_encoding('UTF-8').valid_encoding? ? Base64.decode64(str).force_encoding('UTF-8') : Base64.decode64(str).force_encoding("GBK").encode("UTF-8") c_regex = /\!\[.*?\]\((.*?)\)/ src_regex = /src=\"(.*?)\"/ @@ -107,19 +119,30 @@ module RepositoriesHelper def new_readme_render_decode64_content(str, owner, repo, ref, readme_path, readme_name) file_path = readme_path.include?('/') ? readme_path.gsub("/#{readme_name}", '') : readme_path.gsub("#{readme_name}", '') return nil if str.blank? - content = Base64.decode64(str).force_encoding('UTF-8') + content = Base64.decode64(str).force_encoding('UTF-8').valid_encoding? ? Base64.decode64(str).force_encoding('UTF-8') : Base64.decode64(str).force_encoding("GBK").encode("UTF-8") # s_regex = /\s\!\[.*?\]\((.*?)\)\s/ s_regex_c = /`{1,2}[^`](.*?)`{1,2}/ s_regex = /```([\s\S]*?)```[\s]?/ s_regex_1 = /\[.*?\]\((.*?)\)/ + # 变量图片相对路径 + s_regex_2 = /\[.*?\]:(.*?)\n/ src_regex = /src=\"(.*?)\"/ src_regex_1 = /src=\'(.*?)\'/ + src_regex_2 = /src = (.*?) / + src_regex_3 = /src= (.*?) / + src_regex_4 = /src =(.*?) / + src_regex_5 = /src =(.*?) / ss_c = content.to_s.scan(s_regex_c) ss = content.to_s.scan(s_regex) ss_1 = content.to_s.scan(s_regex_1) + ss_2 = content.to_s.scan(s_regex_2) ss_src = content.to_s.scan(src_regex) ss_src_1 = content.to_s.scan(src_regex_1) - total_sources = {ss_c: ss_c,ss: ss, ss_1: ss_1, ss_src: ss_src, ss_src_1: ss_src_1} + ss_src_2 = content.to_s.scan(src_regex_2) + ss_src_3 = content.to_s.scan(src_regex_3) + ss_src_4 = content.to_s.scan(src_regex_4) + ss_src_5 = content.to_s.scan(src_regex_5) + total_sources = {ss_c: ss_c,ss: ss, ss_1: ss_1, ss_2: ss_2, ss_src: ss_src, ss_src_1: ss_src_1, ss_src_2: ss_src_2, ss_src_3: ss_src_3, ss_src_4: ss_src_4, ss_src_5: ss_src_5} # total_sources.uniq! total_sources.except(:ss, :ss_c).each do |k, sources| sources.each do |s| @@ -128,17 +151,29 @@ module RepositoriesHelper # 链接直接跳过不做替换 next if s_content.starts_with?('http://') || s_content.starts_with?('https://') || s_content.starts_with?('mailto:') || s_content.blank? ext = File.extname(s_content)[1..-1] + ext = ext.split("?")[0] if ext.include?("?") if (image_type?(ext) || download_type(ext)) && !ext.blank? s_content = File.expand_path(s_content, file_path) s_content = s_content.split("#{Rails.root}/")[1] # content = content.gsub(s[0], "/#{s_content}") - s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw/#{s_content}?ref=#{ref}"].join - case k.to_s + join_xxx = s_content.include?("?") ? "&" : "?" + s_content = [base_url, "/api/#{owner&.login}/#{repo.identifier}/raw/#{s_content}#{join_xxx}ref=#{ref}"].join + case k.to_s when 'ss_src' content = content.gsub("src=\"#{s[0]}\"", "src=\"#{s_content}\"") when 'ss_src_1' content = content.gsub("src=\'#{s[0]}\'", "src=\'#{s_content}\'") - else + when 'ss_src_2' + content = content.gsub("src = #{s[0]}", "src=\'#{s_content}\'") + when 'ss_src_3' + content = content.gsub("src= #{s[0]}", "src=\'#{s_content}\'") + when 'ss_src_4' + content = content.gsub("src =#{s[0]}", "src=\'#{s_content}\'") + when 'ss_src_5' + content = content.gsub("src=#{s[0]}", "src=\'#{s_content}\'") + when 'ss_2' + content = content.gsub(/]:#{s[0]}/, "]: #{s_content.to_s.gsub(" ","").gsub("\r", "")}") + else content = content.gsub("(#{s[0]})", "(#{s_content})") end else @@ -150,7 +185,9 @@ module RepositoriesHelper content = content.gsub("src=\"#{s[0]}\"", "src=\"/#{s_content}\"") when 'ss_src_1' content = content.gsub("src=\'#{s[0]}\'", "src=\'/#{s_content}\'") - else + when 'ss_2' + content = content.gsub(/]:#{s[0]}/, "]: /#{s_content.to_s.gsub(" ","").gsub("\r", "")}") + else content = content.gsub("(#{s[0]})", "(/#{s_content})") end end @@ -168,9 +205,10 @@ module RepositoriesHelper after_ss_c_souces.each_with_index do |s, index| content = content.gsub("#{s[0]}","#{total_sources[:ss_c][index][0]}") end - return content - rescue + rescue Exception => e + Rails.logger.error("===================#{readme_path}:#{readme_name}:error:#{e}") + # e.backtrace.each { |msg| Rails.logger.error(msg) } return str end @@ -186,8 +224,7 @@ module RepositoriesHelper def readme_decode64_content(entry, owner, repo, ref, path=nil) Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") - content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] - # Rails.logger.info("content===#{content}") + content = entry['content'].present? ? entry['content'] : Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] # readme_render_decode64_content(content, owner, repo, ref) new_readme_render_decode64_content(content, owner, repo, ref, entry['path'], entry['name']) end @@ -195,10 +232,12 @@ module RepositoriesHelper def decode64_content(entry, owner, repo, ref, path=nil) if is_readme?(entry['type'], entry['name']) Rails.logger.info("entry===#{entry["type"]} #{entry["name"]}") - content = Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] + content = entry['content'].present? ? entry['content'] : Gitea::Repository::Entries::GetService.call(owner, repo.identifier, URI.escape(entry['path']), ref: ref)['content'] # Rails.logger.info("content===#{content}") - # readme_render_decode64_content(content, owner, repo, ref) + return Base64.decode64(content).force_encoding("GBK").encode("UTF-8") unless Base64.decode64(content).force_encoding('UTF-8').valid_encoding? return Base64.decode64(content).force_encoding('UTF-8') + elsif entry['is_text_file'] == true + return render_decode64_content(entry['content']) else file_type = File.extname(entry['name'].to_s)[1..-1] if image_type?(file_type) diff --git a/app/helpers/users/clas_helper.rb b/app/helpers/users/clas_helper.rb new file mode 100644 index 000000000..55e326d0d --- /dev/null +++ b/app/helpers/users/clas_helper.rb @@ -0,0 +1,2 @@ +module Users::ClasHelper +end diff --git a/app/interactors/gitea/delete_file_interactor.rb b/app/interactors/gitea/delete_file_interactor.rb index 103df6cd4..03ddf4230 100644 --- a/app/interactors/gitea/delete_file_interactor.rb +++ b/app/interactors/gitea/delete_file_interactor.rb @@ -42,6 +42,9 @@ module Gitea def render_result(response) if response.status == 200 @result = JSON.parse(response.body) + else + Rails.logger.error("Gitea::Repository::Entries::DeleteService error[#{response.status}]======#{response.body}") + @error = "删除失败,请确认该分支是否是保护分支。" end end diff --git a/app/interactors/gitea/update_file_interactor.rb b/app/interactors/gitea/update_file_interactor.rb index 38cfd98a8..1b729e2c8 100644 --- a/app/interactors/gitea/update_file_interactor.rb +++ b/app/interactors/gitea/update_file_interactor.rb @@ -42,6 +42,9 @@ module Gitea def render_result(response) if response.status == 200 @result = JSON.parse(response.body) + else + Rails.logger.error("Gitea::Repository::Entries::UpdateService error[#{response.status}]======#{response.body}") + @error = "更新失败,请确认该分支是否是保护分支。" end end diff --git a/app/jobs/migrate_remote_repository_job.rb b/app/jobs/migrate_remote_repository_job.rb index 0e30bfefe..2493bff01 100644 --- a/app/jobs/migrate_remote_repository_job.rb +++ b/app/jobs/migrate_remote_repository_job.rb @@ -15,11 +15,13 @@ class MigrateRemoteRepositoryJob < ApplicationJob ## open jianmu devops project_id = repo&.project&.id puts "############ mirror project_id,user_id: #{project_id},#{user_id} ############" - # OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(project_id, user_id) if project_id.present? && user_id.present? + OpenProjectDevOpsJob.set(wait: 5.seconds).perform_later(project_id, user_id) if project_id.present? && user_id.present? + UpdateProjectTopicJob.set(wait: 1.seconds).perform_later(project_id) if project_id.present? puts "############ mirror status: #{repo.mirror.status} ############" else repo&.mirror&.failed! end - BroadcastMirrorRepoMsgJob.perform_later(repo.id) unless repo&.mirror.waiting? + # UpdateProjectTopicJob 中语言要延迟1S才能获取 + BroadcastMirrorRepoMsgJob.set(wait: 1.seconds).perform_later(repo.id) unless repo&.mirror.waiting? end end diff --git a/app/jobs/send_template_message_job.rb b/app/jobs/send_template_message_job.rb index 05572d451..1df1d8c7d 100644 --- a/app/jobs/send_template_message_job.rb +++ b/app/jobs/send_template_message_job.rb @@ -8,7 +8,7 @@ class SendTemplateMessageJob < ApplicationJob receivers_id, template_id, props = args[0], args[1], args[2] template = MessageTemplate.find_by_id(template_id) return unless template.present? - receivers = User.where(id: receivers_id).or(User.where(mail: receivers_id)) + receivers = User.where("id in(?)", receivers_id).or(User.where(mail: receivers_id)) not_exists_receivers = receivers_id - receivers.pluck(:id) - receivers.pluck(:mail) receivers_string, content, notification_url = MessageTemplate::CustomTip.get_message_content(receivers, template, props) Notice::Write::CreateService.call(receivers_string, content, notification_url, source, {receivers_id: receivers_id, template_id: template_id, props: props}) diff --git a/app/jobs/sync_repo_update_time_job.rb b/app/jobs/sync_repo_update_time_job.rb index 4391f2c13..8692391a8 100644 --- a/app/jobs/sync_repo_update_time_job.rb +++ b/app/jobs/sync_repo_update_time_job.rb @@ -3,9 +3,9 @@ class SyncRepoUpdateTimeJob < ApplicationJob def perform(*args) # Do something later - Project.forge.find_each do |project| - update_repo_time!(project) - end + # Project.forge.find_each do |project| + # update_repo_time!(project) + # end end private diff --git a/app/jobs/touch_webhook_job.rb b/app/jobs/touch_webhook_job.rb index 0a8c03e9f..514fa6950 100644 --- a/app/jobs/touch_webhook_job.rb +++ b/app/jobs/touch_webhook_job.rb @@ -49,9 +49,9 @@ class TouchWebhookJob < ApplicationJob when 'IssueComment' issue_id, sender_id, comment_id, action_type, comment_json = args[0], args[1], args[2], args[3], args[4] issue = Issue.find_by_id issue_id - comment = issue.comment_journals.find_by_id comment_id sender = User.find_by_id sender_id - return if issue.nil? || sender.nil? + return if issue.nil? || sender.nil? + comment = issue.comment_journals.find_by_id comment_id return if action_type == 'edited' && comment_json.blank? issue.project.webhooks.each do |webhook| @@ -63,10 +63,10 @@ class TouchWebhookJob < ApplicationJob when 'PullRequestComment' issue_id, sender_id, comment_id, action_type, comment_json = args[0], args[1], args[2], args[3], args[4] issue = Issue.find_by_id(issue_id) - comment = issue.comment_journals.find_by_id comment_id sender = User.find_by_id sender_id pull = issue.try(:pull_request) - return if pull.nil? || sender.nil? + return if issue.nil? || pull.nil? || sender.nil? + comment = issue.comment_journals.find_by_id comment_id return if action_type == 'edited' && comment_json.blank? pull.project.webhooks.each do |webhook| diff --git a/app/jobs/update_project_topic_job.rb b/app/jobs/update_project_topic_job.rb new file mode 100644 index 000000000..52341d672 --- /dev/null +++ b/app/jobs/update_project_topic_job.rb @@ -0,0 +1,33 @@ +class UpdateProjectTopicJob < ApplicationJob + include ProjectsHelper + + queue_as :message + + def perform(project_id) + project = Project.find_by(id: project_id) + return if project.blank? + begin + languages = $gitea_client.get_repos_languages_by_owner_repo(project.owner.login, project.identifier) + puts "#{project.owner.login}/#{project.identifier} get_repos_languages:#{languages}" + topic_count = 0 + if project.project_category_id.present? + project_topic = ProjectTopic.find_or_create_by!(name: project.project_category.name.downcase) + project_topic_ralate = project_topic.project_topic_ralates.find_or_create_by!(project_id: project.id) + if project_topic.present? && project_topic_ralate.present? + topic_count +=1 + end + end + languages.each do |k, _| + next if topic_count >= 3 + project_topic = ProjectTopic.find_or_create_by!(name: k.downcase) + project_topic_ralate = project_topic.project_topic_ralates.find_or_create_by!(project_id: project.id) + if project_topic.present? && project_topic_ralate.present? + topic_count +=1 + end + end + rescue => e + puts "get_repos_languages: error:#{e.message}" + end + end + +end \ No newline at end of file diff --git a/app/libs/getway.rb b/app/libs/getway.rb new file mode 100644 index 000000000..0b876b839 --- /dev/null +++ b/app/libs/getway.rb @@ -0,0 +1,20 @@ +module Getway + class << self + def getway_config + getway_config = {} + + begin + config = Rails.application.config_for(:configuration).symbolize_keys! + getway_config = config[:getway].symbolize_keys! + raise 'getway config missing' if getway_config.blank? + rescue => ex + raise ex if Rails.env.production? + + puts %Q{\033[33m [warning] getway config or configuration.yml missing, + please add it or execute 'cp config/configuration.yml.example config/configuration.yml' \033[0m} + getway_config = {} + end + getway_config + end + end +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 28bbccf63..a99948879 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -30,4 +30,11 @@ class UserMailer < ApplicationMailer def feedback_email(mail, title, content) mail(to: mail, subject: title, content_type: "text/html", body: content) end + + def glcc_pr_check_email(mail, title, name, content) + @content = content + @name = name + mail(to: mail, subject: title) + end + end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 0cbf6fb0f..f79aca153 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -1,41 +1,44 @@ -# == Schema Information -# -# Table name: attachments -# -# id :integer not null, primary key -# container_id :integer -# container_type :string(30) -# filename :string(255) default(""), not null -# disk_filename :string(255) default(""), not null -# filesize :integer default("0"), not null -# content_type :string(255) default("") -# digest :string(60) default(""), not null -# downloads :integer default("0"), not null -# author_id :integer default("0"), not null -# created_on :datetime -# description :text(65535) -# disk_directory :string(255) -# attachtype :integer default("1") -# is_public :integer default("1") -# copy_from :string(255) -# quotes :integer default("0") -# is_publish :integer default("1") -# publish_time :datetime -# resource_bank_id :integer -# unified_setting :boolean default("1") -# cloud_url :string(255) default("") -# course_second_category_id :integer default("0") -# delay_publish :boolean default("0") -# link :string(255) -# clone_id :integer -# -# Indexes -# -# index_attachments_on_author_id (author_id) -# index_attachments_on_clone_id (clone_id) -# index_attachments_on_container_id_and_container_type (container_id,container_type) -# index_attachments_on_created_on (created_on) -# +# == Schema Information +# +# Table name: attachments +# +# id :integer not null, primary key +# container_id :integer +# container_type :string(30) +# filename :string(255) default(""), not null +# disk_filename :string(255) default(""), not null +# filesize :integer default("0"), not null +# content_type :string(255) default("") +# digest :string(60) default(""), not null +# downloads :integer default("0"), not null +# author_id :integer default("0"), not null +# created_on :datetime +# description :text(65535) +# disk_directory :string(255) +# attachtype :integer default("1") +# is_public :integer default("1") +# copy_from :integer +# quotes :integer default("0") +# is_publish :integer default("1") +# publish_time :datetime +# resource_bank_id :integer +# unified_setting :boolean default("1") +# cloud_url :string(255) default("") +# course_second_category_id :integer default("0") +# delay_publish :boolean default("0") +# memo_image :boolean default("0") +# extra_type :integer default("0") +# +# Indexes +# +# index_attachments_on_author_id (author_id) +# index_attachments_on_container_id_and_container_type (container_id,container_type) +# index_attachments_on_course_second_category_id (course_second_category_id) +# index_attachments_on_created_on (created_on) +# index_attachments_on_is_public (is_public) +# index_attachments_on_quotes (quotes) +# + diff --git a/app/models/bot.rb b/app/models/bot.rb index 13ac70a78..e7a14d009 100644 --- a/app/models/bot.rb +++ b/app/models/bot.rb @@ -2,23 +2,24 @@ # # Table name: bot # -# id :integer not null, primary key -# bot_name :string(255) -# bot_des :text(4294967295) -# webhook :string(255) -# is_public :integer -# logo :string(255) -# state :integer -# client_id :string(255) -# client_secret :string(255) -# web_url :string(255) -# category :string(255) -# install_num :integer default("0") -# update_time :datetime not null -# create_time :datetime not null -# private_key :text(65535) -# uid :integer -# owner_id :integer +# id :integer not null, primary key +# bot_name :string(255) not null +# bot_des :text(4294967295) +# webhook :string(255) not null +# is_public :integer +# logo :string(255) +# state :integer +# client_id :string(255) +# client_secret :string(255) +# web_url :string(255) +# category :string(255) +# install_num :integer default("0") +# update_time :datetime not null +# create_time :datetime not null +# uid :integer +# owner_id :integer +# private_key :text(65535) +# oauth_callback_url :string(255) not null # # Indexes # diff --git a/app/models/bot_install.rb b/app/models/bot_install.rb index d5283c131..57e5a750b 100644 --- a/app/models/bot_install.rb +++ b/app/models/bot_install.rb @@ -2,13 +2,18 @@ # # Table name: install_bot # -# id :integer not null, primary key -# bot_id :integer not null -# installer_id :integer not null -# store_id :integer not null -# state :integer not null -# create_time :datetime not null -# update_time :datetime not null +# id :integer not null, primary key +# bot_id :integer not null +# installer_id :integer not null +# store_id :integer not null +# state :integer not null +# create_time :datetime not null +# update_time :datetime not null +# installer_login :string(255) not null +# store_repo :string(255) not null +# webhook_id :integer +# webhook_response_msg :text(65535) +# repo_owner :string(255) # # frozen_string_literal: true diff --git a/app/models/ci/user.rb b/app/models/ci/user.rb index e4a4d0623..aae6e601c 100644 --- a/app/models/ci/user.rb +++ b/app/models/ci/user.rb @@ -39,15 +39,17 @@ # business :boolean default("0") # profile_completed :boolean default("0") # laboratory_id :integer -# is_shixun_marker :boolean default("0") -# admin_visitable :boolean default("0") -# collaborator :boolean default("0") +# platform :string(255) default("0") +# gitea_token :string(255) # gitea_uid :integer +# is_shixun_marker :boolean default("0") # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") -# gitea_token :string(255) -# platform :string(255) +# sign_cla :boolean default("0") +# enabling_cla :boolean default("0") +# id_card_verify :boolean default("0") +# website_permission :boolean default("0") # # Indexes # @@ -56,8 +58,7 @@ # index_users_on_homepage_teacher (homepage_teacher) # index_users_on_laboratory_id (laboratory_id) # index_users_on_login (login) UNIQUE -# index_users_on_mail (mail) UNIQUE -# index_users_on_phone (phone) UNIQUE +# index_users_on_mail (mail) # index_users_on_type (type) # diff --git a/app/models/cla.rb b/app/models/cla.rb new file mode 100644 index 000000000..6bbfb6143 --- /dev/null +++ b/app/models/cla.rb @@ -0,0 +1,46 @@ +# == Schema Information +# +# Table name: clas +# +# id :integer not null, primary key +# name :string(255) not null +# key :string(255) not null +# content :text(65535) +# organization_id :integer not null +# pr_need :boolean default("0") +# count :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_clas_on_key (key) +# index_clas_on_organization_id (organization_id) +# + +class Cla < ApplicationRecord + has_many :user_clas, :dependent => :destroy + has_many :users, through: :user_clas + belongs_to :organization + + def to_param + self.key.parameterize + end + + def self.build(params,org_id) + self.create!(organization_id: org_id, + name: params[:name], + key: params[:key], + content: params[:content], + pr_need: params[:pr_need] + ) + end + + def valid_sign(user_id) + user_clas.where(user_id: user_id, state:1).present? + end + def fresh_count + number = self.user_clas.where(state: 1).count + update(count: number) + end +end diff --git a/app/models/commit_log.rb b/app/models/commit_log.rb index def2846fa..d351e6f48 100644 --- a/app/models/commit_log.rb +++ b/app/models/commit_log.rb @@ -1,3 +1,26 @@ +# == Schema Information +# +# Table name: commit_logs +# +# id :integer not null, primary key +# user_id :integer +# project_id :integer +# repository_id :integer +# name :string(255) +# full_name :string(255) +# commit_id :string(255) +# ref :string(255) +# message :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_commit_logs_on_commit_id (commit_id) +# index_commit_logs_on_project_id (project_id) +# index_commit_logs_on_user_id (user_id) +# + class CommitLog < ApplicationRecord belongs_to :user belongs_to :project diff --git a/app/models/concerns/project_operable.rb b/app/models/concerns/project_operable.rb index 4fee9ea33..9a5efd129 100644 --- a/app/models/concerns/project_operable.rb +++ b/app/models/concerns/project_operable.rb @@ -200,7 +200,7 @@ module ProjectOperable if owner.is_a?(User) managers.exists?(user_id: user.id) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) elsif owner.is_a?(Organization) - managers.exists?(user_id: user.id) || owner.is_owner?(user.id) || (owner.is_only_admin?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) + managers.exists?(user_id: user.id) || owner.is_owner?(user.id) || (owner.is_admin?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) else false end @@ -212,7 +212,7 @@ module ProjectOperable if owner.is_a?(User) developers.exists?(user_id: user.id) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) elsif owner.is_a?(Organization) - developers.exists?(user_id: user.id) || (owner.is_only_write?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) + developers.exists?(user_id: user.id) || (owner.is_write?(user.id) && (teams.pluck(:id) & user.teams.pluck(:id)).size > 0) || (user.platform == "bot" && BotInstall.joins(:bot).where(bot: { uid: user.id }).where(store_id: self.id).exists?) else false end diff --git a/app/models/edu_setting.rb b/app/models/edu_setting.rb index 22575ff15..f4a89c09b 100644 --- a/app/models/edu_setting.rb +++ b/app/models/edu_setting.rb @@ -1,18 +1,19 @@ -# == Schema Information -# -# Table name: edu_settings -# -# id :integer not null, primary key -# name :string(255) -# value :string(255) -# created_at :datetime not null -# updated_at :datetime not null -# description :string(255) -# -# Indexes -# -# index_edu_settings_on_name (name) UNIQUE -# +# == Schema Information +# +# Table name: edu_settings +# +# id :integer not null, primary key +# name :string(255) +# value :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# description :string(255) +# +# Indexes +# +# index_edu_settings_on_name (name) UNIQUE +# + class EduSetting < ApplicationRecord after_commit :expire_value_cache diff --git a/app/models/gitea/public_key.rb b/app/models/gitea/public_key.rb index bb2192358..a9962dc67 100644 --- a/app/models/gitea/public_key.rb +++ b/app/models/gitea/public_key.rb @@ -1,3 +1,25 @@ +# == Schema Information +# +# Table name: public_key +# +# id :integer not null, primary key +# owner_id :integer not null +# name :string(255) not null +# fingerprint :string(255) not null +# content :text(16777215) not null +# mode :integer default("2"), not null +# type :integer default("1"), not null +# login_source_id :integer default("0"), not null +# created_unix :integer +# updated_unix :integer +# verified :boolean default("0"), not null +# +# Indexes +# +# IDX_public_key_fingerprint (fingerprint) +# IDX_public_key_owner_id (owner_id) +# + class Gitea::PublicKey < Gitea::Base self.inheritance_column = nil # FIX The single-table inheritance mechanism failed # establish_connection :gitea_db diff --git a/app/models/gitea/pull.rb b/app/models/gitea/pull.rb index 7adb8c366..70e61f5fa 100644 --- a/app/models/gitea/pull.rb +++ b/app/models/gitea/pull.rb @@ -16,10 +16,12 @@ # head_branch :string(255) # base_branch :string(255) # merge_base :string(40) +# allow_maintainer_edit :boolean default("0"), not null # has_merged :boolean # merged_commit_id :string(40) # merger_id :integer # merged_unix :integer +# flow :integer default("0"), not null # # Indexes # diff --git a/app/models/gitea/webhook.rb b/app/models/gitea/webhook.rb index f60f56788..ec1c836c4 100644 --- a/app/models/gitea/webhook.rb +++ b/app/models/gitea/webhook.rb @@ -1,3 +1,32 @@ +# == Schema Information +# +# Table name: webhook +# +# id :integer not null, primary key +# repo_id :integer +# org_id :integer +# is_system_webhook :boolean +# url :text(65535) +# http_method :string(255) +# content_type :integer +# secret :text(65535) +# events :text(65535) +# is_active :boolean +# type :string(16) +# meta :text(65535) +# last_status :integer +# created_unix :integer +# updated_unix :integer +# +# Indexes +# +# IDX_webhook_created_unix (created_unix) +# IDX_webhook_is_active (is_active) +# IDX_webhook_org_id (org_id) +# IDX_webhook_repo_id (repo_id) +# IDX_webhook_updated_unix (updated_unix) +# + class Gitea::Webhook < Gitea::Base serialize :events, JSON self.inheritance_column = nil @@ -10,4 +39,4 @@ class Gitea::Webhook < Gitea::Base enum hook_task_type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9} enum last_status: {waiting: 0, succeed: 1, fail: 2} enum content_type: {json: 1, form: 2} -end \ No newline at end of file +end diff --git a/app/models/gitea/webhook_task.rb b/app/models/gitea/webhook_task.rb index 7e9bc68a7..b612816ba 100644 --- a/app/models/gitea/webhook_task.rb +++ b/app/models/gitea/webhook_task.rb @@ -1,3 +1,19 @@ +# == Schema Information +# +# Table name: hook_task +# +# id :integer not null, primary key +# hook_id :integer +# uuid :string(255) +# payload_content :text(4294967295) +# event_type :string(255) +# is_delivered :boolean +# delivered :integer +# is_succeed :boolean +# request_content :text(4294967295) +# response_content :text(4294967295) +# + class Gitea::WebhookTask < Gitea::Base serialize :payload_content, JSON serialize :request_content, JSON @@ -11,4 +27,4 @@ class Gitea::WebhookTask < Gitea::Base enum type: {gogs: 1, slack: 2, gitea: 3, discord: 4, dingtalk: 5, telegram: 6, msteams: 7, feishu: 8, matrix: 9} -end \ No newline at end of file +end diff --git a/app/models/glcc_medium_term_examine_material.rb b/app/models/glcc_medium_term_examine_material.rb new file mode 100644 index 000000000..0e22690c8 --- /dev/null +++ b/app/models/glcc_medium_term_examine_material.rb @@ -0,0 +1,103 @@ +# == Schema Information +# +# Table name: glcc_medium_term_examine_material +# +# id :integer not null, primary key +# student_reg_id :integer not null +# task_id :integer not null +# defence_video_url :string(1000) not null +# code_or_pr_url :string(1000) +# PPT_attachment_id :integer not null +# term :integer +# created_on :datetime +# updated_on :datetime +# is_delete :boolean default("0"), not null +# round :integer default("1"), not null +# + +class GlccMediumTermExamineMaterial < ActiveRecord::Base + self.table_name = "glcc_medium_term_examine_material" + belongs_to :glcc_student, :class_name => :GlccRegistrationStudent, :foreign_key => "student_reg_id" + belongs_to :task, :class_name => :GlccRegistrationTask, :foreign_key => "task_id" + + def check_pr_url + state = [] + # code_or_pr_url = "https://www.gitlink.org.cn/Gitlink/forgeplus/pulls/337" + url_array = code_or_pr_url.split("/") + gitlink_index = url_array.index("www.gitlink.org.cn") || url_array.index("gitlink.org.cn") || url_array.index("testforgeplus.trustie.net") + pull_index = url_array.index("pulls") + + #发送没有在gitlink上提交PR的邮件 + unless gitlink_index.present? + state << 1 + end + + # 输入的地址有问题邮件内容 + unless pull_index == 5 + state << 2 + end + user = Owner.find_by(login: url_array[3]) + project = Project.find_by(identifier: url_array[4], user_id:user.try(:id)) + pr = PullRequest.where(project:project, gitea_number:url_array[6]) + + unless pr.present? + state << 3 + end + if white_list #特殊处理 白名单考核不处理 + state = [] + end + state + end + + + def white_list + # 全局设置白名单 key + key = "glcc_white_task_#{self.created_on.year}" + white_task = EduSetting.find_by_name(key) + if white_task + task_ids = white_task.value.split(",") + task_ids.map(&:to_i).include?(self.task_id) + else + false + end + end + + def send_mail + gcs = glcc_student + mail = gcs.mail + return if mail.nil? || code_or_pr_url.nil? + + state = check_pr_url + return unless state.present? + title = "#{self.created_on.year}年GitLink确实开源GLCC开源夏令营#{term == 1 ? "中期考核" : "结项考核"}提醒" + content = gennerate_content(state) + UserMailer.glcc_pr_check_email(mail,title, gcs.student_name, content).deliver_now + end + + def state_to_html + gcs = glcc_student + mail = gcs.mail + return "数据异常,PR链接为空" if mail.nil? || code_or_pr_url.nil? + state = check_pr_url + gennerate_content(state) + end + + def gennerate_content(state) + content = "" + state.map{|e| + content = content + number_to_content(e) + } + content + end + + def number_to_content(num) + case num + when 1 + "

PR链接为非GitLink平台链接

" + when 2 + "

PR链接为GitLink平台链接, 但非PR链接

" + when 3 + "

PR链接中的PR不存在

" + end + end +end diff --git a/app/models/glcc_registration_student.rb b/app/models/glcc_registration_student.rb new file mode 100644 index 000000000..5dd988c95 --- /dev/null +++ b/app/models/glcc_registration_student.rb @@ -0,0 +1,24 @@ +# == Schema Information +# +# Table name: glcc_registration_student +# +# id :integer not null, primary key +# user_id :integer not null +# student_name :string(255) +# school :string(255) +# profession :string(255) +# location :string(255) +# grade :string(255) +# phone :string(255) +# mail :string(255) +# created_on :datetime +# is_delete :boolean default("0"), not null +# prove_attachment_id :integer +# cancel_count :integer default("0") +# round :integer default("1"), not null +# + +class GlccRegistrationStudent < ActiveRecord::Base + self.table_name = "glcc_registration_student" + has_many :examines, :class_name => :GlccMediumTermExamineMaterial, :foreign_key => "student_reg_id" +end diff --git a/app/models/glcc_registration_task.rb b/app/models/glcc_registration_task.rb new file mode 100644 index 000000000..da0d7cfd5 --- /dev/null +++ b/app/models/glcc_registration_task.rb @@ -0,0 +1,30 @@ +# == Schema Information +# +# Table name: glcc_registration_task +# +# id :integer not null, primary key +# reg_id :integer not null +# task_name :string(255) +# task_desc :text(16777215) +# task_difficulty :integer +# task_url :string(1000) +# task_reward :string(255) +# tutor_name :string(255) +# tutor_mail :string(255) +# tutor_phone :string(255) +# created_on :datetime +# is_delete :boolean default("0"), not null +# sort_no :integer default("0") +# locked :boolean default("0") +# round :integer default("1"), not null +# check_status :boolean default("0") +# +# Indexes +# +# idx_glcc_reg_id (reg_id) +# + +class GlccRegistrationTask < ActiveRecord::Base + self.table_name = "glcc_registration_task" + has_many :examines, :class_name => :GlccMediumTermExamineMaterial, :foreign_key => "task_id" +end diff --git a/app/models/help.rb b/app/models/help.rb index 5a4c480b4..8e0bcad78 100644 --- a/app/models/help.rb +++ b/app/models/help.rb @@ -9,7 +9,6 @@ # agreement :text(65535) # status :text(65535) # help_center :text(65535) -# join_us :text(65535) # class Help < ApplicationRecord diff --git a/app/models/identity_verification.rb b/app/models/identity_verification.rb new file mode 100644 index 000000000..3c8c88d9b --- /dev/null +++ b/app/models/identity_verification.rb @@ -0,0 +1,48 @@ +# == Schema Information +# +# Table name: identity_verifications +# +# id :integer not null, primary key +# user_id :integer not null +# number :string(255) not null +# name :string(255) not null +# card_front :integer +# card_back :integer +# hold_card_front :integer +# hold_card_back :integer +# state :integer default("0") +# description :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_identity_verifications_on_user_id (user_id) +# + +class IdentityVerification < ApplicationRecord + belongs_to :user + enum state: { "待审核": 0, "已通过": 1, "已拒绝": 2} + + after_save do + if state == "已通过" + user.update(id_card_verify: true, website_permission: true) + end + end + + def card_front_attachment + Attachment.find_by_id card_front + end + + def card_back_attachment + Attachment.find_by_id card_back + end + + def hold_card_front_attachment + Attachment.find_by_id hold_card_front + end + + def hold_card_back_attachment + Attachment.find_by_id hold_card_back + end +end diff --git a/app/models/import_repo.rb b/app/models/import_repo.rb index 8b53a7765..24c96a715 100644 --- a/app/models/import_repo.rb +++ b/app/models/import_repo.rb @@ -1,3 +1,26 @@ +# == Schema Information +# +# Table name: open_shixuns +# +# id :integer not null, primary key +# name :string(255) +# shixun_user_name :string(255) +# shixun_user_phone :string(255) +# shixun_user_mail :string(255) +# shixun_identifier :string(255) +# git_url :string(255) +# identifier :string(255) +# myshixun_git_url :string(255) +# myshixun_user_name :string(255) +# myshixun_user_phone :string(255) +# myshixun_user_mail :string(255) +# +# Indexes +# +# idx_email (myshixun_user_mail) +# idx_phone (myshixun_user_phone) +# + # for oauth2 application diff --git a/app/models/issue.rb b/app/models/issue.rb index e12f667db..eae5acf6f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -5,7 +5,7 @@ # id :integer not null, primary key # tracker_id :integer not null # project_id :integer not null -# subject :string(255) default(""), not null +# subject :string(255) # description :text(4294967295) # due_date :date # category_id :integer @@ -33,6 +33,7 @@ # issue_classify :string(255) # ref_name :string(255) # branch_name :string(255) +# blockchain_token_num :integer # # Indexes # diff --git a/app/models/issue_participant.rb b/app/models/issue_participant.rb index fa7be6980..d756b28a2 100644 --- a/app/models/issue_participant.rb +++ b/app/models/issue_participant.rb @@ -1,3 +1,20 @@ +# == Schema Information +# +# Table name: issue_participants +# +# id :integer not null, primary key +# issue_id :integer +# participant_id :integer +# participant_type :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_issue_participants_on_issue_id (issue_id) +# index_issue_participants_on_participant_id (participant_id) +# + class IssueParticipant < ApplicationRecord belongs_to :issue diff --git a/app/models/issue_tag.rb b/app/models/issue_tag.rb index b4ee673f7..7251e98f2 100644 --- a/app/models/issue_tag.rb +++ b/app/models/issue_tag.rb @@ -2,17 +2,18 @@ # # Table name: issue_tags # -# id :integer not null, primary key -# name :string(255) -# description :string(255) -# color :string(255) -# user_id :integer -# project_id :integer -# issues_count :integer default("0") -# created_at :datetime not null -# updated_at :datetime not null -# gid :integer -# gitea_url :string(255) +# id :integer not null, primary key +# name :string(190) +# description :string(255) +# color :string(255) +# user_id :integer +# project_id :integer +# issues_count :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# gid :integer +# gitea_url :string(255) +# pull_requests_count :integer default("0") # # Indexes # diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb index 9b409c170..0517c0df6 100644 --- a/app/models/laboratory.rb +++ b/app/models/laboratory.rb @@ -10,7 +10,6 @@ # sync_course :boolean default("0") # sync_subject :boolean default("0") # sync_shixun :boolean default("0") -# is_local :boolean default("0") # # Indexes # diff --git a/app/models/license.rb b/app/models/license.rb index d14a9db14..f84e63573 100644 --- a/app/models/license.rb +++ b/app/models/license.rb @@ -7,6 +7,7 @@ # content :text(65535) # created_at :datetime not null # updated_at :datetime not null +# is_secret :boolean default("0") # class License < ApplicationRecord diff --git a/app/models/mark_file.rb b/app/models/mark_file.rb index c6c834623..663f6aca4 100644 --- a/app/models/mark_file.rb +++ b/app/models/mark_file.rb @@ -1,3 +1,23 @@ +# == Schema Information +# +# Table name: mark_files +# +# id :integer not null, primary key +# pull_request_id :integer +# user_id :integer +# file_path_sha :string(255) +# file_path :string(255) +# mark_as_read :boolean default("0") +# updated_after_read :boolean default("0") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_mark_files_on_file_path_sha (file_path_sha) +# index_mark_files_on_pull_request_id (pull_request_id) +# + class MarkFile < ApplicationRecord belongs_to :pull_request diff --git a/app/models/member.rb b/app/models/member.rb index 521f939c5..aaaf34efc 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -11,6 +11,7 @@ # course_group_id :integer default("0") # is_collect :integer default("1") # graduation_group_id :integer default("0") +# is_apply_signature :boolean default("0") # team_user_id :integer # # Indexes diff --git a/app/models/message_template/issue_creator_expire.rb b/app/models/message_template/issue_creator_expire.rb index e6f42fcc4..c9478aa53 100644 --- a/app/models/message_template/issue_creator_expire.rb +++ b/app/models/message_template/issue_creator_expire.rb @@ -1,3 +1,17 @@ +# == Schema Information +# +# Table name: message_templates +# +# id :integer not null, primary key +# type :string(255) +# sys_notice :text(65535) +# email :text(65535) +# created_at :datetime not null +# updated_at :datetime not null +# notification_url :string(255) +# email_title :string(255) +# + class MessageTemplate::IssueCreatorExpire < MessageTemplate -end \ No newline at end of file +end diff --git a/app/models/organization.rb b/app/models/organization.rb index 75a2db971..cb829626e 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -46,11 +46,10 @@ # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") -# sponsor_certification :integer default("0") -# sponsor_num :integer default("0") -# sponsored_num :integer default("0") -# sponsor_description :text(65535) -# award_time :datetime +# sign_cla :boolean default("0") +# enabling_cla :boolean default("0") +# id_card_verify :boolean default("0") +# website_permission :boolean default("0") # # Indexes # @@ -58,7 +57,7 @@ # index_users_on_homepage_engineer (homepage_engineer) # index_users_on_homepage_teacher (homepage_teacher) # index_users_on_laboratory_id (laboratory_id) -# index_users_on_login (login) +# index_users_on_login (login) UNIQUE # index_users_on_mail (mail) # index_users_on_type (type) # @@ -70,6 +69,8 @@ class Organization < Owner default_scope { where(type: "Organization") } has_one :organization_extension, dependent: :destroy + has_one :cla, dependent: :destroy + has_many :teams, dependent: :destroy has_many :organization_users, dependent: :destroy has_many :team_users, dependent: :destroy @@ -108,6 +109,16 @@ class Organization < Owner organization_users.where(user_id: user_id).present? end + def is_sign?(user_id) + return false if cla.nil? + cla.user_clas.where(user_id: user_id, state: 1).present? + end + + def cla_sign_email(user_id) + cla.user_clas.find_by(user_id: user_id)&.email + end + + def is_owner?(user_id) team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(owner)}).present? end @@ -126,14 +137,24 @@ class Organization < Owner def is_only_admin?(user_id) team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(admin)}).present? + roles = has_roles(user_id) + roles.size > 1 ? false : roles.include?("admin") end def is_only_write?(user_id) - team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write)}).present? + # team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(write)}).present? + roles = has_roles(user_id) + roles.size > 1 ? false : roles.include?("write") end def is_only_read?(user_id) - team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read)}).present? + # team_users.joins(:team).where(user_id: user_id, teams: {authorize: %w(read)}).present? + roles = has_roles(user_id) + roles.size > 1 ? false : roles.include?("read") + end + + def has_roles(user_id) + teams.joins(:team_users).where("team_users.user_id=?", user_id).pluck("teams.authorize").uniq end # 是不是所有者团队的最后一个成员 @@ -183,4 +204,17 @@ class Organization < Owner name end end + + def open_cla! + update_attribute(:enabling_cla, true) + end + + def close_cla! + update_attribute(:enabling_cla, false) + end + + def open_cla? + enabling_cla == true + end + end diff --git a/app/models/organization_extension.rb b/app/models/organization_extension.rb index cb216aca1..11f0c7694 100644 --- a/app/models/organization_extension.rb +++ b/app/models/organization_extension.rb @@ -19,6 +19,8 @@ # news_banner_id :integer # news_content :text(65535) # memo :text(65535) +# news_title :string(255) +# news_url :string(255) # # Indexes # diff --git a/app/models/organization_user.rb b/app/models/organization_user.rb index 4ff6946b7..900710a9a 100644 --- a/app/models/organization_user.rb +++ b/app/models/organization_user.rb @@ -5,6 +5,7 @@ # id :integer not null, primary key # user_id :integer # organization_id :integer +# is_creator :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/page.rb b/app/models/page.rb new file mode 100644 index 000000000..57a30acad --- /dev/null +++ b/app/models/page.rb @@ -0,0 +1,56 @@ +# == Schema Information +# +# Table name: pages +# +# id :integer not null, primary key +# user_id :integer not null +# project_id :integer not null +# identifier :string(255) +# site_name :string(255) +# language_frame :integer default("0") +# theme :string(255) +# last_build_at :datetime +# state :boolean default("1") +# state_description :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# last_build_info :text(65535) +# build_state :boolean +# +# Indexes +# +# index_pages_on_project_id (project_id) +# index_pages_on_user_id (user_id) +# + +class Page < ApplicationRecord + belongs_to :user + belongs_to :project + + # language_frame 前端语言框架 + enum language_frame: { hugo: 0, jekyll: 1, hexo: 2} + + after_create do + PageService.genernate_user(user_id) + end + + before_save do + if state_changed? && state == false + PageService.close_site(user_id, identifier) + end + end + + def deploy_page(branch) + PageService.deploy_page(branch,self.id) + end + + def url + @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) + "http://#{user.login}.#{@deploy_domain}/#{identifier}" + end + + def build_script_path + "#{language_frame}_build" + end + +end diff --git a/app/models/page_theme.rb b/app/models/page_theme.rb new file mode 100644 index 000000000..bce3d5f70 --- /dev/null +++ b/app/models/page_theme.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: page_themes +# +# id :integer not null, primary key +# name :string(255) not null +# language_frame :integer default("0") +# image_url :string(255) +# clone_url :string(255) not null +# order_index :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# + +class PageTheme < ApplicationRecord + enum language_frame: { hugo: 0, jeklly: 1, hexo: 2} + validates :name, presence: {message: "主题名不能为空"}, uniqueness: {message: "主题名已存在",scope: :language_frame},length: {maximum: 255} + + def image + page_image_url('image') + end + private + + def page_image_url(type) + return nil unless Util::FileManage.exists?(self, type) + Util::FileManage.source_disk_file_url(self, type) + end + +end diff --git a/app/models/praise_tread.rb b/app/models/praise_tread.rb index 0250f012e..0618aa575 100644 --- a/app/models/praise_tread.rb +++ b/app/models/praise_tread.rb @@ -1,19 +1,20 @@ -# == Schema Information -# -# Table name: praise_treads -# -# id :integer not null, primary key -# user_id :integer not null -# praise_tread_object_id :integer -# praise_tread_object_type :string(255) -# praise_or_tread :integer default("1") -# created_at :datetime not null -# updated_at :datetime not null -# -# Indexes -# -# praise_tread (praise_tread_object_id,praise_tread_object_type) -# +# == Schema Information +# +# Table name: praise_treads +# +# id :integer not null, primary key +# user_id :integer not null +# praise_tread_object_id :integer +# praise_tread_object_type :string(255) +# praise_or_tread :integer default("1") +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# praise_tread (praise_tread_object_id,praise_tread_object_type) +# + class PraiseTread < ApplicationRecord diff --git a/app/models/project.rb b/app/models/project.rb index a63a8dd42..5f9fcef68 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3,7 +3,7 @@ # Table name: projects # # id :integer not null, primary key -# name :string(255) +# name :string(190) # description :text(4294967295) # homepage :string(255) default("") # is_public :boolean default("1"), not null @@ -57,17 +57,22 @@ # lesson_url :string(255) # is_pinned :boolean default("0") # recommend_index :integer default("0") +# use_blockchain :boolean default("0") +# pr_view_admin :boolean default("0") # # Indexes # +# index_projects_on_forked_count (forked_count) # index_projects_on_forked_from_project_id (forked_from_project_id) # index_projects_on_identifier (identifier) # index_projects_on_invite_code (invite_code) +# index_projects_on_is_pinned (is_pinned) # index_projects_on_is_public (is_public) # index_projects_on_lft (lft) # index_projects_on_license_id (license_id) # index_projects_on_name (name) # index_projects_on_platform (platform) +# index_projects_on_praises_count (praises_count) # index_projects_on_project_category_id (project_category_id) # index_projects_on_project_language_id (project_language_id) # index_projects_on_project_type (project_type) @@ -75,6 +80,7 @@ # index_projects_on_rgt (rgt) # index_projects_on_status (status) # index_projects_on_updated_on (updated_on) +# index_projects_on_user_id (user_id) # class Project < ApplicationRecord @@ -114,6 +120,7 @@ class Project < ApplicationRecord has_many :issues, dependent: :destroy # has_many :user_grades, dependent: :destroy has_many :attachments, as: :container, dependent: :destroy + has_one :page, dependent: :destroy has_one :project_score, dependent: :destroy has_many :versions, -> { order("versions.created_on DESC, versions.name DESC") }, dependent: :destroy has_many :praise_treads, as: :praise_tread_object, dependent: :destroy diff --git a/app/models/project_category.rb b/app/models/project_category.rb index 97a304259..bc6f8427d 100644 --- a/app/models/project_category.rb +++ b/app/models/project_category.rb @@ -15,6 +15,7 @@ # Indexes # # index_project_categories_on_ancestry (ancestry) +# index_project_categories_on_id (id) # class ProjectCategory < ApplicationRecord diff --git a/app/models/project_language.rb b/app/models/project_language.rb index 0770a1efa..22a4a81ff 100644 --- a/app/models/project_language.rb +++ b/app/models/project_language.rb @@ -9,6 +9,10 @@ # created_at :datetime not null # updated_at :datetime not null # +# Indexes +# +# index_project_languages_on_id (id) +# class ProjectLanguage < ApplicationRecord include Projectable diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index e46777951..0142f27f6 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -24,10 +24,11 @@ # class PullRequest < ApplicationRecord - #status 0 默认未合并, 1表示合并, 2表示请求拒绝(或已关闭) + #status 0 默认未合并, 1表示合并, 2表示请求拒绝(或已关闭) ,3 表示未签署CLA OPEN = 0 MERGED = 1 CLOSED = 2 + NEEDCLA = 3 belongs_to :issue belongs_to :user diff --git a/app/models/repository.rb b/app/models/repository.rb index c60164001..7d3f207ea 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -27,6 +27,8 @@ # # Indexes # +# index_name (project_id) +# index_repositories_on_identifier (identifier) # index_repositories_on_project_id (project_id) # index_repositories_on_user_id (user_id) # @@ -43,6 +45,7 @@ class Repository < ApplicationRecord validates :identifier, presence: true delegate :default_branch, to: :project, allow_nil: true + attr_accessor :auth_token def to_param self.identifier.parameterize diff --git a/app/models/school.rb b/app/models/school.rb index 5b30be9a5..d7a41c914 100644 --- a/app/models/school.rb +++ b/app/models/school.rb @@ -15,23 +15,6 @@ # auto_users_trial :boolean default("0") # shool_code :string(255) # authorization_time :datetime -# ec_auth :integer default("0") -# identifier :string(255) -# is_online :boolean default("0") -# video_name :string(255) -# video_desc :string(255) -# course_link :string(255) -# course_name :string(255) -# partner_id :integer -# customer_id :integer -# school_property_id :integer -# -# Indexes -# -# index_schools_on_customer_id (customer_id) -# index_schools_on_identifier (identifier) -# index_schools_on_partner_id (partner_id) -# index_schools_on_school_property_id (school_property_id) # class School < ApplicationRecord diff --git a/app/models/system_notification_history.rb b/app/models/system_notification_history.rb index b629babdf..9ecfc5bb8 100644 --- a/app/models/system_notification_history.rb +++ b/app/models/system_notification_history.rb @@ -2,16 +2,16 @@ # # 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 +# id :integer not null, primary key +# system_notification_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) +# index_system_notification_histories_on_system_notification_id (system_notification_id) +# index_system_notification_histories_on_user_id (user_id) # class SystemNotificationHistory < ApplicationRecord diff --git a/app/models/template_message_setting.rb b/app/models/template_message_setting.rb index 67f9586d0..5e4907b2e 100644 --- a/app/models/template_message_setting.rb +++ b/app/models/template_message_setting.rb @@ -6,9 +6,9 @@ # type :string(255) # name :string(255) # key :string(255) -# openning :boolean -# notification_disabled :boolean -# email_disabled :boolean +# openning :boolean default("1") +# notification_disabled :boolean default("1") +# email_disabled :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/template_message_setting/create_or_assign.rb b/app/models/template_message_setting/create_or_assign.rb index 4c392b4b7..629051305 100644 --- a/app/models/template_message_setting/create_or_assign.rb +++ b/app/models/template_message_setting/create_or_assign.rb @@ -6,9 +6,9 @@ # type :string(255) # name :string(255) # key :string(255) -# openning :boolean -# notification_disabled :boolean -# email_disabled :boolean +# openning :boolean default("1") +# notification_disabled :boolean default("1") +# email_disabled :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/template_message_setting/manage_project.rb b/app/models/template_message_setting/manage_project.rb index 978761f94..c9b2406d7 100644 --- a/app/models/template_message_setting/manage_project.rb +++ b/app/models/template_message_setting/manage_project.rb @@ -6,9 +6,9 @@ # type :string(255) # name :string(255) # key :string(255) -# openning :boolean -# notification_disabled :boolean -# email_disabled :boolean +# openning :boolean default("1") +# notification_disabled :boolean default("1") +# email_disabled :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/template_message_setting/normal.rb b/app/models/template_message_setting/normal.rb index 771fba2f2..acf44009f 100644 --- a/app/models/template_message_setting/normal.rb +++ b/app/models/template_message_setting/normal.rb @@ -6,9 +6,9 @@ # type :string(255) # name :string(255) # key :string(255) -# openning :boolean -# notification_disabled :boolean -# email_disabled :boolean +# openning :boolean default("1") +# notification_disabled :boolean default("1") +# email_disabled :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/template_message_setting/watch_project.rb b/app/models/template_message_setting/watch_project.rb index 35dfef6db..a07e4bb94 100644 --- a/app/models/template_message_setting/watch_project.rb +++ b/app/models/template_message_setting/watch_project.rb @@ -6,9 +6,9 @@ # type :string(255) # name :string(255) # key :string(255) -# openning :boolean -# notification_disabled :boolean -# email_disabled :boolean +# openning :boolean default("1") +# notification_disabled :boolean default("1") +# email_disabled :boolean default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/timeable_visit_record.rb b/app/models/timeable_visit_record.rb index 8411ddf66..aee1d718f 100644 --- a/app/models/timeable_visit_record.rb +++ b/app/models/timeable_visit_record.rb @@ -5,7 +5,7 @@ # id :integer not null, primary key # time :string(255) # project_id :integer -# visits :integer +# visits :integer default("0") # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/token.rb b/app/models/token.rb index 746af6535..fac516eb8 100644 --- a/app/models/token.rb +++ b/app/models/token.rb @@ -1,18 +1,19 @@ -# == Schema Information -# -# Table name: tokens -# -# id :integer not null, primary key -# user_id :integer default("0"), not null -# action :string(30) default(""), not null -# value :string(40) default(""), not null -# created_on :datetime not null -# -# Indexes -# -# index_tokens_on_user_id (user_id) -# tokens_value (value) UNIQUE -# +# == Schema Information +# +# Table name: tokens +# +# id :integer not null, primary key +# user_id :integer default("0"), not null +# action :string(30) default(""), not null +# value :string(40) default(""), not null +# created_on :datetime not null +# +# Indexes +# +# index_tokens_on_user_id (user_id) +# tokens_value (value) UNIQUE +# + # # This program is free software; you can redistribute it and/or diff --git a/app/models/topic.rb b/app/models/topic.rb index e464859ec..163c2afdd 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # @@ -42,11 +41,36 @@ class Topic < ApplicationRecord end end + + def get_visitor_data + data = { + visits: 0, + created_time: format_time(Time.now), + from:"other" + } + + if self.url.include?("gitlink.org.cn/forums/") || self.url.include?("trustie.net/forums/") + request_memo = Forum::Memos::GetService.call(self.uuid) + data[:visits] = request_memo.nil? ? 0 : request_memo["memo"]["viewed_count"] + data[:created_time] = request_memo.nil? ? format_time(Time.now) : request_memo["memo"]["published_time"] + data[:from] = "forums" + end + + if self.url.include?("gitlink.org.cn/zone/") || self.url.include?("trustie.net/zone/") + request_doc = Getway::Cms::GetService.call(self.uuid) + data[:visits] = request_doc.nil? ? 0 : request_doc["data"]["visits"] + data[:created_time] = request_doc.nil? ? format_time(Time.now) : request_doc["data"]["publishTime"] + data[:from] = "zone" + end + + data + end + private def image_url(type) - return nil unless Util::FileManage.exists?(self, type) - Util::FileManage.source_disk_file_url(self, type) + return nil unless Util::FileManage.exists?(self, type) + Util::FileManage.source_disk_file_url(self, type) end end diff --git a/app/models/topic/activity_forum.rb b/app/models/topic/activity_forum.rb index 8cf9adf83..ee2dc9941 100644 --- a/app/models/topic/activity_forum.rb +++ b/app/models/topic/activity_forum.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/banner.rb b/app/models/topic/banner.rb index e5b77bec0..7abd2ac56 100644 --- a/app/models/topic/banner.rb +++ b/app/models/topic/banner.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/card.rb b/app/models/topic/card.rb index 6a54e17ea..3baee486c 100644 --- a/app/models/topic/card.rb +++ b/app/models/topic/card.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/cooperator.rb b/app/models/topic/cooperator.rb index a023d3656..0fab2c3b7 100644 --- a/app/models/topic/cooperator.rb +++ b/app/models/topic/cooperator.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/excellent_project.rb b/app/models/topic/excellent_project.rb index ac08863c7..aec097f62 100644 --- a/app/models/topic/excellent_project.rb +++ b/app/models/topic/excellent_project.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/experience_forum.rb b/app/models/topic/experience_forum.rb index 855a56809..9b48f9ed4 100644 --- a/app/models/topic/experience_forum.rb +++ b/app/models/topic/experience_forum.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/topic/glcc_news.rb b/app/models/topic/glcc_news.rb index 6b707bf07..4b2e758d2 100644 --- a/app/models/topic/glcc_news.rb +++ b/app/models/topic/glcc_news.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # @@ -14,4 +13,4 @@ # GLCC 新闻稿 class Topic::GlccNews < Topic -end \ No newline at end of file +end diff --git a/app/models/topic/pinned_forum.rb b/app/models/topic/pinned_forum.rb index c5a2c8572..78425ce6a 100644 --- a/app/models/topic/pinned_forum.rb +++ b/app/models/topic/pinned_forum.rb @@ -6,7 +6,6 @@ # type :string(255) # title :string(255) # uuid :integer -# image_url :string(255) # url :string(255) # order_index :integer # diff --git a/app/models/user.rb b/app/models/user.rb index ba8afffa4..8f1e29547 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -39,15 +39,17 @@ # business :boolean default("0") # profile_completed :boolean default("0") # laboratory_id :integer -# is_shixun_marker :boolean default("0") -# admin_visitable :boolean default("0") -# collaborator :boolean default("0") +# platform :string(255) default("0") +# gitea_token :string(255) # gitea_uid :integer +# is_shixun_marker :boolean default("0") # is_sync_pwd :boolean default("1") # watchers_count :integer default("0") # devops_step :integer default("0") -# gitea_token :string(255) -# platform :string(255) +# sign_cla :boolean default("0") +# enabling_cla :boolean default("0") +# id_card_verify :boolean default("0") +# website_permission :boolean default("0") # # Indexes # @@ -56,8 +58,7 @@ # index_users_on_homepage_teacher (homepage_teacher) # index_users_on_laboratory_id (laboratory_id) # index_users_on_login (login) UNIQUE -# index_users_on_mail (mail) UNIQUE -# index_users_on_phone (phone) UNIQUE +# index_users_on_mail (mail) # index_users_on_type (type) # @@ -121,19 +122,20 @@ class User < Owner has_many :open_users, dependent: :destroy has_one :wechat_open_user, class_name: 'OpenUsers::Wechat' has_one :qq_open_user, class_name: 'OpenUsers::Qq' + has_one :identity_verification accepts_nested_attributes_for :user_extension, update_only: true has_many :fork_users, dependent: :destroy has_many :versions has_many :issue_times, :dependent => :destroy - has_one :onclick_time, :dependent => :destroy + # has_one :onclick_time, :dependent => :destroy # 新版私信 - has_many :private_messages, dependent: :destroy + # has_many :private_messages, dependent: :destroy has_many :recent_contacts, through: :private_messages, source: :target has_many :tidings, :dependent => :destroy - has_many :journals_for_messages, :as => :jour, :dependent => :destroy + # has_many :journals_for_messages, :as => :jour, :dependent => :destroy has_many :attachments,foreign_key: :author_id, :dependent => :destroy @@ -143,7 +145,7 @@ class User < Owner has_many :apply_user_authentication has_one :process_real_name_apply, -> { processing.real_name_auth.order(created_at: :desc) }, class_name: 'ApplyUserAuthentication' has_one :process_professional_apply, -> { processing.professional_auth.order(created_at: :desc) }, class_name: 'ApplyUserAuthentication' - has_many :apply_actions, dependent: :destroy + # has_many :apply_actions, dependent: :destroy has_many :trail_auth_apply_actions, -> { where(container_type: 'TrialAuthorization') }, class_name: 'ApplyAction' # has_many :attendances @@ -183,12 +185,18 @@ class User < Owner has_many :issue_participants, foreign_key: :participant_id has_many :participant_issues, through: :issue_participants, source: :issue has_many :project_topics + #cla + has_many :user_clas, :dependent => :destroy + has_many :clas, through: :user_clas + + has_many :pages, :dependent => :destroy + # Groups and active users scope :active, lambda { where(status: [STATUS_ACTIVE, STATUS_EDIT_INFO]) } scope :like, lambda { |keywords| # 表情处理 keywords = keywords.to_s.each_char.select { |c| c.bytes.first < 240 }.join('') - sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR nickname LIKE :search" + sql = "CONCAT(lastname, firstname) LIKE :search OR nickname LIKE :search OR login LIKE :search OR mail LIKE :search OR phone LIKE :search" where(sql, :search => "%#{keywords.strip}%") unless keywords.blank? } @@ -463,6 +471,23 @@ class User < Owner end end + + def register_gitea + psd = "12345678" + interactor = Gitea::RegisterInteractor.call({username: self.login, email: self.mail, password: psd}) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(self.login, psd) + self.gitea_token = result['sha1'] + self.gitea_uid = gitea_user[:body]['id'] + self.password = psd + self.password_confirmation = psd + if self.save! + UserExtension.create!(user_id: self.id) + end + end + end + def activate! update_attribute(:status, STATUS_ACTIVE) prohibit_gitea_user_login!(false) @@ -738,6 +763,7 @@ class User < Owner if password salt_password(password) end + check_website_permission end def salt_password(clear_password) @@ -745,6 +771,13 @@ class User < Owner self.hashed_password = User.hash_password("#{salt}#{User.hash_password clear_password}") end + def check_website_permission + if website_permission_changed? && website_permission == false + self.pages.update_all(state: false, state_description:"因违规使用,现关闭Page服务") + PageService.close_site(self.id) + end + end + def self.generate_salt Gitlink::Utils.random_hex(16) end @@ -838,7 +871,8 @@ class User < Owner end def profile_is_completed? - self.nickname.present? && self.mail.present? + #self.nickname.present? && self.mail.present? + self.mail.present? end def trace_token @@ -861,7 +895,7 @@ class User < Owner # 重写gitea_token,当用户为bot类型时,替换成管理员token def gitea_token - if self.platform == "bot" + if self.respond_to?('platform') && self.platform == "bot" GiteaService.gitea_config[:admin_token] else self['gitea_token'] diff --git a/app/models/user_action.rb b/app/models/user_action.rb index 179359695..b4173fe77 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -9,12 +9,14 @@ # created_at :datetime not null # updated_at :datetime not null # ip :string(255) +# data_bank :text(65535) # # Indexes # -# index_user_actions_on_ip (ip) -# index_user_actions_on_user_id (user_id) -# index_user_actions_on_user_id_and_action_type (user_id,action_type) +# index_user_actions_on_action_id (action_id) +# index_user_actions_on_action_type (action_type) +# index_user_actions_on_ip (ip) +# index_user_actions_on_user_id (user_id) # class UserAction < ApplicationRecord diff --git a/app/models/user_agent.rb b/app/models/user_agent.rb index ba519d6fb..49d7b35a1 100644 --- a/app/models/user_agent.rb +++ b/app/models/user_agent.rb @@ -10,13 +10,10 @@ # updated_at :datetime not null # register_status :integer default("0") # action_status :integer default("0") -# is_delete :boolean default("0") -# user_id :integer # # Indexes # -# index_user_agents_on_ip (ip) -# index_user_agents_on_user_id (user_id) +# index_user_agents_on_ip (ip) UNIQUE # class UserAgent < ApplicationRecord diff --git a/app/models/user_cla.rb b/app/models/user_cla.rb new file mode 100644 index 000000000..b109a74fa --- /dev/null +++ b/app/models/user_cla.rb @@ -0,0 +1,63 @@ +# == Schema Information +# +# Table name: user_clas +# +# id :integer not null, primary key +# user_id :integer not null +# cla_id :integer not null +# real_name :string(255) not null +# email :string(255) not null +# state :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# sign_time :datetime +# +# Indexes +# +# index_user_clas_on_cla_id (cla_id) +# index_user_clas_on_user_id (user_id) +# + +class UserCla < ApplicationRecord + belongs_to :user + belongs_to :cla +# identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者 + enum state: { deafult: 0, signed: 1, failed: 2} + + after_save do + cla.fresh_count + end + + before_save do + fresh_pull_request + end + + def self.build(params,current_user_id) + self.create!(user_id: current_user_id, + cla_id: params[:cla_id], + real_name: params[:real_name], + email: params[:email], + sign_time: Time.now, + state: 1 + ) + end + + def update_by_params(params) + update(\ + state: 1, + sign_time: Time.now, + real_name: params[:real_name], + email: params[:email], + ) + end + + def fresh_pull_request + project_ids = cla.organization.projects.pluck(:id) + if state == "signed" + PullRequest.where(user_id: user_id, project_id: project_ids, status:3).update_all(status:0) + else + PullRequest.where(user_id: user_id, project_id: project_ids, status:0).update_all(status:3) + end + end + +end diff --git a/app/models/user_extension.rb b/app/models/user_extension.rb index c1046ac2f..aeb9a9d83 100644 --- a/app/models/user_extension.rb +++ b/app/models/user_extension.rb @@ -20,11 +20,11 @@ # student_realname :string(255) # location_city :string(255) # school_id :integer -# description :string(255) default("") +# description :string(255) # department_id :integer -# province :string(255) -# city :string(255) +# province :text(65535) # custom_department :string(255) +# city :string(255) # show_email :boolean default("0") # show_location :boolean default("0") # show_department :boolean default("0") diff --git a/app/models/version.rb b/app/models/version.rb index 82474f55e..ec2ad68cd 100644 --- a/app/models/version.rb +++ b/app/models/version.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # project_id :integer default("0"), not null -# name :string(255) default(""), not null +# name :string(255) # description :text(65535) # effective_date :date # created_on :datetime diff --git a/app/models/version_release.rb b/app/models/version_release.rb index 00119f806..98c81b61d 100644 --- a/app/models/version_release.rb +++ b/app/models/version_release.rb @@ -4,9 +4,9 @@ # # id :integer not null, primary key # user_id :integer -# name :string(255) +# name :text(4294967295) # body :text(65535) -# tag_name :string(255) +# tag_name :text(65535) # target_commitish :string(255) # draft :boolean default("0") # prerelease :boolean default("0") diff --git a/app/queries/admins/glcc_examine_material.rb b/app/queries/admins/glcc_examine_material.rb new file mode 100644 index 000000000..4e8f2bea7 --- /dev/null +++ b/app/queries/admins/glcc_examine_material.rb @@ -0,0 +1,29 @@ +class Admins::GlccExamineMaterial < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_on, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + materials = GlccMediumTermExamineMaterial.all + + # term + term = params[:term] || [1,2] + if term.present? + materials = materials.where(term: term) + end + #year + year = if params[:date] + params[:date][:year] + end + year = year || Time.now.year + date = Time.now.change(year:year) + materials = materials.where(created_on: [date.beginning_of_year..date.end_of_year]) + custom_sort(materials, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/identity_verification_query.rb b/app/queries/admins/identity_verification_query.rb new file mode 100644 index 000000000..b0c685ae2 --- /dev/null +++ b/app/queries/admins/identity_verification_query.rb @@ -0,0 +1,17 @@ +class Admins::IdentityVerificationQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + state = params[:state] == "all" ? [0,1,2] : params[:state].nil? ? [0] : params[:state].to_i + applies = IdentityVerification.where(state: state) + custom_sort(applies, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/organization_query.rb b/app/queries/admins/organization_query.rb index 09dbab9e3..ff3f301be 100644 --- a/app/queries/admins/organization_query.rb +++ b/app/queries/admins/organization_query.rb @@ -9,6 +9,8 @@ class Admins::OrganizationQuery < ApplicationQuery def call orgs = Organization.all + + orgs = orgs.where(enabling_cla: params[:enabling_cla]) if params[:enabling_cla].present? # 关键字检索 keyword = params[:keyword].to_s.strip.presence if keyword diff --git a/app/queries/admins/page_themes_query.rb b/app/queries/admins/page_themes_query.rb new file mode 100644 index 000000000..72d40943a --- /dev/null +++ b/app/queries/admins/page_themes_query.rb @@ -0,0 +1,17 @@ +class Admins::PageThemesQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + language_frame = params[:language_frame].blank? ? [0..99] : params[:language_frame] + page_themes = PageTheme.where(language_frame: language_frame) + custom_sort(page_themes, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/site_pages_query.rb b/app/queries/admins/site_pages_query.rb new file mode 100644 index 000000000..75694157c --- /dev/null +++ b/app/queries/admins/site_pages_query.rb @@ -0,0 +1,24 @@ +class Admins::SitePagesQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + state = params[:state].blank? ? [true,false] : params[:state] + pages = Page.joins(:user).where(state: state) + # 关键字检索 + keyword = params[:keyword].to_s.strip.presence + if keyword + sql = 'users.nickname LIKE :keyword OR users.login LIKE :keyword OR users.mail LIKE :keyword OR users.phone LIKE :keyword' + pages = pages.where(sql, keyword: "%#{keyword}%") + end + + custom_sort(pages, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/page_query.rb b/app/queries/page_query.rb new file mode 100644 index 000000000..d9496b30e --- /dev/null +++ b/app/queries/page_query.rb @@ -0,0 +1,13 @@ +class PageQuery < ApplicationQuery + attr_reader :params + + def initialize(params, user) + @user = user + @params = params + end + + def call + pages = Page.where(user: @user).order(created_at: :desc) + pages + end +end \ No newline at end of file diff --git a/app/queries/projects/list_query.rb b/app/queries/projects/list_query.rb index 72776f7a2..c67feed61 100644 --- a/app/queries/projects/list_query.rb +++ b/app/queries/projects/list_query.rb @@ -29,6 +29,7 @@ class Projects::ListQuery < ApplicationQuery def filter_projects(collection) # collection = by_pinned(collection) collection = by_search(collection) if params[:search].present? + collection = collection.where(id: params[:ids].to_s.split(",")) if params[:ids].present? collection = by_project_type(collection) collection = by_project_category(collection) collection = by_project_language(collection) diff --git a/app/services/admins/delete_organization_service.rb b/app/services/admins/delete_organization_service.rb index d5c9bd2c5..2a7b4aab1 100644 --- a/app/services/admins/delete_organization_service.rb +++ b/app/services/admins/delete_organization_service.rb @@ -6,9 +6,6 @@ class Admins::DeleteOrganizationService < Gitea::ClientService end def call - response = delete(url, params) - render_status(response) - Gitea::Organization::DeleteService.call(token,name) end diff --git a/app/services/admins/save_laboratory_setting_service.rb b/app/services/admins/save_laboratory_setting_service.rb index c29e374bd..ad040585f 100644 --- a/app/services/admins/save_laboratory_setting_service.rb +++ b/app/services/admins/save_laboratory_setting_service.rb @@ -30,6 +30,7 @@ class Admins::SaveLaboratorySettingService < ApplicationService hash = {} hash[:name] = strip nav[:name] hash[:link] = strip nav[:link] + hash[:index] = strip nav[:index] hash[:hidden] = nav[:hidden].to_s != '0' hash end diff --git a/app/services/admins/update_user_service.rb b/app/services/admins/update_user_service.rb index 9d116cffe..4dd80b9d0 100644 --- a/app/services/admins/update_user_service.rb +++ b/app/services/admins/update_user_service.rb @@ -32,7 +32,7 @@ class Admins::UpdateUserService < ApplicationService def user_attributes params.slice(*%i[lastname nickname mail phone admin business is_test login - professional_certification authentication is_shixun_marker]) + professional_certification authentication is_shixun_marker website_permission]) end def user_extension_attributes diff --git a/app/services/api/v1/issues/list_service.rb b/app/services/api/v1/issues/list_service.rb index 5a3f97e98..b6ef11789 100644 --- a/app/services/api/v1/issues/list_service.rb +++ b/app/services/api/v1/issues/list_service.rb @@ -76,7 +76,7 @@ class Api::V1::Issues::ListService < ApplicationService end # keyword - issues = issues.ransack(id_eq: keyword).result.or(issues.ransack(subject_or_description_cont: keyword).result) if keyword.present? + issues = issues.ransack(id_or_project_issues_index_eq: keyword).result.or(issues.ransack(subject_or_description_cont: keyword).result) if keyword.present? @total_issues_count = issues.distinct.size @closed_issues_count = issues.closed.distinct.size diff --git a/app/services/api/v1/projects/contributors/stat_service.rb b/app/services/api/v1/projects/contributors/stat_service.rb index d264a109b..ab20dd657 100644 --- a/app/services/api/v1/projects/contributors/stat_service.rb +++ b/app/services/api/v1/projects/contributors/stat_service.rb @@ -6,7 +6,7 @@ class Api::V1::Projects::Contributors::StatService < ApplicationService def initialize(project, params, token=nil) @project = project @branch = params[:branch] - @pass_year = params[:pass_year] + @pass_year = params[:pass_year] || 3 @page = params[:page] || 1 @limit = params[:limit] || 15 @owner = project&.owner.login @@ -23,10 +23,10 @@ class Api::V1::Projects::Contributors::StatService < ApplicationService private def request_params param = { - access_token: token + access_token: token, page: page, limit: limit } param.merge!(branch: branch) if branch.present? - param.merge!(pass_year: pass_year) if pass_year.present? + param.merge!(pass_year: @pass_year) param end diff --git a/app/services/api_limit_service.rb b/app/services/api_limit_service.rb new file mode 100644 index 000000000..d22684962 --- /dev/null +++ b/app/services/api_limit_service.rb @@ -0,0 +1,137 @@ +class ApiLimitService < ApplicationService + Error = Class.new(StandardError) + + def initialize() end + + def call + + end + + # 时间窗口法 + def is_action_allowed?(user_id, action_key, period, max_count) + key = "#{user_id}:#{action_key}" + count = $redis_cache.multi do |multi| + multi.incr(key) + multi.expire(key, period) + end + count[0] <= max_count + end + + # 漏桶法 + def is_action_allowed_bucket?(user_id, action_key, capacity, rate) + key = "#{user_id}:#{action_key}" + # now = (("%10.3f" % Time.now.to_f).to_f * 1000).to_i + now = Time.now.to_i + count = $redis_cache.multi do |multi| + multi.zadd(key, now, SecureRandom.uuid.gsub("-", "")) + multi.zremrangebyscore(key, 0, now - capacity) + multi.zcard(key) + multi.expire(key, (capacity / rate + 1).to_i) + end + # puts "count1==#{count}" + # puts "count2==#{count[2]}" + count[2] <= capacity + end + + def is_action_allowed_aaa?(user_id, action_key, period, maxCount) + key = "#{user_id}:#{action_key}" + now = (("%10.3f" % Time.now.to_f).to_f * 1000).to_i + count = $redis_cache.multi do |multi| + # 添加命令 + multi.zadd(key, now, SecureRandom.uuid.gsub("-", "")) + # 清楚无用数据 + multi.zremrangebyscore(key, 0, now - period * 1000) + # 判断规定时间内请求数量 + multi.zcard(key) + # 重新设置过期时间 + multi.expire(key, period + 1) + end + # puts "count1==#{count}" + # puts "count2==#{count[2]}" + count[2] <= maxCount + end + + def sdfsf + + # 每秒钟漏斗的容量 + + capacity = 10 + + + # 漏斗填充速度 + + leak_rate = 0.3 + + # 当前漏斗中的水量 + + current_volume = 0 + + # 上一次漏水的时间 + + last_leak_time = Time.now.to_i + + funnel_name = "tests" + + # $redis_cache.hset(funnel_name, "volume", capacity) + $redis_cache.hset(funnel_name, "last_leak_time", Time.now.to_i) + + # 构造事务命令 + + # result = $redis_cache.multi do |pipe| + # # 把当前时间与上一次漏水时间的差值,作为漏斗流出水量 + # + # pipe.hget(funnel_name, "last_leak_time") + # + # pipe.hset(funnel_name, "last_leak_time", Time.now.to_i) + # + # pipe.hget(funnel_name, "volume") + # + # pipe.hget(funnel_name, "capacity") + # + # pipe.hget(funnel_name, "leak_rate") + # end + + current_volume = $redis_cache.hget(funnel_name, "volume") + last_leak_time = $redis_cache.hget(funnel_name, "last_leak_time") + # 先漏水 + leaked_volume = (Time.now.to_i - last_leak_time.to_i) * leak_rate.to_f + + current_volume = [current_volume.to_f + leaked_volume, capacity.to_f].sort.first + puts "current_volume====#{current_volume}" + # 判断是否可以通过请求 + if current_volume >= 1 + $redis_cache.hset(funnel_name, "volume", current_volume.to_i - 1) + # $redis_cache.hset(funnel_name, "last_leak_time", Time.now.to_i) + true + else + false + end + + end + + def done_test3 + 100.times.each do |t| + if t % 2== 0 + sleep(1) + end + puts "#{t}:res====#{sdfsf}" + end + end + + def done_test + 100.times.each do |t| + if t % 10== 0 + sleep(5) + end + puts "#{t}:res====#{is_action_allowed_bucket?("123", "test2", 10, 0.2)}" + end + end + + def done_test2 + 100.times.each do |t| + sleep(1) + puts "#{t}:res====#{is_action_allowed_aaa?("123", "test3", 10, 5)}" + end + end + +end diff --git a/app/services/getway/client_service.rb b/app/services/getway/client_service.rb new file mode 100644 index 000000000..cd774772e --- /dev/null +++ b/app/services/getway/client_service.rb @@ -0,0 +1,94 @@ +class Getway::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 + Getway.getway_config[:base_url] + end + + def domain + Getway.getway_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 "[getway] 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 diff --git a/app/services/getway/cms/get_service.rb b/app/services/getway/cms/get_service.rb new file mode 100644 index 000000000..6b7050e84 --- /dev/null +++ b/app/services/getway/cms/get_service.rb @@ -0,0 +1,21 @@ +class Getway::Cms::GetService < Getway::ClientService + attr_reader :doc_id + + def initialize(doc_id) + @doc_id = doc_id + end + + def call + response = get(url) + code, message, body = render_response(response) + if code == 200 && body["code"] == 200 + return body + else + return nil + end + end + + def url + "/cms/doc/open/#{doc_id}".freeze + end +end \ No newline at end of file diff --git a/app/services/gitea/client_service.rb b/app/services/gitea/client_service.rb index 62e040d82..5418be159 100644 --- a/app/services/gitea/client_service.rb +++ b/app/services/gitea/client_service.rb @@ -115,6 +115,9 @@ class Gitea::ClientService < ApplicationService url = [api_url(is_hat), api_rest].join('').freeze url = action === 'get' ? url : URI.escape(url) url = URI.escape(url) unless url.ascii_only? + url = url.gsub(")", "%29") if url.to_s.include?(")") + url = url.gsub("]", "%5D") if url.to_s.include?("]") + url = url.gsub("[", "%5B") if url.to_s.include?("[") puts "[gitea] request url: #{url}" return url end diff --git a/app/services/gitea/commit/diff_service.rb b/app/services/gitea/commit/diff_service.rb index 712302505..76bba852a 100644 --- a/app/services/gitea/commit/diff_service.rb +++ b/app/services/gitea/commit/diff_service.rb @@ -16,7 +16,7 @@ class Gitea::Commit::DiffService < Gitea::ClientService end def call - response = get(url, params) + response = get(url, params, true) render_result(response) end diff --git a/app/services/gitea/repository/commits/file_list_service.rb b/app/services/gitea/repository/commits/file_list_service.rb index 14c873fd3..b0bba7abe 100644 --- a/app/services/gitea/repository/commits/file_list_service.rb +++ b/app/services/gitea/repository/commits/file_list_service.rb @@ -20,7 +20,7 @@ class Gitea::Repository::Commits::FileListService < Gitea::ClientService private def params - {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "", stat: args[:page].to_i != 1 } + {sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "", stat: args[:page].to_i != 1 && args[:limit] !=1 } end def url diff --git a/app/services/gitea/repository/commits/list_service.rb b/app/services/gitea/repository/commits/list_service.rb index 124cf544d..700508539 100644 --- a/app/services/gitea/repository/commits/list_service.rb +++ b/app/services/gitea/repository/commits/list_service.rb @@ -19,7 +19,7 @@ class Gitea::Repository::Commits::ListService < Gitea::ClientService private def params - { sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "" } + { sha: args[:sha] || 'master', page: args[:page] || PAGINATE_DEFAULT_PAGE, limit: args[:limit] || PAGINATE_DEFAULT_LIMIT, token: args[:token] || "", stat: args[:page].to_i != 1 && args[:limit] !=1 } end def url diff --git a/app/services/gitea/repository/entries/create_service.rb b/app/services/gitea/repository/entries/create_service.rb index 14b373335..ac27b3afb 100644 --- a/app/services/gitea/repository/entries/create_service.rb +++ b/app/services/gitea/repository/entries/create_service.rb @@ -53,7 +53,7 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService def status_payload(status, body) case status when 201 then success(json_parse!(body)) - when 403 then error("你没有权限操作!") + when 403 then error("你没有权限操作,请确认该分支是否是保护分支。") when 404 then error("你操作的链接不存在!") when 422 if @body[:new_branch].present? && (@body[:new_branch].include?('/') || @body[:new_branch].include?('\'') || @body[:new_branch].include?('^') || @body[:new_branch].include?('*')) @@ -61,7 +61,9 @@ class Gitea::Repository::Entries::CreateService < Gitea::ClientService else error("#{filepath}文件已存在,不能重复创建!") end - else error("系统错误!") + else + Rails.logger.error("Gitea api url==#{url},status:#{status},body=#{body}") + error("系统错误!") end end end diff --git a/app/services/gitea/repository/fork_service.rb b/app/services/gitea/repository/fork_service.rb index c43a9ddf2..ca0c0fe68 100644 --- a/app/services/gitea/repository/fork_service.rb +++ b/app/services/gitea/repository/fork_service.rb @@ -1,5 +1,5 @@ class Gitea::Repository::ForkService < Gitea::ClientService - attr_reader :old_owner, :target_owner, :repo_name, :organization + attr_reader :old_owner, :target_owner, :repo_name, :organization, :new_identifier # old_owner: 被clone的项目(源项目)拥有者 # target_owner: clone后的醒目(新项目)的拥有者 @@ -7,10 +7,12 @@ class Gitea::Repository::ForkService < Gitea::ClientService # { # "organization": "string" #组织名称 # } - def initialize(old_owner, target_owner, repo_name, organization=nil) + def initialize(old_owner, target_owner, repo_name, organization=nil, new_identifier=nil) @old_owner = old_owner @target_owner = target_owner @repo_name = repo_name + @organization = organization + @new_identifier = new_identifier end def call @@ -24,6 +26,7 @@ class Gitea::Repository::ForkService < Gitea::ClientService def request_params hash = Hash.new.merge(token: target_owner.gitea_token) hash = hash.merge(data: {organization: organization}) if organization + hash = hash.merge(data: {name: new_identifier}) if new_identifier hash end diff --git a/app/services/gitea/repository/migrate_service.rb b/app/services/gitea/repository/migrate_service.rb index ab60b42f3..c605df0bb 100644 --- a/app/services/gitea/repository/migrate_service.rb +++ b/app/services/gitea/repository/migrate_service.rb @@ -34,6 +34,9 @@ class Gitea::Repository::MigrateService < Gitea::ClientService response = post(url, request_params) render_response(response) + rescue => e + puts "MigrateService error: #{e.message}" + [500, e.message, ""] end private diff --git a/app/services/gitea/user/get_token_service.rb b/app/services/gitea/user/get_token_service.rb index 22d9cc590..d751ca9f4 100644 --- a/app/services/gitea/user/get_token_service.rb +++ b/app/services/gitea/user/get_token_service.rb @@ -8,20 +8,17 @@ class Gitea::User::GetTokenService < Gitea::ClientService def call params = {} - url = "/users/#{username}".freeze - params = params.merge(data: request_params) - get(url, params) + url = "/users/#{username}/tokens".freeze + params = params.merge(sudo: username, page: 1, limit: 200, token: token) + response = get(url, params) + render_status(response) end private - # def token_params - # { - # username: username, - # password: password - # } - # end - - def request_params - { username: username } + def token + { + username: GiteaService.gitea_config[:access_key_id], + password: GiteaService.gitea_config[:access_key_secret] + } end end diff --git a/app/services/gitea/user/register_service.rb b/app/services/gitea/user/register_service.rb index f9e42e9e5..afea81080 100644 --- a/app/services/gitea/user/register_service.rb +++ b/app/services/gitea/user/register_service.rb @@ -31,7 +31,7 @@ class Gitea::User::RegisterService < Gitea::ClientService case status when 201 then success(body) else - error(message, status) + error(message || body["message"], status) end end diff --git a/app/services/leaky_bucket_rate_limiter.rb b/app/services/leaky_bucket_rate_limiter.rb new file mode 100644 index 000000000..2508e99e0 --- /dev/null +++ b/app/services/leaky_bucket_rate_limiter.rb @@ -0,0 +1,42 @@ +class LeakyBucketRateLimiter + def initialize(user_id, rate, capacity) + @rate = rate # 令牌发放速率(每秒发放的令牌数) + @capacity = capacity # 桶的容量(最大令牌数) + @redis = $redis_cache + @key = "#{user_id}:LeakyBucket" + @current_time = Time.now.to_f + end + + def allow_request + current_time = Time.now.to_f + last_drip_time = @redis.getset(@key + ':last_drip_time', current_time).to_f + tokens = @redis.getset(@key + ':tokens', @capacity).to_i + + # 计算已经漏掉的令牌数量 + leaked_tokens = (current_time - last_drip_time) * @rate + puts leaked_tokens + # 如果桶中的令牌多于漏掉的令牌数量,则漏水 + tokens = [@capacity, tokens + leaked_tokens].min + puts tokens + if tokens >= 1 + @redis.set(@key + ':last_drip_time', current_time) + @redis.decr(@key + ':tokens') + return true + end + false + end +end + + +=begin + 失败 + limiter = LeakyBucketRateLimiter.new(110,10, 40) # 设置令牌发放速率为10,桶的容量为40 + 30.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file diff --git a/app/services/page_service.rb b/app/services/page_service.rb new file mode 100644 index 000000000..687added1 --- /dev/null +++ b/app/services/page_service.rb @@ -0,0 +1,58 @@ +require 'net/http' +require 'uri' + +class PageService + + def self.get_deploykey() + @deploy_key = EduSetting.find_by_name("site_page_deploy_key").try(:value) + @deploy_domain = EduSetting.find_by_name("site_page_deploy_domain").try(:value) + end + + + def self.genernate_user(user_id) + get_deploykey() + Rails.logger.info "################### PageService genernate_user #{user_id}" + user = User.find user_id + if user.id_card_verify == true && user.website_permission == true + uri = URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=create_dir&owner=#{user.login.downcase}") + response = Net::HTTP.get_response(uri) + end + Rails.logger.info "################### PageService genernate_user end #{response.body}" + return response.body + end + + def self.close_site(user_id,identifier=nil) + get_deploykey() + Rails.logger.info "################### PageService close_site #{user_id} / #{identifier}" + user = User.find user_id + uri = if identifier.present? + URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/#{identifier}/") + else + URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=remove_dir&owner=#{user.login.downcase}/") + end + response = Net::HTTP.get_response(uri) + Rails.logger.info "################### PageService close_site end #{response.body}" + return response.body + end + + def self.deploy_page(branch, page_id) + get_deploykey() + Rails.logger.info "################### PageService deploy #{branch} for page #{page_id}" + page = Page.find page_id + user = page.user + project = page.project + owner = user.login.downcase + + project_dir = page.identifier + repo_link = project.repository.url + repo = project.repository.identifier + branch = branch + script_path =page.build_script_path + if script_path.present? + uri = URI.parse("http://gitlink.#{@deploy_domain}/gitlink_execute_script?key=#{@deploy_key}&script_path=#{script_path}&project_dir=#{project_dir}&repo=#{repo}&repo_link=#{repo_link}&branch=#{branch}&owner=#{owner}") + response = Net::HTTP.get_response(uri) + Rails.logger.info "################### PageService deploy #{response.body}" + return response.body + end + end +end \ No newline at end of file diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index f8b38df47..37edf56ee 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -1,10 +1,12 @@ class Projects::ForkService < ApplicationService - attr_reader :target_owner, :project, :organization + attr_reader :target_owner, :project, :organization, :new_name, :new_identifier - def initialize(target_owner, project, organization=nil) + def initialize(target_owner, project, organization=nil, new_name=nil, new_identifier=nil) @target_owner = target_owner @project = project @organization = organization + @new_name = new_name + @new_identifier = new_identifier end def call @@ -15,11 +17,12 @@ class Projects::ForkService < ApplicationService :rep_identifier, :project_category_id, :project_language_id, :license_id, :ignore_id, {repository: [:identifier, :hidden]}] - result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization).call - + result = Gitea::Repository::ForkService.new(@project.owner, @target_owner, @project.identifier, @organization, @new_identifier).call clone_project.owner = @target_owner clone_project.forked_from_project_id = @project.id clone_project.gpid = result['id'] + clone_project.name = @new_name if @new_name.present? + clone_project.identifier = @new_identifier if @new_identifier.present? clone_project.save! new_repository = clone_project.repository diff --git a/app/services/projects/migrate_service.rb b/app/services/projects/migrate_service.rb index 68ed9f642..f157ada1d 100644 --- a/app/services/projects/migrate_service.rb +++ b/app/services/projects/migrate_service.rb @@ -9,7 +9,6 @@ class Projects::MigrateService < ApplicationService def call raise Error, "user_id不正确." unless authroize_user_id_success - @project = Project.new(project_params) if @project.save! ProjectUnit.init_types(@project.id, project.project_type) @@ -55,6 +54,7 @@ class Projects::MigrateService < ApplicationService user_id: params[:user_id], login: params[:auth_username], password: params[:auth_password], + auth_token: params[:auth_token], is_mirror: params[:is_mirror], source_clone_url: params[:source_clone_url] } diff --git a/app/services/projects/verify_auth_token_service.rb b/app/services/projects/verify_auth_token_service.rb new file mode 100644 index 000000000..36dcb106f --- /dev/null +++ b/app/services/projects/verify_auth_token_service.rb @@ -0,0 +1,83 @@ +class Projects::VerifyAuthTokenService < ApplicationService + attr_accessor :url, :token + + def initialize(url, token) + @url = url + @token = token + @repo = nil + @owner = nil + @website = nil + @success = false + end + + def call + Rails.logger.info("###### VerifyAuthTokenService begin ######") + regular_url + to_verify + Rails.logger.info("##### VerifyAuthTokenService end ######") + return @success + end + + private + def regular_url + regx = /\/\/[\s\S]*.git$/ #获取字串 + data = (regx.match @url).to_s[2..-5].split("/") + @website = data[0] + @owner = data[1] + @repo = data[2] + end + + + def to_verify + data = case @website + when "github.com" + github_verify + when "gitlab.com" + gitlab_verify + when "gitee.com" + gitee_verify + end + end + + def gitee_verify + url = "/api/v5/repos/#{@owner}/#{@repo}" + api_url= "https://gitee.com" + client = Faraday.new(url: api_url) + client.options["open_timeout"] = 1 + client.options["timeout"] = 1 + client.options["write_timeout"] = 1 + req_params={ + access_token: @token, + owner: @owner, + repo: @repo + } + response = client.public_send("get", url, req_params) + @success = true if response.status == 200 + end + + def github_verify + url = "/octocat" + api_url= "https://api.github.com" + client = Faraday.new(url: api_url) + client.options["open_timeout"] = 1 + client.options["timeout"] = 1 + client.options["write_timeout"] = 1 + client.headers["Authorization"] = "Bearer #{@token}" + response = client.public_send("get", url) + @success = true if response.status == 200 + end + + def gitlab_verify + url = "/api/v4/projects" + api_url= "https://gitlab.com" + client = Faraday.new(url: api_url) + client.options["open_timeout"] = 1 + client.options["timeout"] = 1 + client.options["write_timeout"] = 1 + req_params={ + private_token: @token + } + response = client.public_send("get", url, req_params) + @success = true if response.status == 200 + end +end \ No newline at end of file diff --git a/app/services/pull_requests/create_service.rb b/app/services/pull_requests/create_service.rb index 2b4767f31..070b564d9 100644 --- a/app/services/pull_requests/create_service.rb +++ b/app/services/pull_requests/create_service.rb @@ -180,7 +180,7 @@ class PullRequests::CreateService < ApplicationService end def compare_head_base! - head = pull_request.is_original && @params[:merge_user_login] ? "#{@params[:merge_user_login]}/#{@project.identifier}:#{@params[:head]}" : @params[:head] + head = pull_request.is_original && @params[:merge_user_login] ? "#{@params[:merge_user_login]}/#{@params[:merge_project_identifier]}:#{@params[:head]}" : @params[:head] compare_result = Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, Addressable::URI.escape(@params[:base]), Addressable::URI.escape(head), @current_user.gitea_token) raise '分支内容相同,无需创建合并请求' if compare_result["Commits"].blank? && compare_result["Diff"].blank? end diff --git a/app/services/pull_requests/send_journal_service.rb b/app/services/pull_requests/send_journal_service.rb new file mode 100644 index 000000000..578faa4b9 --- /dev/null +++ b/app/services/pull_requests/send_journal_service.rb @@ -0,0 +1,34 @@ +class PullRequests::SendJournalService < ApplicationService + + def initialize(project, pull_request,current_user) + @project = project + @pull_request = pull_request + @issue = pull_request.issue + @current_user = current_user + @org = project.owner + end + + def call + if @org.enabling_cla && @org.cla.present? && @org.cla.pr_need && !@org.is_member?(@current_user&.id) && !@org.cla.valid_sign(@current_user&.id) + ActiveRecord::Base.transaction do + sender_id = if Rails.env.development? + User.last.id + else + 87461 + end + journal_params = { + journalized_id: @issue.id , + journalized_type: "Issue", + user_id: sender_id , + notes: "#{@current_user.nickname}您好!欢迎参与 #{@project.name} 的贡献。首次进行贡献请完成《#{@project.owner.cla.name}》的签署,签署完成后,项目成员才可查看到您的合并请求", + } + journal = Journal.new journal_params + if journal.save + @pull_request.update_attributes(status: 3) + TouchWebhookJob.set(wait: 5.seconds).perform_later('PullRequestComment', @issue&.id, sender_id, journal.id, 'created', {}) + push_activity_2_blockchain("issue_comment_create", journal) if Site.has_blockchain? && @project.use_blockchain + end + end + end + end +end diff --git a/app/services/repositories/migrate_service.rb b/app/services/repositories/migrate_service.rb index 7cf1cb3e7..deb0e30d3 100644 --- a/app/services/repositories/migrate_service.rb +++ b/app/services/repositories/migrate_service.rb @@ -32,7 +32,8 @@ class Repositories::MigrateService < ApplicationService private: params[:hidden], mirror: wrapper_mirror || false, auth_username: params[:login], - auth_password: Base64.decode64(params[:password] || "") + auth_password: Base64.decode64(params[:password] || ""), + auth_token: params[:auth_token] } end diff --git a/app/services/sliding_window_rate_limiter.rb b/app/services/sliding_window_rate_limiter.rb new file mode 100644 index 000000000..a999345ef --- /dev/null +++ b/app/services/sliding_window_rate_limiter.rb @@ -0,0 +1,39 @@ + +class SlidingWindowRateLimiter + def initialize(user_id, rate, window_size) + @rate = rate # 请求速率限制(每秒的请求数) + @window_size = window_size # 时间窗口大小(秒) + @redis = $redis_cache + @key = "#{user_id}:SlidingWindow" + @current_timestamp = Time.now.to_f + end + + def allow_request + current_timestamp = Time.now.to_f + score = current_timestamp.to_i + start_time = current_timestamp - @window_size + + @redis.zremrangebyscore(@key, '-inf', start_time) + count = @redis.zcount(@key, '-inf', '+inf').to_i + + return false if count >= @rate + + @redis.zadd(@key, score, current_timestamp) + true + end +end + + +=begin + #测试通过 + + limiter = SlidingWindowRateLimiter.new(user_id,10, 1) # 设置请求速率限制为10次/秒,时间窗口大小为1秒 + 40.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file diff --git a/app/services/token_bucket_rate_limiter.rb b/app/services/token_bucket_rate_limiter.rb new file mode 100644 index 000000000..c0d726b37 --- /dev/null +++ b/app/services/token_bucket_rate_limiter.rb @@ -0,0 +1,48 @@ +class TokenBucketRateLimiter + def initialize(user_id,rate, capacity) + @rate = rate # 令牌发放速率(每秒发放的令牌数) + @capacity = capacity # 令牌桶容量 + @redis = $redis_cache + @key = "#{user_id}:TokenBucket" + end + + def refill + now = Time.now.to_f * 1000 + tokens = $redis_cache.hget(@key, 'tokens') + timestamp = $redis_cache.hget(@key, 'timestamp') + + if tokens.nil? + tokens = @capacity + timestamp = now + else + tokens = [@capacity, tokens.to_i + (now - timestamp.to_f) * @rate / 1000].min + timestamp = now + end + + $redis_cache.hset(@key, 'tokens', tokens) + $redis_cache.hset(@key, 'timestamp', timestamp) + end + + def allow_request + refill + tokens = @redis.hget(@key, 'tokens') + if !tokens.nil? && tokens.to_i >= 1 + @redis.hset(@key, 'tokens', tokens.to_i - 1) + return true + end + false + end +end + +=begin + #测试通过 + limiter = TokenBucketRateLimiter.new(user_id,10, 40) # 设置令牌发放速率为10,令牌桶容量为40 + 30.times do + if limiter.allow_request + puts "Allow" + else + puts "Reject" + end + end + +=end \ No newline at end of file diff --git a/app/views/admins/feedbacks/_list.html.erb b/app/views/admins/feedbacks/_list.html.erb index 1b7b045ed..a630837f1 100644 --- a/app/views/admins/feedbacks/_list.html.erb +++ b/app/views/admins/feedbacks/_list.html.erb @@ -17,12 +17,8 @@ <%= feedback&.user&.login%> <%= feedback&.user&.mail%> <%= feedback.created_at&.strftime('%Y-%m-%d %H:%M') %> - - - - <%= feedback.content.truncate(20) %> - - + + <%= feedback.content %> <%= link_to "发送邮件", new_history_admins_feedback_path(feedback), remote: true, class: "action" %> diff --git a/app/views/admins/glcc_pr_check/_examine_material.html.erb b/app/views/admins/glcc_pr_check/_examine_material.html.erb new file mode 100644 index 000000000..ac8e550ac --- /dev/null +++ b/app/views/admins/glcc_pr_check/_examine_material.html.erb @@ -0,0 +1,58 @@ + + + + + + + + + + + 60 + + + + + + + + <% if examine_materials.present? %> + <% examine_materials.each_with_index do |material, index| %> + + + + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号学生姓名课题ID课题名称邮件地址视频地址PR地址考核阶段白名单检测状态提交时间
<%= list_index_no((params[:page] || 1).to_i, index) %><%= material.glcc_student.student_name %><%= material.task_id %><%= material.task.task_name %> + + <%=material.glcc_student.mail.truncate(20) %> + + + + + <%= material.defence_video_url.nil? ? "" : material.defence_video_url.truncate(30) %> + + + + + + <%= material.code_or_pr_url.nil? ? "" : material.code_or_pr_url.truncate(30) %> + + + <%= material.term == 1 ? "中期考核" : "结项考核"%><%= material.white_list ? "是":"否" %><%= material.state_to_html.blank?&&!material.white_list ? "通过" : material.state_to_html.html_safe %><%= material.created_on.strftime("%Y-%m-%d %H:%M")%>
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: examine_materials } %> diff --git a/app/views/admins/glcc_pr_check/index.html.erb b/app/views/admins/glcc_pr_check/index.html.erb new file mode 100644 index 000000000..a70a09f6e --- /dev/null +++ b/app/views/admins/glcc_pr_check/index.html.erb @@ -0,0 +1,44 @@ +
+ <%= form_tag(admins_glcc_pr_check_index_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
+ + <% status_options = [['中期考核',1], ['结项考核', 2]] %> + <%= select_tag(:term, options_for_select(status_options), class: 'form-control') %> +
+ +
+ + <%= select_year(Date.today, {field_name:"year"}, {class:"form-control"})%> + +
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + +
+ + + +
+ <%= render partial: 'examine_material', locals: { examine_materials: @examine_materials } %> +
+ + diff --git a/app/views/admins/glcc_pr_check/index.js.erb b/app/views/admins/glcc_pr_check/index.js.erb new file mode 100644 index 000000000..8bc043f70 --- /dev/null +++ b/app/views/admins/glcc_pr_check/index.js.erb @@ -0,0 +1 @@ +$('.glcc-examine-list-container').html("<%= j( render partial: 'examine_material', locals: { examine_materials: @examine_materials } ) %>"); \ No newline at end of file diff --git a/app/views/admins/identity_verifications/edit.html.erb b/app/views/admins/identity_verifications/edit.html.erb new file mode 100644 index 000000000..580c86f3f --- /dev/null +++ b/app/views/admins/identity_verifications/edit.html.erb @@ -0,0 +1,119 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('身份审核列表', admins_identity_verifications_path) + add_admin_breadcrumb('用户身份审核详情') + end +%> + +
+
+ <%= link_to "/#{@identity_verification.user.login}", class: 'user-info-avatar col-md-1', target: '_blank', data: { toggle: 'tooltip', title: '个人中心' } do %> + + <% end %> + +
+ + <%= simple_form_for(@identity_verification, url: admins_identity_verification_path(@identity_verification)) do |f| %> + +
+
审核信息
+ + +
+ <%= f.input :name, label: '姓名', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-11' , value: @identity_verification.name } %> +
+
+ <%= f.input :number, label: '身份证号', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-11' , value: @identity_verification.number } %> +
+ +
+ + <% if @identity_verification.card_front_attachment.present? %> + <%= image_tag("/api/attachments/#{@identity_verification.card_front}", width: 100, height: 100, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <%else%> +

图片无法展示,图片已丢失

+ <%end%> + +
+ +
+ + <% if @identity_verification.card_back_attachment.present? %> + <%= image_tag("/api/attachments/#{@identity_verification.card_back}", width: 100, height: 100, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <%else%> +

图片无法展示,图片已丢失

+ <%end%> + +
+ +
+ + <% if @identity_verification.hold_card_front_attachment.present? %> + <%= image_tag("/api/attachments/#{@identity_verification.hold_card_front}", width: 100, height: 100, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <%else%> +

图片无法展示,图片已丢失

+ <%end%> + +
+ +
+ + <% if @identity_verification.hold_card_back_attachment.present? %> + <%= image_tag("/api/attachments/#{@identity_verification.hold_card_back}", width: 100, height: 100, class: 'preview-image auth-image', data: { toggle: 'tooltip', title: '点击预览' }) %> + <%else%> +

图片无法展示,图片已丢失

+ <%end%> + +
+ + <% unless @identity_verification.state == "已通过" %> +
+ <%= radio_button_tag("identity_verification[state]","已通过", required: 'required') %>通过   + <%= radio_button_tag("identity_verification[state]","已拒绝", required: 'required') %>拒绝 +
+
+
+ <%= f.input :description, as: :text,label: '拒绝理由:(拒绝时请填写拒绝理由,可以为空)', wrapper_html: { class: 'col-md-12' }, input_html: { maxlength: 100, size: 40, class: 'col-md-11' , value: @identity_verification.description } %> +
+
+ <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %> + <%= link_to '取消', admins_identity_verifications_path, class: 'btn btn-secondary px-4' %> +
+ <% end %> + +
+ <% end %> +
diff --git a/app/views/admins/identity_verifications/index.html.erb b/app/views/admins/identity_verifications/index.html.erb new file mode 100644 index 000000000..5cb0f8c93 --- /dev/null +++ b/app/views/admins/identity_verifications/index.html.erb @@ -0,0 +1,21 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('身份审核列表', admins_identity_verifications_path) %> +<% end %> + +
+ <%= form_tag(admins_identity_verifications_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
+ + <% state_options = [['待审核', 0], ['全部',"all"], ['已通过', 1], ['已拒绝', 2]] %> + <%= select_tag(:state, options_for_select(state_options), class: 'form-control') %> +
+ + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + +
+ +
+ <%= render partial: 'admins/identity_verifications/shared/info_list', locals: { identity_verifications: @identity_verifications } %> +
+ diff --git a/app/views/admins/identity_verifications/index.js.erb b/app/views/admins/identity_verifications/index.js.erb new file mode 100644 index 000000000..7b0ae2d82 --- /dev/null +++ b/app/views/admins/identity_verifications/index.js.erb @@ -0,0 +1 @@ +$('.identity-verifications-list-container').html("<%= j( render partial: 'admins/identity_verifications/shared/info_list', locals: { identity_verifications: @identity_verifications } ) %>"); \ No newline at end of file diff --git a/app/views/admins/identity_verifications/shared/_info_list.html.erb b/app/views/admins/identity_verifications/shared/_info_list.html.erb new file mode 100644 index 000000000..8ab17ad42 --- /dev/null +++ b/app/views/admins/identity_verifications/shared/_info_list.html.erb @@ -0,0 +1,35 @@ + + + + + + + + + + + + <% if identity_verifications.present? %> + <% identity_verifications.each_with_index do |identity_verification, index| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号昵称审核状态<%= sort_tag('创建于', name: 'created_at', path: admins_identity_verifications_path) %>操作
<%= list_index_no((params[:page] || 1).to_i, index) %> + <%= link_to "/#{identity_verification.user.login}", target: '_blank' do %> + <%= overflow_hidden_span identity_verification.user.real_name, width: 100 %> + <% end %> + <%= display_text(identity_verification.state) %><%= display_text(identity_verification.created_at&.strftime('%Y-%m-%d %H:%M')) %> + <%= link_to "#{ identity_verification.state == "待审核" ? '审核' : "查看"}", edit_admins_identity_verification_path(identity_verification), class: 'action' %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: identity_verifications } %> diff --git a/app/views/admins/laboratory_settings/_add_tr.html.erb b/app/views/admins/laboratory_settings/_add_tr.html.erb index 2d6ff25d5..91e252d55 100644 --- a/app/views/admins/laboratory_settings/_add_tr.html.erb +++ b/app/views/admins/laboratory_settings/_add_tr.html.erb @@ -1,6 +1,7 @@ <%= text_field_tag('navbar[][name]', "", id: nil, class: 'form-control', placeholder: "导航名称") %> <%= text_field_tag('navbar[][link]', "", id: nil, class: 'form-control', placeholder: "导航链接") %> + <%= number_field_tag('navbar[][index]', "1", id: nil, class: 'form-control',min:0, max:99999, placeholder: "顺序", title:"请输入正整数 范围 0-99999", pattern:'^\d+')%> <%= check_box_tag('navbar[][hidden]', 0, false, id: nil, class: 'font-16') %> diff --git a/app/views/admins/laboratory_settings/show.html.erb b/app/views/admins/laboratory_settings/show.html.erb index f30e92b8f..df1db4fad 100644 --- a/app/views/admins/laboratory_settings/show.html.erb +++ b/app/views/admins/laboratory_settings/show.html.erb @@ -4,7 +4,7 @@ <% end %>
- <%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %> + <%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'put', html: { enctype: 'multipart/form-data' }) do |f| %> <% setting = @laboratory.laboratory_setting %>
@@ -16,9 +16,9 @@ https://
<%= text_field_tag :identifier, @laboratory.identifier, - maxlength: 15, class: 'form-control font-16', - 'onKeyUp': 'value=value.replace(/[^\w\-\/]/ig,"").toLowerCase()', - style: 'text-transform:lowercase'%> + maxlength: 15, class: 'form-control font-16', + 'onKeyUp': 'value=value.replace(/[^\w\-\/]/ig,"").toLowerCase()', + style: 'text-transform:lowercase'%>
<% rails_env = EduSetting.get('rails_env') %> <%= rails_env && rails_env != 'production' ? ".#{rails_env}.gitlink.org.cn" : '.gitlink.org.cn' %> @@ -158,16 +158,18 @@ 导航名称 - 导航链接 - 是否展示 + 导航链接 + 顺序 + 是否展示 操作 - - <% (setting.navbar || setting.default_navbar).each do |nav| %> + + <% (setting.navbar || setting.default_navbar).sort_by{|e| e["index"].to_i }.each do |nav| %> <%= text_field_tag('navbar[][name]', nav['name'], id: nil, class: 'form-control') %> <%= text_field_tag('navbar[][link]', nav['link'], id: nil, class: 'form-control') %> + <%= number_field_tag('navbar[][index]', nav['index'].to_i, id: nil, class: 'form-control',min:0,max:99999, title:"请输入正整数 范围 0-99999", pattern:'^\d+')%> <%= check_box_tag('navbar[][hidden]', 0, !nav['hidden'], id: nil, class: 'font-16') %> diff --git a/app/views/admins/organizations/index.html.erb b/app/views/admins/organizations/index.html.erb index 7cd2ba8fa..2444d87f3 100644 --- a/app/views/admins/organizations/index.html.erb +++ b/app/views/admins/organizations/index.html.erb @@ -5,6 +5,10 @@ <%= form_tag(admins_organizations_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'login/昵称') %> + + <%= check_box_tag("enabling_cla", true, false, id: "user_enabling_cla", style: 'margin-left: 2px;') %> <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/organizations/shared/_org_list.html.erb b/app/views/admins/organizations/shared/_org_list.html.erb index 25878b296..148681fb2 100644 --- a/app/views/admins/organizations/shared/_org_list.html.erb +++ b/app/views/admins/organizations/shared/_org_list.html.erb @@ -27,11 +27,12 @@ <%= link_to org.organization_users_count, "/#{org.login}", target: "_blank" %> <%= link_to org.projects_count, "/#{org.login}", target: "_blank" %> + <%= javascript_void_link '开通CLA', class: 'action open-cla-action', data: { id: org.id }, style: org.open_cla? ? 'display: none;' : '' %> + <%= javascript_void_link '关闭CLA', class: 'action close-cla-action', data: { id: org.id }, style: org.open_cla? ? '' : 'display: none;' %> <%= link_to '查看', admins_organization_path(org), class: 'action' %>
<%= javascript_void_link('更多', class: 'action dropdown-toggle', 'data-toggle': 'dropdown', 'aria-haspopup': true, 'aria-expanded': false) %>
diff --git a/app/views/admins/organizations/show.html.erb b/app/views/admins/organizations/show.html.erb index b40f1c258..501b1b5ad 100644 --- a/app/views/admins/organizations/show.html.erb +++ b/app/views/admins/organizations/show.html.erb @@ -28,8 +28,9 @@
<%= f.input :lastname, label: '姓名', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-11', value: @org.real_name } %>
- - +
+ <%= f.input :enabling_cla, as: :boolean, label: '开通CLA', checked_value: 1, unchecked_value: 0 %> +
<% end %>

组织项目

diff --git a/app/views/admins/page_themes/_form_modal.html.erb b/app/views/admins/page_themes/_form_modal.html.erb new file mode 100644 index 000000000..3d0a97588 --- /dev/null +++ b/app/views/admins/page_themes/_form_modal.html.erb @@ -0,0 +1,60 @@ + \ No newline at end of file diff --git a/app/views/admins/page_themes/_list.html.erb b/app/views/admins/page_themes/_list.html.erb new file mode 100644 index 000000000..d5280206c --- /dev/null +++ b/app/views/admins/page_themes/_list.html.erb @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + <% if page_themes.present? %> + <% page_themes.each_with_index do |theme, index| %> + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号建站工具顺序主题名图片仓库url操作
<%= list_index_no((params[:page] || 1).to_i, index) %><%= theme.language_frame %><%= theme.order_index %><%= theme.name %><%= theme.clone_url %> + <%= link_to "编辑", edit_admins_page_theme_path(theme), remote: true, class: "action" %> + <%= link_to "删除", admins_page_theme_path(theme), method: :delete, data:{confirm: "确认删除的吗?"}, class: "action" %> +
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: page_themes } %> \ No newline at end of file diff --git a/app/views/admins/page_themes/edit.js.erb b/app/views/admins/page_themes/edit.js.erb new file mode 100644 index 000000000..a9f963c5d --- /dev/null +++ b/app/views/admins/page_themes/edit.js.erb @@ -0,0 +1,18 @@ +$("#page-themes-modals").html("<%= j render(partial: 'admins/page_themes/form_modal', locals: {type: 'update'}) %>") +$(".page-themes-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 { + } +}); diff --git a/app/views/admins/page_themes/index.html.erb b/app/views/admins/page_themes/index.html.erb new file mode 100644 index 000000000..4b4392d88 --- /dev/null +++ b/app/views/admins/page_themes/index.html.erb @@ -0,0 +1,21 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('站点主题配置') %> +<% end %> + +
+ <%= form_tag(admins_page_themes_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
+ + <% state_options = [['全部',nil], ['hugo', 0], ['jeklly', 1],['hexo',2]] %> + <%= select_tag(:language_frame, options_for_select(state_options), class: 'form-control') %> +
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + <%= link_to "新增", new_admins_page_theme_path, remote: true, class: "btn btn-primary pull-right", "data-disabled-with":"...新增" %> +
+ +
+ <%= render partial: 'admins/page_themes/list', locals: { page_themes: @page_themes } %> +
+
+
diff --git a/app/views/admins/page_themes/index.js.erb b/app/views/admins/page_themes/index.js.erb new file mode 100644 index 000000000..30ea36a4e --- /dev/null +++ b/app/views/admins/page_themes/index.js.erb @@ -0,0 +1 @@ +$('.page-themes-list-container').html("<%= j( render partial: 'admins/page_themes/list', locals: { page_themes: @page_themes } ) %>"); \ No newline at end of file diff --git a/app/views/admins/page_themes/new.js.erb b/app/views/admins/page_themes/new.js.erb new file mode 100644 index 000000000..4c3d847d4 --- /dev/null +++ b/app/views/admins/page_themes/new.js.erb @@ -0,0 +1,18 @@ +$("#page-themes-modals").html("<%= j render(partial: 'admins/page_themes/form_modal', locals: {type: 'create'}) %>") +$(".page-themes-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 { + } +}); diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index fc2fe6068..813e2338d 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -24,6 +24,16 @@ <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
  • <%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
  • <%= sidebar_item(admins_organizations_path, '组织列表', icon: 'user', controller: 'admins-organizations') %>
  • + + <% end %> + + +
  • + <%= sidebar_item_group('#pages-submenu', '站点管理', icon: 'cogs') do %> +
  • <%= sidebar_item(admins_identity_verifications_path, '身份审核列表', icon: 'user', controller: 'admins-identity_verifications') %>
  • +
  • <%= sidebar_item(admins_site_pages_path, '用户站点列表', icon: 'sitemap', controller: 'admins-site_pages') %>
  • +
  • <%= sidebar_item(admins_page_themes_path, '站点主题配置', icon: 'cogs', controller: 'admins-page_themes') %>
  • + <% end %>
  • @@ -57,6 +67,7 @@ <%= sidebar_item(EduSetting.get("glcc_apply_informations_admin_url"), '报名列表', icon: 'user', controller: 'root') %> <% end %>
  • +
  • <%= sidebar_item(admins_glcc_pr_check_index_path, '考核PR检测', icon: 'edit', controller: 'admins-glcc_pr_check') %>
  • <% end %>
  • diff --git a/app/views/admins/site_pages/edit.html.erb b/app/views/admins/site_pages/edit.html.erb new file mode 100644 index 000000000..a6cf322b3 --- /dev/null +++ b/app/views/admins/site_pages/edit.html.erb @@ -0,0 +1,99 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('用户站点列表', admins_site_pages_path) + add_admin_breadcrumb('站点详情') + end +%> + +
    +
    + <%= link_to "/#{@site_page.user.login}", class: 'user-info-avatar col-md-1', target: '_blank', data: { toggle: 'tooltip', title: '个人中心' } do %> + + <% end %> + + +
    + + <%= simple_form_for(@site_page, url: admins_site_page_path(@site_page)) do |f| %> + +
    + +
    + +
    + <%= f.input :identifier, label: '站点标识', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-5' , value: @site_page.identifier } %> +
    + +
    + <%= f.input :site_name, label: '站点名', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-5' , value: @site_page.site_name } %> +
    + +
    + <%= f.input :language_frame, label: '建站工具', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, type:'text', class: 'col-md-5' , value: @site_page.language_frame } %> +
    + +
    + <%= f.input :theme, label: '主题', wrapper_html: { class: 'col-md-3' }, input_html: { readonly: true, class: 'col-md-5' , value: @site_page.theme } %> +
    +
    + + +
    + + <%= @site_page.url%> +
    +
    +
    + + <%= f.radio_button :state, 'false' %> 关闭 +
    +
    +
    + +
    +
    +
    + <%= f.input :state_description, as: :text,label: '关闭理由,如需关闭 理由不能为空', wrapper_html: { class: 'col-md-12' }, input_html: { maxlength: 100, size: 40, class: 'col-md-11' , value: @site_page.state_description } %> +
    + +
    + <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %> + <%= link_to '取消', admins_site_pages_path, class: 'btn btn-secondary px-4' %> +
    +
    + + <% end %> +
    diff --git a/app/views/admins/site_pages/index.html.erb b/app/views/admins/site_pages/index.html.erb new file mode 100644 index 000000000..449bc7f54 --- /dev/null +++ b/app/views/admins/site_pages/index.html.erb @@ -0,0 +1,21 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('用户站点列表', admins_site_pages_path) %> +<% end %> + +
    + <%= form_tag(admins_site_pages_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
    + + <% state_options = [['全部',nil], ['正常', true], ['已关闭', false]] %> + <%= select_tag(:state, options_for_select(state_options), class: 'form-control') %> +
    + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '输入用户 ID/姓名/邮箱/手机号 检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + +
    + +
    + <%= render partial: 'admins/site_pages/shared/info_list', locals: { site_pages: @site_pages } %> +
    + diff --git a/app/views/admins/site_pages/index.js.erb b/app/views/admins/site_pages/index.js.erb new file mode 100644 index 000000000..a2a0b3bac --- /dev/null +++ b/app/views/admins/site_pages/index.js.erb @@ -0,0 +1 @@ +$('.identity-site-pages-list-container').html("<%= j( render partial: 'admins/site_pages/shared/info_list', locals: { site_pages: @site_pages } ) %>"); \ No newline at end of file diff --git a/app/views/admins/site_pages/shared/_info_list.html.erb b/app/views/admins/site_pages/shared/_info_list.html.erb new file mode 100644 index 000000000..49143d3f5 --- /dev/null +++ b/app/views/admins/site_pages/shared/_info_list.html.erb @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + <% if site_pages.present? %> + <% site_pages.each_with_index do |site_page, index| %> + + + + + + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    序号昵称仓库站点状态站点名站点标识站点地址建站工具主题上次部署时间<%= sort_tag('创建于', name: 'created_at', path: admins_site_pages_path) %>操作
    <%= list_index_no((params[:page] || 1).to_i, index) %> + <%= link_to "/#{site_page.user.login}", target: '_blank' do %> + <%= overflow_hidden_span site_page.user.real_name, width: 100 %> + <% end %> + + <%= link_to "/#{site_page.user.login}/#{site_page.project.try(:identifier)}", target: '_blank' do %> + <%= overflow_hidden_span site_page.project.try(:name), width: 100 %> + <% end %> + <%= display_text(site_page.state == true ? "正常" : "已关闭") %><%= display_text(site_page.site_name) %><%= display_text(site_page.identifier) %> + <%= link_to "#{site_page.url}", target: '_blank' do %> + <%= overflow_hidden_span site_page.url, width: 120 %> + <% end %> + <%= display_text(site_page.language_frame) %><%= display_text(site_page.theme) %><%= display_text(site_page.last_build_at&.strftime('%Y-%m-%d %H:%M')) %><%= display_text(site_page.created_at&.strftime('%Y-%m-%d %H:%M')) %> + <%= link_to "查看", edit_admins_site_page_path(site_page), class: 'action' %> + <%= link_to "删除", admins_site_page_path(site_page), method: :delete, data:{confirm: "确认删除吗,删除后站点将无法访问?"}, class: "action" %> +
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: site_pages } %> diff --git a/app/views/admins/users/edit.html.erb b/app/views/admins/users/edit.html.erb index b407cbbe7..be952e7cb 100644 --- a/app/views/admins/users/edit.html.erb +++ b/app/views/admins/users/edit.html.erb @@ -92,8 +92,15 @@
  • <% end %> - - + + <% if @user.id_card_verify %> +
    + <%= f.label :role, label: '站点权限' %> +
    + <%= f.input :website_permission, as: :boolean, label: "开通站点(关闭后,将清除该用户所有站点信息,此操作不可逆,请谨慎操作! 请谨慎操作!! 请谨慎操作!!!)".html_safe, checked_value: 1, unchecked_value: 0 %> +
    +
    + <% end %>
    <%= f.input :password, as: :password, label: '修改密码', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11', autocomplete: 'new-password' } %> <%= f.input :password_confirmation, as: :password, label: '确认密码', wrapper_html: { class: 'col-md-3' }, input_html: { class: 'col-sm-11', autocomplete: 'new-password' } %> @@ -101,7 +108,7 @@
    - <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %> + <%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4 action' ,data:{confirm: "确认更改?"}%> <%= link_to '取消', admins_users_path, class: 'btn btn-secondary px-4' %>
    <% end %> diff --git a/app/views/compare/show.json.jbuilder b/app/views/compare/show.json.jbuilder index 26a7cb769..135537cda 100644 --- a/app/views/compare/show.json.jbuilder +++ b/app/views/compare/show.json.jbuilder @@ -1,19 +1,21 @@ json.commits_count @compare_result['CommitsCount'] # json.commits @compare_result['Commits'], partial: 'pull_requests/commit', as: :commit json.commits do - json.array! @compare_result['Commits'][@page_offset...(@page_offset + @page_limit)] do |commit| - json.author do - json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } - end + if @compare_result['Commits'].present? + json.array! @compare_result['Commits'][@page_offset...(@page_offset + @page_limit)] do |commit| + json.author do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } + end - json.committer do - json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } + json.committer do + json.partial! 'repositories/commit_author', locals: { user: render_cache_commit_author(commit['Committer']), name: commit['Committer']['Name'] } + end + json.timestamp render_unix_time(commit['Committer']['When']) + json.time_from_now time_from_now(commit['Committer']['When']) + json.created_at render_format_time_with_date(commit['Committer']['When']) + json.message commit['CommitMessage'] + json.sha commit['Sha'] end - json.timestamp render_unix_time(commit['Committer']['When']) - json.time_from_now time_from_now(commit['Committer']['When']) - json.created_at render_format_time_with_date(commit['Committer']['When']) - json.message commit['CommitMessage'] - json.sha commit['Sha'] end end diff --git a/app/views/identity_verifications/_info.json.jbuilder b/app/views/identity_verifications/_info.json.jbuilder new file mode 100644 index 000000000..8235bafa6 --- /dev/null +++ b/app/views/identity_verifications/_info.json.jbuilder @@ -0,0 +1,10 @@ +json.id id_verify.id +json.number id_verify.number +json.name id_verify.name +json.card_front id_verify.card_front +json.card_back id_verify.card_back +json.hold_card_front id_verify.hold_card_front +json.hold_card_back id_verify.hold_card_back +json.state id_verify.state +json.description id_verify.description +json.created_at id_verify.created_at.strftime("%Y-%m-%d %H:%M") diff --git a/app/views/identity_verifications/create.json.jbuilder b/app/views/identity_verifications/create.json.jbuilder new file mode 100644 index 000000000..400230393 --- /dev/null +++ b/app/views/identity_verifications/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'info', locals: {id_verify: @id_verify} \ No newline at end of file diff --git a/app/views/identity_verifications/index.json.jbuilder b/app/views/identity_verifications/index.json.jbuilder new file mode 100644 index 000000000..400230393 --- /dev/null +++ b/app/views/identity_verifications/index.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'info', locals: {id_verify: @id_verify} \ No newline at end of file diff --git a/app/views/identity_verifications/update.json.jbuilder b/app/views/identity_verifications/update.json.jbuilder new file mode 100644 index 000000000..400230393 --- /dev/null +++ b/app/views/identity_verifications/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'info', locals: {id_verify: @id_verify} \ No newline at end of file diff --git a/app/views/organizations/clas/_detail.json.jbuilder b/app/views/organizations/clas/_detail.json.jbuilder new file mode 100644 index 000000000..b9d50183b --- /dev/null +++ b/app/views/organizations/clas/_detail.json.jbuilder @@ -0,0 +1,6 @@ +json.id cla.id +json.content cla.content +json.key cla.key +json.name cla.name +json.pr_need cla.pr_need +json.count cla.count \ No newline at end of file diff --git a/app/views/organizations/clas/create.json.jbuilder b/app/views/organizations/clas/create.json.jbuilder new file mode 100644 index 000000000..e95312d8c --- /dev/null +++ b/app/views/organizations/clas/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "detail", cla: @cla, organization: @organization \ No newline at end of file diff --git a/app/views/organizations/clas/index.json.jbuilder b/app/views/organizations/clas/index.json.jbuilder new file mode 100644 index 000000000..632fde017 --- /dev/null +++ b/app/views/organizations/clas/index.json.jbuilder @@ -0,0 +1,3 @@ +if @cla.present? + json.partial! "detail", cla: @cla +end \ No newline at end of file diff --git a/app/views/organizations/clas/show.json.jbuilder b/app/views/organizations/clas/show.json.jbuilder new file mode 100644 index 000000000..8c55f38e2 --- /dev/null +++ b/app/views/organizations/clas/show.json.jbuilder @@ -0,0 +1,8 @@ +json.partial! "detail", cla: @cla, organization: @organization +json.is_admin @is_admin +json.is_sign @is_sign +json.cla_sign_email @cla_sign_email +json.is_member @is_member +json.organization do + json.partial! "organizations/organizations/simple", organization: @organization +end \ No newline at end of file diff --git a/app/views/organizations/clas/update.json.jbuilder b/app/views/organizations/clas/update.json.jbuilder new file mode 100644 index 000000000..e95312d8c --- /dev/null +++ b/app/views/organizations/clas/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! "detail", cla: @cla, organization: @organization \ No newline at end of file diff --git a/app/views/organizations/organizations/_detail.json.jbuilder b/app/views/organizations/organizations/_detail.json.jbuilder index c7614f039..1968e78c8 100644 --- a/app/views/organizations/organizations/_detail.json.jbuilder +++ b/app/views/organizations/organizations/_detail.json.jbuilder @@ -16,4 +16,5 @@ json.news_banner_id organization.news_banner_id json.news_content organization.news_content json.memo organization.memo json.news_title organization.news_title -json.news_url organization.news_url \ No newline at end of file +json.news_url organization.news_url +json.enabling_cla organization.enabling_cla \ No newline at end of file diff --git a/app/views/projects/_detail.json.jbuilder b/app/views/projects/_detail.json.jbuilder index ed23fdef2..58cdbcc0c 100644 --- a/app/views/projects/_detail.json.jbuilder +++ b/app/views/projects/_detail.json.jbuilder @@ -3,6 +3,7 @@ json.identifier project.identifier json.name project.name json.description project.description json.is_public project.is_public +json.pr_need project.owner.class == User ? false : project.owner&.cla.try(:pr_need) json.owner do json.partial! "/users/user_simple", locals: {user: project.owner} end \ No newline at end of file diff --git a/app/views/pull_requests/_merge_item.json.jbuilder b/app/views/pull_requests/_merge_item.json.jbuilder index 0db17e265..202530c0b 100644 --- a/app/views/pull_requests/_merge_item.json.jbuilder +++ b/app/views/pull_requests/_merge_item.json.jbuilder @@ -4,6 +4,7 @@ json.issue_versions @project_versions json.issue_priories @project_priories json.project_author @project.owner.try(:show_real_name) json.project_name @project.try(:name) +json.disable_pr_vew @project.pr_view_admin? && !@project.manager?(current_user) json.members do json.array! @project_members.to_a.each do |user| json.id user.id diff --git a/app/views/pull_requests/index.json.jbuilder b/app/views/pull_requests/index.json.jbuilder index f231e5796..07c4b322b 100644 --- a/app/views/pull_requests/index.json.jbuilder +++ b/app/views/pull_requests/index.json.jbuilder @@ -10,6 +10,8 @@ json.project_name @project.name json.project_author @project.owner.try(:login) json.project_author_name @project.owner.try(:show_real_name) json.has_created_pull_requests @project.pull_requests.size > 0 +json.disable_pr_vew @project.pr_view_admin? && !@project.manager?(current_user) +json.pr_need @project.owner.class == User ? false : @project.owner&.cla.try(:pr_need) json.issues do json.array! @issues.to_a do |issue| @@ -21,10 +23,10 @@ json.issues do json.pull_request_base pr.base json.pull_request_staus pr.status == 1 ? "merged" : (pr.status == 2 ? "closed" : "open") json.is_original pr.is_original - json.fork_project_id pr&.fork_project_id - json.fork_project_identifier pr&.fork_project&.identifier - json.fork_project_user pr&.fork_project&.owner.try(:login) - json.fork_project_user_name pr&.fork_project&.owner.try(:show_real_name) + json.fork_project_id pr.fork_project_id.present? ? pr.fork_project_id : pr.project_id + json.fork_project_identifier pr.fork_project_id.present? ? pr&.fork_project&.identifier : pr.project&.identifier + json.fork_project_user pr.fork_project_id.present? ? pr&.fork_project&.owner.try(:login) : pr.project&.owner.try(:login) + json.fork_project_user_name pr.fork_project_id.present? ? pr&.fork_project&.owner.try(:show_real_name) : pr.project&.owner.try(:show_real_name) json.reviewers pr.reviewers.pluck(:login) json.id issue.id diff --git a/app/views/pull_requests/show.json.jbuilder b/app/views/pull_requests/show.json.jbuilder index 71f0641b6..4c731b9ac 100644 --- a/app/views/pull_requests/show.json.jbuilder +++ b/app/views/pull_requests/show.json.jbuilder @@ -23,10 +23,11 @@ if @last_review.present? end json.pull_request do - json.extract! @pull_request, :id,:base, :head, :status,:fork_project_id, :is_original + json.extract! @pull_request, :id,:base, :head, :status, :is_original json.pull_request_staus @pull_request.status == 1 ? "merged" : (@pull_request.status == 2 ? "closed" : "open") - json.fork_project_user @pull_request&.fork_project&.owner.try(:login) - json.fork_project_user_name @pull_request&.fork_project&.owner.try(:show_real_name) + json.fork_project_id @pull_request.fork_project_id.present? ? @pull_request.fork_project_id : @pull_request.project_id + json.fork_project_user @pull_request.fork_project_id.present? ? @pull_request&.fork_project&.owner.try(:login) : @pull_request.project&.owner.try(:login) + json.fork_project_user_name @pull_request.fork_project_id.present? ? @pull_request&.fork_project&.owner.try(:show_real_name) : @pull_request.project&.owner.try(:show_real_name) json.create_user @pull_request&.user&.login json.mergeable @gitea_pull["mergeable"] json.state @gitea_pull["state"] diff --git a/app/views/repositories/_contributor.json.jbuilder b/app/views/repositories/_contributor.json.jbuilder index b0547eba3..861efb8bb 100644 --- a/app/views/repositories/_contributor.json.jbuilder +++ b/app/views/repositories/_contributor.json.jbuilder @@ -1,20 +1,33 @@ -user = $redis_cache.hgetall("v2-owner-common:#{contributor["name"] || contributor["login"]}-#{contributor["email"]}") -if user.blank? +user = render_cache_commit_author(contributor) +if user.present? + if user.is_a?(Hash) + json.contributions contributor["commits"] || contributor["contributions"] + json.id user["id"] + json.login user["login"] + json.email user["email"] + json.type user["type"] + json.name (user["name"] || contributor["name"] || contributor["login"]).to_s.downcase + json.image_url user["avatar_url"] + db_user = User.find_by_id(user["id"]) + json.contribution_perc db_user.simple_contribution_perc(project, contributor["contribution_perc"]) if db_user.present? + else + json.contributions contributor["commits"] || contributor["contributions"] + json.id user.id + json.login user.login + json.email user.mail + json.type user.type + json.name (user["name"] || contributor["name"] || contributor["login"]).to_s.downcase + json.image_url url_to_avatar(user) + json.contribution_perc user.simple_contribution_perc(project, contributor["contribution_perc"]) if user.present? + end +else json.contributions contributor["commits"] || contributor["contributions"] + json.id nil json.login nil json.type nil json.name (contributor["name"] || contributor["login"]).to_s.downcase json.email contributor["email"] json.image_url User::Avatar.get_letter_avatar_url(contributor["name"] || contributor["login"]) json.contribution_perc User.new(login: (contributor["name"] || contributor["login"]), mail: contributor["email"]).simple_contribution_perc(project, contributor["contribution_perc"]) -else - json.contributions contributor["commits"] || contributor["contributions"] - json.id user["id"] - json.login user["login"] - json.email user["email"] - json.type user["type"] - json.name (user["name"] || contributor["name"] || contributor["login"]).to_s.downcase - json.image_url user["avatar_url"] - db_user = User.find_by_id(user["id"]) - json.contribution_perc db_user.simple_contribution_perc(project, contributor["contribution_perc"]) if db_user.present? end + diff --git a/app/views/repositories/_simple_entry.json.jbuilder b/app/views/repositories/_simple_entry.json.jbuilder index 9ad5e3fc2..56b75c198 100644 --- a/app/views/repositories/_simple_entry.json.jbuilder +++ b/app/views/repositories/_simple_entry.json.jbuilder @@ -2,7 +2,8 @@ if @project.forge? is_dir = @sub_entries.is_a?(Array) file_name = entry['name'] file_type = file_name.starts_with?('.') ? file_name[1..-1] : File.extname(file_name.to_s)[1..-1] - direct_download = %w(makefile dockerfile readme).exclude?(file_name.to_s.downcase) && download_type(file_type) + # direct_download = %w(makefile dockerfile readme).exclude?(file_name.to_s.downcase) && download_type(file_type) + direct_download = entry['is_text_file'] == true ? false : true image_type = image_type?(file_type) json.name file_name json.sha entry['sha'] diff --git a/app/views/repositories/edit.json.jbuilder b/app/views/repositories/edit.json.jbuilder index e0b601f6d..7ef3c9081 100644 --- a/app/views/repositories/edit.json.jbuilder +++ b/app/views/repositories/edit.json.jbuilder @@ -10,7 +10,8 @@ json.project_units @project.project_units.pluck(:unit_type) json.lesson_url @project.lesson_url json.permission render_permission(current_user, @project) json.is_transfering @project.is_transfering -json.transfer do +json.pr_view_admin @project.pr_view_admin? +json.transfer do json.partial! "/users/user_simple", locals: {user: @project&.applied_transfer_project&.owner} end json.is_pinned @project.has_pinned_users.include?(current_user) \ No newline at end of file diff --git a/app/views/settings/show.json.jbuilder b/app/views/settings/show.json.jbuilder index be79dc594..1027be670 100644 --- a/app/views/settings/show.json.jbuilder +++ b/app/views/settings/show.json.jbuilder @@ -32,6 +32,7 @@ json.setting do json.nav_logo_url default_setting.nav_logo_url&.[](1..-1) json.login_logo_url default_setting.login_logo_url&.[](1..-1) json.tab_logo_url default_setting.tab_logo_url&.[](1..-1) + json.site_page_deploy_domain @deploy_domain json.subject_banner_url default_setting.subject_banner_url&.[](1..-1) json.course_banner_url default_setting.course_banner_url&.[](1..-1) diff --git a/app/views/site_pages/_info.json.jbuilder b/app/views/site_pages/_info.json.jbuilder new file mode 100644 index 000000000..bfd2e1171 --- /dev/null +++ b/app/views/site_pages/_info.json.jbuilder @@ -0,0 +1,16 @@ +json.id page.id +json.identifier page.identifier +json.owner page.user.login +json.repo page.project.identifier +json.user_id page.user_id +json.project_id page.project_id +json.site_name page.site_name +json.theme page.theme +json.state page.state +json.state_description page.state_description +json.language_frame page.language_frame +json.build_state page.build_state +json.last_build_info page.last_build_info.to_s.split("\n") +json.url page.url +json.created_at page.created_at.strftime("%Y-%m-%d %H:%M:%S") +json.last_build_at page.last_build_at.nil? ? nil : page.last_build_at.strftime("%Y-%m-%d %H:%M:%S") \ No newline at end of file diff --git a/app/views/site_pages/create.json.jbuilder b/app/views/site_pages/create.json.jbuilder new file mode 100644 index 000000000..b706f67b8 --- /dev/null +++ b/app/views/site_pages/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! "info", page: @page \ No newline at end of file diff --git a/app/views/site_pages/index.json.jbuilder b/app/views/site_pages/index.json.jbuilder new file mode 100644 index 000000000..482a7c70c --- /dev/null +++ b/app/views/site_pages/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @total_count +json.pages @pages.each do |page| + json.partial! 'info', locals: {page: page} +end diff --git a/app/views/site_pages/show.json.jbuilder b/app/views/site_pages/show.json.jbuilder new file mode 100644 index 000000000..3b89bc90f --- /dev/null +++ b/app/views/site_pages/show.json.jbuilder @@ -0,0 +1,6 @@ +json.status 0 +json.message "success" +json.data { + json.partial! 'info', locals: {page: @page} +} + diff --git a/app/views/site_pages/themes.json.jbuilder b/app/views/site_pages/themes.json.jbuilder new file mode 100644 index 000000000..25a1d7015 --- /dev/null +++ b/app/views/site_pages/themes.json.jbuilder @@ -0,0 +1,10 @@ +json.total_count @themes.size +json.themes @themes.map{|e| + { + name: e.name, + language_frame: e.language_frame, + image: e.image, + clone_url: e.clone_url, + order_index: e.order_index, + } +} diff --git a/app/views/topics/_activity_forum.json.jbuilder b/app/views/topics/_activity_forum.json.jbuilder index cd624ba76..c1d81f87c 100644 --- a/app/views/topics/_activity_forum.json.jbuilder +++ b/app/views/topics/_activity_forum.json.jbuilder @@ -1,4 +1,5 @@ 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"] \ No newline at end of file +data = activity_forum.get_visitor_data +json.visits data[:visits] +json.created_time data[:created_time] +json.from data[:from] \ No newline at end of file diff --git a/app/views/topics/_experience_forum.json.jbuilder b/app/views/topics/_experience_forum.json.jbuilder index c65a00ef8..414990236 100644 --- a/app/views/topics/_experience_forum.json.jbuilder +++ b/app/views/topics/_experience_forum.json.jbuilder @@ -1,4 +1,5 @@ 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"] \ No newline at end of file +data = experience_forum.get_visitor_data +json.visits data[:visits] +json.created_time data[:created_time] +json.from data[:from] \ No newline at end of file diff --git a/app/views/topics/_glcc_news.json.jbuilder b/app/views/topics/_glcc_news.json.jbuilder index 0147aff3f..6c1227429 100644 --- a/app/views/topics/_glcc_news.json.jbuilder +++ b/app/views/topics/_glcc_news.json.jbuilder @@ -1,4 +1,5 @@ json.(glcc_news, :id, :title, :url, :uuid) -request_memo = Forum::Memos::GetService.call(glcc_news&.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"] \ No newline at end of file +data = glcc_news.get_visitor_data +json.visits data[:visits] +json.created_time data[:created_time] +json.from data[:from] \ No newline at end of file diff --git a/app/views/topics/_pinned_forum.json.jbuilder b/app/views/topics/_pinned_forum.json.jbuilder index 3d5adce26..db250e55c 100644 --- a/app/views/topics/_pinned_forum.json.jbuilder +++ b/app/views/topics/_pinned_forum.json.jbuilder @@ -1,4 +1,5 @@ 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"] \ No newline at end of file +data = pinned_forum.get_visitor_data +json.visits data[:visits] +json.created_time data[:created_time] +json.from data[:from] \ No newline at end of file diff --git a/app/views/user_mailer/glcc_pr_check_email.html.erb b/app/views/user_mailer/glcc_pr_check_email.html.erb new file mode 100644 index 000000000..aa01d45bf --- /dev/null +++ b/app/views/user_mailer/glcc_pr_check_email.html.erb @@ -0,0 +1,71 @@ + + + + + 验证码发送 + + + + + +
    +
    +
    + + + +
    +
    +
    + +

    + 尊敬的<%=@name%>同学: +

    + +

    +   您好!经过一个多月的开发,GLCC开源编程夏令营终于迎来中期考核。为了确保您能够顺利通过考核,现对您提交的PR链接进行检测,发现存在以下问题: +

    + +
    + <%=@content.html_safe%> +
    + +

    +   您提交的PR链接的真实有效性对于后期的开发和奖金的发放至关重要,请您务必予以重视。请您尽快核查链接的有效性,并按照组委会要求提交中期考核材料。若确认链接无误,请忽略本邮件。​ +

    + +

    +   如有其他问题,请联系:glcc@ccf.org.cn +

    + +

    + GLCC组委会 +

    +

    + <%=Time.now.strftime('%Y年%m月%d日')%> +

    +
    +
    + www.gitlink.org.cn +
    +
    +
    + + diff --git a/app/views/user_rank/_detail.json.jbuilder b/app/views/user_rank/_detail.json.jbuilder index d6c5c23ae..cb6692e07 100644 --- a/app/views/user_rank/_detail.json.jbuilder +++ b/app/views/user_rank/_detail.json.jbuilder @@ -25,11 +25,17 @@ if popular_project.blank? json.project nil else popular_project_common = $redis_cache.hgetall("v2-project-common:#{popular_project[0]}") - json.project do - json.id popular_project[0] - json.name popular_project_common["name"] - json.identifier popular_project_common["identifier"] - json.description popular_project_common["description"] + if popular_project_common["owner_id"].to_s == item[0].to_s + json.project do + json.id popular_project[0] + json.name popular_project_common["name"] + json.identifier popular_project_common["identifier"] + json.description popular_project_common["description"] + end + else + # owner changed remove rank + $redis_cache.zrem("v2-user-project-rank:#{item[0]}", popular_project[0]) + json.project nil end end diff --git a/app/views/users/_user_small.json.jbuilder b/app/views/users/_user_small.json.jbuilder index f5929ef6b..f495680e1 100644 --- a/app/views/users/_user_small.json.jbuilder +++ b/app/views/users/_user_small.json.jbuilder @@ -4,6 +4,7 @@ json.array! users do |user| json.login user.login json.user_id user.id json.mail user.mail + json.phone user.phone.present? ? "#{user.phone[0..2]}****#{user.phone[-4..-1]}" : "" json.custom_department user.custom_department json.image_url url_to_avatar(user) json.profile_completed user.profile_completed diff --git a/app/views/users/clas/_detail.json.jbuilder b/app/views/users/clas/_detail.json.jbuilder new file mode 100644 index 000000000..106145f6e --- /dev/null +++ b/app/views/users/clas/_detail.json.jbuilder @@ -0,0 +1,11 @@ +json.id user_cla.id +json.real_name user_cla.real_name +json.email user_cla.email +json.state user_cla.state +json.created_at format_time(user_cla.sign_time) +json.cla do + json.partial! "/organizations/clas/detail", locals: {cla: user_cla.cla} +end +json.organization do + json.partial! "organizations/organizations/simple", organization: user_cla.cla.organization + end \ No newline at end of file diff --git a/app/views/users/clas/index.json.jbuilder b/app/views/users/clas/index.json.jbuilder new file mode 100644 index 000000000..d4e645c6e --- /dev/null +++ b/app/views/users/clas/index.json.jbuilder @@ -0,0 +1,3 @@ +json.user_clas @user_clas do |user_cla| + json.partial! "detail", user_cla: user_cla +end diff --git a/app/views/users/clas/show.json.jbuilder b/app/views/users/clas/show.json.jbuilder new file mode 100644 index 000000000..a34e77b7f --- /dev/null +++ b/app/views/users/clas/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "detail", user_cla: @user_cla diff --git a/app/views/users/get_user_info.json.jbuilder b/app/views/users/get_user_info.json.jbuilder index f2793b0f0..901ea87f0 100644 --- a/app/views/users/get_user_info.json.jbuilder +++ b/app/views/users/get_user_info.json.jbuilder @@ -26,7 +26,9 @@ json.super_description @user.super_description json.(@user, :show_email, :show_department, :show_location, :show_super_description) json.message_unread_total @message_unread_total json.has_trace_user @user.trace_user.present? -json.is_new @user.login.present? && params[:login].to_s.include?("#{@user.login}") +json.is_new params[:login].present? && (UserAction.where(action_type: "sync_educoder_user", user_id: @user.id).blank? || @user.mail.include?("@gitlink.org.cn") || @user.mail.include?("@forge.com")) json.nps EduSetting.get("nps-on-off-switch").to_s == 'true' && UserNp.where(user_id: current_user.id).where("created_at >= ?", (Time.now - 30.days).beginning_of_day ).blank? json.open_blockchain EduSetting.get("open_blockchain_users").to_s.split(",").include?(@user.id.to_s) || EduSetting.get("open_blockchain_users").to_s.split(",").include?(@user.login) json.sign_cla @user.sign_cla +json.id_card_verify @user.id_card_verify +json.website_permission @user.website_permission diff --git a/config/admins/page_themes.yml b/config/admins/page_themes.yml new file mode 100644 index 000000000..45e3ac901 --- /dev/null +++ b/config/admins/page_themes.yml @@ -0,0 +1,22 @@ +hugo: + - + name: "默认主题" + imgage_url: "https://pic1.zhimg.com/50/v2-02ed5def1f25e0de21f40eb9f96d28b9_200x0.jpg?source=b6762063" + clone_url: "https://gitlink.org.cn/SiteTheme/hugo.git" + +jekyll: + - + name: "默认主题" + imgage_url: "https://pic1.zhimg.com/50/v2-02ed5def1f25e0de21f40eb9f96d28b9_200x0.jpg?source=b6762063" + clone_url: "https://gitlink.org.cn/SiteTheme/hugo.git" + +hexo: + - + name: "默认主题" + imgage_url: "https://pic1.zhimg.com/50/v2-02ed5def1f25e0de21f40eb9f96d28b9_200x0.jpg?source=b6762063" + clone_url: "https://gitlink.org.cn/SiteTheme/hexo.git" + - + name: "Icarus" + imgage_url: "https://pic1.zhimg.com/50/v2-02ed5def1f25e0de21f40eb9f96d28b9_200x0.jpg?source=b6762063" + clone_url: "https://gitlink.org.cn/SiteTheme/hexo-theme-icarus.git" + diff --git a/config/initializers/gitea_client.rb b/config/initializers/gitea_client.rb index 15777ac30..9a490fbb5 100644 --- a/config/initializers/gitea_client.rb +++ b/config/initializers/gitea_client.rb @@ -16,5 +16,5 @@ $gitea_hat_client = Gitea::Api::Hat::Client.new({ hat_base_url: gitea_config[:hat_base_url], username: gitea_config[:access_key_id], password: gitea_config[:access_key_secret], - log_filepath: "log/gitea-client.log" + log_filepath: "log/gitea-hat-client.log" }) \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f36aaa165..cc10bd987 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,6 +19,7 @@ Rails.application.routes.draw do get 'attachments/entries/get_file', to: 'attachments#get_file' get 'attachments/download/:id', to: 'attachments#show' get 'attachments/download/:id/:filename', to: 'attachments#show' + get 'check_pr_url',to: "settings#check_url" # get 'auth/qq/callback', to: 'oauth/qq#create' get 'auth/failure', to: 'oauth/base#auth_failure' @@ -88,7 +89,7 @@ Rails.application.routes.draw do resources :project_rank, only: [:index] resources :user_rank, only: [:index] resources :nps, only: [:create] - + resources :statistic, only: [:index] do collection do get :platform_profile @@ -147,6 +148,7 @@ Rails.application.routes.draw do delete :quit end end + resources :clas resources :teams, except: [:edit, :new] do collection do get :search @@ -237,9 +239,11 @@ Rails.application.routes.draw do collection do post :migrate + post :page_migrate get :group_type_list get :recommend get :banner_recommend + post :verify_auth_token end end @@ -257,6 +261,7 @@ Rails.application.routes.draw do post :remote_password post :change_password post :check + post :check_keywords post :login_check post :simple_update end @@ -389,7 +394,7 @@ Rails.application.routes.draw do scope module: :users do resource :interest, only: [:create] - + resources :clas resources :accounts, only: [:show, :update] do resource :phone_bind, only: [:create] resource :email_bind, only: [:create] @@ -476,6 +481,16 @@ Rails.application.routes.draw do end end end + resources :identity_verifications + resources :site_pages do + member do + post :build + end + collection do + post :softbot_build + get :themes + end + end namespace :traces do resources :trace_users, only: [:create] @@ -574,6 +589,7 @@ Rails.application.routes.draw do get :index_chosen post :clean post :series_update + get :index_to_name end member do post :copy @@ -786,12 +802,21 @@ Rails.application.routes.draw do resources :glcc_news resources :pinned_forums end + resources :glcc_pr_check do + collection do + post :send_mail + end + end + resources :project_statistics, only: [:index] do collection do get :visits_static end end resources :users_rank, only: [:index] + resources :identity_verifications + resources :site_pages + resources :page_themes resources :projects_rank, only: [:index] resources :sites resources :edu_settings @@ -846,7 +871,12 @@ Rails.application.routes.draw do resources :school_statistics, only: [:index] do get :contrast, on: :collection end - resources :organizations, only: [:index, :edit, :show, :destroy] + resources :organizations, only: [:index, :edit, :show, :destroy] do + member do + post :open_cla + post :close_cla + end + end resources :users, only: [:index, :edit, :update, :destroy] do member do post :reward_grade diff --git a/db/migrate/20230520133729_add_user_login_uniq_index.rb b/db/migrate/20230520133729_add_user_login_uniq_index.rb new file mode 100644 index 000000000..f1990afce --- /dev/null +++ b/db/migrate/20230520133729_add_user_login_uniq_index.rb @@ -0,0 +1,12 @@ +class AddUserLoginUniqIndex < ActiveRecord::Migration[5.2] + def change + + users = User.group("login").having("count(1) >1") + ids = users.pluck(:id) + logins = users.pluck(:login) + User.where(login: logins).where.not(id: ids).destroy_all + + remove_index :users, :login + add_index :users, :login, unique: true + end +end diff --git a/db/migrate/20230530141227_add_user_action_index.rb b/db/migrate/20230530141227_add_user_action_index.rb new file mode 100644 index 000000000..2d76bd478 --- /dev/null +++ b/db/migrate/20230530141227_add_user_action_index.rb @@ -0,0 +1,7 @@ +class AddUserActionIndex < ActiveRecord::Migration[5.2] + def change + add_index :user_actions, :user_id + add_index :user_actions, :action_type + add_index :user_actions, :action_id + end +end diff --git a/db/migrate/20230530353459_add_project_pr_admin.rb b/db/migrate/20230530353459_add_project_pr_admin.rb new file mode 100644 index 000000000..85655ae60 --- /dev/null +++ b/db/migrate/20230530353459_add_project_pr_admin.rb @@ -0,0 +1,5 @@ +class AddProjectPrAdmin < ActiveRecord::Migration[5.2] + def change + add_column :projects, :pr_view_admin, :boolean, default: false + end +end diff --git a/db/migrate/20230612020506_add_cla_to_users.rb b/db/migrate/20230612020506_add_cla_to_users.rb new file mode 100644 index 000000000..d16650c49 --- /dev/null +++ b/db/migrate/20230612020506_add_cla_to_users.rb @@ -0,0 +1,5 @@ +class AddClaToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :cla, :boolean, default: false + end +end diff --git a/db/migrate/20230612031416_create_clas.rb b/db/migrate/20230612031416_create_clas.rb new file mode 100644 index 000000000..67ffb2986 --- /dev/null +++ b/db/migrate/20230612031416_create_clas.rb @@ -0,0 +1,15 @@ +class CreateClas < ActiveRecord::Migration[5.2] + def change + create_table :clas do |t| + t.string :name, null:false + t.string :key, null:false + t.text :content + t.integer :organization_id, null:false + t.boolean :pr_need, default: false + t.integer :count, default: 0 + t.timestamps + end + add_index :clas, :key, :length =>190 + add_index :clas, :organization_id + end +end diff --git a/db/migrate/20230612031448_create_user_clas.rb b/db/migrate/20230612031448_create_user_clas.rb new file mode 100644 index 000000000..b79aafec3 --- /dev/null +++ b/db/migrate/20230612031448_create_user_clas.rb @@ -0,0 +1,15 @@ +class CreateUserClas < ActiveRecord::Migration[5.2] + def change + create_table :user_clas do |t| + t.integer :user_id, null:false + t.integer :cla_id, null:false + t.string :real_name, null:false + t.string :email, null:false + t.integer :state, default:0 + t.timestamps + end + add_index :user_clas, :user_id + add_index :user_clas, :cla_id + + end +end diff --git a/db/migrate/20230613082850_change_cal_to_enabling_cla_for_users.rb b/db/migrate/20230613082850_change_cal_to_enabling_cla_for_users.rb new file mode 100644 index 000000000..1c4a9e93b --- /dev/null +++ b/db/migrate/20230613082850_change_cal_to_enabling_cla_for_users.rb @@ -0,0 +1,5 @@ +class ChangeCalToEnablingClaForUsers < ActiveRecord::Migration[5.2] + def change + rename_column :users, :cla ,:enabling_cla + end +end diff --git a/db/migrate/20230620030511_add_sign_time_to_user_clas.rb b/db/migrate/20230620030511_add_sign_time_to_user_clas.rb new file mode 100644 index 000000000..f913f244f --- /dev/null +++ b/db/migrate/20230620030511_add_sign_time_to_user_clas.rb @@ -0,0 +1,5 @@ +class AddSignTimeToUserClas < ActiveRecord::Migration[5.2] + def change + add_column :user_clas, :sign_time, :datetime + end +end diff --git a/db/migrate/20230725020157_create_pages.rb b/db/migrate/20230725020157_create_pages.rb new file mode 100644 index 000000000..97d6be8a7 --- /dev/null +++ b/db/migrate/20230725020157_create_pages.rb @@ -0,0 +1,16 @@ +class CreatePages < ActiveRecord::Migration[5.2] + def change + create_table :pages do |t| + t.integer :user_id, null:false, index:true + t.integer :project_id, null:false, index:true + t.string :identifier + t.string :site_name + t.integer :language_frame, default: 0 + t.string :theme + t.datetime :last_build_at + t.boolean :state, default: true + t.string :state_description + t.timestamps + end + end +end diff --git a/db/migrate/20230725060833_create_identity_verifications.rb b/db/migrate/20230725060833_create_identity_verifications.rb new file mode 100644 index 000000000..5310e026b --- /dev/null +++ b/db/migrate/20230725060833_create_identity_verifications.rb @@ -0,0 +1,16 @@ +class CreateIdentityVerifications < ActiveRecord::Migration[5.2] + def change + create_table :identity_verifications do |t| + t.integer :user_id, null:false, index: true + t.string :number, null:false + t.string :name, null:false + t.integer :card_front + t.integer :card_back + t.integer :hold_card_front + t.integer :hold_card_back + t.integer :state, default: 0 + t.string :description + t.timestamps + end + end +end diff --git a/db/migrate/20230727083929_add_id_card_verify_to_users.rb b/db/migrate/20230727083929_add_id_card_verify_to_users.rb new file mode 100644 index 000000000..1bb47f760 --- /dev/null +++ b/db/migrate/20230727083929_add_id_card_verify_to_users.rb @@ -0,0 +1,6 @@ +class AddIdCardVerifyToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :id_card_verify, :boolean, default: false + add_column :users, :website_permission, :boolean, default: false + end +end diff --git a/db/migrate/20230823071725_create_page_themes.rb b/db/migrate/20230823071725_create_page_themes.rb new file mode 100644 index 000000000..22cb7ab11 --- /dev/null +++ b/db/migrate/20230823071725_create_page_themes.rb @@ -0,0 +1,12 @@ +class CreatePageThemes < ActiveRecord::Migration[5.2] + def change + create_table :page_themes do |t| + t.string :name, null:false + t.integer :language_frame, default:0 + t.string :image_url + t.string :clone_url, null:false + t.integer :order_index,default: 0 + t.timestamps + end + end +end diff --git a/db/migrate/20230901023417_add_last_build_info_and_state_to_pages.rb b/db/migrate/20230901023417_add_last_build_info_and_state_to_pages.rb new file mode 100644 index 000000000..b5df0bd2d --- /dev/null +++ b/db/migrate/20230901023417_add_last_build_info_and_state_to_pages.rb @@ -0,0 +1,6 @@ +class AddLastBuildInfoAndStateToPages < ActiveRecord::Migration[5.2] + def change + add_column :pages, :last_build_info, :text + add_column :pages, :build_state, :boolean + end +end diff --git a/db/migrate/20231010030020_add_data_bank_to_user_actions.rb b/db/migrate/20231010030020_add_data_bank_to_user_actions.rb new file mode 100644 index 000000000..66f792854 --- /dev/null +++ b/db/migrate/20231010030020_add_data_bank_to_user_actions.rb @@ -0,0 +1,5 @@ +class AddDataBankToUserActions < ActiveRecord::Migration[5.2] + def change + add_column :user_actions, :data_bank, :text + end +end diff --git a/lib/tasks/batch_forked_project.rake b/lib/tasks/batch_forked_project.rake index 4cb50a77a..e0fea6f5b 100644 --- a/lib/tasks/batch_forked_project.rake +++ b/lib/tasks/batch_forked_project.rake @@ -15,7 +15,7 @@ namespace :batch_forked_project do user = User.find_by(login: username) next if user.blank? next if Project.exists?(user_id: user.id, identifier: project.identifier) - new_project = Projects::ForkService.new(user, project, nil).call + new_project = Projects::ForkService.new(user, project, nil, nil, nil).call random_num = rand(5..20) members = user_logins.sample(random_num) members.each do |m| diff --git a/lib/tasks/init_project_topic.rake b/lib/tasks/init_project_topic.rake index f6fcd4b96..5255a4aa2 100644 --- a/lib/tasks/init_project_topic.rake +++ b/lib/tasks/init_project_topic.rake @@ -4,7 +4,7 @@ namespace :init_project_topic do desc "Init Project Topic for Project" task project: :environment do - Project.order(created_at: :desc).find_each do |p| + Project.where(platform: 'forge').order(created_at: :desc).find_each do |p| next unless p.owner.present? next if p.project_topics.size >= 3 begin diff --git a/lib/tasks/repair_gitea_user.rake b/lib/tasks/repair_gitea_user.rake new file mode 100644 index 000000000..7f14325be --- /dev/null +++ b/lib/tasks/repair_gitea_user.rake @@ -0,0 +1,25 @@ +namespace :repair_gitea_user do + desc "If forge users gitea_uid is null repair gitea user" + task done: :environment do + users = User.find_by_sql("SELECT * FROM `users` WHERE id !=2 AND type='User' AND NOT EXISTS (SELECT * FROM forgegitea.`user` WHERE forgegitea.`user`.id=users.gitea_uid)") + puts "total users count:#{users.count}" + success_count = 0 + users.each do |user| + begin + interactor = Gitea::RegisterInteractor.call({ username: user.login, email: user.mail, password: "abc12345678" }) + if interactor.success? + gitea_user = interactor.result + result = Gitea::User::GenerateTokenService.call(user.login, password) + user.gitea_token = result['sha1'] + user.gitea_uid = gitea_user[:body]['id'] + user.is_sync_pwd = false + user.save! + success_count = success_count + 1 + end + rescue Exception => e + puts "repair gitea user: #{user.id}:#{user.login}:, error:#{e}" + end + end + puts "repair gitea user success total: #{success_count}" + end +end \ No newline at end of file diff --git a/public/assets/.sprockets-manifest-dd73942813e820b803d35c9915c0bb25.json b/public/assets/.sprockets-manifest-dd73942813e820b803d35c9915c0bb25.json index af170e1c3..a95972dde 100644 --- a/public/assets/.sprockets-manifest-dd73942813e820b803d35c9915c0bb25.json +++ b/public/assets/.sprockets-manifest-dd73942813e820b803d35c9915c0bb25.json @@ -1 +1 @@ -{"files":{"admin-5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd.js":{"logical_path":"admin.js","mtime":"2023-05-16T20:42:39+08:00","size":4522862,"digest":"5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd","integrity":"sha256-Wkj0mIG6yeIsg3wg2yLqO1G0JHUd2xIgijtCK3UuYP0="},"admin-a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470.css":{"logical_path":"admin.css","mtime":"2023-05-08T15:53:49+08:00","size":369999,"digest":"a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470","integrity":"sha256-p+36AT9egixDrBofgTwZKf96qeEs0TE/nhtykvpAZHA="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2021-11-24T09:25:49+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2021-11-24T09:25:49+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2021-11-24T09:25:49+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2021-11-24T09:25:49+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2021-11-24T09:25:49+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-f9c1b76699b39750bee976e1dbb5d832fdce2fb05edaf1b818c6e76fdf5ba77c.js":{"logical_path":"college.js","mtime":"2023-05-08T15:53:49+08:00","size":3505839,"digest":"f9c1b76699b39750bee976e1dbb5d832fdce2fb05edaf1b818c6e76fdf5ba77c","integrity":"sha256-+cG3Zpmzl1C+6Xbh27XYMv3OL7Be2vG4GMbnb99bp3w="},"college-fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc.css":{"logical_path":"college.css","mtime":"2022-02-22T09:55:36+08:00","size":218568,"digest":"fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc","integrity":"sha256-/ltEwtL+j80kFDosud+CVL6RBNP+Tj2g8+MxeDihbrw="},"cooperative-2efdf753e39599e78fb02527e864784df9a34dd971576db8231b63f618f9786e.js":{"logical_path":"cooperative.js","mtime":"2023-05-08T15:53:49+08:00","size":4413858,"digest":"2efdf753e39599e78fb02527e864784df9a34dd971576db8231b63f618f9786e","integrity":"sha256-Lv33U+OVmeePsCUn6GR4TfmjTdlxV224Ixtj9hj5eG4="},"cooperative-cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77.css":{"logical_path":"cooperative.css","mtime":"2022-02-22T09:55:38+08:00","size":354672,"digest":"cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77","integrity":"sha256-z0rcJZ7kZQjw+6DMqk8sO+sz27cVzDVOww+7fbcpync="},"oauth-79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c.css":{"logical_path":"oauth.css","mtime":"2022-02-22T09:55:36+08:00","size":1497,"digest":"79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c","integrity":"sha256-ea2et/0cjMga7VafLJM9C71agj3EUKEDjANO7yp7a5w="},"logo-9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9.png":{"logical_path":"logo.png","mtime":"2022-09-09T17:53:11+08:00","size":10051,"digest":"9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9","integrity":"sha256-nZcVmxxmGa+dZnYWtNoO9DIB9F2Cs/WRyT/NXyM9osk="},"application-1852bb2a261b8a9ce0d4a83dcc076d63be1105549c174f335c1e0f466fa9f8c1.js":{"logical_path":"application.js","mtime":"2023-05-08T15:53:49+08:00","size":1197927,"digest":"1852bb2a261b8a9ce0d4a83dcc076d63be1105549c174f335c1e0f466fa9f8c1","integrity":"sha256-GFK7KiYbipzg1Kg9zAdtY74RBVScF08zXB4PRm+p+ME="},"bootstrap/bootstrap-toggle.min.js-fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650.map":{"logical_path":"bootstrap/bootstrap-toggle.min.js.map","mtime":"2023-03-12T10:19:45+08:00","size":4106,"digest":"fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650","integrity":"sha256-+sUE5DvIGvRpCb1xVIQGr4qP7ghqo+bQ74b+k8r7xlA="},"bootstrap/bootstrap2-toggle.min.js-b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c.map":{"logical_path":"bootstrap/bootstrap2-toggle.min.js.map","mtime":"2023-03-12T10:19:45+08:00","size":4094,"digest":"b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c","integrity":"sha256-s1mL6MopkdOukYqFXDtQZwLdr7Na5RePNkc/JwXsSpw="},"application-346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff.css":{"logical_path":"application.css","mtime":"2022-02-22T09:55:36+08:00","size":159095,"digest":"346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff","integrity":"sha256-NGtnhQBwjz9OnNcsRtHp5UqvduBHi3ZlGawIO/k/vP8="},"doorkeeper/admin/application-1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c.css":{"logical_path":"doorkeeper/admin/application.css","mtime":"2022-07-11T17:10:38+08:00","size":135395,"digest":"1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c","integrity":"sha256-HqswUVd7QTdJOoHbLBtRRtw3B4n5FqfbZmc8fRVfzjw="},"doorkeeper/application-748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86.css":{"logical_path":"doorkeeper/application.css","mtime":"2022-07-11T17:10:38+08:00","size":135960,"digest":"748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86","integrity":"sha256-dItHeK81ZaDo+rgZr3n4pQBblcSdiuNaQ25KGkvN+4Y="}},"assets":{"admin.js":"admin-5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd.js","admin.css":"admin-a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-f9c1b76699b39750bee976e1dbb5d832fdce2fb05edaf1b818c6e76fdf5ba77c.js","college.css":"college-fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc.css","cooperative.js":"cooperative-2efdf753e39599e78fb02527e864784df9a34dd971576db8231b63f618f9786e.js","cooperative.css":"cooperative-cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77.css","oauth.css":"oauth-79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c.css","logo.png":"logo-9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9.png","application.js":"application-1852bb2a261b8a9ce0d4a83dcc076d63be1105549c174f335c1e0f466fa9f8c1.js","bootstrap/bootstrap-toggle.min.js.map":"bootstrap/bootstrap-toggle.min.js-fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650.map","bootstrap/bootstrap2-toggle.min.js.map":"bootstrap/bootstrap2-toggle.min.js-b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c.map","application.css":"application-346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff.css","doorkeeper/admin/application.css":"doorkeeper/admin/application-1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c.css","doorkeeper/application.css":"doorkeeper/application-748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86.css"}} \ No newline at end of file +{"files":{"admin-3e4fde7de877298349280a53b1de578b155575be742d34a72de7574db7f26219.js":{"logical_path":"admin.js","mtime":"2022-09-02T16:02:23+08:00","size":4587814,"digest":"3e4fde7de877298349280a53b1de578b155575be742d34a72de7574db7f26219","integrity":"sha256-Pk/efeh3KYNJKApTsd5XixVVdb50LTSnLedXTbfyYhk="},"admin-5fa03af0e91aea5a8125dc7a7bb31e7a8c4d0a480e2c09ccc7b6db230063c555.css":{"logical_path":"admin.css","mtime":"2022-09-02T16:02:23+08:00","size":369866,"digest":"5fa03af0e91aea5a8125dc7a7bb31e7a8c4d0a480e2c09ccc7b6db230063c555","integrity":"sha256-X6A68Oka6lqBJdx6e7MeeoxNCkgOLAnMx7bbIwBjxVU="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2021-11-16T13:43:10+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2021-11-16T13:43:10+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2021-11-16T13:43:10+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2021-11-16T13:43:10+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2021-11-16T13:43:10+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-f9c1b76699b39750bee976e1dbb5d832fdce2fb05edaf1b818c6e76fdf5ba77c.js":{"logical_path":"college.js","mtime":"2023-06-21T16:19:54+08:00","size":3505839,"digest":"f9c1b76699b39750bee976e1dbb5d832fdce2fb05edaf1b818c6e76fdf5ba77c","integrity":"sha256-+cG3Zpmzl1C+6Xbh27XYMv3OL7Be2vG4GMbnb99bp3w="},"college-fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc.css":{"logical_path":"college.css","mtime":"2022-02-22T09:55:36+08:00","size":218568,"digest":"fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc","integrity":"sha256-/ltEwtL+j80kFDosud+CVL6RBNP+Tj2g8+MxeDihbrw="},"cooperative-2efdf753e39599e78fb02527e864784df9a34dd971576db8231b63f618f9786e.js":{"logical_path":"cooperative.js","mtime":"2023-06-21T16:19:54+08:00","size":4413858,"digest":"2efdf753e39599e78fb02527e864784df9a34dd971576db8231b63f618f9786e","integrity":"sha256-Lv33U+OVmeePsCUn6GR4TfmjTdlxV224Ixtj9hj5eG4="},"cooperative-cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77.css":{"logical_path":"cooperative.css","mtime":"2022-02-22T09:55:38+08:00","size":354672,"digest":"cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77","integrity":"sha256-z0rcJZ7kZQjw+6DMqk8sO+sz27cVzDVOww+7fbcpync="},"oauth-79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c.css":{"logical_path":"oauth.css","mtime":"2022-02-22T09:55:36+08:00","size":1497,"digest":"79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c","integrity":"sha256-ea2et/0cjMga7VafLJM9C71agj3EUKEDjANO7yp7a5w="},"logo-9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9.png":{"logical_path":"logo.png","mtime":"2023-05-31T17:17:47+08:00","size":10051,"digest":"9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9","integrity":"sha256-nZcVmxxmGa+dZnYWtNoO9DIB9F2Cs/WRyT/NXyM9osk="},"application-1852bb2a261b8a9ce0d4a83dcc076d63be1105549c174f335c1e0f466fa9f8c1.js":{"logical_path":"application.js","mtime":"2023-06-21T16:19:54+08:00","size":1197927,"digest":"1852bb2a261b8a9ce0d4a83dcc076d63be1105549c174f335c1e0f466fa9f8c1","integrity":"sha256-GFK7KiYbipzg1Kg9zAdtY74RBVScF08zXB4PRm+p+ME="},"bootstrap/bootstrap-toggle.min.js-fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650.map":{"logical_path":"bootstrap/bootstrap-toggle.min.js.map","mtime":"2023-05-31T17:17:47+08:00","size":4106,"digest":"fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650","integrity":"sha256-+sUE5DvIGvRpCb1xVIQGr4qP7ghqo+bQ74b+k8r7xlA="},"bootstrap/bootstrap2-toggle.min.js-b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c.map":{"logical_path":"bootstrap/bootstrap2-toggle.min.js.map","mtime":"2023-05-31T17:17:47+08:00","size":4094,"digest":"b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c","integrity":"sha256-s1mL6MopkdOukYqFXDtQZwLdr7Na5RePNkc/JwXsSpw="},"application-346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff.css":{"logical_path":"application.css","mtime":"2022-02-22T09:55:36+08:00","size":159095,"digest":"346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff","integrity":"sha256-NGtnhQBwjz9OnNcsRtHp5UqvduBHi3ZlGawIO/k/vP8="},"doorkeeper/admin/application-1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c.css":{"logical_path":"doorkeeper/admin/application.css","mtime":"2022-09-01T11:37:27+08:00","size":135395,"digest":"1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c","integrity":"sha256-HqswUVd7QTdJOoHbLBtRRtw3B4n5FqfbZmc8fRVfzjw="},"doorkeeper/application-748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86.css":{"logical_path":"doorkeeper/application.css","mtime":"2022-09-01T11:37:27+08:00","size":135960,"digest":"748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86","integrity":"sha256-dItHeK81ZaDo+rgZr3n4pQBblcSdiuNaQ25KGkvN+4Y="},"admin-ac55ef6d0ab68e7b7fc33b36616fb687dab371f3c6c8414daffe341ab264ab2c.js":{"logical_path":"admin.js","mtime":"2023-06-21T16:19:54+08:00","size":4590295,"digest":"ac55ef6d0ab68e7b7fc33b36616fb687dab371f3c6c8414daffe341ab264ab2c","integrity":"sha256-rFXvbQq2jnt/wzs2YW+2h9qzcfPGyEFNr/40GrJkqyw="},"admin-a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470.css":{"logical_path":"admin.css","mtime":"2023-08-15T13:59:17+08:00","size":369999,"digest":"a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470","integrity":"sha256-p+36AT9egixDrBofgTwZKf96qeEs0TE/nhtykvpAZHA="},"admin-9e014925064b501053c6807d66239f20c5e16526b27522e403bbdaa85f8ead79.js":{"logical_path":"admin.js","mtime":"2023-08-15T17:24:40+08:00","size":1608986,"digest":"9e014925064b501053c6807d66239f20c5e16526b27522e403bbdaa85f8ead79","integrity":"sha256-ngFJJQZLUBBTxoB9ZiOfIMXhZSaydSLkA7vaqF+OrXk="},"college-bd7fb1ab1da64733a0172f89c8b9d5cfe4db736bb3ee51e0ca0701b40e2e49cc.js":{"logical_path":"college.js","mtime":"2023-08-15T13:59:17+08:00","size":1090655,"digest":"bd7fb1ab1da64733a0172f89c8b9d5cfe4db736bb3ee51e0ca0701b40e2e49cc","integrity":"sha256-vX+xqx2mRzOgFy+JyLnVz+Tbc2uz7lHgygcBtA4uScw="},"cooperative-1f8e268f2c629e3453e71d3ad4bc2c043e73989a8804598ad807c4050ee3c346.js":{"logical_path":"cooperative.js","mtime":"2023-08-15T13:59:17+08:00","size":1495462,"digest":"1f8e268f2c629e3453e71d3ad4bc2c043e73989a8804598ad807c4050ee3c346","integrity":"sha256-H44mjyxinjRT5x061LwsBD5zmJqIBFmK2AfEBQ7jw0Y="},"application-860dba70259cdc6c5c72c499f8be717c050d517f1fd6a1543c0b6863af987000.js":{"logical_path":"application.js","mtime":"2023-08-15T13:59:17+08:00","size":506517,"digest":"860dba70259cdc6c5c72c499f8be717c050d517f1fd6a1543c0b6863af987000","integrity":"sha256-hg26cCWc3GxccsSZ+L5xfAUNUX8f1qFUPAtoY6+YcAA="}},"assets":{"admin.js":"admin-9e014925064b501053c6807d66239f20c5e16526b27522e403bbdaa85f8ead79.js","admin.css":"admin-a7edfa013f5e822c43ac1a1f813c1929ff7aa9e12cd1313f9e1b7292fa406470.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-bd7fb1ab1da64733a0172f89c8b9d5cfe4db736bb3ee51e0ca0701b40e2e49cc.js","college.css":"college-fe5b44c2d2fe8fcd24143a2cb9df8254be9104d3fe4e3da0f3e3317838a16ebc.css","cooperative.js":"cooperative-1f8e268f2c629e3453e71d3ad4bc2c043e73989a8804598ad807c4050ee3c346.js","cooperative.css":"cooperative-cf4adc259ee46508f0fba0ccaa4f2c3beb33dbb715cc354ec30fbb7db729ca77.css","oauth.css":"oauth-79ad9eb7fd1c8cc81aed569f2c933d0bbd5a823dc450a1038c034eef2a7b6b9c.css","logo.png":"logo-9d97159b1c6619af9d667616b4da0ef43201f45d82b3f591c93fcd5f233da2c9.png","application.js":"application-860dba70259cdc6c5c72c499f8be717c050d517f1fd6a1543c0b6863af987000.js","bootstrap/bootstrap-toggle.min.js.map":"bootstrap/bootstrap-toggle.min.js-fac504e43bc81af46909bd71548406af8a8fee086aa3e6d0ef86fe93cafbc650.map","bootstrap/bootstrap2-toggle.min.js.map":"bootstrap/bootstrap2-toggle.min.js-b3598be8ca2991d3ae918a855c3b506702ddafb35ae5178f36473f2705ec4a9c.map","application.css":"application-346b678500708f3f4e9cd72c46d1e9e54aaf76e0478b766519ac083bf93fbcff.css","doorkeeper/admin/application.css":"doorkeeper/admin/application-1eab3051577b4137493a81db2c1b5146dc370789f916a7db66673c7d155fce3c.css","doorkeeper/application.css":"doorkeeper/application-748b4778af3565a0e8fab819af79f8a5005b95c49d8ae35a436e4a1a4bcdfb86.css"}} \ No newline at end of file diff --git a/public/assets/admin-5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd.js b/public/assets/admin-5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd.js deleted file mode 100644 index a0e5031fb..000000000 --- a/public/assets/admin-5a48f49881bac9e22c837c20db22ea3b51b424751ddb12208a3b422b752e60fd.js +++ /dev/null @@ -1,139386 +0,0 @@ -/* -Unobtrusive JavaScript -https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts -Released under the MIT license - */ -; - -(function() { - var context = this; - - (function() { - (function() { - this.Rails = { - linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]', - buttonClickSelector: { - selector: 'button[data-remote]:not([form]), button[data-confirm]:not([form])', - exclude: 'form button' - }, - inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', - formSubmitSelector: 'form', - formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])', - formDisableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled', - formEnableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled', - fileInputSelector: 'input[name][type=file]:not([disabled])', - linkDisableSelector: 'a[data-disable-with], a[data-disable]', - buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]' - }; - - }).call(this); - }).call(context); - - var Rails = context.Rails; - - (function() { - (function() { - var nonce; - - nonce = null; - - Rails.loadCSPNonce = function() { - var ref; - return nonce = (ref = document.querySelector("meta[name=csp-nonce]")) != null ? ref.content : void 0; - }; - - Rails.cspNonce = function() { - return nonce != null ? nonce : Rails.loadCSPNonce(); - }; - - }).call(this); - (function() { - var expando, m; - - m = Element.prototype.matches || Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector; - - Rails.matches = function(element, selector) { - if (selector.exclude != null) { - return m.call(element, selector.selector) && !m.call(element, selector.exclude); - } else { - return m.call(element, selector); - } - }; - - expando = '_ujsData'; - - Rails.getData = function(element, key) { - var ref; - return (ref = element[expando]) != null ? ref[key] : void 0; - }; - - Rails.setData = function(element, key, value) { - if (element[expando] == null) { - element[expando] = {}; - } - return element[expando][key] = value; - }; - - Rails.$ = function(selector) { - return Array.prototype.slice.call(document.querySelectorAll(selector)); - }; - - }).call(this); - (function() { - var $, csrfParam, csrfToken; - - $ = Rails.$; - - csrfToken = Rails.csrfToken = function() { - var meta; - meta = document.querySelector('meta[name=csrf-token]'); - return meta && meta.content; - }; - - csrfParam = Rails.csrfParam = function() { - var meta; - meta = document.querySelector('meta[name=csrf-param]'); - return meta && meta.content; - }; - - Rails.CSRFProtection = function(xhr) { - var token; - token = csrfToken(); - if (token != null) { - return xhr.setRequestHeader('X-CSRF-Token', token); - } - }; - - Rails.refreshCSRFTokens = function() { - var param, token; - token = csrfToken(); - param = csrfParam(); - if ((token != null) && (param != null)) { - return $('form input[name="' + param + '"]').forEach(function(input) { - return input.value = token; - }); - } - }; - - }).call(this); - (function() { - var CustomEvent, fire, matches, preventDefault; - - matches = Rails.matches; - - CustomEvent = window.CustomEvent; - - if (typeof CustomEvent !== 'function') { - CustomEvent = function(event, params) { - var evt; - evt = document.createEvent('CustomEvent'); - evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail); - return evt; - }; - CustomEvent.prototype = window.Event.prototype; - preventDefault = CustomEvent.prototype.preventDefault; - CustomEvent.prototype.preventDefault = function() { - var result; - result = preventDefault.call(this); - if (this.cancelable && !this.defaultPrevented) { - Object.defineProperty(this, 'defaultPrevented', { - get: function() { - return true; - } - }); - } - return result; - }; - } - - fire = Rails.fire = function(obj, name, data) { - var event; - event = new CustomEvent(name, { - bubbles: true, - cancelable: true, - detail: data - }); - obj.dispatchEvent(event); - return !event.defaultPrevented; - }; - - Rails.stopEverything = function(e) { - fire(e.target, 'ujs:everythingStopped'); - e.preventDefault(); - e.stopPropagation(); - return e.stopImmediatePropagation(); - }; - - Rails.delegate = function(element, selector, eventType, handler) { - return element.addEventListener(eventType, function(e) { - var target; - target = e.target; - while (!(!(target instanceof Element) || matches(target, selector))) { - target = target.parentNode; - } - if (target instanceof Element && handler.call(target, e) === false) { - e.preventDefault(); - return e.stopPropagation(); - } - }); - }; - - }).call(this); - (function() { - var AcceptHeaders, CSRFProtection, createXHR, cspNonce, fire, prepareOptions, processResponse; - - cspNonce = Rails.cspNonce, CSRFProtection = Rails.CSRFProtection, fire = Rails.fire; - - AcceptHeaders = { - '*': '*/*', - text: 'text/plain', - html: 'text/html', - xml: 'application/xml, text/xml', - json: 'application/json, text/javascript', - script: 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript' - }; - - Rails.ajax = function(options) { - var xhr; - options = prepareOptions(options); - xhr = createXHR(options, function() { - var ref, response; - response = processResponse((ref = xhr.response) != null ? ref : xhr.responseText, xhr.getResponseHeader('Content-Type')); - if (Math.floor(xhr.status / 100) === 2) { - if (typeof options.success === "function") { - options.success(response, xhr.statusText, xhr); - } - } else { - if (typeof options.error === "function") { - options.error(response, xhr.statusText, xhr); - } - } - return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : void 0; - }); - if ((options.beforeSend != null) && !options.beforeSend(xhr, options)) { - return false; - } - if (xhr.readyState === XMLHttpRequest.OPENED) { - return xhr.send(options.data); - } - }; - - prepareOptions = function(options) { - options.url = options.url || location.href; - options.type = options.type.toUpperCase(); - if (options.type === 'GET' && options.data) { - if (options.url.indexOf('?') < 0) { - options.url += '?' + options.data; - } else { - options.url += '&' + options.data; - } - } - if (AcceptHeaders[options.dataType] == null) { - options.dataType = '*'; - } - options.accept = AcceptHeaders[options.dataType]; - if (options.dataType !== '*') { - options.accept += ', */*; q=0.01'; - } - return options; - }; - - createXHR = function(options, done) { - var xhr; - xhr = new XMLHttpRequest(); - xhr.open(options.type, options.url, true); - xhr.setRequestHeader('Accept', options.accept); - if (typeof options.data === 'string') { - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); - } - if (!options.crossDomain) { - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - } - CSRFProtection(xhr); - xhr.withCredentials = !!options.withCredentials; - xhr.onreadystatechange = function() { - if (xhr.readyState === XMLHttpRequest.DONE) { - return done(xhr); - } - }; - return xhr; - }; - - processResponse = function(response, type) { - var parser, script; - if (typeof response === 'string' && typeof type === 'string') { - if (type.match(/\bjson\b/)) { - try { - response = JSON.parse(response); - } catch (error) {} - } else if (type.match(/\b(?:java|ecma)script\b/)) { - script = document.createElement('script'); - script.setAttribute('nonce', cspNonce()); - script.text = response; - document.head.appendChild(script).parentNode.removeChild(script); - } else if (type.match(/\b(xml|html|svg)\b/)) { - parser = new DOMParser(); - type = type.replace(/;.+/, ''); - try { - response = parser.parseFromString(response, type); - } catch (error) {} - } - } - return response; - }; - - Rails.href = function(element) { - return element.href; - }; - - Rails.isCrossDomain = function(url) { - var e, originAnchor, urlAnchor; - originAnchor = document.createElement('a'); - originAnchor.href = location.href; - urlAnchor = document.createElement('a'); - try { - urlAnchor.href = url; - return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) || (originAnchor.protocol + '//' + originAnchor.host === urlAnchor.protocol + '//' + urlAnchor.host)); - } catch (error) { - e = error; - return true; - } - }; - - }).call(this); - (function() { - var matches, toArray; - - matches = Rails.matches; - - toArray = function(e) { - return Array.prototype.slice.call(e); - }; - - Rails.serializeElement = function(element, additionalParam) { - var inputs, params; - inputs = [element]; - if (matches(element, 'form')) { - inputs = toArray(element.elements); - } - params = []; - inputs.forEach(function(input) { - if (!input.name || input.disabled) { - return; - } - if (matches(input, 'select')) { - return toArray(input.options).forEach(function(option) { - if (option.selected) { - return params.push({ - name: input.name, - value: option.value - }); - } - }); - } else if (input.checked || ['radio', 'checkbox', 'submit'].indexOf(input.type) === -1) { - return params.push({ - name: input.name, - value: input.value - }); - } - }); - if (additionalParam) { - params.push(additionalParam); - } - return params.map(function(param) { - if (param.name != null) { - return (encodeURIComponent(param.name)) + "=" + (encodeURIComponent(param.value)); - } else { - return param; - } - }).join('&'); - }; - - Rails.formElements = function(form, selector) { - if (matches(form, 'form')) { - return toArray(form.elements).filter(function(el) { - return matches(el, selector); - }); - } else { - return toArray(form.querySelectorAll(selector)); - } - }; - - }).call(this); - (function() { - var allowAction, fire, stopEverything; - - fire = Rails.fire, stopEverything = Rails.stopEverything; - - Rails.handleConfirm = function(e) { - if (!allowAction(this)) { - return stopEverything(e); - } - }; - - allowAction = function(element) { - var answer, callback, message; - message = element.getAttribute('data-confirm'); - if (!message) { - return true; - } - answer = false; - if (fire(element, 'confirm')) { - try { - answer = confirm(message); - } catch (error) {} - callback = fire(element, 'confirm:complete', [answer]); - } - return answer && callback; - }; - - }).call(this); - (function() { - var disableFormElement, disableFormElements, disableLinkElement, enableFormElement, enableFormElements, enableLinkElement, formElements, getData, matches, setData, stopEverything; - - matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, stopEverything = Rails.stopEverything, formElements = Rails.formElements; - - Rails.handleDisabledElement = function(e) { - var element; - element = this; - if (element.disabled) { - return stopEverything(e); - } - }; - - Rails.enableElement = function(e) { - var element; - element = e instanceof Event ? e.target : e; - if (matches(element, Rails.linkDisableSelector)) { - return enableLinkElement(element); - } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formEnableSelector)) { - return enableFormElement(element); - } else if (matches(element, Rails.formSubmitSelector)) { - return enableFormElements(element); - } - }; - - Rails.disableElement = function(e) { - var element; - element = e instanceof Event ? e.target : e; - if (matches(element, Rails.linkDisableSelector)) { - return disableLinkElement(element); - } else if (matches(element, Rails.buttonDisableSelector) || matches(element, Rails.formDisableSelector)) { - return disableFormElement(element); - } else if (matches(element, Rails.formSubmitSelector)) { - return disableFormElements(element); - } - }; - - disableLinkElement = function(element) { - var replacement; - replacement = element.getAttribute('data-disable-with'); - if (replacement != null) { - setData(element, 'ujs:enable-with', element.innerHTML); - element.innerHTML = replacement; - } - element.addEventListener('click', stopEverything); - return setData(element, 'ujs:disabled', true); - }; - - enableLinkElement = function(element) { - var originalText; - originalText = getData(element, 'ujs:enable-with'); - if (originalText != null) { - element.innerHTML = originalText; - setData(element, 'ujs:enable-with', null); - } - element.removeEventListener('click', stopEverything); - return setData(element, 'ujs:disabled', null); - }; - - disableFormElements = function(form) { - return formElements(form, Rails.formDisableSelector).forEach(disableFormElement); - }; - - disableFormElement = function(element) { - var replacement; - replacement = element.getAttribute('data-disable-with'); - if (replacement != null) { - if (matches(element, 'button')) { - setData(element, 'ujs:enable-with', element.innerHTML); - element.innerHTML = replacement; - } else { - setData(element, 'ujs:enable-with', element.value); - element.value = replacement; - } - } - element.disabled = true; - return setData(element, 'ujs:disabled', true); - }; - - enableFormElements = function(form) { - return formElements(form, Rails.formEnableSelector).forEach(enableFormElement); - }; - - enableFormElement = function(element) { - var originalText; - originalText = getData(element, 'ujs:enable-with'); - if (originalText != null) { - if (matches(element, 'button')) { - element.innerHTML = originalText; - } else { - element.value = originalText; - } - setData(element, 'ujs:enable-with', null); - } - element.disabled = false; - return setData(element, 'ujs:disabled', null); - }; - - }).call(this); - (function() { - var stopEverything; - - stopEverything = Rails.stopEverything; - - Rails.handleMethod = function(e) { - var csrfParam, csrfToken, form, formContent, href, link, method; - link = this; - method = link.getAttribute('data-method'); - if (!method) { - return; - } - href = Rails.href(link); - csrfToken = Rails.csrfToken(); - csrfParam = Rails.csrfParam(); - form = document.createElement('form'); - formContent = ""; - if ((csrfParam != null) && (csrfToken != null) && !Rails.isCrossDomain(href)) { - formContent += ""; - } - formContent += ''; - form.method = 'post'; - form.action = href; - form.target = link.target; - form.innerHTML = formContent; - form.style.display = 'none'; - document.body.appendChild(form); - form.querySelector('[type="submit"]').click(); - return stopEverything(e); - }; - - }).call(this); - (function() { - var ajax, fire, getData, isCrossDomain, isRemote, matches, serializeElement, setData, stopEverything, - slice = [].slice; - - matches = Rails.matches, getData = Rails.getData, setData = Rails.setData, fire = Rails.fire, stopEverything = Rails.stopEverything, ajax = Rails.ajax, isCrossDomain = Rails.isCrossDomain, serializeElement = Rails.serializeElement; - - isRemote = function(element) { - var value; - value = element.getAttribute('data-remote'); - return (value != null) && value !== 'false'; - }; - - Rails.handleRemote = function(e) { - var button, data, dataType, element, method, url, withCredentials; - element = this; - if (!isRemote(element)) { - return true; - } - if (!fire(element, 'ajax:before')) { - fire(element, 'ajax:stopped'); - return false; - } - withCredentials = element.getAttribute('data-with-credentials'); - dataType = element.getAttribute('data-type') || 'script'; - if (matches(element, Rails.formSubmitSelector)) { - button = getData(element, 'ujs:submit-button'); - method = getData(element, 'ujs:submit-button-formmethod') || element.method; - url = getData(element, 'ujs:submit-button-formaction') || element.getAttribute('action') || location.href; - if (method.toUpperCase() === 'GET') { - url = url.replace(/\?.*$/, ''); - } - if (element.enctype === 'multipart/form-data') { - data = new FormData(element); - if (button != null) { - data.append(button.name, button.value); - } - } else { - data = serializeElement(element, button); - } - setData(element, 'ujs:submit-button', null); - setData(element, 'ujs:submit-button-formmethod', null); - setData(element, 'ujs:submit-button-formaction', null); - } else if (matches(element, Rails.buttonClickSelector) || matches(element, Rails.inputChangeSelector)) { - method = element.getAttribute('data-method'); - url = element.getAttribute('data-url'); - data = serializeElement(element, element.getAttribute('data-params')); - } else { - method = element.getAttribute('data-method'); - url = Rails.href(element); - data = element.getAttribute('data-params'); - } - ajax({ - type: method || 'GET', - url: url, - data: data, - dataType: dataType, - beforeSend: function(xhr, options) { - if (fire(element, 'ajax:beforeSend', [xhr, options])) { - return fire(element, 'ajax:send', [xhr]); - } else { - fire(element, 'ajax:stopped'); - return false; - } - }, - success: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return fire(element, 'ajax:success', args); - }, - error: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return fire(element, 'ajax:error', args); - }, - complete: function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; - return fire(element, 'ajax:complete', args); - }, - crossDomain: isCrossDomain(url), - withCredentials: (withCredentials != null) && withCredentials !== 'false' - }); - return stopEverything(e); - }; - - Rails.formSubmitButtonClick = function(e) { - var button, form; - button = this; - form = button.form; - if (!form) { - return; - } - if (button.name) { - setData(form, 'ujs:submit-button', { - name: button.name, - value: button.value - }); - } - setData(form, 'ujs:formnovalidate-button', button.formNoValidate); - setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction')); - return setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod')); - }; - - Rails.preventInsignificantClick = function(e) { - var data, insignificantMetaClick, link, metaClick, method, nonPrimaryMouseClick; - link = this; - method = (link.getAttribute('data-method') || 'GET').toUpperCase(); - data = link.getAttribute('data-params'); - metaClick = e.metaKey || e.ctrlKey; - insignificantMetaClick = metaClick && method === 'GET' && !data; - nonPrimaryMouseClick = (e.button != null) && e.button !== 0; - if (nonPrimaryMouseClick || insignificantMetaClick) { - return e.stopImmediatePropagation(); - } - }; - - }).call(this); - (function() { - var $, CSRFProtection, delegate, disableElement, enableElement, fire, formSubmitButtonClick, getData, handleConfirm, handleDisabledElement, handleMethod, handleRemote, loadCSPNonce, preventInsignificantClick, refreshCSRFTokens; - - fire = Rails.fire, delegate = Rails.delegate, getData = Rails.getData, $ = Rails.$, refreshCSRFTokens = Rails.refreshCSRFTokens, CSRFProtection = Rails.CSRFProtection, loadCSPNonce = Rails.loadCSPNonce, enableElement = Rails.enableElement, disableElement = Rails.disableElement, handleDisabledElement = Rails.handleDisabledElement, handleConfirm = Rails.handleConfirm, preventInsignificantClick = Rails.preventInsignificantClick, handleRemote = Rails.handleRemote, formSubmitButtonClick = Rails.formSubmitButtonClick, handleMethod = Rails.handleMethod; - - if ((typeof jQuery !== "undefined" && jQuery !== null) && (jQuery.ajax != null)) { - if (jQuery.rails) { - throw new Error('If you load both jquery_ujs and rails-ujs, use rails-ujs only.'); - } - jQuery.rails = Rails; - jQuery.ajaxPrefilter(function(options, originalOptions, xhr) { - if (!options.crossDomain) { - return CSRFProtection(xhr); - } - }); - } - - Rails.start = function() { - if (window._rails_loaded) { - throw new Error('rails-ujs has already been loaded!'); - } - window.addEventListener('pageshow', function() { - $(Rails.formEnableSelector).forEach(function(el) { - if (getData(el, 'ujs:disabled')) { - return enableElement(el); - } - }); - return $(Rails.linkDisableSelector).forEach(function(el) { - if (getData(el, 'ujs:disabled')) { - return enableElement(el); - } - }); - }); - delegate(document, Rails.linkDisableSelector, 'ajax:complete', enableElement); - delegate(document, Rails.linkDisableSelector, 'ajax:stopped', enableElement); - delegate(document, Rails.buttonDisableSelector, 'ajax:complete', enableElement); - delegate(document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement); - delegate(document, Rails.linkClickSelector, 'click', preventInsignificantClick); - delegate(document, Rails.linkClickSelector, 'click', handleDisabledElement); - delegate(document, Rails.linkClickSelector, 'click', handleConfirm); - delegate(document, Rails.linkClickSelector, 'click', disableElement); - delegate(document, Rails.linkClickSelector, 'click', handleRemote); - delegate(document, Rails.linkClickSelector, 'click', handleMethod); - delegate(document, Rails.buttonClickSelector, 'click', preventInsignificantClick); - delegate(document, Rails.buttonClickSelector, 'click', handleDisabledElement); - delegate(document, Rails.buttonClickSelector, 'click', handleConfirm); - delegate(document, Rails.buttonClickSelector, 'click', disableElement); - delegate(document, Rails.buttonClickSelector, 'click', handleRemote); - delegate(document, Rails.inputChangeSelector, 'change', handleDisabledElement); - delegate(document, Rails.inputChangeSelector, 'change', handleConfirm); - delegate(document, Rails.inputChangeSelector, 'change', handleRemote); - delegate(document, Rails.formSubmitSelector, 'submit', handleDisabledElement); - delegate(document, Rails.formSubmitSelector, 'submit', handleConfirm); - delegate(document, Rails.formSubmitSelector, 'submit', handleRemote); - delegate(document, Rails.formSubmitSelector, 'submit', function(e) { - return setTimeout((function() { - return disableElement(e); - }), 13); - }); - delegate(document, Rails.formSubmitSelector, 'ajax:send', disableElement); - delegate(document, Rails.formSubmitSelector, 'ajax:complete', enableElement); - delegate(document, Rails.formInputClickSelector, 'click', preventInsignificantClick); - delegate(document, Rails.formInputClickSelector, 'click', handleDisabledElement); - delegate(document, Rails.formInputClickSelector, 'click', handleConfirm); - delegate(document, Rails.formInputClickSelector, 'click', formSubmitButtonClick); - document.addEventListener('DOMContentLoaded', refreshCSRFTokens); - document.addEventListener('DOMContentLoaded', loadCSPNonce); - return window._rails_loaded = true; - }; - - if (window.Rails === Rails && fire(document, 'rails:attachBindings')) { - Rails.start(); - } - - }).call(this); - }).call(this); - - if (typeof module === "object" && module.exports) { - module.exports = Rails; - } else if (typeof define === "function" && define.amd) { - define(Rails); - } -}).call(this); -(function(global, factory) { - typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : factory(global.ActiveStorage = {}); -})(this, function(exports) { - "use strict"; - function createCommonjsModule(fn, module) { - return module = { - exports: {} - }, fn(module, module.exports), module.exports; - } - var sparkMd5 = createCommonjsModule(function(module, exports) { - (function(factory) { - { - module.exports = factory(); - } - })(function(undefined) { - var hex_chr = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" ]; - function md5cycle(x, k) { - var a = x[0], b = x[1], c = x[2], d = x[3]; - a += (b & c | ~b & d) + k[0] - 680876936 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[1] - 389564586 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[2] + 606105819 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[3] - 1044525330 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[4] - 176418897 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[5] + 1200080426 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[6] - 1473231341 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[7] - 45705983 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[8] + 1770035416 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[9] - 1958414417 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[10] - 42063 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[11] - 1990404162 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[12] + 1804603682 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[13] - 40341101 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[14] - 1502002290 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[15] + 1236535329 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & d | c & ~d) + k[1] - 165796510 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[6] - 1069501632 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[11] + 643717713 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[0] - 373897302 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[5] - 701558691 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[10] + 38016083 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[15] - 660478335 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[4] - 405537848 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[9] + 568446438 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[14] - 1019803690 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[3] - 187363961 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[8] + 1163531501 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[13] - 1444681467 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[2] - 51403784 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[7] + 1735328473 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[12] - 1926607734 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b ^ c ^ d) + k[5] - 378558 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[8] - 2022574463 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[11] + 1839030562 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[14] - 35309556 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[1] - 1530992060 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[4] + 1272893353 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[7] - 155497632 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[10] - 1094730640 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[13] + 681279174 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[0] - 358537222 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[3] - 722521979 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[6] + 76029189 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[9] - 640364487 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[12] - 421815835 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[15] + 530742520 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[2] - 995338651 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; - b = (b << 21 | b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; - b = (b << 21 | b >>> 11) + c | 0; - x[0] = a + x[0] | 0; - x[1] = b + x[1] | 0; - x[2] = c + x[2] | 0; - x[3] = d + x[3] | 0; - } - function md5blk(s) { - var md5blks = [], i; - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24); - } - return md5blks; - } - function md5blk_array(a) { - var md5blks = [], i; - for (i = 0; i < 64; i += 4) { - md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); - } - return md5blks; - } - function md51(s) { - var n = s.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; - for (i = 64; i <= n; i += 64) { - md5cycle(state, md5blk(s.substring(i - 64, i))); - } - s = s.substring(i - 64); - length = s.length; - tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= s.charCodeAt(i) << (i % 4 << 3); - } - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { - md5cycle(state, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; - } - } - tmp = n * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(state, tail); - return state; - } - function md51_array(a) { - var n = a.length, state = [ 1732584193, -271733879, -1732584194, 271733878 ], i, length, tail, tmp, lo, hi; - for (i = 64; i <= n; i += 64) { - md5cycle(state, md5blk_array(a.subarray(i - 64, i))); - } - a = i - 64 < n ? a.subarray(i - 64) : new Uint8Array(0); - length = a.length; - tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= a[i] << (i % 4 << 3); - } - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { - md5cycle(state, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; - } - } - tmp = n * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(state, tail); - return state; - } - function rhex(n) { - var s = "", j; - for (j = 0; j < 4; j += 1) { - s += hex_chr[n >> j * 8 + 4 & 15] + hex_chr[n >> j * 8 & 15]; - } - return s; - } - function hex(x) { - var i; - for (i = 0; i < x.length; i += 1) { - x[i] = rhex(x[i]); - } - return x.join(""); - } - if (hex(md51("hello")) !== "5d41402abc4b2a76b9719d911017c592") ; - if (typeof ArrayBuffer !== "undefined" && !ArrayBuffer.prototype.slice) { - (function() { - function clamp(val, length) { - val = val | 0 || 0; - if (val < 0) { - return Math.max(val + length, 0); - } - return Math.min(val, length); - } - ArrayBuffer.prototype.slice = function(from, to) { - var length = this.byteLength, begin = clamp(from, length), end = length, num, target, targetArray, sourceArray; - if (to !== undefined) { - end = clamp(to, length); - } - if (begin > end) { - return new ArrayBuffer(0); - } - num = end - begin; - target = new ArrayBuffer(num); - targetArray = new Uint8Array(target); - sourceArray = new Uint8Array(this, begin, num); - targetArray.set(sourceArray); - return target; - }; - })(); - } - function toUtf8(str) { - if (/[\u0080-\uFFFF]/.test(str)) { - str = unescape(encodeURIComponent(str)); - } - return str; - } - function utf8Str2ArrayBuffer(str, returnUInt8Array) { - var length = str.length, buff = new ArrayBuffer(length), arr = new Uint8Array(buff), i; - for (i = 0; i < length; i += 1) { - arr[i] = str.charCodeAt(i); - } - return returnUInt8Array ? arr : buff; - } - function arrayBuffer2Utf8Str(buff) { - return String.fromCharCode.apply(null, new Uint8Array(buff)); - } - function concatenateArrayBuffers(first, second, returnUInt8Array) { - var result = new Uint8Array(first.byteLength + second.byteLength); - result.set(new Uint8Array(first)); - result.set(new Uint8Array(second), first.byteLength); - return returnUInt8Array ? result : result.buffer; - } - function hexToBinaryString(hex) { - var bytes = [], length = hex.length, x; - for (x = 0; x < length - 1; x += 2) { - bytes.push(parseInt(hex.substr(x, 2), 16)); - } - return String.fromCharCode.apply(String, bytes); - } - function SparkMD5() { - this.reset(); - } - SparkMD5.prototype.append = function(str) { - this.appendBinary(toUtf8(str)); - return this; - }; - SparkMD5.prototype.appendBinary = function(contents) { - this._buff += contents; - this._length += contents.length; - var length = this._buff.length, i; - for (i = 64; i <= length; i += 64) { - md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i))); - } - this._buff = this._buff.substring(i - 64); - return this; - }; - SparkMD5.prototype.end = function(raw) { - var buff = this._buff, length = buff.length, i, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], ret; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= buff.charCodeAt(i) << (i % 4 << 3); - } - this._finish(tail, length); - ret = hex(this._hash); - if (raw) { - ret = hexToBinaryString(ret); - } - this.reset(); - return ret; - }; - SparkMD5.prototype.reset = function() { - this._buff = ""; - this._length = 0; - this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; - return this; - }; - SparkMD5.prototype.getState = function() { - return { - buff: this._buff, - length: this._length, - hash: this._hash - }; - }; - SparkMD5.prototype.setState = function(state) { - this._buff = state.buff; - this._length = state.length; - this._hash = state.hash; - return this; - }; - SparkMD5.prototype.destroy = function() { - delete this._hash; - delete this._buff; - delete this._length; - }; - SparkMD5.prototype._finish = function(tail, length) { - var i = length, tmp, lo, hi; - tail[i >> 2] |= 128 << (i % 4 << 3); - if (i > 55) { - md5cycle(this._hash, tail); - for (i = 0; i < 16; i += 1) { - tail[i] = 0; - } - } - tmp = this._length * 8; - tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/); - lo = parseInt(tmp[2], 16); - hi = parseInt(tmp[1], 16) || 0; - tail[14] = lo; - tail[15] = hi; - md5cycle(this._hash, tail); - }; - SparkMD5.hash = function(str, raw) { - return SparkMD5.hashBinary(toUtf8(str), raw); - }; - SparkMD5.hashBinary = function(content, raw) { - var hash = md51(content), ret = hex(hash); - return raw ? hexToBinaryString(ret) : ret; - }; - SparkMD5.ArrayBuffer = function() { - this.reset(); - }; - SparkMD5.ArrayBuffer.prototype.append = function(arr) { - var buff = concatenateArrayBuffers(this._buff.buffer, arr, true), length = buff.length, i; - this._length += arr.byteLength; - for (i = 64; i <= length; i += 64) { - md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); - } - this._buff = i - 64 < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0); - return this; - }; - SparkMD5.ArrayBuffer.prototype.end = function(raw) { - var buff = this._buff, length = buff.length, tail = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], i, ret; - for (i = 0; i < length; i += 1) { - tail[i >> 2] |= buff[i] << (i % 4 << 3); - } - this._finish(tail, length); - ret = hex(this._hash); - if (raw) { - ret = hexToBinaryString(ret); - } - this.reset(); - return ret; - }; - SparkMD5.ArrayBuffer.prototype.reset = function() { - this._buff = new Uint8Array(0); - this._length = 0; - this._hash = [ 1732584193, -271733879, -1732584194, 271733878 ]; - return this; - }; - SparkMD5.ArrayBuffer.prototype.getState = function() { - var state = SparkMD5.prototype.getState.call(this); - state.buff = arrayBuffer2Utf8Str(state.buff); - return state; - }; - SparkMD5.ArrayBuffer.prototype.setState = function(state) { - state.buff = utf8Str2ArrayBuffer(state.buff, true); - return SparkMD5.prototype.setState.call(this, state); - }; - SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy; - SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish; - SparkMD5.ArrayBuffer.hash = function(arr, raw) { - var hash = md51_array(new Uint8Array(arr)), ret = hex(hash); - return raw ? hexToBinaryString(ret) : ret; - }; - return SparkMD5; - }); - }); - var classCallCheck = function(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - }; - var createClass = function() { - function defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - return function(Constructor, protoProps, staticProps) { - if (protoProps) defineProperties(Constructor.prototype, protoProps); - if (staticProps) defineProperties(Constructor, staticProps); - return Constructor; - }; - }(); - var fileSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; - var FileChecksum = function() { - createClass(FileChecksum, null, [ { - key: "create", - value: function create(file, callback) { - var instance = new FileChecksum(file); - instance.create(callback); - } - } ]); - function FileChecksum(file) { - classCallCheck(this, FileChecksum); - this.file = file; - this.chunkSize = 2097152; - this.chunkCount = Math.ceil(this.file.size / this.chunkSize); - this.chunkIndex = 0; - } - createClass(FileChecksum, [ { - key: "create", - value: function create(callback) { - var _this = this; - this.callback = callback; - this.md5Buffer = new sparkMd5.ArrayBuffer(); - this.fileReader = new FileReader(); - this.fileReader.addEventListener("load", function(event) { - return _this.fileReaderDidLoad(event); - }); - this.fileReader.addEventListener("error", function(event) { - return _this.fileReaderDidError(event); - }); - this.readNextChunk(); - } - }, { - key: "fileReaderDidLoad", - value: function fileReaderDidLoad(event) { - this.md5Buffer.append(event.target.result); - if (!this.readNextChunk()) { - var binaryDigest = this.md5Buffer.end(true); - var base64digest = btoa(binaryDigest); - this.callback(null, base64digest); - } - } - }, { - key: "fileReaderDidError", - value: function fileReaderDidError(event) { - this.callback("Error reading " + this.file.name); - } - }, { - key: "readNextChunk", - value: function readNextChunk() { - if (this.chunkIndex < this.chunkCount || this.chunkIndex == 0 && this.chunkCount == 0) { - var start = this.chunkIndex * this.chunkSize; - var end = Math.min(start + this.chunkSize, this.file.size); - var bytes = fileSlice.call(this.file, start, end); - this.fileReader.readAsArrayBuffer(bytes); - this.chunkIndex++; - return true; - } else { - return false; - } - } - } ]); - return FileChecksum; - }(); - function getMetaValue(name) { - var element = findElement(document.head, 'meta[name="' + name + '"]'); - if (element) { - return element.getAttribute("content"); - } - } - function findElements(root, selector) { - if (typeof root == "string") { - selector = root; - root = document; - } - var elements = root.querySelectorAll(selector); - return toArray$1(elements); - } - function findElement(root, selector) { - if (typeof root == "string") { - selector = root; - root = document; - } - return root.querySelector(selector); - } - function dispatchEvent(element, type) { - var eventInit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - var disabled = element.disabled; - var bubbles = eventInit.bubbles, cancelable = eventInit.cancelable, detail = eventInit.detail; - var event = document.createEvent("Event"); - event.initEvent(type, bubbles || true, cancelable || true); - event.detail = detail || {}; - try { - element.disabled = false; - element.dispatchEvent(event); - } finally { - element.disabled = disabled; - } - return event; - } - function toArray$1(value) { - if (Array.isArray(value)) { - return value; - } else if (Array.from) { - return Array.from(value); - } else { - return [].slice.call(value); - } - } - var BlobRecord = function() { - function BlobRecord(file, checksum, url) { - var _this = this; - classCallCheck(this, BlobRecord); - this.file = file; - this.attributes = { - filename: file.name, - content_type: file.type, - byte_size: file.size, - checksum: checksum - }; - this.xhr = new XMLHttpRequest(); - this.xhr.open("POST", url, true); - this.xhr.responseType = "json"; - this.xhr.setRequestHeader("Content-Type", "application/json"); - this.xhr.setRequestHeader("Accept", "application/json"); - this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); - this.xhr.setRequestHeader("X-CSRF-Token", getMetaValue("csrf-token")); - this.xhr.addEventListener("load", function(event) { - return _this.requestDidLoad(event); - }); - this.xhr.addEventListener("error", function(event) { - return _this.requestDidError(event); - }); - } - createClass(BlobRecord, [ { - key: "create", - value: function create(callback) { - this.callback = callback; - this.xhr.send(JSON.stringify({ - blob: this.attributes - })); - } - }, { - key: "requestDidLoad", - value: function requestDidLoad(event) { - if (this.status >= 200 && this.status < 300) { - var response = this.response; - var direct_upload = response.direct_upload; - delete response.direct_upload; - this.attributes = response; - this.directUploadData = direct_upload; - this.callback(null, this.toJSON()); - } else { - this.requestDidError(event); - } - } - }, { - key: "requestDidError", - value: function requestDidError(event) { - this.callback('Error creating Blob for "' + this.file.name + '". Status: ' + this.status); - } - }, { - key: "toJSON", - value: function toJSON() { - var result = {}; - for (var key in this.attributes) { - result[key] = this.attributes[key]; - } - return result; - } - }, { - key: "status", - get: function get$$1() { - return this.xhr.status; - } - }, { - key: "response", - get: function get$$1() { - var _xhr = this.xhr, responseType = _xhr.responseType, response = _xhr.response; - if (responseType == "json") { - return response; - } else { - return JSON.parse(response); - } - } - } ]); - return BlobRecord; - }(); - var BlobUpload = function() { - function BlobUpload(blob) { - var _this = this; - classCallCheck(this, BlobUpload); - this.blob = blob; - this.file = blob.file; - var _blob$directUploadDat = blob.directUploadData, url = _blob$directUploadDat.url, headers = _blob$directUploadDat.headers; - this.xhr = new XMLHttpRequest(); - this.xhr.open("PUT", url, true); - this.xhr.responseType = "text"; - for (var key in headers) { - this.xhr.setRequestHeader(key, headers[key]); - } - this.xhr.addEventListener("load", function(event) { - return _this.requestDidLoad(event); - }); - this.xhr.addEventListener("error", function(event) { - return _this.requestDidError(event); - }); - } - createClass(BlobUpload, [ { - key: "create", - value: function create(callback) { - this.callback = callback; - this.xhr.send(this.file.slice()); - } - }, { - key: "requestDidLoad", - value: function requestDidLoad(event) { - var _xhr = this.xhr, status = _xhr.status, response = _xhr.response; - if (status >= 200 && status < 300) { - this.callback(null, response); - } else { - this.requestDidError(event); - } - } - }, { - key: "requestDidError", - value: function requestDidError(event) { - this.callback('Error storing "' + this.file.name + '". Status: ' + this.xhr.status); - } - } ]); - return BlobUpload; - }(); - var id = 0; - var DirectUpload = function() { - function DirectUpload(file, url, delegate) { - classCallCheck(this, DirectUpload); - this.id = ++id; - this.file = file; - this.url = url; - this.delegate = delegate; - } - createClass(DirectUpload, [ { - key: "create", - value: function create(callback) { - var _this = this; - FileChecksum.create(this.file, function(error, checksum) { - if (error) { - callback(error); - return; - } - var blob = new BlobRecord(_this.file, checksum, _this.url); - notify(_this.delegate, "directUploadWillCreateBlobWithXHR", blob.xhr); - blob.create(function(error) { - if (error) { - callback(error); - } else { - var upload = new BlobUpload(blob); - notify(_this.delegate, "directUploadWillStoreFileWithXHR", upload.xhr); - upload.create(function(error) { - if (error) { - callback(error); - } else { - callback(null, blob.toJSON()); - } - }); - } - }); - }); - } - } ]); - return DirectUpload; - }(); - function notify(object, methodName) { - if (object && typeof object[methodName] == "function") { - for (var _len = arguments.length, messages = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { - messages[_key - 2] = arguments[_key]; - } - return object[methodName].apply(object, messages); - } - } - var DirectUploadController = function() { - function DirectUploadController(input, file) { - classCallCheck(this, DirectUploadController); - this.input = input; - this.file = file; - this.directUpload = new DirectUpload(this.file, this.url, this); - this.dispatch("initialize"); - } - createClass(DirectUploadController, [ { - key: "start", - value: function start(callback) { - var _this = this; - var hiddenInput = document.createElement("input"); - hiddenInput.type = "hidden"; - hiddenInput.name = this.input.name; - this.input.insertAdjacentElement("beforebegin", hiddenInput); - this.dispatch("start"); - this.directUpload.create(function(error, attributes) { - if (error) { - hiddenInput.parentNode.removeChild(hiddenInput); - _this.dispatchError(error); - } else { - hiddenInput.value = attributes.signed_id; - } - _this.dispatch("end"); - callback(error); - }); - } - }, { - key: "uploadRequestDidProgress", - value: function uploadRequestDidProgress(event) { - var progress = event.loaded / event.total * 100; - if (progress) { - this.dispatch("progress", { - progress: progress - }); - } - } - }, { - key: "dispatch", - value: function dispatch(name) { - var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - detail.file = this.file; - detail.id = this.directUpload.id; - return dispatchEvent(this.input, "direct-upload:" + name, { - detail: detail - }); - } - }, { - key: "dispatchError", - value: function dispatchError(error) { - var event = this.dispatch("error", { - error: error - }); - if (!event.defaultPrevented) { - alert(error); - } - } - }, { - key: "directUploadWillCreateBlobWithXHR", - value: function directUploadWillCreateBlobWithXHR(xhr) { - this.dispatch("before-blob-request", { - xhr: xhr - }); - } - }, { - key: "directUploadWillStoreFileWithXHR", - value: function directUploadWillStoreFileWithXHR(xhr) { - var _this2 = this; - this.dispatch("before-storage-request", { - xhr: xhr - }); - xhr.upload.addEventListener("progress", function(event) { - return _this2.uploadRequestDidProgress(event); - }); - } - }, { - key: "url", - get: function get$$1() { - return this.input.getAttribute("data-direct-upload-url"); - } - } ]); - return DirectUploadController; - }(); - var inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])"; - var DirectUploadsController = function() { - function DirectUploadsController(form) { - classCallCheck(this, DirectUploadsController); - this.form = form; - this.inputs = findElements(form, inputSelector).filter(function(input) { - return input.files.length; - }); - } - createClass(DirectUploadsController, [ { - key: "start", - value: function start(callback) { - var _this = this; - var controllers = this.createDirectUploadControllers(); - var startNextController = function startNextController() { - var controller = controllers.shift(); - if (controller) { - controller.start(function(error) { - if (error) { - callback(error); - _this.dispatch("end"); - } else { - startNextController(); - } - }); - } else { - callback(); - _this.dispatch("end"); - } - }; - this.dispatch("start"); - startNextController(); - } - }, { - key: "createDirectUploadControllers", - value: function createDirectUploadControllers() { - var controllers = []; - this.inputs.forEach(function(input) { - toArray$1(input.files).forEach(function(file) { - var controller = new DirectUploadController(input, file); - controllers.push(controller); - }); - }); - return controllers; - } - }, { - key: "dispatch", - value: function dispatch(name) { - var detail = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; - return dispatchEvent(this.form, "direct-uploads:" + name, { - detail: detail - }); - } - } ]); - return DirectUploadsController; - }(); - var processingAttribute = "data-direct-uploads-processing"; - var submitButtonsByForm = new WeakMap(); - var started = false; - function start() { - if (!started) { - started = true; - document.addEventListener("click", didClick, true); - document.addEventListener("submit", didSubmitForm); - document.addEventListener("ajax:before", didSubmitRemoteElement); - } - } - function didClick(event) { - var target = event.target; - if ((target.tagName == "INPUT" || target.tagName == "BUTTON") && target.type == "submit" && target.form) { - submitButtonsByForm.set(target.form, target); - } - } - function didSubmitForm(event) { - handleFormSubmissionEvent(event); - } - function didSubmitRemoteElement(event) { - if (event.target.tagName == "FORM") { - handleFormSubmissionEvent(event); - } - } - function handleFormSubmissionEvent(event) { - var form = event.target; - if (form.hasAttribute(processingAttribute)) { - event.preventDefault(); - return; - } - var controller = new DirectUploadsController(form); - var inputs = controller.inputs; - if (inputs.length) { - event.preventDefault(); - form.setAttribute(processingAttribute, ""); - inputs.forEach(disable); - controller.start(function(error) { - form.removeAttribute(processingAttribute); - if (error) { - inputs.forEach(enable); - } else { - submitForm(form); - } - }); - } - } - function submitForm(form) { - var button = submitButtonsByForm.get(form) || findElement(form, "input[type=submit], button[type=submit]"); - if (button) { - var _button = button, disabled = _button.disabled; - button.disabled = false; - button.focus(); - button.click(); - button.disabled = disabled; - } else { - button = document.createElement("input"); - button.type = "submit"; - button.style.display = "none"; - form.appendChild(button); - button.click(); - form.removeChild(button); - } - submitButtonsByForm.delete(form); - } - function disable(input) { - input.disabled = true; - } - function enable(input) { - input.disabled = false; - } - function autostart() { - if (window.ActiveStorage) { - start(); - } - } - setTimeout(autostart, 1); - exports.start = start; - exports.DirectUpload = DirectUpload; - Object.defineProperty(exports, "__esModule", { - value: true - }); -}); -/* -Turbolinks 5.2.0 -Copyright © 2018 Basecamp, LLC - */ - -(function(){var t=this;(function(){(function(){this.Turbolinks={supported:function(){return null!=window.history.pushState&&null!=window.requestAnimationFrame&&null!=window.addEventListener}(),visit:function(t,r){return e.controller.visit(t,r)},clearCache:function(){return e.controller.clearCache()},setProgressBarDelay:function(t){return e.controller.setProgressBarDelay(t)}}}).call(this)}).call(t);var e=t.Turbolinks;(function(){(function(){var t,r,n,o=[].slice;e.copyObject=function(t){var e,r,n;r={};for(e in t)n=t[e],r[e]=n;return r},e.closest=function(e,r){return t.call(e,r)},t=function(){var t,e;return t=document.documentElement,null!=(e=t.closest)?e:function(t){var e;for(e=this;e;){if(e.nodeType===Node.ELEMENT_NODE&&r.call(e,t))return e;e=e.parentNode}}}(),e.defer=function(t){return setTimeout(t,1)},e.throttle=function(t){var e;return e=null,function(){var r;return r=1<=arguments.length?o.call(arguments,0):[],null!=e?e:e=requestAnimationFrame(function(n){return function(){return e=null,t.apply(n,r)}}(this))}},e.dispatch=function(t,e){var r,o,i,s,a,u;return a=null!=e?e:{},u=a.target,r=a.cancelable,o=a.data,i=document.createEvent("Events"),i.initEvent(t,!0,r===!0),i.data=null!=o?o:{},i.cancelable&&!n&&(s=i.preventDefault,i.preventDefault=function(){return this.defaultPrevented||Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),s.call(this)}),(null!=u?u:document).dispatchEvent(i),i},n=function(){var t;return t=document.createEvent("Events"),t.initEvent("test",!0,!0),t.preventDefault(),t.defaultPrevented}(),e.match=function(t,e){return r.call(t,e)},r=function(){var t,e,r,n;return t=document.documentElement,null!=(e=null!=(r=null!=(n=t.matchesSelector)?n:t.webkitMatchesSelector)?r:t.msMatchesSelector)?e:t.mozMatchesSelector}(),e.uuid=function(){var t,e,r;for(r="",t=e=1;36>=e;t=++e)r+=9===t||14===t||19===t||24===t?"-":15===t?"4":20===t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16);return r}}).call(this),function(){e.Location=function(){function t(t){var e,r;null==t&&(t=""),r=document.createElement("a"),r.href=t.toString(),this.absoluteURL=r.href,e=r.hash.length,2>e?this.requestURL=this.absoluteURL:(this.requestURL=this.absoluteURL.slice(0,-e),this.anchor=r.hash.slice(1))}var e,r,n,o;return t.wrap=function(t){return t instanceof this?t:new this(t)},t.prototype.getOrigin=function(){return this.absoluteURL.split("/",3).join("/")},t.prototype.getPath=function(){var t,e;return null!=(t=null!=(e=this.requestURL.match(/\/\/[^\/]*(\/[^?;]*)/))?e[1]:void 0)?t:"/"},t.prototype.getPathComponents=function(){return this.getPath().split("/").slice(1)},t.prototype.getLastPathComponent=function(){return this.getPathComponents().slice(-1)[0]},t.prototype.getExtension=function(){var t,e;return null!=(t=null!=(e=this.getLastPathComponent().match(/\.[^.]*$/))?e[0]:void 0)?t:""},t.prototype.isHTML=function(){return this.getExtension().match(/^(?:|\.(?:htm|html|xhtml))$/)},t.prototype.isPrefixedBy=function(t){var e;return e=r(t),this.isEqualTo(t)||o(this.absoluteURL,e)},t.prototype.isEqualTo=function(t){return this.absoluteURL===(null!=t?t.absoluteURL:void 0)},t.prototype.toCacheKey=function(){return this.requestURL},t.prototype.toJSON=function(){return this.absoluteURL},t.prototype.toString=function(){return this.absoluteURL},t.prototype.valueOf=function(){return this.absoluteURL},r=function(t){return e(t.getOrigin()+t.getPath())},e=function(t){return n(t,"/")?t:t+"/"},o=function(t,e){return t.slice(0,e.length)===e},n=function(t,e){return t.slice(-e.length)===e},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.HttpRequest=function(){function r(r,n,o){this.delegate=r,this.requestCanceled=t(this.requestCanceled,this),this.requestTimedOut=t(this.requestTimedOut,this),this.requestFailed=t(this.requestFailed,this),this.requestLoaded=t(this.requestLoaded,this),this.requestProgressed=t(this.requestProgressed,this),this.url=e.Location.wrap(n).requestURL,this.referrer=e.Location.wrap(o).absoluteURL,this.createXHR()}return r.NETWORK_FAILURE=0,r.TIMEOUT_FAILURE=-1,r.timeout=60,r.prototype.send=function(){var t;return this.xhr&&!this.sent?(this.notifyApplicationBeforeRequestStart(),this.setProgress(0),this.xhr.send(),this.sent=!0,"function"==typeof(t=this.delegate).requestStarted?t.requestStarted():void 0):void 0},r.prototype.cancel=function(){return this.xhr&&this.sent?this.xhr.abort():void 0},r.prototype.requestProgressed=function(t){return t.lengthComputable?this.setProgress(t.loaded/t.total):void 0},r.prototype.requestLoaded=function(){return this.endRequest(function(t){return function(){var e;return 200<=(e=t.xhr.status)&&300>e?t.delegate.requestCompletedWithResponse(t.xhr.responseText,t.xhr.getResponseHeader("Turbolinks-Location")):(t.failed=!0,t.delegate.requestFailedWithStatusCode(t.xhr.status,t.xhr.responseText))}}(this))},r.prototype.requestFailed=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.NETWORK_FAILURE)}}(this))},r.prototype.requestTimedOut=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.TIMEOUT_FAILURE)}}(this))},r.prototype.requestCanceled=function(){return this.endRequest()},r.prototype.notifyApplicationBeforeRequestStart=function(){return e.dispatch("turbolinks:request-start",{data:{url:this.url,xhr:this.xhr}})},r.prototype.notifyApplicationAfterRequestEnd=function(){return e.dispatch("turbolinks:request-end",{data:{url:this.url,xhr:this.xhr}})},r.prototype.createXHR=function(){return this.xhr=new XMLHttpRequest,this.xhr.open("GET",this.url,!0),this.xhr.timeout=1e3*this.constructor.timeout,this.xhr.setRequestHeader("Accept","text/html, application/xhtml+xml"),this.xhr.setRequestHeader("Turbolinks-Referrer",this.referrer),this.xhr.onprogress=this.requestProgressed,this.xhr.onload=this.requestLoaded,this.xhr.onerror=this.requestFailed,this.xhr.ontimeout=this.requestTimedOut,this.xhr.onabort=this.requestCanceled},r.prototype.endRequest=function(t){return this.xhr?(this.notifyApplicationAfterRequestEnd(),null!=t&&t.call(this),this.destroy()):void 0},r.prototype.setProgress=function(t){var e;return this.progress=t,"function"==typeof(e=this.delegate).requestProgressed?e.requestProgressed(this.progress):void 0},r.prototype.destroy=function(){var t;return this.setProgress(1),"function"==typeof(t=this.delegate).requestFinished&&t.requestFinished(),this.delegate=null,this.xhr=null},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ProgressBar=function(){function e(){this.trickle=t(this.trickle,this),this.stylesheetElement=this.createStylesheetElement(),this.progressElement=this.createProgressElement()}var r;return r=300,e.defaultCSS=".turbolinks-progress-bar {\n position: fixed;\n display: block;\n top: 0;\n left: 0;\n height: 3px;\n background: #0076ff;\n z-index: 9999;\n transition: width "+r+"ms ease-out, opacity "+r/2+"ms "+r/2+"ms ease-in;\n transform: translate3d(0, 0, 0);\n}",e.prototype.show=function(){return this.visible?void 0:(this.visible=!0,this.installStylesheetElement(),this.installProgressElement(),this.startTrickling())},e.prototype.hide=function(){return this.visible&&!this.hiding?(this.hiding=!0,this.fadeProgressElement(function(t){return function(){return t.uninstallProgressElement(),t.stopTrickling(),t.visible=!1,t.hiding=!1}}(this))):void 0},e.prototype.setValue=function(t){return this.value=t,this.refresh()},e.prototype.installStylesheetElement=function(){return document.head.insertBefore(this.stylesheetElement,document.head.firstChild)},e.prototype.installProgressElement=function(){return this.progressElement.style.width=0,this.progressElement.style.opacity=1,document.documentElement.insertBefore(this.progressElement,document.body),this.refresh()},e.prototype.fadeProgressElement=function(t){return this.progressElement.style.opacity=0,setTimeout(t,1.5*r)},e.prototype.uninstallProgressElement=function(){return this.progressElement.parentNode?document.documentElement.removeChild(this.progressElement):void 0},e.prototype.startTrickling=function(){return null!=this.trickleInterval?this.trickleInterval:this.trickleInterval=setInterval(this.trickle,r)},e.prototype.stopTrickling=function(){return clearInterval(this.trickleInterval),this.trickleInterval=null},e.prototype.trickle=function(){return this.setValue(this.value+Math.random()/100)},e.prototype.refresh=function(){return requestAnimationFrame(function(t){return function(){return t.progressElement.style.width=10+90*t.value+"%"}}(this))},e.prototype.createStylesheetElement=function(){var t;return t=document.createElement("style"),t.type="text/css",t.textContent=this.constructor.defaultCSS,t},e.prototype.createProgressElement=function(){var t;return t=document.createElement("div"),t.className="turbolinks-progress-bar",t},e}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.BrowserAdapter=function(){function r(r){this.controller=r,this.showProgressBar=t(this.showProgressBar,this),this.progressBar=new e.ProgressBar}var n,o,i;return i=e.HttpRequest,n=i.NETWORK_FAILURE,o=i.TIMEOUT_FAILURE,r.prototype.visitProposedToLocationWithAction=function(t,e){return this.controller.startVisitToLocationWithAction(t,e)},r.prototype.visitStarted=function(t){return t.issueRequest(),t.changeHistory(),t.loadCachedSnapshot()},r.prototype.visitRequestStarted=function(t){return this.progressBar.setValue(0),t.hasCachedSnapshot()||"restore"!==t.action?this.showProgressBarAfterDelay():this.showProgressBar()},r.prototype.visitRequestProgressed=function(t){return this.progressBar.setValue(t.progress)},r.prototype.visitRequestCompleted=function(t){return t.loadResponse()},r.prototype.visitRequestFailedWithStatusCode=function(t,e){switch(e){case n:case o:return this.reload();default:return t.loadResponse()}},r.prototype.visitRequestFinished=function(t){return this.hideProgressBar()},r.prototype.visitCompleted=function(t){return t.followRedirect()},r.prototype.pageInvalidated=function(){return this.reload()},r.prototype.showProgressBarAfterDelay=function(){return this.progressBarTimeout=setTimeout(this.showProgressBar,this.controller.progressBarDelay)},r.prototype.showProgressBar=function(){return this.progressBar.show()},r.prototype.hideProgressBar=function(){return this.progressBar.hide(),clearTimeout(this.progressBarTimeout)},r.prototype.reload=function(){return window.location.reload()},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.History=function(){function r(e){this.delegate=e,this.onPageLoad=t(this.onPageLoad,this),this.onPopState=t(this.onPopState,this)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("popstate",this.onPopState,!1),addEventListener("load",this.onPageLoad,!1),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("popstate",this.onPopState,!1),removeEventListener("load",this.onPageLoad,!1),this.started=!1):void 0},r.prototype.push=function(t,r){return t=e.Location.wrap(t),this.update("push",t,r)},r.prototype.replace=function(t,r){return t=e.Location.wrap(t),this.update("replace",t,r)},r.prototype.onPopState=function(t){var r,n,o,i;return this.shouldHandlePopState()&&(i=null!=(n=t.state)?n.turbolinks:void 0)?(r=e.Location.wrap(window.location),o=i.restorationIdentifier,this.delegate.historyPoppedToLocationWithRestorationIdentifier(r,o)):void 0},r.prototype.onPageLoad=function(t){return e.defer(function(t){return function(){return t.pageLoaded=!0}}(this))},r.prototype.shouldHandlePopState=function(){return this.pageIsLoaded()},r.prototype.pageIsLoaded=function(){return this.pageLoaded||"complete"===document.readyState},r.prototype.update=function(t,e,r){var n;return n={turbolinks:{restorationIdentifier:r}},history[t+"State"](n,null,e)},r}()}.call(this),function(){e.HeadDetails=function(){function t(t){var e,r,n,s,a,u;for(this.elements={},n=0,a=t.length;a>n;n++)u=t[n],u.nodeType===Node.ELEMENT_NODE&&(s=u.outerHTML,r=null!=(e=this.elements)[s]?e[s]:e[s]={type:i(u),tracked:o(u),elements:[]},r.elements.push(u))}var e,r,n,o,i;return t.fromHeadElement=function(t){var e;return new this(null!=(e=null!=t?t.childNodes:void 0)?e:[])},t.prototype.hasElementWithKey=function(t){return t in this.elements},t.prototype.getTrackedElementSignature=function(){var t,e;return function(){var r,n;r=this.elements,n=[];for(t in r)e=r[t].tracked,e&&n.push(t);return n}.call(this).join("")},t.prototype.getScriptElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("script",t)},t.prototype.getStylesheetElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("stylesheet",t)},t.prototype.getElementsMatchingTypeNotInDetails=function(t,e){var r,n,o,i,s,a;o=this.elements,s=[];for(n in o)i=o[n],a=i.type,r=i.elements,a!==t||e.hasElementWithKey(n)||s.push(r[0]);return s},t.prototype.getProvisionalElements=function(){var t,e,r,n,o,i,s;r=[],n=this.elements;for(e in n)o=n[e],s=o.type,i=o.tracked,t=o.elements,null!=s||i?t.length>1&&r.push.apply(r,t.slice(1)):r.push.apply(r,t);return r},t.prototype.getMetaValue=function(t){var e;return null!=(e=this.findMetaElementByName(t))?e.getAttribute("content"):void 0},t.prototype.findMetaElementByName=function(t){var r,n,o,i;r=void 0,i=this.elements;for(o in i)n=i[o].elements,e(n[0],t)&&(r=n[0]);return r},i=function(t){return r(t)?"script":n(t)?"stylesheet":void 0},o=function(t){return"reload"===t.getAttribute("data-turbolinks-track")},r=function(t){var e;return e=t.tagName.toLowerCase(),"script"===e},n=function(t){var e;return e=t.tagName.toLowerCase(),"style"===e||"link"===e&&"stylesheet"===t.getAttribute("rel")},e=function(t,e){var r;return r=t.tagName.toLowerCase(),"meta"===r&&t.getAttribute("name")===e},t}()}.call(this),function(){e.Snapshot=function(){function t(t,e){this.headDetails=t,this.bodyElement=e}return t.wrap=function(t){return t instanceof this?t:"string"==typeof t?this.fromHTMLString(t):this.fromHTMLElement(t)},t.fromHTMLString=function(t){var e;return e=document.createElement("html"),e.innerHTML=t,this.fromHTMLElement(e)},t.fromHTMLElement=function(t){var r,n,o,i;return o=t.querySelector("head"),r=null!=(i=t.querySelector("body"))?i:document.createElement("body"),n=e.HeadDetails.fromHeadElement(o),new this(n,r)},t.prototype.clone=function(){return new this.constructor(this.headDetails,this.bodyElement.cloneNode(!0))},t.prototype.getRootLocation=function(){var t,r;return r=null!=(t=this.getSetting("root"))?t:"/",new e.Location(r)},t.prototype.getCacheControlValue=function(){return this.getSetting("cache-control")},t.prototype.getElementForAnchor=function(t){try{return this.bodyElement.querySelector("[id='"+t+"'], a[name='"+t+"']")}catch(e){}},t.prototype.getPermanentElements=function(){return this.bodyElement.querySelectorAll("[id][data-turbolinks-permanent]")},t.prototype.getPermanentElementById=function(t){return this.bodyElement.querySelector("#"+t+"[data-turbolinks-permanent]")},t.prototype.getPermanentElementsPresentInSnapshot=function(t){var e,r,n,o,i;for(o=this.getPermanentElements(),i=[],r=0,n=o.length;n>r;r++)e=o[r],t.getPermanentElementById(e.id)&&i.push(e);return i},t.prototype.findFirstAutofocusableElement=function(){return this.bodyElement.querySelector("[autofocus]")},t.prototype.hasAnchor=function(t){return null!=this.getElementForAnchor(t)},t.prototype.isPreviewable=function(){return"no-preview"!==this.getCacheControlValue()},t.prototype.isCacheable=function(){return"no-cache"!==this.getCacheControlValue()},t.prototype.isVisitable=function(){return"reload"!==this.getSetting("visit-control")},t.prototype.getSetting=function(t){return this.headDetails.getMetaValue("turbolinks-"+t)},t}()}.call(this),function(){var t=[].slice;e.Renderer=function(){function e(){}var r;return e.render=function(){var e,r,n,o;return n=arguments[0],r=arguments[1],e=3<=arguments.length?t.call(arguments,2):[],o=function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(this,e,function(){}),o.delegate=n,o.render(r),o},e.prototype.renderView=function(t){return this.delegate.viewWillRender(this.newBody),t(),this.delegate.viewRendered(this.newBody)},e.prototype.invalidateView=function(){return this.delegate.viewInvalidated()},e.prototype.createScriptElement=function(t){var e;return"false"===t.getAttribute("data-turbolinks-eval")?t:(e=document.createElement("script"),e.textContent=t.textContent,e.async=!1,r(e,t),e)},r=function(t,e){var r,n,o,i,s,a,u;for(i=e.attributes,a=[],r=0,n=i.length;n>r;r++)s=i[r],o=s.name,u=s.value,a.push(t.setAttribute(o,u));return a},e}()}.call(this),function(){var t,r,n=function(t,e){function r(){this.constructor=t}for(var n in e)o.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},o={}.hasOwnProperty;e.SnapshotRenderer=function(e){function o(t,e,r){this.currentSnapshot=t,this.newSnapshot=e,this.isPreview=r,this.currentHeadDetails=this.currentSnapshot.headDetails,this.newHeadDetails=this.newSnapshot.headDetails,this.currentBody=this.currentSnapshot.bodyElement,this.newBody=this.newSnapshot.bodyElement}return n(o,e),o.prototype.render=function(t){return this.shouldRender()?(this.mergeHead(),this.renderView(function(e){return function(){return e.replaceBody(),e.isPreview||e.focusFirstAutofocusableElement(),t()}}(this))):this.invalidateView()},o.prototype.mergeHead=function(){return this.copyNewHeadStylesheetElements(),this.copyNewHeadScriptElements(),this.removeCurrentHeadProvisionalElements(),this.copyNewHeadProvisionalElements()},o.prototype.replaceBody=function(){var t;return t=this.relocateCurrentBodyPermanentElements(),this.activateNewBodyScriptElements(),this.assignNewBody(),this.replacePlaceholderElementsWithClonedPermanentElements(t)},o.prototype.shouldRender=function(){return this.newSnapshot.isVisitable()&&this.trackedElementsAreIdentical()},o.prototype.trackedElementsAreIdentical=function(){return this.currentHeadDetails.getTrackedElementSignature()===this.newHeadDetails.getTrackedElementSignature()},o.prototype.copyNewHeadStylesheetElements=function(){var t,e,r,n,o;for(n=this.getNewHeadStylesheetElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},o.prototype.copyNewHeadScriptElements=function(){var t,e,r,n,o;for(n=this.getNewHeadScriptElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(this.createScriptElement(t)));return o},o.prototype.removeCurrentHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getCurrentHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.removeChild(t));return o},o.prototype.copyNewHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getNewHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},o.prototype.relocateCurrentBodyPermanentElements=function(){var e,n,o,i,s,a,u;for(a=this.getCurrentBodyPermanentElements(),u=[],e=0,n=a.length;n>e;e++)i=a[e],s=t(i),o=this.newSnapshot.getPermanentElementById(i.id),r(i,s.element),r(o,i),u.push(s);return u},o.prototype.replacePlaceholderElementsWithClonedPermanentElements=function(t){var e,n,o,i,s,a,u;for(u=[],o=0,i=t.length;i>o;o++)a=t[o],n=a.element,s=a.permanentElement,e=s.cloneNode(!0),u.push(r(n,e));return u},o.prototype.activateNewBodyScriptElements=function(){var t,e,n,o,i,s;for(i=this.getNewBodyScriptElements(),s=[],e=0,o=i.length;o>e;e++)n=i[e],t=this.createScriptElement(n),s.push(r(n,t));return s},o.prototype.assignNewBody=function(){return document.body=this.newBody},o.prototype.focusFirstAutofocusableElement=function(){var t;return null!=(t=this.newSnapshot.findFirstAutofocusableElement())?t.focus():void 0},o.prototype.getNewHeadStylesheetElements=function(){return this.newHeadDetails.getStylesheetElementsNotInDetails(this.currentHeadDetails)},o.prototype.getNewHeadScriptElements=function(){return this.newHeadDetails.getScriptElementsNotInDetails(this.currentHeadDetails)},o.prototype.getCurrentHeadProvisionalElements=function(){return this.currentHeadDetails.getProvisionalElements()},o.prototype.getNewHeadProvisionalElements=function(){return this.newHeadDetails.getProvisionalElements()},o.prototype.getCurrentBodyPermanentElements=function(){return this.currentSnapshot.getPermanentElementsPresentInSnapshot(this.newSnapshot)},o.prototype.getNewBodyScriptElements=function(){return this.newBody.querySelectorAll("script")},o}(e.Renderer),t=function(t){var e;return e=document.createElement("meta"),e.setAttribute("name","turbolinks-permanent-placeholder"),e.setAttribute("content",t.id),{element:e,permanentElement:t}},r=function(t,e){var r;return(r=t.parentNode)?r.replaceChild(e,t):void 0}}.call(this),function(){var t=function(t,e){function n(){this.constructor=t}for(var o in e)r.call(e,o)&&(t[o]=e[o]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},r={}.hasOwnProperty;e.ErrorRenderer=function(e){function r(t){var e;e=document.createElement("html"),e.innerHTML=t,this.newHead=e.querySelector("head"),this.newBody=e.querySelector("body")}return t(r,e),r.prototype.render=function(t){return this.renderView(function(e){return function(){return e.replaceHeadAndBody(),e.activateBodyScriptElements(),t()}}(this))},r.prototype.replaceHeadAndBody=function(){var t,e;return e=document.head,t=document.body,e.parentNode.replaceChild(this.newHead,e),t.parentNode.replaceChild(this.newBody,t)},r.prototype.activateBodyScriptElements=function(){var t,e,r,n,o,i;for(n=this.getScriptElements(),i=[],e=0,r=n.length;r>e;e++)o=n[e],t=this.createScriptElement(o),i.push(o.parentNode.replaceChild(t,o));return i},r.prototype.getScriptElements=function(){return document.documentElement.querySelectorAll("script")},r}(e.Renderer)}.call(this),function(){e.View=function(){function t(t){this.delegate=t,this.htmlElement=document.documentElement}return t.prototype.getRootLocation=function(){return this.getSnapshot().getRootLocation()},t.prototype.getElementForAnchor=function(t){return this.getSnapshot().getElementForAnchor(t)},t.prototype.getSnapshot=function(){return e.Snapshot.fromHTMLElement(this.htmlElement)},t.prototype.render=function(t,e){var r,n,o;return o=t.snapshot,r=t.error,n=t.isPreview,this.markAsPreview(n),null!=o?this.renderSnapshot(o,n,e):this.renderError(r,e)},t.prototype.markAsPreview=function(t){return t?this.htmlElement.setAttribute("data-turbolinks-preview",""):this.htmlElement.removeAttribute("data-turbolinks-preview")},t.prototype.renderSnapshot=function(t,r,n){return e.SnapshotRenderer.render(this.delegate,n,this.getSnapshot(),e.Snapshot.wrap(t),r)},t.prototype.renderError=function(t,r){return e.ErrorRenderer.render(this.delegate,r,t)},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ScrollManager=function(){function r(r){this.delegate=r,this.onScroll=t(this.onScroll,this),this.onScroll=e.throttle(this.onScroll)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("scroll",this.onScroll,!1),this.onScroll(),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("scroll",this.onScroll,!1),this.started=!1):void 0},r.prototype.scrollToElement=function(t){return t.scrollIntoView()},r.prototype.scrollToPosition=function(t){var e,r;return e=t.x,r=t.y,window.scrollTo(e,r)},r.prototype.onScroll=function(t){return this.updatePosition({x:window.pageXOffset,y:window.pageYOffset})},r.prototype.updatePosition=function(t){var e;return this.position=t,null!=(e=this.delegate)?e.scrollPositionChanged(this.position):void 0},r}()}.call(this),function(){e.SnapshotCache=function(){function t(t){this.size=t,this.keys=[],this.snapshots={}}var r;return t.prototype.has=function(t){var e;return e=r(t),e in this.snapshots},t.prototype.get=function(t){var e;if(this.has(t))return e=this.read(t),this.touch(t),e},t.prototype.put=function(t,e){return this.write(t,e),this.touch(t),e},t.prototype.read=function(t){var e;return e=r(t),this.snapshots[e]},t.prototype.write=function(t,e){var n;return n=r(t),this.snapshots[n]=e},t.prototype.touch=function(t){var e,n;return n=r(t),e=this.keys.indexOf(n),e>-1&&this.keys.splice(e,1),this.keys.unshift(n),this.trim()},t.prototype.trim=function(){var t,e,r,n,o;for(n=this.keys.splice(this.size),o=[],t=0,r=n.length;r>t;t++)e=n[t],o.push(delete this.snapshots[e]);return o},r=function(t){return e.Location.wrap(t).toCacheKey()},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Visit=function(){function r(r,n,o){this.controller=r,this.action=o,this.performScroll=t(this.performScroll,this),this.identifier=e.uuid(),this.location=e.Location.wrap(n),this.adapter=this.controller.adapter,this.state="initialized",this.timingMetrics={}}var n;return r.prototype.start=function(){return"initialized"===this.state?(this.recordTimingMetric("visitStart"),this.state="started",this.adapter.visitStarted(this)):void 0},r.prototype.cancel=function(){var t;return"started"===this.state?(null!=(t=this.request)&&t.cancel(),this.cancelRender(),this.state="canceled"):void 0},r.prototype.complete=function(){var t;return"started"===this.state?(this.recordTimingMetric("visitEnd"),this.state="completed","function"==typeof(t=this.adapter).visitCompleted&&t.visitCompleted(this),this.controller.visitCompleted(this)):void 0},r.prototype.fail=function(){var t;return"started"===this.state?(this.state="failed","function"==typeof(t=this.adapter).visitFailed?t.visitFailed(this):void 0):void 0},r.prototype.changeHistory=function(){var t,e;return this.historyChanged?void 0:(t=this.location.isEqualTo(this.referrer)?"replace":this.action,e=n(t),this.controller[e](this.location,this.restorationIdentifier),this.historyChanged=!0)},r.prototype.issueRequest=function(){return this.shouldIssueRequest()&&null==this.request?(this.progress=0,this.request=new e.HttpRequest(this,this.location,this.referrer),this.request.send()):void 0},r.prototype.getCachedSnapshot=function(){var t;return!(t=this.controller.getCachedSnapshotForLocation(this.location))||null!=this.location.anchor&&!t.hasAnchor(this.location.anchor)||"restore"!==this.action&&!t.isPreviewable()?void 0:t},r.prototype.hasCachedSnapshot=function(){return null!=this.getCachedSnapshot()},r.prototype.loadCachedSnapshot=function(){var t,e;return(e=this.getCachedSnapshot())?(t=this.shouldIssueRequest(),this.render(function(){var r;return this.cacheSnapshot(),this.controller.render({snapshot:e,isPreview:t},this.performScroll),"function"==typeof(r=this.adapter).visitRendered&&r.visitRendered(this),t?void 0:this.complete()})):void 0},r.prototype.loadResponse=function(){return null!=this.response?this.render(function(){var t,e;return this.cacheSnapshot(),this.request.failed?(this.controller.render({error:this.response},this.performScroll),"function"==typeof(t=this.adapter).visitRendered&&t.visitRendered(this),this.fail()):(this.controller.render({snapshot:this.response},this.performScroll),"function"==typeof(e=this.adapter).visitRendered&&e.visitRendered(this),this.complete())}):void 0},r.prototype.followRedirect=function(){return this.redirectedToLocation&&!this.followedRedirect?(this.location=this.redirectedToLocation,this.controller.replaceHistoryWithLocationAndRestorationIdentifier(this.redirectedToLocation,this.restorationIdentifier),this.followedRedirect=!0):void 0},r.prototype.requestStarted=function(){var t;return this.recordTimingMetric("requestStart"),"function"==typeof(t=this.adapter).visitRequestStarted?t.visitRequestStarted(this):void 0},r.prototype.requestProgressed=function(t){var e;return this.progress=t,"function"==typeof(e=this.adapter).visitRequestProgressed?e.visitRequestProgressed(this):void 0},r.prototype.requestCompletedWithResponse=function(t,r){return this.response=t,null!=r&&(this.redirectedToLocation=e.Location.wrap(r)),this.adapter.visitRequestCompleted(this)},r.prototype.requestFailedWithStatusCode=function(t,e){return this.response=e,this.adapter.visitRequestFailedWithStatusCode(this,t)},r.prototype.requestFinished=function(){var t;return this.recordTimingMetric("requestEnd"),"function"==typeof(t=this.adapter).visitRequestFinished?t.visitRequestFinished(this):void 0},r.prototype.performScroll=function(){return this.scrolled?void 0:("restore"===this.action?this.scrollToRestoredPosition()||this.scrollToTop():this.scrollToAnchor()||this.scrollToTop(),this.scrolled=!0)},r.prototype.scrollToRestoredPosition=function(){var t,e;return t=null!=(e=this.restorationData)?e.scrollPosition:void 0,null!=t?(this.controller.scrollToPosition(t),!0):void 0},r.prototype.scrollToAnchor=function(){return null!=this.location.anchor?(this.controller.scrollToAnchor(this.location.anchor),!0):void 0},r.prototype.scrollToTop=function(){return this.controller.scrollToPosition({x:0,y:0})},r.prototype.recordTimingMetric=function(t){var e;return null!=(e=this.timingMetrics)[t]?e[t]:e[t]=(new Date).getTime()},r.prototype.getTimingMetrics=function(){return e.copyObject(this.timingMetrics)},n=function(t){switch(t){case"replace":return"replaceHistoryWithLocationAndRestorationIdentifier";case"advance":case"restore":return"pushHistoryWithLocationAndRestorationIdentifier"}},r.prototype.shouldIssueRequest=function(){return"restore"===this.action?!this.hasCachedSnapshot():!0},r.prototype.cacheSnapshot=function(){return this.snapshotCached?void 0:(this.controller.cacheSnapshot(),this.snapshotCached=!0)},r.prototype.render=function(t){return this.cancelRender(),this.frame=requestAnimationFrame(function(e){return function(){return e.frame=null,t.call(e)}}(this))},r.prototype.cancelRender=function(){return this.frame?cancelAnimationFrame(this.frame):void 0},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Controller=function(){function r(){this.clickBubbled=t(this.clickBubbled,this),this.clickCaptured=t(this.clickCaptured,this),this.pageLoaded=t(this.pageLoaded,this),this.history=new e.History(this),this.view=new e.View(this),this.scrollManager=new e.ScrollManager(this),this.restorationData={},this.clearCache(),this.setProgressBarDelay(500)}return r.prototype.start=function(){return e.supported&&!this.started?(addEventListener("click",this.clickCaptured,!0),addEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.start(),this.startHistory(),this.started=!0,this.enabled=!0):void 0},r.prototype.disable=function(){return this.enabled=!1},r.prototype.stop=function(){return this.started?(removeEventListener("click",this.clickCaptured,!0),removeEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.stop(),this.stopHistory(),this.started=!1):void 0},r.prototype.clearCache=function(){return this.cache=new e.SnapshotCache(10)},r.prototype.visit=function(t,r){var n,o;return null==r&&(r={}),t=e.Location.wrap(t),this.applicationAllowsVisitingLocation(t)?this.locationIsVisitable(t)?(n=null!=(o=r.action)?o:"advance",this.adapter.visitProposedToLocationWithAction(t,n)):window.location=t:void 0},r.prototype.startVisitToLocationWithAction=function(t,r,n){var o;return e.supported?(o=this.getRestorationDataForIdentifier(n),this.startVisit(t,r,{restorationData:o})):window.location=t},r.prototype.setProgressBarDelay=function(t){return this.progressBarDelay=t},r.prototype.startHistory=function(){return this.location=e.Location.wrap(window.location),this.restorationIdentifier=e.uuid(),this.history.start(),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.stopHistory=function(){return this.history.stop()},r.prototype.pushHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.push(this.location,this.restorationIdentifier)},r.prototype.replaceHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.historyPoppedToLocationWithRestorationIdentifier=function(t,r){var n;return this.restorationIdentifier=r,this.enabled?(n=this.getRestorationDataForIdentifier(this.restorationIdentifier),this.startVisit(t,"restore",{restorationIdentifier:this.restorationIdentifier,restorationData:n,historyChanged:!0}),this.location=e.Location.wrap(t)):this.adapter.pageInvalidated()},r.prototype.getCachedSnapshotForLocation=function(t){var e;return null!=(e=this.cache.get(t))?e.clone():void 0},r.prototype.shouldCacheSnapshot=function(){return this.view.getSnapshot().isCacheable(); -},r.prototype.cacheSnapshot=function(){var t,r;return this.shouldCacheSnapshot()?(this.notifyApplicationBeforeCachingSnapshot(),r=this.view.getSnapshot(),t=this.lastRenderedLocation,e.defer(function(e){return function(){return e.cache.put(t,r.clone())}}(this))):void 0},r.prototype.scrollToAnchor=function(t){var e;return(e=this.view.getElementForAnchor(t))?this.scrollToElement(e):this.scrollToPosition({x:0,y:0})},r.prototype.scrollToElement=function(t){return this.scrollManager.scrollToElement(t)},r.prototype.scrollToPosition=function(t){return this.scrollManager.scrollToPosition(t)},r.prototype.scrollPositionChanged=function(t){var e;return e=this.getCurrentRestorationData(),e.scrollPosition=t},r.prototype.render=function(t,e){return this.view.render(t,e)},r.prototype.viewInvalidated=function(){return this.adapter.pageInvalidated()},r.prototype.viewWillRender=function(t){return this.notifyApplicationBeforeRender(t)},r.prototype.viewRendered=function(){return this.lastRenderedLocation=this.currentVisit.location,this.notifyApplicationAfterRender()},r.prototype.pageLoaded=function(){return this.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()},r.prototype.clickCaptured=function(){return removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},r.prototype.clickBubbled=function(t){var e,r,n;return this.enabled&&this.clickEventIsSignificant(t)&&(r=this.getVisitableLinkForNode(t.target))&&(n=this.getVisitableLocationForLink(r))&&this.applicationAllowsFollowingLinkToLocation(r,n)?(t.preventDefault(),e=this.getActionForLink(r),this.visit(n,{action:e})):void 0},r.prototype.applicationAllowsFollowingLinkToLocation=function(t,e){var r;return r=this.notifyApplicationAfterClickingLinkToLocation(t,e),!r.defaultPrevented},r.prototype.applicationAllowsVisitingLocation=function(t){var e;return e=this.notifyApplicationBeforeVisitingLocation(t),!e.defaultPrevented},r.prototype.notifyApplicationAfterClickingLinkToLocation=function(t,r){return e.dispatch("turbolinks:click",{target:t,data:{url:r.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationBeforeVisitingLocation=function(t){return e.dispatch("turbolinks:before-visit",{data:{url:t.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationAfterVisitingLocation=function(t){return e.dispatch("turbolinks:visit",{data:{url:t.absoluteURL}})},r.prototype.notifyApplicationBeforeCachingSnapshot=function(){return e.dispatch("turbolinks:before-cache")},r.prototype.notifyApplicationBeforeRender=function(t){return e.dispatch("turbolinks:before-render",{data:{newBody:t}})},r.prototype.notifyApplicationAfterRender=function(){return e.dispatch("turbolinks:render")},r.prototype.notifyApplicationAfterPageLoad=function(t){return null==t&&(t={}),e.dispatch("turbolinks:load",{data:{url:this.location.absoluteURL,timing:t}})},r.prototype.startVisit=function(t,e,r){var n;return null!=(n=this.currentVisit)&&n.cancel(),this.currentVisit=this.createVisit(t,e,r),this.currentVisit.start(),this.notifyApplicationAfterVisitingLocation(t)},r.prototype.createVisit=function(t,r,n){var o,i,s,a,u;return i=null!=n?n:{},a=i.restorationIdentifier,s=i.restorationData,o=i.historyChanged,u=new e.Visit(this,t,r),u.restorationIdentifier=null!=a?a:e.uuid(),u.restorationData=e.copyObject(s),u.historyChanged=o,u.referrer=this.location,u},r.prototype.visitCompleted=function(t){return this.notifyApplicationAfterPageLoad(t.getTimingMetrics())},r.prototype.clickEventIsSignificant=function(t){return!(t.defaultPrevented||t.target.isContentEditable||t.which>1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey)},r.prototype.getVisitableLinkForNode=function(t){return this.nodeIsVisitable(t)?e.closest(t,"a[href]:not([target]):not([download])"):void 0},r.prototype.getVisitableLocationForLink=function(t){var r;return r=new e.Location(t.getAttribute("href")),this.locationIsVisitable(r)?r:void 0},r.prototype.getActionForLink=function(t){var e;return null!=(e=t.getAttribute("data-turbolinks-action"))?e:"advance"},r.prototype.nodeIsVisitable=function(t){var r;return(r=e.closest(t,"[data-turbolinks]"))?"false"!==r.getAttribute("data-turbolinks"):!0},r.prototype.locationIsVisitable=function(t){return t.isPrefixedBy(this.view.getRootLocation())&&t.isHTML()},r.prototype.getCurrentRestorationData=function(){return this.getRestorationDataForIdentifier(this.restorationIdentifier)},r.prototype.getRestorationDataForIdentifier=function(t){var e;return null!=(e=this.restorationData)[t]?e[t]:e[t]={}},r}()}.call(this),function(){!function(){var t,e;if((t=e=document.currentScript)&&!e.hasAttribute("data-turbolinks-suppress-warning"))for(;t=t.parentNode;)if(t===document.body)return console.warn("You are loading Turbolinks from a