diff --git a/Gemfile b/Gemfile index 55170961..c203692d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem "rails", "~> 7.0.3.1" +gem "rails", "~> 7.0.4.3" gem "mysql2" diff --git a/Gemfile.lock b/Gemfile.lock index abb43275..6afa8f28 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,83 +2,83 @@ GEM remote: https://rubygems.org/ specs: Ascii85 (1.1.0) - actioncable (7.0.3.1) - actionpack (= 7.0.3.1) - activesupport (= 7.0.3.1) + actioncable (7.0.4.3) + actionpack (= 7.0.4.3) + activesupport (= 7.0.4.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.3.1) - actionpack (= 7.0.3.1) - activejob (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + actionmailbox (7.0.4.3) + actionpack (= 7.0.4.3) + activejob (= 7.0.4.3) + activerecord (= 7.0.4.3) + activestorage (= 7.0.4.3) + activesupport (= 7.0.4.3) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.0.3.1) - actionpack (= 7.0.3.1) - actionview (= 7.0.3.1) - activejob (= 7.0.3.1) - activesupport (= 7.0.3.1) + actionmailer (7.0.4.3) + actionpack (= 7.0.4.3) + actionview (= 7.0.4.3) + activejob (= 7.0.4.3) + activesupport (= 7.0.4.3) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.0) - actionpack (7.0.3.1) - actionview (= 7.0.3.1) - activesupport (= 7.0.3.1) + actionpack (7.0.4.3) + actionview (= 7.0.4.3) + activesupport (= 7.0.4.3) rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) actionpack-page_caching (1.2.4) actionpack (>= 4.0.0) - actiontext (7.0.3.1) - actionpack (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + actiontext (7.0.4.3) + actionpack (= 7.0.4.3) + activerecord (= 7.0.4.3) + activestorage (= 7.0.4.3) + activesupport (= 7.0.4.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.3.1) - activesupport (= 7.0.3.1) + actionview (7.0.4.3) + activesupport (= 7.0.4.3) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.3.1) - activesupport (= 7.0.3.1) + activejob (7.0.4.3) + activesupport (= 7.0.4.3) globalid (>= 0.3.6) - activemodel (7.0.3.1) - activesupport (= 7.0.3.1) - activerecord (7.0.3.1) - activemodel (= 7.0.3.1) - activesupport (= 7.0.3.1) - activerecord-typedstore (1.4.0) - activerecord (>= 5.2) - activestorage (7.0.3.1) - actionpack (= 7.0.3.1) - activejob (= 7.0.3.1) - activerecord (= 7.0.3.1) - activesupport (= 7.0.3.1) + activemodel (7.0.4.3) + activesupport (= 7.0.4.3) + activerecord (7.0.4.3) + activemodel (= 7.0.4.3) + activesupport (= 7.0.4.3) + activerecord-typedstore (1.5.1) + activerecord (>= 6.1) + activestorage (7.0.4.3) + actionpack (= 7.0.4.3) + activejob (= 7.0.4.3) + activerecord (= 7.0.4.3) + activesupport (= 7.0.4.3) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (7.0.3.1) + activesupport (7.0.4.3) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.3) + public_suffix (>= 2.0.2, < 6.0) afm (0.2.2) ast (2.4.2) bcrypt (3.1.18) builder (3.2.4) byebug (11.1.3) - capybara (3.37.1) + capybara (3.39.0) addressable matrix mini_mime (>= 0.1.3) @@ -88,21 +88,21 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) chunky_png (1.4.0) - commonmarker (0.23.6) - concurrent-ruby (1.1.10) + commonmarker (0.23.8) + concurrent-ruby (1.2.2) crack (0.4.5) rexml crass (1.0.6) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.1) + database_cleaner (2.0.2) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.1.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) + date (3.3.3) diff-lcs (1.5.0) - digest (3.1.0) docile (1.4.0) - erubi (1.10.0) + erubi (1.12.0) exception_notification (4.5.0) actionmailer (>= 5.2, < 8) activesupport (>= 5.2, < 8) @@ -112,128 +112,132 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) - faker (2.21.0) + faker (3.1.1) i18n (>= 1.8.11, < 2) ffi (1.15.5) flamegraph (0.9.5) - globalid (1.0.0) + globalid (1.1.0) activesupport (>= 5.0) hashdiff (1.0.1) hashery (2.1.2) + hashie (5.0.0) htmlentities (4.3.4) - i18n (1.11.0) + i18n (1.12.0) concurrent-ruby (~> 1.0) jaro_winkler (1.5.4) - jquery-rails (4.4.0) + jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.6.2) - listen (3.7.1) + json (2.6.3) + listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.18.0) + loofah (2.20.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.1) + mail (2.8.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp marcel (1.0.2) matrix (0.4.2) - memory_profiler (1.0.0) + memory_profiler (1.0.1) method_source (1.0.0) mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitest (5.16.2) - mysql2 (0.5.4) - net-imap (0.2.3) - digest + mini_portile2 (2.8.1) + minitest (5.18.0) + mysql2 (0.5.5) + net-imap (0.3.4) + date net-protocol - strscan - net-pop (0.1.1) - digest + net-pop (0.1.2) net-protocol + net-protocol (0.2.1) timeout - net-protocol (0.1.3) - timeout - net-smtp (0.3.1) - digest + net-smtp (0.3.3) net-protocol - timeout - nio4r (2.5.8) - nokogiri (1.13.9) + nio4r (2.5.9) + nokogiri (1.14.2) mini_portile2 (~> 2.8.0) racc (~> 1.4) - nokogiri (1.13.9-x86_64-linux) + nokogiri (1.14.2-x86_64-linux) racc (~> 1.4) - oauth (0.5.10) + oauth (1.1.0) + oauth-tty (~> 1.0, >= 1.0.1) + snaky_hash (~> 2.0) + version_gem (~> 1.1) + oauth-tty (1.0.5) + version_gem (~> 1.1, >= 1.1.1) parallel (1.22.1) - parser (3.1.2.0) + parser (3.2.2.0) ast (~> 2.4.1) - pdf-reader (2.10.0) + pdf-reader (2.11.0) Ascii85 (~> 1.0) afm (~> 0.2.1) hashery (~> 2.0) ruby-rc4 ttfunk - public_suffix (4.0.7) - puma (5.6.4) + public_suffix (5.0.1) + puma (6.2.1) nio4r (~> 2.0) - racc (1.6.0) - rack (2.2.4) + racc (1.6.2) + rack (2.2.6.4) rack-attack (6.6.1) rack (>= 1.0, < 3) rack-mini-profiler (3.0.0) rack (>= 1.2.0) - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) - rails (7.0.3.1) - actioncable (= 7.0.3.1) - actionmailbox (= 7.0.3.1) - actionmailer (= 7.0.3.1) - actionpack (= 7.0.3.1) - actiontext (= 7.0.3.1) - actionview (= 7.0.3.1) - activejob (= 7.0.3.1) - activemodel (= 7.0.3.1) - activerecord (= 7.0.3.1) - activestorage (= 7.0.3.1) - activesupport (= 7.0.3.1) + rails (7.0.4.3) + actioncable (= 7.0.4.3) + actionmailbox (= 7.0.4.3) + actionmailer (= 7.0.4.3) + actionpack (= 7.0.4.3) + actiontext (= 7.0.4.3) + actionview (= 7.0.4.3) + activejob (= 7.0.4.3) + activemodel (= 7.0.4.3) + activerecord (= 7.0.4.3) + activestorage (= 7.0.4.3) + activesupport (= 7.0.4.3) bundler (>= 1.15.0) - railties (= 7.0.3.1) + railties (= 7.0.4.3) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) - railties (7.0.3.1) - actionpack (= 7.0.3.1) - activesupport (= 7.0.3.1) + rails-html-sanitizer (1.5.0) + loofah (~> 2.19, >= 2.19.1) + railties (7.0.4.3) + actionpack (= 7.0.4.3) + activesupport (= 7.0.4.3) method_source rake (>= 12.2) thor (~> 1.0) zeitwerk (~> 2.5) rainbow (3.1.1) rake (13.0.6) - rb-fsevent (0.11.1) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rb-readline (0.5.5) - regexp_parser (2.4.0) + regexp_parser (2.7.0) rexml (3.2.5) - rotp (6.2.0) - rqrcode (2.1.1) + rotp (6.2.2) + rqrcode (2.1.2) chunky_png (~> 1.0) rqrcode_core (~> 1.0) rqrcode_core (1.2.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec-core (3.12.1) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-mocks (3.11.1) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-rails (6.0.0.rc1) + rspec-support (~> 3.12.0) + rspec-rails (6.0.1) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) @@ -241,7 +245,7 @@ GEM rspec-expectations (~> 3.11) rspec-mocks (~> 3.11) rspec-support (~> 3.11) - rspec-support (3.11.0) + rspec-support (3.12.0) rubocop (0.81.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) @@ -256,24 +260,27 @@ GEM rubocop (>= 0.72.0) rubocop-rspec (1.41.0) rubocop (>= 0.68.1) - ruby-progressbar (1.11.0) + ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) ruumba (0.1.17) rubocop - scenic (1.6.0) + scenic (1.7.0) activerecord (>= 4.0.0) railties (>= 4.0.0) scenic-mysql_adapter (1.0.1) mysql2 scenic (>= 1.4.0) - simplecov (0.21.2) + simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sitemap_generator (6.2.1) + sitemap_generator (6.3.0) builder (~> 3.0) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -281,24 +288,24 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - stackprof (0.2.19) - strscan (3.0.3) + stackprof (0.2.24) svg-graph (2.2.1) thor (1.2.1) - timeout (0.3.0) + timeout (0.3.2) transaction_isolation (1.0.5) activerecord (>= 3.0.11) transaction_retry (1.0.3) activerecord (>= 3.0.11) transaction_isolation (>= 1.0.2) ttfunk (1.7.0) - tzinfo (2.0.4) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode-display_width (1.8.0) vcr (6.1.0) - webmock (3.14.0) + version_gem (1.1.2) + webmock (3.18.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -307,7 +314,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.0) + zeitwerk (2.6.7) PLATFORMS ruby @@ -338,7 +345,7 @@ DEPENDENCIES puma (>= 5.6.2) rack-attack rack-mini-profiler - rails (~> 7.0.3.1) + rails (~> 7.0.4.3) rb-readline rotp rqrcode diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 40c1b98a..298d1c45 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -637,6 +637,7 @@ onPageLoad(() => { }); on('click', '.comment #flag_dropdown a', (event) => { + event.preventDefault(); if (event.target.getAttribute('data') != '') { Lobster.voteComment(parentSelector(event.target, '.comment'), -1, event.target.getAttribute('data')); } @@ -727,4 +728,12 @@ onPageLoad(() => { response.text().then(text => replace(comment, text)); }); }); + + on('click', '.comment_unread', (event) => { + const nodes = document.getElementsByClassName('comment_unread') + const foundIndex = Array.from(nodes).findIndex(node => node === event.target) + const targetIndex = (foundIndex + 1) % nodes.length; + const targetY = nodes[targetIndex].getBoundingClientRect().top + window.scrollY + window.scrollTo({ top: targetY, behavior: 'smooth' }) + }); }); diff --git a/app/assets/stylesheets/application.css.erb b/app/assets/stylesheets/application.css.erb index 3aa9522c..c4124fd3 100644 --- a/app/assets/stylesheets/application.css.erb +++ b/app/assets/stylesheets/application.css.erb @@ -59,6 +59,7 @@ light = <<-LIGHT --color-tag-bg: #fffcd7; --color-tag-border: #d5d458; --color-tag-media-bg: #ddebf9; + --color-tag-bg-img: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); --color-tag-media-border: #b2ccf0; --color-tag-meta-bg: #e0e0e0; --color-tag-meta-border: #c8c8c8; @@ -135,6 +136,7 @@ dark = <<-DARK --color-table-row-border: #262626; --color-tag-bg: #3b320d; + --color-tag-bg-img: none; --color-tag-border: #665501; --color-tag-media-bg: #15293d; --color-tag-media-border: #214669; @@ -1177,6 +1179,7 @@ div.comment_form_container textarea { span.comment_unread { color: var(--color-fg-accent); font-weight: 600; + cursor: pointer; } /* trees */ @@ -1482,11 +1485,11 @@ div.flash-success h2 { } #story_holder .ts-control .data-ts-item { /* item already selected by user*/ - background-color: #e4e4e4; - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - border: 1px solid #aaaaaa; + background-color: var(--color-tag-bg); + background-image: var(--color-tag-bg-img); + border: 1px solid var(--color-tagborder); border-radius: 3px; - color: #333; + color: var(--color-fgcontrast-10); line-height: 13px; margin: 3px 5px 3px 0; padding: 3px 0.5rem 3px 1rem !important; @@ -1808,7 +1811,7 @@ input[type="submit"].link_post.pushover_button { height: 7px; left: 4px; position: absolute; - width: 5px; + width: 5.4px; z-index: 10; } ol.stories.list li.story .mobile_comments span:after { diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index f37ef954..d63ca85a 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -286,7 +286,7 @@ class CommentsController < ApplicationController } @title = "Upvoted Comments" - @saved_subnav = true + @above = 'saved/subnav' @page = params[:page].to_i if @page == 0 diff --git a/app/controllers/hats_controller.rb b/app/controllers/hats_controller.rb index ed31d41f..0ddc7143 100644 --- a/app/controllers/hats_controller.rb +++ b/app/controllers/hats_controller.rb @@ -14,7 +14,7 @@ class HatsController < ApplicationController @hat_groups = {} - Hat.all.includes(:user).each do |h| + Hat.active.includes(:user).each do |h| @hat_groups[h.hat] ||= [] @hat_groups[h.hat].push h end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 622f36b3..8d1f5b60 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -101,6 +101,7 @@ class SettingsController < ApplicationController module_size: 5, shape_rendering: "crispEdges") + @qr_secret = totp.secret @qr_svg = "#{qr}" end diff --git a/app/helpers/interval_helper.rb b/app/helpers/interval_helper.rb index eefe2971..76b6f535 100644 --- a/app/helpers/interval_helper.rb +++ b/app/helpers/interval_helper.rb @@ -1,5 +1,9 @@ module IntervalHelper - TIME_INTERVALS = { "d" => "Day", "w" => "Week", "m" => "Month", "y" => "Year" }.freeze + TIME_INTERVALS = { "h" => "Hour", + "d" => "Day", + "w" => "Week", + "m" => "Month", + "y" => "Year", }.freeze def time_interval(param) if (m = param.to_s.match(/\A(\d+)([#{TIME_INTERVALS.keys.join}])\z/)) diff --git a/app/models/hat.rb b/app/models/hat.rb index 8fe40f15..847ee353 100644 --- a/app/models/hat.rb +++ b/app/models/hat.rb @@ -1,12 +1,14 @@ class Hat < ApplicationRecord belongs_to :user - belongs_to :granted_by_user, :class_name => "User", :inverse_of => false + belongs_to :granted_by_user, class_name: "User", inverse_of: false after_create :log_moderation validates :user, :granted_by_user, :hat, presence: true validates :hat, :link, length: { maximum: 255 } + scope :active, -> { joins(:user).where(doffed_at: nil).merge(User.active) } + def doff_by_user_with_reason(user, reason) m = Moderation.new m.user_id = self.user_id diff --git a/app/models/story.rb b/app/models/story.rb index b12b7b65..7d4f3f15 100644 --- a/app/models/story.rb +++ b/app/models/story.rb @@ -237,6 +237,28 @@ class Story < ApplicationRecord urls = urls2.uniq end + # www.youtube.com + # m.youtube.com + # youtube.com redirects to www.youtube.com + # youtu.be redirects to www.youtube.com + # www.m.youtube.com doesn't work + # www.youtu.be doesn't exist + # m.youtu.be doesn't exist + if /^https?:\/\/((?:www\d*|m)\.)?(youtube\.com|youtu\.be)/i.match(url) + urls.each do |u| + id = /^https?:\/\/(?:(?:m|www)\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([A-z0-9\-_]+)/i + .match(u)[1] + + urls2.push "https://www.youtube.com/watch?v=#{id}" + # In theory, youtube redirects https://youtube.com to https://www.youtube.com + # let's check it just in case + urls2.push "https://youtube.com/watch?v=#{id}" + urls2.push "https://youtu.be/#{id}" + urls2.push "https://m.youtube.com/watch?v=#{id}" + end + urls = urls2.uniq + end + # https urls.each do |u| urls2.push u.gsub(/^http:\/\//i, "https://") @@ -999,6 +1021,9 @@ class Story < ApplicationRecord @fetched_attributes[:title] = title + # strip off common GitHub site + repo owner + @fetched_attributes[:title].sub!(/GitHub - [a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}\//i, '') + # attempt to get the canonical url if it can be parsed, # if it is not the domain root path, and if it # responds to GET with a 200-level code diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb index 04c7628b..9b5c1a22 100644 --- a/app/views/comments/_comment.html.erb +++ b/app/views/comments/_comment.html.erb @@ -134,7 +134,7 @@ class="comment <%= comment.current_vote ? (comment.current_vote[:vote] == 1 ? <% end %> -
diff --git a/app/views/comments/index.html.erb b/app/views/comments/index.html.erb index fdc39798..e1e7414b 100644 --- a/app/views/comments/index.html.erb +++ b/app/views/comments/index.html.erb @@ -1,4 +1,4 @@ -<% render partial: 'upvoted/subnav' if @upvoted %> +<%= render partial: @above if @above %>
-
External Accounts
- --
Security Settings
- <%= f.submit "Save All Settings" %> +
+ + <%= f.submit "Save Account Settings" %> + <% end %> + +
+
+ +
External Accounts
+ +@@ -293,7 +296,8 @@
Delete Account
diff --git a/app/views/settings/twofa_enroll.html.erb b/app/views/settings/twofa_enroll.html.erb index 23522856..fd04ed5a 100644 --- a/app/views/settings/twofa_enroll.html.erb +++ b/app/views/settings/twofa_enroll.html.erb @@ -10,6 +10,13 @@
<%= raw @qr_svg %> ++ Or to add to a device manually enter the secret: +
+
+
Reveal Secret
+ <%= raw @qr_secret %> +Once you have finished registering with your TOTP application, proceed to diff --git a/app/views/stories/_form.html.erb b/app/views/stories/_form.html.erb index f1358196..7720d035 100644 --- a/app/views/stories/_form.html.erb +++ b/app/views/stories/_form.html.erb @@ -6,7 +6,7 @@ <% if f.object.url_is_editable_by_user?(@user) %> <%= f.label :url, "URL:", :class => "required" %> <%= f.text_field :url, :autocomplete => "off" %> - <%= button_tag "Fetch Title", :id => "story_fetch_title", + <%= button_tag raw("Fetch Title"), :id => "story_fetch_title", :type => "button" %> <% elsif !f.object.new_record? && !f.object.url.blank? %> <%= f.label :url, "URL:", :class => "required" %> diff --git a/app/views/stories/_listdetail.html.erb b/app/views/stories/_listdetail.html.erb index ff6fac9c..19a9830b 100644 --- a/app/views/stories/_listdetail.html.erb +++ b/app/views/stories/_listdetail.html.erb @@ -24,7 +24,7 @@ class="story <%= story.vote && story.vote[:vote] == 1 ? "upvoted" : "" %>
-
+