diff --git a/Gemfile.lock b/Gemfile.lock index 90b65a5e..7c2e6ae3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,14 +85,14 @@ GEM rack (>= 0.9.0) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - bootsnap (1.14.0) + bootsnap (1.15.0) msgpack (~> 1.2) bootstrap_form (4.5.0) actionpack (>= 5.2) activemodel (>= 5.2) buftok (0.2.0) builder (3.2.4) - bullet (7.0.3) + bullet (7.0.4) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) carrierwave (2.1.0) @@ -183,7 +183,7 @@ GEM glob (0.3.1) globalid (1.0.0) activesupport (>= 5.0) - haml (6.0.10) + haml (6.0.12) temple (>= 0.8.2) thor tilt @@ -307,7 +307,7 @@ GEM pundit (2.2.0) activesupport (>= 3.0.0) questiongenerator (1.0.0) - racc (1.6.0) + racc (1.6.1) rack (2.2.4) rack-protection (2.2.2) rack @@ -425,13 +425,13 @@ GEM sprockets-rails tilt semantic_range (3.0.0) - sentry-rails (5.6.0) + sentry-rails (5.7.0) railties (>= 5.0) - sentry-ruby (~> 5.6.0) - sentry-ruby (5.6.0) + sentry-ruby (~> 5.7.0) + sentry-ruby (5.7.0) concurrent-ruby (~> 1.0, >= 1.0.2) - sentry-sidekiq (5.6.0) - sentry-ruby (~> 5.6.0) + sentry-sidekiq (5.7.0) + sentry-ruby (~> 5.7.0) sidekiq (>= 3.0) shoulda-matchers (5.2.0) activesupport (>= 5.2.0) diff --git a/app/controllers/ajax/anonymous_block_controller.rb b/app/controllers/ajax/anonymous_block_controller.rb deleted file mode 100644 index 20503553..00000000 --- a/app/controllers/ajax/anonymous_block_controller.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -class Ajax::AnonymousBlockController < AjaxController - def create - params.require :question - - question = Question.find(params[:question]) - - raise Errors::Forbidden if params[:global] && !current_user.mod? - - AnonymousBlock.create!( - user: params[:global] ? nil : current_user, - identifier: question.author_identifier, - question: - ) - - question.inboxes.first&.destroy - - @response[:status] = :okay - @response[:message] = t(".success") - @response[:success] = true - end - - def destroy - params.require :id - - block = AnonymousBlock.find(params[:id]) - if current_user != block.user - @response[:status] = :nopriv - @response[:message] = t(".nopriv") - end - - block.destroy! - - @response[:status] = :okay - @response[:message] = t(".success") - @response[:success] = true - end -end diff --git a/app/controllers/anonymous_block_controller.rb b/app/controllers/anonymous_block_controller.rb new file mode 100644 index 00000000..404d396e --- /dev/null +++ b/app/controllers/anonymous_block_controller.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +class AnonymousBlockController < ApplicationController + before_action :authenticate_user! + + def create + params.require :question + + question = Question.find(params[:question]) + authorize AnonymousBlock, :create_global? if params[:global] + + AnonymousBlock.create!( + user: params[:global] ? nil : current_user, + identifier: question.author_identifier, + question: + ) + + inbox_id = question.inboxes.first.id + question.inboxes.first&.destroy + + respond_to do |format| + format.turbo_stream do + render turbo_stream: turbo_stream.remove("inbox_#{inbox_id}") + end + + format.html { redirect_back(fallback_location: inbox_path) } + end + end + + def destroy + params.require :id + + block = AnonymousBlock.find(params[:id]) + authorize block + + block.destroy! + + respond_to do |format| + format.turbo_stream do + render turbo_stream: turbo_stream.remove("block_#{params[:id]}") + end + + format.html { redirect_back(fallback_location: settings_blocks_path) } + end + end +end diff --git a/app/controllers/inbox_controller.rb b/app/controllers/inbox_controller.rb index c5af4593..6954b5a1 100644 --- a/app/controllers/inbox_controller.rb +++ b/app/controllers/inbox_controller.rb @@ -49,7 +49,11 @@ class InboxController < ApplicationController @disabled = true if @inbox.empty? respond_to do |format| format.html - format.turbo_stream { render "show", layout: false, status: :see_other } + format.turbo_stream do + render "show", layout: false, status: :see_other + + @inbox.update_all(new: false) + end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 12434f81..b8162617 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -33,15 +33,6 @@ module ApplicationHelper !current_user.nil? && ((current_user == user) || current_user.mod?) end - def ios_web_app? - user_agent = request.env["HTTP_USER_AGENT"] || "Mozilla/5.0" - # normal MobileSafari.app UA: Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B435 Safari/600.1.4 - # webapp UA: Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B435 - return true if user_agent.match(/^Mozilla\/\d+\.\d+ \(i(?:Phone|Pad|Pod); CPU(?:.*) like Mac OS X\)(?:.*) Mobile(?:\S*)$/) - - false - end - def rails_admin_path_for_resource(resource) [rails_admin_path, resource.model_name.param_key, resource.id].join("/") end diff --git a/app/javascript/packs/application.ts b/app/javascript/packs/application.ts index b4f80ef9..e6a85b3e 100644 --- a/app/javascript/packs/application.ts +++ b/app/javascript/packs/application.ts @@ -4,7 +4,6 @@ import { Application } from '@hotwired/stimulus'; import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers'; import start from 'retrospring/common'; -import initAnnouncements from 'retrospring/features/announcement'; import initAnswerbox from 'retrospring/features/answerbox/index'; import initInbox from 'retrospring/features/inbox/index'; import initUser from 'retrospring/features/user'; @@ -27,7 +26,6 @@ document.addEventListener('turbo:load', initQuestionbox); document.addEventListener('DOMContentLoaded', initQuestion); document.addEventListener('DOMContentLoaded', initModeration); document.addEventListener('DOMContentLoaded', initMemes); -document.addEventListener('turbo:load', initAnnouncements); document.addEventListener('turbo:load', initLocales); document.addEventListener('turbo:load', initFront); diff --git a/app/javascript/retrospring/controllers/announcement_controller.ts b/app/javascript/retrospring/controllers/announcement_controller.ts new file mode 100644 index 00000000..ee062b23 --- /dev/null +++ b/app/javascript/retrospring/controllers/announcement_controller.ts @@ -0,0 +1,19 @@ +import { Controller } from '@hotwired/stimulus'; + +export default class extends Controller { + static values = { + id: Number + }; + + declare readonly idValue: number; + + connect(): void { + if (!window.localStorage.getItem(`announcement${this.idValue}`)) { + this.element.classList.remove('d-none'); + } + } + + close(): void { + window.localStorage.setItem(`announcement${this.idValue}`, 'true'); + } +} diff --git a/app/javascript/retrospring/features/announcement/close.ts b/app/javascript/retrospring/features/announcement/close.ts deleted file mode 100644 index 0068d07f..00000000 --- a/app/javascript/retrospring/features/announcement/close.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default function (event: Event): void { - const announcement = (event.target as HTMLElement).closest(".announcement") as HTMLDivElement; - const announcementId = announcement.dataset.announcementId; - window.localStorage.setItem(`announcement${announcementId}`, 'true'); -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/announcement/index.ts b/app/javascript/retrospring/features/announcement/index.ts deleted file mode 100644 index 077bb4c9..00000000 --- a/app/javascript/retrospring/features/announcement/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import registerEvents from 'utilities/registerEvents'; -import closeAnnouncementHandler from './close'; - -export default (): void => { - registerEvents([ - { type: 'click', target: document.querySelector('.announcement button.close'), handler: closeAnnouncementHandler }, - ]); - - document.querySelectorAll('.announcement').forEach(function (el: HTMLDivElement) { - if (!window.localStorage.getItem(`announcement${el.dataset.announcementId}`)) { - el.classList.remove('d-none'); - } - }); -} diff --git a/app/javascript/retrospring/features/inbox/entry/blockAnon.ts b/app/javascript/retrospring/features/inbox/entry/blockAnon.ts deleted file mode 100644 index f0d184b7..00000000 --- a/app/javascript/retrospring/features/inbox/entry/blockAnon.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { post } from '@rails/request.js'; - -import { showErrorNotification, showNotification } from "utilities/notifications"; -import I18n from "retrospring/i18n"; - -export function blockAnonEventHandler(event: Event): void { - const element: HTMLAnchorElement = event.target as HTMLAnchorElement; - - const data = { - question: element.getAttribute('data-q-id'), - }; - - post('/ajax/block_anon', { - body: data, - contentType: 'application/json' - }) - .then(async response => { - const data = await response.json; - - if (!data.success) return false; - const inboxEntry: Node = element.closest('.inbox-entry'); - - showNotification(data.message); - - (inboxEntry as HTMLElement).remove(); - }) - .catch(err => { - console.log(err); - showErrorNotification(I18n.translate('frontend.error.message')); - }); -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/inbox/entry/index.ts b/app/javascript/retrospring/features/inbox/entry/index.ts index 27bbc5ac..54022129 100644 --- a/app/javascript/retrospring/features/inbox/entry/index.ts +++ b/app/javascript/retrospring/features/inbox/entry/index.ts @@ -3,15 +3,13 @@ import { answerEntryHandler, answerEntryInputHandler } from './answer'; import { deleteEntryHandler } from './delete'; import optionsEntryHandler from './options'; import { reportEventHandler } from './report'; -import { blockAnonEventHandler } from "retrospring/features/inbox/entry/blockAnon"; export default (): void => { registerEvents([ { type: 'click', target: 'button[name="ib-answer"]', handler: answerEntryHandler, global: true }, { type: 'click', target: '[name="ib-destroy"]', handler: deleteEntryHandler, global: true }, { type: 'click', target: '[name=ib-report]', handler: reportEventHandler, global: true }, - { type: 'click', target: '[name=ib-block-anon]', handler: blockAnonEventHandler, global: true }, { type: 'click', target: 'button[name=ib-options]', handler: optionsEntryHandler, global: true }, { type: 'keydown', target: 'textarea[name=ib-answer]', handler: answerEntryInputHandler, global: true } ]); -} \ No newline at end of file +} diff --git a/app/javascript/retrospring/features/moderation/blockAnon.ts b/app/javascript/retrospring/features/moderation/blockAnon.ts deleted file mode 100644 index 4f04004c..00000000 --- a/app/javascript/retrospring/features/moderation/blockAnon.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { post } from '@rails/request.js'; -import swal from 'sweetalert'; - -import { showErrorNotification, showNotification } from "utilities/notifications"; -import I18n from "retrospring/i18n"; - -export function blockAnonEventHandler(event: Event): void { - event.preventDefault(); - - swal({ - title: I18n.translate('frontend.mod_mute.confirm.title'), - text: I18n.translate('frontend.mod_mute.confirm.text'), - type: 'warning', - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: I18n.translate('voc.y'), - cancelButtonText: I18n.translate('voc.n'), - closeOnConfirm: true, - }, (dialogResult) => { - if (!dialogResult) { - return; - } - - const sender: HTMLAnchorElement = event.target as HTMLAnchorElement; - - const data = { - question: sender.getAttribute('data-q-id'), - global: 'true' - }; - - post('/ajax/block_anon', { - body: data, - contentType: 'application/json' - }) - .then(async response => { - const data = await response.json; - - if (!data.success) return false; - - showNotification(data.message); - }) - .catch(err => { - console.log(err); - showErrorNotification(I18n.translate('frontend.error.message')); - }); - }); -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/moderation/index.ts b/app/javascript/retrospring/features/moderation/index.ts index 7d0e639a..bafa6130 100644 --- a/app/javascript/retrospring/features/moderation/index.ts +++ b/app/javascript/retrospring/features/moderation/index.ts @@ -2,15 +2,13 @@ import registerEvents from 'utilities/registerEvents'; import { banCheckboxHandler, banFormHandler, permanentBanCheckboxHandler } from './ban'; import { destroyReportHandler } from './destroy'; import { privilegeCheckHandler } from './privilege'; -import { blockAnonEventHandler } from './blockAnon'; export default (): void => { registerEvents([ { type: 'click', target: '[type="checkbox"][name="check-your-privileges"]', handler: privilegeCheckHandler, global: true }, { type: 'click', target: '[name="mod-delete-report"]', handler: destroyReportHandler, global: true }, - { type: 'click', target: '[name="mod-block-anon"]', handler: blockAnonEventHandler, global: true }, { type: 'change', target: '[name="ban"][type="checkbox"]', handler: banCheckboxHandler, global: true }, { type: 'change', target: '[name="permaban"][type="checkbox"]', handler: permanentBanCheckboxHandler, global: true }, { type: 'submit', target: '#modal-ban form', handler: banFormHandler, global: true } ]); -} \ No newline at end of file +} diff --git a/app/javascript/retrospring/features/settings/block.ts b/app/javascript/retrospring/features/settings/block.ts deleted file mode 100644 index a84136d7..00000000 --- a/app/javascript/retrospring/features/settings/block.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { destroy } from '@rails/request.js'; -import { showNotification, showErrorNotification } from 'utilities/notifications'; -import I18n from 'retrospring/i18n'; - -export function unblockAnonymousHandler(event: Event): void { - const button: HTMLButtonElement = event.currentTarget as HTMLButtonElement; - const targetId = button.dataset.target; - - destroy(`/ajax/block_anon/${targetId}`) - .then(async response => { - if (!response.ok) return; - - const data = await response.json; - showNotification(data.message, data.success); - button.closest('.list-group-item').remove(); - }) - .catch(err => { - console.log(err); - showErrorNotification(I18n.translate('frontend.error.message')); - }); -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/settings/index.ts b/app/javascript/retrospring/features/settings/index.ts index 0b525372..629d5264 100644 --- a/app/javascript/retrospring/features/settings/index.ts +++ b/app/javascript/retrospring/features/settings/index.ts @@ -2,7 +2,6 @@ import registerEvents from "utilities/registerEvents"; import { profileHeaderChangeHandler, profilePictureChangeHandler } from "./crop"; import { themeDocumentHandler, themeSubmitHandler } from "./theme"; import { userSubmitHandler } from "./password"; -import { unblockAnonymousHandler } from "./block"; export default (): void => { themeDocumentHandler(); @@ -11,7 +10,6 @@ export default (): void => { { type: 'submit', target: document.querySelector('form.edit_theme, form.new_theme'), handler: themeSubmitHandler }, { type: 'submit', target: document.querySelector('#edit_user'), handler: userSubmitHandler }, { type: 'change', target: document.querySelector('#user_profile_picture[type=file]'), handler: profilePictureChangeHandler }, - { type: 'change', target: document.querySelector('#user_profile_header[type=file]'), handler: profileHeaderChangeHandler }, - { type: 'click', target: document.querySelectorAll('[data-action="anon-unblock"]'), handler: unblockAnonymousHandler } + { type: 'change', target: document.querySelector('#user_profile_header[type=file]'), handler: profileHeaderChangeHandler } ]); } diff --git a/app/policies/anonymous_block_policy.rb b/app/policies/anonymous_block_policy.rb new file mode 100644 index 00000000..b16d04bf --- /dev/null +++ b/app/policies/anonymous_block_policy.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AnonymousBlockPolicy + attr_reader :user, :anonymous_block + + def initialize(user, anonymous_block) + @user = user + @anonymous_block = anonymous_block + end + + def create_global? + user.mod? + end + + def destroy? + user == anonymous_block.user || user.mod? + end +end diff --git a/app/views/actions/_question.html.haml b/app/views/actions/_question.html.haml index f82a1613..8e960655 100644 --- a/app/views/actions/_question.html.haml +++ b/app/views/actions/_question.html.haml @@ -8,11 +8,11 @@ %i.fa.fa-fw.fa-exclamation-triangle = t("voc.report") - if question.anonymous? && !question.generated? - %a.dropdown-item{ name: "ib-block-anon", data: { q_id: question.id } } + = button_to anonymous_block_path, method: :post, params: { question: question.id }, class: "dropdown-item" do %i.fa.fa-fw.fa-minus-circle = t("voc.block") - if current_user.mod? - %a.dropdown-item{ href: "#", name: "mod-block-anon", data: { q_id: question.id } } + = button_to anonymous_block_path, method: :post, params: { question: question.id, global: true }, class: "dropdown-item" do %i.fa.fa-fw.fa-volume-off = t("voc.block_site_wide") %a.dropdown-item{ href: moderation_questions_path(author_identifier: question.author_identifier) } diff --git a/app/views/inbox/_entry.html.haml b/app/views/inbox/_entry.html.haml index 2a1b6e0b..25931da2 100644 --- a/app/views/inbox/_entry.html.haml +++ b/app/views/inbox/_entry.html.haml @@ -1,4 +1,4 @@ -.card.inbox-entry{ class: i.new? ? "inbox-entry--new" : "", data: { id: i.id } } +.card.inbox-entry{ id: "inbox_#{i.id}", class: i.new? ? "inbox-entry--new" : "", data: { id: i.id } } .card-header .media - unless i.question.author_is_anonymous diff --git a/app/views/layouts/inbox.html.haml b/app/views/layouts/inbox.html.haml index 0aff256e..e87d6cbb 100644 --- a/app/views/layouts/inbox.html.haml +++ b/app/views/layouts/inbox.html.haml @@ -8,6 +8,6 @@ = render 'shared/links' :ruby - Inbox.where(id: @inbox.ids).update_all(new: false) + @inbox.update_all(new: false) provide(:title, generate_title('Inbox')) parent_layout 'base' diff --git a/app/views/navigation/_desktop.html.haml b/app/views/navigation/_desktop.html.haml index 89bf4c5b..278c45a1 100644 --- a/app/views/navigation/_desktop.html.haml +++ b/app/views/navigation/_desktop.html.haml @@ -1,5 +1,5 @@ %nav.navbar.navbar-themed.navbar-expand-lg.bg-primary.fixed-top.d-lg-block.d-none.d-print-none{ role: :navigation } - .container{ class: ios_web_app? ? 'ios-web-app' : '' } + .container %a.navbar-brand{ href: '/', title: APP_CONFIG["site_name"] } - if APP_CONFIG["use_svg_logo"] = render inline: Rails.application.config.justask_svg_logo diff --git a/app/views/navigation/_guest.html.haml b/app/views/navigation/_guest.html.haml index 282fbcab..bdcfdb86 100644 --- a/app/views/navigation/_guest.html.haml +++ b/app/views/navigation/_guest.html.haml @@ -1,5 +1,5 @@ %nav.navbar.navbar-themed.navbar-expand-lg.bg-primary.fixed-top{ role: :navigation } - .container{ class: ios_web_app? ? 'ios-web-app' : '' } + .container %a.navbar-brand{ href: '/', title: APP_CONFIG["site_name"] } - if APP_CONFIG["use_svg_logo"] = render inline: Rails.application.config.justask_svg_logo diff --git a/app/views/navigation/_mobile.html.haml b/app/views/navigation/_mobile.html.haml index 0cd8a89f..37bec6d4 100644 --- a/app/views/navigation/_mobile.html.haml +++ b/app/views/navigation/_mobile.html.haml @@ -2,7 +2,7 @@ = render 'navigation/dropdown/notifications', notifications: notifications, size: "mobile" - notifications_icon = notification_count.nil? ? 'bell-o' : 'bell' %nav.navbar.navbar-themed.bg-primary.fixed-bottom.d-lg-none.d-block.d-print-none#rs-mobile-nav{ role: :navigation } - .container{ class: ios_web_app? ? 'ios-web-app' : '' } + .container %ul.nav.navbar-nav.navbar-icon-row = nav_entry t("navigation.timeline"), root_path, icon: 'home', icon_only: true = nav_entry t("navigation.inbox"), '/inbox', diff --git a/app/views/navigation/dropdown/_notifications.html.haml b/app/views/navigation/dropdown/_notifications.html.haml index 633f2a51..e8e90e05 100644 --- a/app/views/navigation/dropdown/_notifications.html.haml +++ b/app/views/navigation/dropdown/_notifications.html.haml @@ -1,16 +1,16 @@ .dropdown-menu.dropdown-menu-right.notification-dropdown{ id: "rs-#{size}-nav-notifications" } - if notifications.count.zero? - .dropdown-item.text-center.p-2 - %i.fa.fa-bell-o.notification__bell-icon - %p= t(".none") %a.dropdown-item.text-center{ href: notifications_path('all') } %i.fa.fa-fw.fa-chevron-right = t(".all") + .dropdown-item.text-center.p-2 + %i.fa.fa-bell-o.notification__bell-icon + %p= t(".none") - else + %a.dropdown-item.text-center{ href: notifications_path } + %i.fa.fa-fw.fa-chevron-right + = t(".new") - notifications.each do |notification| .dropdown-item = render "notifications/type/#{notification.target.class.name.downcase.split('::').last}", notification: notification - %a.dropdown-item.text-center{ href: notifications_path } - %i.fa.fa-fw.fa-chevron-right - = t(".new") diff --git a/app/views/shared/_announcements.html.haml b/app/views/shared/_announcements.html.haml index 96fcca85..ab0d70ac 100644 --- a/app/views/shared/_announcements.html.haml +++ b/app/views/shared/_announcements.html.haml @@ -1,8 +1,8 @@ .announcement__container - @active_announcements.each do |announcement| - .alert.announcement.alert-info.alert-dismissable.d-none{ data: { 'announcement-id': announcement.id } } + .alert.announcement.alert-info.alert-dismissable.d-none{ data: { controller: 'announcement', "announcement-id-value": announcement.id } } .container - %button.close{ type: :button, data: { dismiss: :alert } } + %button.close{ type: :button, data: { dismiss: :alert, action: "click->announcement#close" } } %span{ aria: { hidden: true } } × %p = announcement.content diff --git a/app/views/shared/_anonymous_block.html.haml b/app/views/shared/_anonymous_block.html.haml index 39cb20a9..e3e21349 100644 --- a/app/views/shared/_anonymous_block.html.haml +++ b/app/views/shared/_anonymous_block.html.haml @@ -1,4 +1,4 @@ -%li.list-group-item +%li.list-group-item{ id: "block_#{block.id}" } .d-flex %div - if block.question.present? @@ -7,5 +7,5 @@ %p.mb-0.text-muted.font-italic= t(".deleted_question") %p.text-muted.mb-0= t(".blocked", time: time_ago_in_words(block.created_at)) .ml-auto.d-inline-flex - %button.btn.btn-default.align-self-center{ data: { action: "anon-unblock", target: block.id } } - %span.pe-none= t("voc.unblock") \ No newline at end of file + = button_to anonymous_block_path, method: :delete, params: { id: block.id }, class: "btn btn-default align-self-center" do + %span.pe-none= t("voc.unblock") diff --git a/config/routes.rb b/config/routes.rb index eacd6264..2b500d0a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -132,12 +132,11 @@ Rails.application.routes.draw do post "/list_membership", to: "list#membership", as: :list_membership post "/subscribe", to: "subscription#subscribe", as: :subscribe_answer post "/unsubscribe", to: "subscription#unsubscribe", as: :unsubscribe_answer - post "/block_anon", to: "anonymous_block#create", as: :block_anon - delete "/block_anon/:id", to: "anonymous_block#destroy", as: :unblock_anon end post "/lists", to: "lists#index", as: :lists delete "/lists/:list", to: "lists#destroy", as: :lists_destroy + resource :anonymous_block, controller: :anonymous_block, only: %i[create destroy] get "/discover", to: "discover#index", as: :discover match "/public", to: "timeline#public", via: [:get, :post], as: :public_timeline if APP_CONFIG.dig(:features, :public, :enabled) diff --git a/lib/exporter.rb b/lib/exporter.rb index e64b5558..e08abd78 100644 --- a/lib/exporter.rb +++ b/lib/exporter.rb @@ -133,7 +133,7 @@ class Exporter end def publish - `tar czvf #{Rails.root.join "public", "export", "#{@export_filename}.tar.gz"} -C /tmp/rs_export #{@export_dirname}` + `tar czvf #{Rails.public_path.join "export", "#{@export_filename}.tar.gz"} #{@export_dirname}` url = "#{APP_CONFIG['https'] ? 'https' : 'http'}://#{APP_CONFIG['hostname']}/export/#{@export_filename}.tar.gz" @user.export_processing = false @user.export_url = url diff --git a/lib/version.rb b/lib/version.rb index 68a395d7..d2c2e802 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -15,9 +15,9 @@ module Retrospring def year = 2022 - def month = 11 + def month = 12 - def day = 20 + def day = 4 def patch = 0 diff --git a/package.json b/package.json index 0fc7affd..911b9d62 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "dependencies": { "@babel/preset-typescript": "^7.18.6", - "@hotwired/stimulus": "^3.1.1", + "@hotwired/stimulus": "^3.2.1", "@hotwired/stimulus-webpack-helpers": "^1.0.1", "@fontsource/lexend": "^4.5.13", "@hotwired/turbo-rails": "^7.2.4", @@ -27,7 +27,7 @@ "typescript": "^4.9.3" }, "devDependencies": { - "@babel/core": "^7.20.2", + "@babel/core": "^7.20.5", "@babel/preset-env": "^7.20.2", "@rails/webpacker": "5.4.3", "@typescript-eslint/eslint-plugin": "^4.11.0", diff --git a/spec/controllers/ajax/anonymous_block_controller_spec.rb b/spec/controllers/anonymous_block_controller_spec.rb similarity index 50% rename from spec/controllers/ajax/anonymous_block_controller_spec.rb rename to spec/controllers/anonymous_block_controller_spec.rb index 689916d8..15dfef01 100644 --- a/spec/controllers/ajax/anonymous_block_controller_spec.rb +++ b/spec/controllers/anonymous_block_controller_spec.rb @@ -2,9 +2,9 @@ require "rails_helper" -describe Ajax::AnonymousBlockController, :ajax_controller, type: :controller do +describe AnonymousBlockController, type: :controller do describe "#create" do - subject { post(:create, params: params) } + subject { post(:create, params:) } context "user signed in" do let(:user) { FactoryBot.create(:user) } @@ -20,19 +20,9 @@ describe Ajax::AnonymousBlockController, :ajax_controller, type: :controller do { question: question.id } end - let(:expected_response) do - { - "success" => true, - "status" => "okay", - "message" => anything - } - end - it "creates an anonymous block" do expect { subject }.to(change { AnonymousBlock.count }.by(1)) end - - include_examples "returns the expected response" end context "when blocking a user globally" do @@ -43,14 +33,6 @@ describe Ajax::AnonymousBlockController, :ajax_controller, type: :controller do end context "as a moderator" do - let(:expected_response) do - { - "success" => true, - "status" => "okay", - "message" => anything - } - end - before do user.add_role(:moderator) end @@ -59,48 +41,19 @@ describe Ajax::AnonymousBlockController, :ajax_controller, type: :controller do expect { subject }.to(change { AnonymousBlock.count }.by(1)) expect(AnonymousBlock.last.user_id).to be_nil end - - include_examples "returns the expected response" end context "as a regular user" do - let(:expected_response) do - { - "success" => false, - "status" => "forbidden", - "message" => anything - } - end - it "does not create an anonymous block" do - expect { subject }.not_to(change { AnonymousBlock.count }) + expect { subject }.to raise_error(Pundit::NotAuthorizedError) end - - include_examples "returns the expected response" end end - - context "when parameters are missing" do - let(:params) { {} } - let(:expected_response) do - { - "success" => false, - "status" => "parameter_error", - "message" => anything - } - end - - it "does not create an anonymous block" do - expect { subject }.not_to(change { AnonymousBlock.count }) - end - - include_examples "returns the expected response" - end end end describe "#destroy" do - subject { delete(:destroy, params: params) } + subject { delete(:destroy, params:) } context "user signed in" do let(:user) { FactoryBot.create(:user) } @@ -111,20 +64,16 @@ describe Ajax::AnonymousBlockController, :ajax_controller, type: :controller do context "when all parameters are given" do let(:question) { FactoryBot.create(:question, author_identifier: "someidentifier") } - let(:block) { AnonymousBlock.create(user: user, identifier: "someidentifier", question: question) } + let(:block) { AnonymousBlock.create(user:, identifier: "someidentifier", question:) } let(:params) do { id: block.id } end - let(:expected_response) do - { - "success" => true, - "status" => "okay", - "message" => anything - } - end + it "destroys the anonymous block" do + subject - include_examples "returns the expected response" + expect(AnonymousBlock.exists?(block.id)).to eq(false) + end end end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 3889f50a..abec3188 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -87,32 +87,6 @@ describe ApplicationHelper, type: :helper do end end - describe "#ios_web_app" do - context "Non-iOS device" do - it "should not return true" do - controller.request.user_agent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2;" - - expect(helper.ios_web_app?).to eq(false) - end - end - - context "iOS device (not in web app mode)" do - it "should not return true" do - controller.request.user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B435 Safari/600.1.4" - - expect(helper.ios_web_app?).to eq(false) - end - end - - context "iOS device" do - it "should return true" do - controller.request.user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B435" - - expect(helper.ios_web_app?).to eq(true) - end - end - end - describe "#rails_admin_path_for_resource" do context "user resource" do let(:resource) { FactoryBot.create(:user) } diff --git a/spec/lib/exporter_spec.rb b/spec/lib/exporter_spec.rb index 65f0dccb..526b2978 100644 --- a/spec/lib/exporter_spec.rb +++ b/spec/lib/exporter_spec.rb @@ -296,11 +296,13 @@ RSpec.describe Exporter do describe "#publish" do let(:fake_rails_root) { Pathname(Dir.mktmpdir) } + let(:fake_rails_public_path) { fake_rails_root.join('public') } let(:name) { instance.instance_variable_get(:@export_filename) } before do FileUtils.mkdir_p("#{fake_rails_root}/public/export") allow(Rails).to receive(:root).and_return(fake_rails_root) + allow(Rails).to receive(:public_path).and_return(fake_rails_public_path) user.export_processing = true user.save! diff --git a/yarn.lock b/yarn.lock index 9dc02594..69bde41b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -34,33 +34,33 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== -"@babel/core@^7.15.0", "@babel/core@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" - integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== +"@babel/core@^7.15.0", "@babel/core@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.2" + "@babel/generator" "^7.20.5" "@babel/helper-compilation-targets" "^7.20.0" "@babel/helper-module-transforms" "^7.20.2" - "@babel/helpers" "^7.20.1" - "@babel/parser" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.2" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.20.1", "@babel/generator@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.2.tgz#c2e89e22613a039285c1e7b749e2cd0b30b9a481" - integrity sha512-SD75PMIK6i9H8G/tfGvB4KKl4Nw6Ssos9nGgYwxbgyTP0iX/Z55DveoH86rmUB/YHTQQ+ZC0F7xxaY8l2OF44Q== +"@babel/generator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.20.5" "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" @@ -366,14 +366,14 @@ "@babel/traverse" "^7.18.11" "@babel/types" "^7.18.10" -"@babel/helpers@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" - integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== +"@babel/helpers@^7.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== dependencies: "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" "@babel/highlight@^7.10.4": version "7.16.7" @@ -393,10 +393,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.2.tgz#9aeb9b92f64412b5f81064d46f6a1ac0881337f4" - integrity sha512-afk318kh2uKbo7BEj2QtEi8HVCGrwHUffrYDy7dgVcSa2j9lY3LDjPzcyGdpX7xgm35aWqvciZJ4WKmdF/SxYg== +"@babel/parser@^7.18.10", "@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -1050,26 +1050,26 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.6", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== +"@babel/traverse@^7.13.0", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.6", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== dependencies: "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" + "@babel/generator" "^7.20.5" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.19.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.16.7", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.4.4": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" - integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== +"@babel/types@^7.16.7", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.4.4": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -1115,10 +1115,10 @@ resolved "https://registry.yarnpkg.com/@hotwired/stimulus-webpack-helpers/-/stimulus-webpack-helpers-1.0.1.tgz#4cd74487adeca576c9865ac2b9fe5cb20cef16dd" integrity sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ== -"@hotwired/stimulus@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.1.1.tgz#652f08a8e1d5edcb407340e58818fcff463b5848" - integrity sha512-e0JpzIaYLsRRXevRDVs0yevabiCvieIWWCwh7VqVXjXM5AOHdjb7AjaKIj34zYFmY1N6HIRRfk915WVMYlHnDA== +"@hotwired/stimulus@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.1.tgz#e3de23623b0c52c247aba4cd5d530d257008676b" + integrity sha512-HGlzDcf9vv/EQrMJ5ZG6VWNs8Z/xMN+1o2OhV1gKiSG6CqZt5MCBB1gRg5ILiN3U0jEAxuDTNPRfBcnZBDmupQ== "@hotwired/turbo-rails@^7.1.0", "@hotwired/turbo-rails@^7.2.4": version "7.2.4" @@ -2984,9 +2984,9 @@ decamelize@^1.2.0: integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== deep-is@^0.1.3: version "0.1.4"