diff --git a/Gemfile.lock b/Gemfile.lock index 0b53df82e0..07e6afb1c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,35 +10,35 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3) - actionpack (= 7.1.3) - activesupport (= 7.1.3) + actioncable (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3) - actionpack (= 7.1.3) - activejob (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + actionmailbox (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (>= 2.7.1) net-imap net-pop net-smtp - actionmailer (7.1.3) - actionpack (= 7.1.3) - actionview (= 7.1.3) - activejob (= 7.1.3) - activesupport (= 7.1.3) + actionmailer (7.1.3.2) + actionpack (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activesupport (= 7.1.3.2) mail (~> 2.5, >= 2.5.4) net-imap net-pop net-smtp rails-dom-testing (~> 2.2) - actionpack (7.1.3) - actionview (= 7.1.3) - activesupport (= 7.1.3) + actionpack (7.1.3.2) + actionview (= 7.1.3.2) + activesupport (= 7.1.3.2) nokogiri (>= 1.8.5) racc rack (>= 2.2.4) @@ -46,15 +46,15 @@ GEM rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3) - actionpack (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + actiontext (7.1.3.2) + actionpack (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3) - activesupport (= 7.1.3) + actionview (7.1.3.2) + activesupport (= 7.1.3.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -64,22 +64,22 @@ GEM activemodel (>= 4.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) - activejob (7.1.3) - activesupport (= 7.1.3) + activejob (7.1.3.2) + activesupport (= 7.1.3.2) globalid (>= 0.3.6) - activemodel (7.1.3) - activesupport (= 7.1.3) - activerecord (7.1.3) - activemodel (= 7.1.3) - activesupport (= 7.1.3) + activemodel (7.1.3.2) + activesupport (= 7.1.3.2) + activerecord (7.1.3.2) + activemodel (= 7.1.3.2) + activesupport (= 7.1.3.2) timeout (>= 0.4.0) - activestorage (7.1.3) - actionpack (= 7.1.3) - activejob (= 7.1.3) - activerecord (= 7.1.3) - activesupport (= 7.1.3) + activestorage (7.1.3.2) + actionpack (= 7.1.3.2) + activejob (= 7.1.3.2) + activerecord (= 7.1.3.2) + activesupport (= 7.1.3.2) marcel (~> 1.0) - activesupport (7.1.3) + activesupport (7.1.3.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -219,7 +219,7 @@ GEM docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - doorkeeper (5.6.8) + doorkeeper (5.6.9) railties (>= 5) dotenv (2.8.1) dotenv-rails (2.8.1) @@ -309,7 +309,7 @@ GEM activesupport (>= 5.1) haml (>= 4.0.6) railties (>= 5.1) - haml_lint (0.56.0) + haml_lint (0.57.0) haml (>= 5.0) parallel (~> 1.10) rainbow @@ -444,7 +444,7 @@ GEM uri net-http-persistent (4.0.2) connection_pool (~> 2.2) - net-imap (0.4.9.1) + net-imap (0.4.10) date net-protocol net-ldap (0.19.0) @@ -532,7 +532,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.7.3) - rack (2.2.8) + rack (2.2.8.1) rack-attack (6.7.0) rack (>= 1.0, < 4) rack-cors (2.0.1) @@ -554,20 +554,20 @@ GEM rackup (1.0.0) rack (< 3) webrick - rails (7.1.3) - actioncable (= 7.1.3) - actionmailbox (= 7.1.3) - actionmailer (= 7.1.3) - actionpack (= 7.1.3) - actiontext (= 7.1.3) - actionview (= 7.1.3) - activejob (= 7.1.3) - activemodel (= 7.1.3) - activerecord (= 7.1.3) - activestorage (= 7.1.3) - activesupport (= 7.1.3) + rails (7.1.3.2) + actioncable (= 7.1.3.2) + actionmailbox (= 7.1.3.2) + actionmailer (= 7.1.3.2) + actionpack (= 7.1.3.2) + actiontext (= 7.1.3.2) + actionview (= 7.1.3.2) + activejob (= 7.1.3.2) + activemodel (= 7.1.3.2) + activerecord (= 7.1.3.2) + activestorage (= 7.1.3.2) + activesupport (= 7.1.3.2) bundler (>= 1.15.0) - railties (= 7.1.3) + railties (= 7.1.3.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -582,9 +582,9 @@ GEM rails-i18n (7.0.8) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) - railties (7.1.3) - actionpack (= 7.1.3) - activesupport (= 7.1.3) + railties (7.1.3.2) + actionpack (= 7.1.3.2) + activesupport (= 7.1.3.2) irb rackup (>= 1.0.0) rake (>= 12.2) @@ -691,7 +691,7 @@ GEM scenic (1.7.0) activerecord (>= 4.0.0) railties (>= 4.0.0) - selenium-webdriver (4.17.0) + selenium-webdriver (4.18.1) base64 (~> 0.2) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) @@ -793,7 +793,7 @@ GEM webfinger (1.2.0) activesupport httpclient (>= 2.4) - webmock (3.20.0) + webmock (3.21.2) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -811,7 +811,7 @@ GEM xorcist (1.1.3) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.12) + zeitwerk (2.6.13) PLATFORMS ruby diff --git a/app/javascript/flavours/glitch/styles/components.scss b/app/javascript/flavours/glitch/styles/components.scss index 64f606ac21..305c519d5d 100644 --- a/app/javascript/flavours/glitch/styles/components.scss +++ b/app/javascript/flavours/glitch/styles/components.scss @@ -774,6 +774,28 @@ body > [data-popper-placement] { opacity 0.4s ease; } +.compose-form__textarea-icons { + display: block; + position: absolute; + top: 29px; + inset-inline-end: 5px; + bottom: 5px; + overflow: hidden; + + & > .textarea_icon { + display: block; + margin-top: 2px; + margin-inline-start: 2px; + width: 24px; + height: 24px; + color: $lighter-text-color; + font-size: 18px; + line-height: 24px; + text-align: center; + opacity: 0.8; + } +} + .sign-in-banner { padding: 10px; @@ -852,21 +874,33 @@ body > [data-popper-placement] { clear: both; } +.status__content, .reply-indicator__content { position: relative; - font-size: 14px; + font-size: 15px; line-height: 20px; word-wrap: break-word; font-weight: 400; overflow: hidden; - padding-top: 5px; - color: $inverted-text-color; - white-space: pre-wrap; + text-overflow: ellipsis; + padding-top: 2px; + color: $primary-text-color; + + &:focus { + outline: 0; + } + + .emojione { + width: 20px; + height: 20px; + margin: -3px 0 0; + } p, pre { margin-bottom: 20px; white-space: pre-wrap; + unicode-bidi: plaintext; &:last-child { margin-bottom: 0; @@ -874,8 +908,9 @@ body > [data-popper-placement] { } a { - color: $lighter-text-color; + color: $secondary-text-color; text-decoration: none; + unicode-bidi: isolate; &:hover { text-decoration: underline; @@ -892,11 +927,47 @@ body > [data-popper-placement] { } } - .emojione { - width: 20px; - height: 20px; - margin: -5px 0 0; + a.unhandled-link { + color: $highlight-text-color; + + .link-origin-tag { + color: $gold-star; + font-size: 0.8em; + } } + + .status__content__spoiler-link { + background: $action-button-color; + + &:hover, + &:focus { + background: lighten($action-button-color, 7%); + text-decoration: none; + } + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + } + + .status__content__spoiler { + display: none; + + &.status__content__spoiler--visible { + display: block; + } + } +} + +.status__content { + // glitch: necessary for fullwidth media options + overflow: visible; } .announcements__item__content { @@ -942,6 +1013,30 @@ body > [data-popper-placement] { } } +.status__content__read-more-button, +.status__content__translate-button { + display: flex; + align-items: center; + font-size: 15px; + line-height: 22px; + color: $highlight-text-color; + border: 0; + background: transparent; + padding: 0; + padding-top: 16px; + text-decoration: none; + + &:hover, + &:active { + text-decoration: underline; + } + + .icon { + width: 15px; + height: 15px; + } +} + .translate-button { margin-top: 16px; font-size: 15px; @@ -954,7 +1049,7 @@ body > [data-popper-placement] { .status__content__spoiler-link { display: inline-flex; border-radius: 2px; - background: lighten($ui-base-color, 30%); + background: transparent; border: 0; color: $inverted-text-color; font-weight: 700; @@ -1016,14 +1111,6 @@ body > [data-popper-placement] { outline: 0; background: lighten($ui-base-color, 4%); - &.status.status-direct { - background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%); - - &.muted { - background: transparent; - } - } - .detailed-status, .detailed-status__action-bar { background: lighten($ui-base-color, 8%); @@ -1057,11 +1144,6 @@ body > [data-popper-placement] { margin-top: 8px; // glitch: reduced margins } - &.status-direct { - background: mix($ui-base-color, $ui-highlight-color, 95%); - border-bottom-color: lighten($ui-base-color, 12%); - } - &.light { .status__relative-time, .status__visibility-icon { @@ -1110,6 +1192,10 @@ body > [data-popper-placement] { margin-inline-start: $thread-margin; width: calc(100% - ($thread-margin)); } + + .status__content__read-more-button { + margin-inline-start: $thread-margin; + } } &--first-in-thread { @@ -1209,6 +1295,7 @@ body > [data-popper-placement] { ); } + // TODO: review &.status-direct > .status__content::after { background: linear-gradient( rgba(mix($ui-base-color, $ui-highlight-color, 95%), 0), @@ -1329,6 +1416,50 @@ body > [data-popper-placement] { } } +@keyframes spring-flip-in { + 0% { + transform: rotate(0deg); + } + + 30% { + transform: rotate(-242.4deg); + } + + 60% { + transform: rotate(-158.35deg); + } + + 90% { + transform: rotate(-187.5deg); + } + + 100% { + transform: rotate(-180deg); + } +} + +@keyframes spring-flip-out { + 0% { + transform: rotate(-180deg); + } + + 30% { + transform: rotate(62.4deg); + } + + 60% { + transform: rotate(-21.635deg); + } + + 90% { + transform: rotate(7.5deg); + } + + 100% { + transform: rotate(0deg); + } +} + .status-check-box__status { display: block; box-sizing: border-box; @@ -1385,6 +1516,18 @@ body > [data-popper-placement] { } } +.status__wrapper-direct { + background: mix($ui-base-color, $ui-highlight-color, 95%); + + &:focus { + background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%); + } + + .status__prepend { + color: $highlight-text-color; + } +} + .status__action-bar { display: flex; align-items: center; @@ -1434,6 +1577,11 @@ body > [data-popper-placement] { height: 24px; margin: -1px 0 0; } + + .status__content__spoiler-link { + line-height: 24px; + margin: -1px 0 0; + } } .media-gallery, @@ -1469,25 +1617,29 @@ body > [data-popper-placement] { padding: 8px 0; // glitch: reduced padding } -.compose-form__textarea-icons { - display: block; - position: absolute; - top: 29px; - inset-inline-end: 5px; - bottom: 5px; - overflow: hidden; +.detailed-status__wrapper-direct { + .detailed-status, + .detailed-status__action-bar { + background: mix($ui-base-color, $ui-highlight-color, 95%); + } - & > .textarea_icon { - display: block; - margin-top: 2px; - margin-inline-start: 2px; - width: 24px; - height: 24px; - color: $lighter-text-color; - font-size: 18px; - line-height: 24px; - text-align: center; - opacity: 0.8; + &:focus { + .detailed-status, + .detailed-status__action-bar { + background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%); + } + } + + .detailed-status__action-bar { + border-top-color: mix( + lighten($ui-base-color, 8%), + $ui-highlight-color, + 95% + ); + } + + .status__prepend { + color: $highlight-text-color; } } @@ -1512,6 +1664,15 @@ body > [data-popper-placement] { line-height: 18px; } +.reply-indicator__content { + color: $inverted-text-color; + font-size: 14px; + + a { + color: $lighter-text-color; + } +} + .domain { padding: 10px; border-bottom: 1px solid lighten($ui-base-color, 8%); @@ -4137,7 +4298,7 @@ a.status-card { margin: 0; font-size: inherit; flex: auto; - background-color: $ui-base-color; + background-color: lighten($ui-base-color, 8%); transition: all 0.2s ease; transition-property: background-color, box-shadow; @@ -4615,10 +4776,10 @@ a.status-card { margin-bottom: 4px; display: block; background-color: rgba($black, 0.45); - text-transform: uppercase; font-size: 11px; - font-weight: 500; - padding: 4px; + text-transform: uppercase; + font-weight: 700; + padding: 2px 6px; border-radius: 4px; opacity: 0.7; @@ -5173,128 +5334,6 @@ a.status-card { } } -@keyframes spring-flip-in { - 0% { - transform: rotate(0deg); - } - - 30% { - transform: rotate(-242.4deg); - } - - 60% { - transform: rotate(-158.35deg); - } - - 90% { - transform: rotate(-187.5deg); - } - - 100% { - transform: rotate(-180deg); - } -} - -@keyframes spring-flip-out { - 0% { - transform: rotate(-180deg); - } - - 30% { - transform: rotate(62.4deg); - } - - 60% { - transform: rotate(-21.635deg); - } - - 90% { - transform: rotate(7.5deg); - } - - 100% { - transform: rotate(0deg); - } -} - -.status__content { - position: relative; - font-size: 15px; - line-height: 20px; - word-wrap: break-word; - font-weight: 400; - overflow: visible; - padding-top: 5px; - - &:focus { - outline: 0; - } - - .emojione { - width: 20px; - height: 20px; - margin: -3px 0 0; - } - - p, - pre { - margin-bottom: 20px; - white-space: pre-wrap; - unicode-bidi: plaintext; - - &:last-child { - margin-bottom: 0; - } - } - - a { - color: $secondary-text-color; - text-decoration: none; - unicode-bidi: isolate; - - &:hover { - text-decoration: underline; - } - - &.mention { - &:hover { - text-decoration: none; - - span { - text-decoration: underline; - } - } - } - } - - .status__content__spoiler { - display: none; - - &.status__content__spoiler--visible { - display: block; - } - } - - a.unhandled-link { - color: $highlight-text-color; - - .link-origin-tag { - color: $gold-star; - font-size: 0.8em; - } - } - - .status__content__spoiler-link { - background: lighten($ui-base-color, 30%); - - &:hover, - &:focus { - background: lighten($ui-base-color, 33%); - text-decoration: none; - } - } -} - .notif-cleaning { .status, .notification { @@ -5302,18 +5341,6 @@ a.status-card { } } -.notification-follow, -.notification-follow-request { - position: relative; - - // same like Status - border-bottom: 1px solid lighten($ui-base-color, 8%); - - .account { - border-bottom: 0 none; - } -} - .language-dropdown { &__dropdown { background: $simple-background-color; @@ -8648,6 +8675,7 @@ noscript { inset-inline-end: 20px; width: 300px; + // glitch: feature to chose which side the pop-in player is displayed &.left { inset-inline-end: unset; inset-inline-start: 20px; diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 2a6fbb0ab7..8990b2a847 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -61,6 +61,12 @@ class AdminMailer < ApplicationMailer end end + def auto_close_registrations + locale_for_account(@me) do + mail subject: default_i18n_subject(instance: @instance) + end + end + private def process_params diff --git a/app/services/verify_link_service.rb b/app/services/verify_link_service.rb index 707aeb4e08..b317fc31a8 100644 --- a/app/services/verify_link_service.rb +++ b/app/services/verify_link_service.rb @@ -19,7 +19,7 @@ class VerifyLinkService < BaseService def perform_request! @body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res| - res.code == 200 ? res.body_with_limit : nil + res.code == 200 ? res.truncated_body : nil end end diff --git a/app/views/admin_mailer/auto_close_registrations.text.erb b/app/views/admin_mailer/auto_close_registrations.text.erb new file mode 100644 index 0000000000..c0f8486929 --- /dev/null +++ b/app/views/admin_mailer/auto_close_registrations.text.erb @@ -0,0 +1,3 @@ +<%= raw t('admin_mailer.auto_close_registrations.body', instance: @instance) %> + +<%= raw t('application_mailer.view')%> <%= admin_settings_registrations_url %> diff --git a/app/workers/scheduler/auto_close_registrations_scheduler.rb b/app/workers/scheduler/auto_close_registrations_scheduler.rb new file mode 100644 index 0000000000..6874502915 --- /dev/null +++ b/app/workers/scheduler/auto_close_registrations_scheduler.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class Scheduler::AutoCloseRegistrationsScheduler + include Sidekiq::Worker + include Redisable + + sidekiq_options retry: 0 + + # Automatically switch away from open registrations if no + # moderator had any activity in that period of time + OPEN_REGISTRATIONS_MODERATOR_THRESHOLD = 1.week + UserTrackingConcern::SIGN_IN_UPDATE_FREQUENCY + + def perform + return if Rails.configuration.x.email_domains_whitelist.present? || ENV['DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS'] == 'true' + return unless Setting.registrations_mode == 'open' + + switch_to_approval_mode! unless active_moderators? + end + + private + + def active_moderators? + User.those_who_can(:manage_reports).exists?(current_sign_in_at: OPEN_REGISTRATIONS_MODERATOR_THRESHOLD.ago...) + end + + def switch_to_approval_mode! + Setting.registrations_mode = 'approved' + + User.those_who_can(:manage_settings).includes(:account).find_each do |user| + AdminMailer.with(recipient: user.account).auto_close_registrations.deliver_later + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 4389793663..6e9b972895 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -973,6 +973,9 @@ en: title: Webhooks webhook: Webhook admin_mailer: + auto_close_registrations: + body: Due to a lack of recent moderator activity, registrations on %{instance} have been automatically switched to requiring manual review, to prevent %{instance} from being used as a platform for potential bad actors. You can switch it back to open registrations at any time. + subject: Registrations for %{instance} have been automatically switched to requiring approval new_appeal: actions: delete_statuses: to delete their posts diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 30e8b2daf6..f5c6e352c0 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -63,3 +63,7 @@ interval: 30 minutes class: Scheduler::SoftwareUpdateCheckScheduler queue: scheduler + auto_close_registrations_scheduler: + interval: 1 hour + class: Scheduler::AutoCloseRegistrationsScheduler + queue: scheduler diff --git a/package.json b/package.json index abb65a98da..f38457272d 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,6 @@ "mark-loader": "^0.1.6", "marky": "^1.2.5", "mini-css-extract-plugin": "^1.6.2", - "mkdirp": "^3.0.1", "path-complete-extname": "^1.0.0", "postcss": "^8.4.24", "postcss-loader": "^4.3.0", @@ -121,7 +120,6 @@ "redux-immutable": "^4.0.0", "regenerator-runtime": "^0.14.0", "requestidlecallback": "^0.3.0", - "rimraf": "^5.0.1", "sass": "^1.62.1", "sass-loader": "^10.2.0", "stacktrace-js": "^2.0.2", @@ -178,7 +176,6 @@ "@types/redux-immutable": "^4.0.3", "@types/requestidlecallback": "^0.3.5", "@types/webpack": "^4.41.33", - "@types/yargs": "^17.0.24", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.17.0", "babel-jest": "^29.5.0", @@ -203,8 +200,7 @@ "stylelint": "^16.0.2", "stylelint-config-standard-scss": "^13.0.0", "typescript": "^5.0.4", - "webpack-dev-server": "^3.11.3", - "yargs": "^17.7.2" + "webpack-dev-server": "^3.11.3" }, "resolutions": { "kind-of": "^6.0.3", diff --git a/spec/services/verify_link_service_spec.rb b/spec/services/verify_link_service_spec.rb index 415788cb58..d06344f9cc 100644 --- a/spec/services/verify_link_service_spec.rb +++ b/spec/services/verify_link_service_spec.rb @@ -76,6 +76,20 @@ RSpec.describe VerifyLinkService, type: :service do end context 'when a document is truncated but the link back is valid' do + let(:html) do + " + + + + " + end + + it 'marks the field as verified' do + expect(field.verified?).to be true + end + end + + context 'when a link tag might be truncated' do let(:html) do " @@ -89,19 +103,6 @@ RSpec.describe VerifyLinkService, type: :service do end end - context 'when a link back might be truncated' do - let(:html) do - " - - -