Merge branch 'master' into tailwind-redesign

This commit is contained in:
Andrew Brown 2022-04-15 16:10:05 -05:00 committed by GitHub
commit 10e7998c10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 673 additions and 178 deletions

View File

@ -3,7 +3,7 @@ ruby "~> 2.7"
# Server
gem 'rails'
gem 'puma', '~> 5.5'
gem 'puma', '~> 5.6'
gem 'puma-heroku'
# gem 'bootsnap', require: false
gem 'sprockets', '~> 3.7.2'
@ -107,7 +107,7 @@ group :production do
end
group :test, :production do
gem 'pg', '~> 1.2'
gem 'pg', '~> 1.3'
gem "mini_racer", "~> 0.6.2"
end

View File

@ -41,68 +41,71 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
actioncable (6.1.4.6)
actionpack (= 6.1.4.6)
activesupport (= 6.1.4.6)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionmailbox (6.1.4.6)
actionpack (= 6.1.4.6)
activejob (= 6.1.4.6)
activerecord (= 6.1.4.6)
activestorage (= 6.1.4.6)
activesupport (= 6.1.4.6)
mail (>= 2.7.1)
actionmailer (6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionmailer (6.1.4.6)
actionpack (= 6.1.4.6)
actionview (= 6.1.4.6)
activejob (= 6.1.4.6)
activesupport (= 6.1.4.6)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.4.1)
actionview (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionpack (6.1.4.6)
actionview (= 6.1.4.6)
activesupport (= 6.1.4.6)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.4.1)
actionpack (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actiontext (6.1.4.6)
actionpack (= 6.1.4.6)
activerecord (= 6.1.4.6)
activestorage (= 6.1.4.6)
activesupport (= 6.1.4.6)
nokogiri (>= 1.8.5)
actionview (6.1.4.1)
activesupport (= 6.1.4.1)
actionview (6.1.4.6)
activesupport (= 6.1.4.6)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_record_union (1.3.0)
activerecord (>= 4.0)
active_storage_validations (0.9.5)
rails (>= 5.2.0)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
active_storage_validations (0.9.7)
activejob (>= 5.2.0)
activemodel (>= 5.2.0)
activestorage (>= 5.2.0)
activesupport (>= 5.2.0)
activejob (6.1.4.6)
activesupport (= 6.1.4.6)
globalid (>= 0.3.6)
activemodel (6.1.4.1)
activesupport (= 6.1.4.1)
activemodel (6.1.4.6)
activesupport (= 6.1.4.6)
activemodel-serializers-xml (1.0.2)
activemodel (> 5.x)
activesupport (> 5.x)
builder (~> 3.1)
activerecord (6.1.4.1)
activemodel (= 6.1.4.1)
activesupport (= 6.1.4.1)
activestorage (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activesupport (= 6.1.4.1)
activerecord (6.1.4.6)
activemodel (= 6.1.4.6)
activesupport (= 6.1.4.6)
activestorage (6.1.4.6)
actionpack (= 6.1.4.6)
activejob (= 6.1.4.6)
activerecord (= 6.1.4.6)
activesupport (= 6.1.4.6)
marcel (~> 1.0.0)
mini_mime (>= 1.1.0)
activesupport (6.1.4.1)
activesupport (6.1.4.6)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@ -117,7 +120,7 @@ GEM
autoprefixer-rails (10.3.3.0)
execjs (~> 2)
aws-eventstream (1.2.0)
aws-partitions (1.543.0)
aws-partitions (1.558.0)
aws-sdk (3.1.0)
aws-sdk-resources (~> 3)
aws-sdk-accessanalyzer (1.23.0)
@ -315,7 +318,7 @@ GEM
aws-sdk-connectparticipant (1.14.0)
aws-sdk-core (~> 3, >= 3.120.0)
aws-sigv4 (~> 1.1)
aws-sdk-core (3.125.0)
aws-sdk-core (3.127.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
@ -578,8 +581,8 @@ GEM
aws-sdk-kinesisvideosignalingchannels (1.13.0)
aws-sdk-core (~> 3, >= 3.120.0)
aws-sigv4 (~> 1.1)
aws-sdk-kms (1.53.0)
aws-sdk-core (~> 3, >= 3.125.0)
aws-sdk-kms (1.55.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-lakeformation (1.17.0)
aws-sdk-core (~> 3, >= 3.120.0)
@ -1086,8 +1089,8 @@ GEM
aws-sdk-route53resolver (1.30.0)
aws-sdk-core (~> 3, >= 3.120.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.110.0)
aws-sdk-core (~> 3, >= 3.125.0)
aws-sdk-s3 (1.113.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sdk-s3control (1.40.0)
@ -1258,7 +1261,7 @@ GEM
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
builder (3.2.4)
bullet (6.1.5)
bullet (7.0.1)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundler-audit (0.9.0.1)
@ -1295,7 +1298,7 @@ GEM
db_text_search (0.3.2)
activerecord (>= 4.1.15, < 7.0)
debug_inspector (1.1.0)
devise (4.8.0)
devise (4.8.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@ -1312,7 +1315,7 @@ GEM
docile (1.1.5)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
engtagger (0.2.1)
engtagger (0.2.2)
erubi (1.10.0)
event_emitter (0.2.6)
eventmachine (1.2.7)
@ -1324,7 +1327,7 @@ GEM
faye-websocket (0.11.1)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
ffi (1.15.4)
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
@ -1334,7 +1337,7 @@ GEM
railties (>= 3.2, < 8.0)
friendly_id (5.4.2)
activerecord (>= 4.0.0)
globalid (0.5.2)
globalid (1.0.0)
activesupport (>= 5.0)
haml (5.2.2)
temple (>= 0.8.0)
@ -1354,7 +1357,7 @@ GEM
http-form_data (2.3.0)
http-parser (1.2.3)
ffi-compiler (>= 1.0, < 2.0)
i18n (1.8.11)
i18n (1.10.0)
concurrent-ruby (~> 1.0)
ibm_cloud_sdk_core (1.1.3)
concurrent-ruby (~> 1.0)
@ -1367,13 +1370,13 @@ GEM
http (~> 4.4.0)
ibm_cloud_sdk_core (~> 1.1.3)
jwt (~> 2.2.1)
image_processing (1.12.1)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
inline_svg (1.7.2)
activesupport (>= 3.0)
nokogiri (>= 1.6)
jmespath (1.4.0)
jmespath (1.6.0)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
@ -1401,10 +1404,10 @@ GEM
language_filter (0.3.01)
libv8-node (16.10.0.0)
libv8-node (16.10.0.0-x86_64-linux)
listen (3.7.0)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loofah (2.13.0)
loofah (2.15.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@ -1426,7 +1429,7 @@ GEM
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.7.1)
mini_portile2 (2.8.0)
mini_racer (0.6.2)
libv8-node (~> 16.10.0.0)
minitest (5.15.0)
@ -1435,12 +1438,12 @@ GEM
mustache (1.1.1)
nested_form (0.3.2)
netrc (0.11.0)
newrelic_rpm (8.3.0)
newrelic_rpm (8.5.0)
nio4r (2.5.8)
nokogiri (1.13.1)
mini_portile2 (~> 2.7.0)
nokogiri (1.13.3)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
nokogiri (1.13.1-x86_64-linux)
nokogiri (1.13.3-x86_64-linux)
racc (~> 1.4)
onebox (2.2.19)
addressable (~> 2.8.0)
@ -1458,7 +1461,7 @@ GEM
mime-types
mimemagic (~> 0.3.0)
terrapin (~> 0.6.0)
paranoia (2.5.0)
paranoia (2.5.2)
activerecord (>= 5.1, < 7.1)
paypal-checkout-sdk (1.0.4)
paypalhttp (~> 1.0.1)
@ -1467,12 +1470,12 @@ GEM
faraday (~> 0.15)
faraday_middleware (~> 0.12)
paypalhttp (1.0.1)
pg (1.2.3)
pg (1.3.4)
pry (0.14.1)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (4.0.6)
puma (5.5.1)
puma (5.6.2)
nio4r (~> 2.0)
puma-heroku (2.0.0)
puma (>= 5.0, < 6.0)
@ -1480,7 +1483,7 @@ GEM
activesupport (>= 3.0.0)
racc (1.6.0)
rack (2.2.3)
rack-mini-profiler (2.3.3)
rack-mini-profiler (3.0.0)
rack (>= 1.2.0)
rack-pjax (1.1.0)
nokogiri (~> 1.5)
@ -1489,20 +1492,20 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.1.4.1)
actioncable (= 6.1.4.1)
actionmailbox (= 6.1.4.1)
actionmailer (= 6.1.4.1)
actionpack (= 6.1.4.1)
actiontext (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activemodel (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
rails (6.1.4.6)
actioncable (= 6.1.4.6)
actionmailbox (= 6.1.4.6)
actionmailer (= 6.1.4.6)
actionpack (= 6.1.4.6)
actiontext (= 6.1.4.6)
actionview (= 6.1.4.6)
activejob (= 6.1.4.6)
activemodel (= 6.1.4.6)
activerecord (= 6.1.4.6)
activestorage (= 6.1.4.6)
activesupport (= 6.1.4.6)
bundler (>= 1.15.0)
railties (= 6.1.4.1)
railties (= 6.1.4.6)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
@ -1527,9 +1530,9 @@ GEM
sassc-rails (>= 1.3, < 3)
rails_gravatar (1.0.4)
actionview
railties (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
railties (6.1.4.6)
actionpack (= 6.1.4.6)
activesupport (= 6.1.4.6)
method_source
rake (>= 0.13)
thor (~> 1.0)
@ -1544,7 +1547,7 @@ GEM
railties (>= 3.2)
tilt
redcarpet (3.5.1)
redis (4.5.1)
redis (4.6.0)
remotipart (1.4.4)
responders (3.0.1)
actionpack (>= 5.0)
@ -1557,8 +1560,8 @@ GEM
rexml (3.2.5)
rinku (2.0.6)
rmagick (4.2.4)
ruby-vips (2.0.17)
ffi (~> 1.9)
ruby-vips (2.1.4)
ffi (~> 1.12)
rubyzip (2.3.2)
sanitize (6.0.0)
crass (~> 1.0.2)
@ -1578,7 +1581,7 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2)
semantic_range (3.0.0)
sidekiq (6.2.2)
sidekiq (6.4.1)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
@ -1595,15 +1598,15 @@ GEM
babel-source (>= 5.8.11)
babel-transpiler
sprockets (>= 3.0.0)
sprockets-rails (3.2.2)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2)
stackprof (0.2.17)
stackprof (0.2.19)
statsd-ruby (1.5.0)
stripe (5.42.0)
stripe_event (2.3.1)
stripe (5.45.0)
stripe_event (2.3.2)
activesupport (>= 3.1)
stripe (>= 2.8, < 6)
temple (0.8.2)
@ -1645,7 +1648,7 @@ GEM
websocket-extensions (0.1.5)
word_count_analyzer (1.0.1)
engtagger
zeitwerk (2.5.3)
zeitwerk (2.5.4)
PLATFORMS
ruby
@ -1691,9 +1694,9 @@ DEPENDENCIES
paranoia
paypal-checkout-sdk
paypal_client
pg (~> 1.2)
pg (~> 1.3)
pry
puma (~> 5.5)
puma (~> 5.6)
puma-heroku
rack-mini-profiler
rails

View File

@ -34,7 +34,8 @@ p.long-form {
a {
display: block;
padding: 10px 20px;
padding: 10px;
padding-right: 0;
}
}

View File

@ -304,6 +304,7 @@ class ContentController < ApplicationController
@content = content_type.find_by(id: params[:id])
return redirect_to(root_path, notice: "You don't have permission to view that content.") if @content.nil?
@serialized_content = ContentSerializer.new(@content)
return redirect_to(root_path, notice: "You don't have permission to view that content.") unless @content.updatable_by?(current_user || User.new)
if user_signed_in?
@navbar_actions << {

View File

@ -4,8 +4,6 @@ class DocumentsController < ApplicationController
# todo Uh, this is a hack. The CSRF token on document editor model to add entities is being rejected... for whatever reason.
skip_before_action :verify_authenticity_token, only: [:link_entity]
skip_before_action :cache_most_used_page_information, only: [:update]
before_action :set_document, only: [:show, :analysis, :plaintext, :queue_analysis, :edit, :destroy]
before_action :set_sidenav_expansion, except: [:plaintext]
before_action :set_navbar_color, except: [:plaintext]

View File

@ -2,6 +2,9 @@ class ExportController < ApplicationController
before_action :authenticate_user!
before_action :whitelist_pluralized_model, only: [:csv]
skip_before_action :cache_most_used_page_information, only: [:outline, :notebook_json]
skip_before_action :cache_forums_unread_counts, only: [:outline, :notebook_json]
def index
@sidenav_expansion = 'my account'
end
@ -11,11 +14,17 @@ class ExportController < ApplicationController
end
def outline
send_data content_to_outline, filename: "notebook-#{Date.today}.txt"
export = ExportService.text_outline_export(current_user.universes.pluck(:id))
send_data export, filename: "notebook-#{Date.today}.txt"
end
def markdown
export = ExportService.text_markdown_export(current_user.universes.pluck(:id))
send_data export, filename: "notebook-#{Date.today}.md"
end
def notebook_json
json_dump = current_user.content.except('Document').except('Timeline').map { |category, content| {"#{category}": fill_relations(category.constantize, content)} }.to_json
json_dump = ExportService.json_export(current_user.universes.pluck(:id))
send_data json_dump, filename: "notebook-#{Date.today}.json"
end

14
app/jobs/export_job.rb Normal file
View File

@ -0,0 +1,14 @@
class ExportJob < ApplicationJob
queue_as :export
def perform(*args)
universe_ids = args.shift
format = args.shift
# design: left side 1/2 - list universes with toggle/checkboxes to include
# right side 1/2 - radios for format, checkboxes for options after
# (include hidden? include IDs? include blank fields?)
end
end

View File

@ -27,6 +27,10 @@ class PayPalPrepayProcessingJob < ApplicationJob
unless invoice.status == 'COMPLETED'
invoice.update(status: 'COMPLETED')
invoice.generate_promo_code!
# Add the extra Premium space
SubscriptionService.add_any_referral_bonuses(invoice.user, 'premium')
PremiumDowngradeJob.set(wait: (invoice.months).months).perform_later(invoice.user_id)
end
else

View File

@ -0,0 +1,15 @@
class PremiumDowngradeJob < ApplicationJob
queue_as :low_priority
def perform(*args)
user_id = args.shift
user = User.find_by(id: user_id)
raise "No user to downgrade; id=#{user_id}" if user.nil?
# Remove any bonus bandwidth granted by the plan
premium_bandwidth_bonus = BillingPlan.find(4).bonus_bandwidth_kb
user.update!(upload_bandwidth_kb: user.upload_bandwidth_kb - premium_bandwidth_bonus)
end
end

View File

@ -6,8 +6,9 @@ module HasPageReferences
included do
# Pages that reference this one
has_many :incoming_page_references,
as: :referenced_page,
class_name: PageReference.name
as: :referenced_page,
class_name: PageReference.name,
dependent: :destroy
Rails.application.config.content_type_names[:all].each do |page_type|
has_many "referencing_#{page_type.downcase}_pages".to_sym,
through: :incoming_page_references,
@ -26,7 +27,8 @@ module HasPageReferences
# Pages referenced by this one
has_many :outgoing_page_references,
as: :referencing_page,
class_name: PageReference.name
class_name: PageReference.name,
dependent: :destroy
Rails.application.config.content_type_names[:all].each do |page_type|
has_many "referenced_#{page_type.downcase}_pages".to_sym,
through: :outgoing_page_references,

View File

@ -7,6 +7,8 @@ class AttributeField < ApplicationRecord
belongs_to :attribute_category
has_many :attribute_values, class_name: 'Attribute', dependent: :destroy
has_many :page_references, dependent: :destroy
validates_presence_of :user_id
acts_as_list scope: [:user_id, :attribute_category_id]

View File

@ -20,7 +20,7 @@ class Creature < ApplicationRecord
include Serendipitous::Concern
include Authority::Abilities
self.authorizer_name = 'CoreContentAuthorizer'
self.authorizer_name = 'ExtendedContentAuthorizer'
# Locations
relates :habitats, with: :wildlifeships

View File

@ -16,8 +16,7 @@ class Language < ApplicationRecord
self.authorizer_name = 'ExtendedContentAuthorizer'
def description
num_speakers = Lingualism.where(spoken_language_id: id).count
"Language spoken by #{ActionController::Base.helpers.pluralize num_speakers, 'character'}"
overview_field_value('Description')
end
def self.color

View File

@ -0,0 +1,323 @@
class ExportService < Service
INCLUDE_EMPTY_FIELD_LABELS = false
def self.text_outline_export(universe_ids)
page_types = Rails.application.config.content_types[:all]
temporary_user_id_reference = Universe.find_by(id: universe_ids).user_id
export_text = StringIO.new
categories = fetch_categories(user_id: temporary_user_id_reference)
fields = fetch_fields(category_ids: categories.map(&:id))
values = fetch_values(field_ids: fields.map(&:id))
page_types.each do |page_type|
# Get all pages in the given universe(s)
pages = (page_type.name == 'Universe') ? page_type.where(id: universe_ids)
: page_type.where(universe_id: universe_ids)
if pages.any?
export_text << "# #{page_type.name.pluralize}\n"
categories_for_this_page_type = categories.select { |c| c.entity_type == page_type.name.downcase }
fields_for_this_page_type = fields.select { |f| categories_for_this_page_type.map(&:id).include? f.attribute_category_id }
pages.each do |page|
export_text << "* Name: #{page.name}\n"
export_text << " ID: #{page.id}\n"
values_for_this_page = values.select { |v| v.entity_type == page_type.name && v.entity_id == page.id }
categories_for_this_page_type.each do |category|
# Delay printing our category header until we've found a value to print, so we don't print
# empty headers and blank sections.
printed_category_header = false
fields_for_this_category = fields_for_this_page_type.select { |f| f.attribute_category_id == category.id }
values_for_these_fields = values_for_this_page.select { |v| fields_for_this_category.map(&:id).include? v.attribute_field_id }
fields_for_this_category.each do |field|
value_for_this_field = values_for_these_fields.detect do |value|
value.attribute_field_id == field.id &&
value.entity_type == page_type.name &&
value.entity_id == page.id
end
# If we have a field to print but haven't printed the header yet, print it
if value_for_this_field.try(:value).try(:present?) && !printed_category_header
export_text << " #{category.label}\n"
printed_category_header = true
end
case field.field_type
when 'text_area', 'textarea'
# For answers to text fields, we can just output their value directly
if value_for_this_field.present? || INCLUDE_EMPTY_FIELD_LABELS
export_text << " #{field.label}: #{value_for_this_field.try :value}\n"
end
when 'link'
# For link fields, we have link codes ([[Character-1]]) and need to translate
# them into the linked page's name in order for them to actually be useful
if value_for_this_field.present?
json_list = JSON.parse(value_for_this_field.value)
formatted_names = json_list.map do |link_code|
query_link_code_with_cache(*link_code.split('-')).name
end
export_text << " #{field.label}: #{formatted_names.to_sentence}\n"
else
if INCLUDE_EMPTY_FIELD_LABELS
export_text << " #{field.label}:\n"
end
end
end
end
end
export_text << "\n" # spacer between pages
end
export_text << "\n" # spacer between sections
end
end
return export_text.string
end
def self.text_markdown_export(universe_ids)
page_types = Rails.application.config.content_types[:all]
temporary_user_id_reference = Universe.find_by(id: universe_ids).user_id
export_text = StringIO.new
categories = fetch_categories(user_id: temporary_user_id_reference)
fields = fetch_fields(category_ids: categories.map(&:id))
values = fetch_values(field_ids: fields.map(&:id))
page_types.each do |page_type|
# Get all pages in the given universe(s)
pages = (page_type.name == 'Universe') ? page_type.where(id: universe_ids)
: page_type.where(universe_id: universe_ids)
if pages.any?
export_text << "# #{page_type.name.pluralize}\n"
categories_for_this_page_type = categories.select { |c| c.entity_type == page_type.name.downcase }
fields_for_this_page_type = fields.select { |f| categories_for_this_page_type.map(&:id).include? f.attribute_category_id }
pages.each do |page|
export_text << "## #{page.name}\n"
export_text << "ID: #{page.id}\n"
values_for_this_page = values.select { |v| v.entity_type == page_type.name && v.entity_id == page.id }
categories_for_this_page_type.each do |category|
# Delay printing our category header until we've found a value to print, so we don't print
# empty headers and blank sections.
printed_category_header = false
fields_for_this_category = fields_for_this_page_type.select { |f| f.attribute_category_id == category.id }
values_for_these_fields = values_for_this_page.select { |v| fields_for_this_category.map(&:id).include? v.attribute_field_id }
fields_for_this_category.each do |field|
value_for_this_field = values_for_these_fields.detect do |value|
value.attribute_field_id == field.id &&
value.entity_type == page_type.name &&
value.entity_id == page.id
end
# If we have a field to print but haven't printed the header yet, print it
if value_for_this_field.try(:value).try(:present?) && !printed_category_header
export_text << "### #{category.label}\n"
printed_category_header = true
end
case field.field_type
when 'text_area', 'textarea'
# For answers to text fields, we can just output their value directly
if value_for_this_field.try(:value).present? || INCLUDE_EMPTY_FIELD_LABELS
export_text << "#### #{field.label}\n#{value_for_this_field.try :value}\n"
end
when 'link'
# For link fields, we have link codes ([[Character-1]]) and need to translate
# them into the linked page's name in order for them to actually be useful
if value_for_this_field.present?
json_list = JSON.parse(value_for_this_field.value)
formatted_names = json_list.map do |link_code|
link_type, link_id = link_code.split('-')
page_ref = query_link_code_with_cache(link_type, link_id)
"[#{page_ref.name}](https://www.notebook.ai/plan/#{link_type.downcase.pluralize}/#{link_id})"
end
export_text << "#### #{field.label}\n#{formatted_names.to_sentence}\n"
else
if INCLUDE_EMPTY_FIELD_LABELS
export_text << "#### #{field.label}\n"
end
end
end
end
end
export_text << "\n" # spacer between pages
end
export_text << "\n" # spacer between sections
end
end
return export_text.string
end
def self.csv_export(universe_ids, separator=',')
# todo: figure out zip
end
def self.json_export(universe_ids)
page_types = Rails.application.config.content_types[:all]
temporary_user_id_reference = Universe.find_by(id: universe_ids).user_id
export_object = {}
categories = fetch_categories(user_id: temporary_user_id_reference)
fields = fetch_fields(category_ids: categories.map(&:id))
values = fetch_values(field_ids: fields.map(&:id))
page_types.each do |page_type|
# Get all pages in the given universe(s)
pages = (page_type.name == 'Universe') ? page_type.where(id: universe_ids)
: page_type.where(universe_id: universe_ids)
if pages.any?
export_object[page_type.name] = []
categories_for_this_page_type = categories.select { |c| c.entity_type == page_type.name.downcase }
fields_for_this_page_type = fields.select { |f| categories_for_this_page_type.map(&:id).include? f.attribute_category_id }
page_object = {}
pages.each do |page|
page_object['name'] = page.name
page_object['id'] = page.id
values_for_this_page = values.select { |v| v.entity_type == page_type.name && v.entity_id == page.id }
categories_for_this_page_type.each do |category|
page_object[category.label] ||= {}
fields_for_this_category = fields_for_this_page_type.select { |f| f.attribute_category_id == category.id }
values_for_these_fields = values_for_this_page.select { |v| fields_for_this_category.map(&:id).include? v.attribute_field_id }
fields_for_this_category.each do |field|
value_for_this_field = values_for_these_fields.detect do |value|
value.attribute_field_id == field.id &&
value.entity_type == page_type.name &&
value.entity_id == page.id
end
case field.field_type
when 'text_area', 'textarea'
# For answers to text fields, we can just output their value directly
if value_for_this_field.try(:value).present? || INCLUDE_EMPTY_FIELD_LABELS
page_object[category.label][field.label] = value_for_this_field.try :value
end
when 'link'
# For link fields, we have link codes ([[Character-1]]) and need to translate
# them into the linked page's name in order for them to actually be useful
if value_for_this_field.present?
json_list = JSON.parse(value_for_this_field.value)
formatted_name_objects = json_list.map do |link_code|
link_type, link_id = link_code.split('-')
page_ref = query_link_code_with_cache(link_type, link_id)
name_object = {
name: page_ref.name,
id: page_ref.id,
}
end
page_object[category.label][field.label] = formatted_name_objects
else
if INCLUDE_EMPTY_FIELD_LABELS
page_object[category.label][field.label] = nil
end
end
end
end
end
end
# Append the JSON page object we just built to the list of pages of that kind
export_object[page_type.name].push page_object
end
end
return export_object.to_json
end
def self.xml_export(universe_ids)
end
def self.yaml_export(universe_ids)
end
def self.html_export(universe_ids)
end
def self.scrivener_export(universe_ids)
end
private
def self.fetch_categories(user_id:)
AttributeCategory.where(user_id: user_id)
.order('position ASC')
.select(:id, :label, :entity_type)
end
def self.fetch_fields(category_ids:)
AttributeField.where(attribute_category_id: category_ids)
.order('position ASC')
.select(:id, :label, :attribute_category_id, :field_type)
end
def self.fetch_values(field_ids:)
Attribute.where(attribute_field_id: field_ids)
.select(:attribute_field_id, :value, :entity_type, :entity_id)
end
def self.fetch_universes(universe_ids)
end
def self.query_link_code_with_cache(content_type_name, content_id)
cache_key = "#{content_type_name}-#{content_id}"
# Pull from the cache to avoid a query if we can
@content_cache ||= {}
if @content_cache.key?(cache_key)
return @content_cache[cache_key]
end
# TODO: we should probably whitelist content_type from valid page types here
# If there's no cache, we unfortunately need to do a query to resolve the link code
content = class_from_name(content_type_name).find(content_id)
@content_cache[cache_key] = content
return content
end
def self.class_from_name(content_type_name)
# This lookup is ~3x faster than .constantize
Rails.application.config.content_types_by_name[content_type_name]
end
end

View File

@ -2,8 +2,40 @@ class ForumReplacementService < Service
# Cool replacements we could eventually do not for pranking
# [roll N] = random number between 1 and N
# [roll N, M] = random number between N and M
# [@reader] = username of the reader
WORD_REPLACEMENTS = {
# NOTE: could do this shader style on word replacements if we want a ui :) [ReplacementList(color, replacements, &active?)]
# blackout replacements
FORBIDDEN_WORD_REPLACEMENTS = {
}
SPAM_WORD_REPLACEMENTS = {
'cash app' => 'pls ban me andrew',
'cashapp' => 'hey mr andrew come over here'
}
# perspective changes, always surrounded by {} (e.g. {@reader} )
PERSPECTIVE_REPLACEMENTS = {
'@reader' => Proc.new { |trigger, user| (user.nil? ? 'anonymous reader' : user.try(:display_name)) || 'anonymous reader' }
# 'their' => Proc.new { |_, _| ["they're", "their", "there"].sample }
# 'there' => Proc.new { |_, _| ["they're", "their", "there"].sample }
# "they're" => Proc.new { |_, _| ["they're", "their", "there"].sample }
}
# turn on and off at will
CASUAL_CHAOS_WORD_REPLACEMENTS = {
'andrew' => 'andrew (Our Supreme Lord and Overseer)',
'amy' => 'amy (the most wonderful woman in the world)',
}
REFERENCE_REPLACEMENTS = {
# [[Character-4]]
}
# gremlin replacements
GREMLINS_WORD_REPLACEMENTS = {
'<3' => "<span class='red-text'>&heart;</span>",
'$5' => '[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]',
'0/10' => '10/10',
@ -22,9 +54,8 @@ class ForumReplacementService < Service
'alchohol' => 'giggle juice',
'alien' => 'extraterrestrial bopper',
'aliens' => 'extraterrestrial boppers',
'amy' => 'the most wonderful woman in the world',
'alpha' => 'beta',
'an hour' => 'exactly 3600 seconds',
'andrew' => 'andrew (Our Supreme Lord and Overseer)',
'announcement' => 'shouty spouty',
'announcements' => 'shouty spouties',
"April Fool's Day" => 'the best holiday of the year',
@ -36,13 +67,13 @@ class ForumReplacementService < Service
'askew' => 'cattywampus',
'attorney' => 'lawyerboi-at-law',
'attornies' => 'lawyerbois-at-law',
'automatically' => 'automagically',
'autumn' => 'rainseason (bestseason)',
'aviators' => 'cool kid shades',
'avocado' => 'greenseed toastmash',
'aware' => 'cognizant',
'awesome' => "bee's knees",
'B&B' => 'Bungeons & Bragons',
'bad' => 'bodge',
'ball' => 'blimpy bounce bounce',
'balls' => 'blimpy bounce bounces',
'balloon' => 'elastic breath trap',
@ -53,6 +84,7 @@ class ForumReplacementService < Service
'bears' => '(ᵔᴥᵔ)(ᵔᴥᵔ)',
'bed' => 'nighttime dreamvessel',
'beds' => 'nighttime dreamvessels',
'beta' => 'alpha',
'bird' => 'government spy drone',
'birds' => 'government spy drones',
'blood' => 'human syrup',
@ -152,14 +184,13 @@ class ForumReplacementService < Service
'grass' => 'groundfuzzy',
'gravy' => 'meat water',
'greenhouse' => 'clearhouse',
'gremlin' => 'extremely smart alien being that is here to help',
'gremlins' => 'extremely smart alien beings that are here to help',
'gun' => 'rooty tooty point-n-shooty',
'guns' => 'rooty tooty point-n-shooties',
'hamburger' => 'beef wellington ensemble with lettuce',
'hamburgers' => 'beef wellington ensembles with lettuce',
'hat' => 'fuzzy head chimney',
'hate' => 'absolutely love',
'h a t e' => 'L O V E',
'heart' => '<em>corazón</em>',
'hearts' => '<em>corazones</em>',
'hell' => 'heck',
@ -179,8 +210,6 @@ class ForumReplacementService < Service
'keyboards' => 'hoighty toighty tippy typers',
'kidnap' => 'surprise adoption',
'kitchen' => 'fridge-and-oven combination room',
'knee' => 'leg hinge',
'knees' => 'leg hinges',
'knife' => 'stabby stick',
'knives' => 'stabby sticks',
'la croix' => 'water that has been in the vicinity of a fruit at one point in the past few years',
@ -188,7 +217,7 @@ class ForumReplacementService < Service
'laptop' => 'electrotablet with keys',
'lechuga' => 'lettuce',
'lettuce' => 'lechuga',
'life' => 'like a box of chocolates',
'library' => 'libary',
'lightbulb' => 'ceiling-bright',
'lightbulbs' => 'ceiling-brights',
'lizard people' => 'definitely normal human beings',
@ -243,7 +272,7 @@ class ForumReplacementService < Service
'prison' => 'hoosegow locker',
'raccoon' => 'trash burgler',
'radio' => 'magic musicbox',
'rain' => 'cloudy waterdrops',
'rain' => 'cloudy juice',
'recursion' => 'recursion',
'replaced' => 'improved',
'reverse' => 'esrever',
@ -281,6 +310,7 @@ class ForumReplacementService < Service
'Stardew Valley' => 'Farming Simulator 2016',
'sticky' => 'icky wicky',
'stolen' => 'nick wicketed',
'stupid' => 'stoopid',
'success' => 'Yahtzee!',
'summer' => 'sunseason',
'sup' => 'soup',
@ -338,55 +368,90 @@ class ForumReplacementService < Service
'writing' => 'scribble scrabbling',
'writer' => 'scribble scrabbler',
'writers' => 'scribble scrabblers',
'word' => 'wod',
'words' => 'wods',
'wordsmith' => 'wodsmith',
'yuge' => '<span style="font-size: 40px">yuge</span>',
'zalgo' => 'H̶̛̼̼̪̝̞͓̞͕͇̯͚͎͚̘̳͕̱̤̠̗͔͇̙̣̰͓̖̰̯̀̓̐̑̇͊͂̀͋̒̐̓͒̒͊͊̕͜͝ͅE̴̡̧̨̨̲̥̯͎̭̻̩̞̘̞̪̞̗̭͖̻͙͕͎̮͕̺͕̲̘̻̣͚̳̥͍̙͈͚͍͉̗͙̱͖͚̾̂̇͛̉͋͊̾͛̆̀́͑͛̅̋͊̕͘͜͜͜͝ͅͅͅͅ ̸̡̡̨̡̨̛̞͎̹̩̬̗̗̞̬̰̮̙̪̖͈̣̹͔̺̫̰̓̔̉̋̈̈́͐́̿̈̀͊̿̈̉̅̃̊̽͗̈̿̈́̓̈́̎͌̄̀̆̌̎͗̋͒̋̿̋̊̈́͆̋̾̈̏̈́̋̿̕̕̚͝͝͠͠ͅͅͅC̵̛̘̳͙̪̭͖̲̞̯̰̜͇̈̾̈́͋̌̉̽̽͑̎͌̾̈́͌̑͊̊̔̀͆̌̀̇̓͊̀̂̇̿̃͑́̈́̆͂̈́̾̓́̂̂̓̂̍̍͛͆͌͌̽̎̍̀̒̆̀͗͋͘͘͘͝͠͝͝͠͝͝Ǫ̸͕̻̞̝̜͚̗̮̼͎̤͔̤̱͔̫͂̄̉̋̈͊͐͂̇̀̌̎́͑̐̀̈́͋̓̾̅͒̒̄͑̒̆̑̾͜͝͝͝͝M̷̧̧̡̨̛̛̩̭̞͍̼̝̗͕̖͇̣̣̩͆̿̑͒́̉̅̓̌̆̈́͐͒̾̐̂̿̓̚͘̚͜E̵̡̨̢̧̢̢̡̢̨̛̠̱̻̺̦͚̹͓̬͔̪̟̼̥̯̠̘͚̫̯͍̺͔̫̟͇̱̦̟̪͚͉̣̳͓͍̬̙̲͔̘͙͔̤̰̜͍̠̩͉͐̂̊̏̐̿̊̋͑̿̇̊̈́͗̎̋́́̉̓̂̐͑̇̐̐͋́̒̈́͛͑͒̂͒̂̔̀̄̈́̓͂͆̈́͒̌͆̓͗̋͐̔̑͐̕͘ͅͅͅŞ̴̧̧̡̢̧̡̢͕̝͚̝̖͚̣̞̫̻̯͔̳̗̝̰̗̰̰̥̭͕̜̜̫͍̪̳̘̣̺̠͉̗̟͕̹͇̬̘̘̪͆͗̎̕',
## Crazy mode
# 'depression' => 'megasadness',
# 'ice cream' => 'eyes cream',
# 'strawberry' => 'plastic tubefruit',
# 'soul' => 'inner ghost',
# 'dance' => 'woopwoop',
# 'curling iron' => 'medieval torture device',
# 'gay' => 'rainbow',
# 'insurance' => 'the biggest scam known to man',
# 'lemonade' => 'sour drank',
# 'salamander' => 'baby dragon',
# 'scrolling' => 'vertically surfing through a screen',
# 'subtext' => 'subtweeting but IRL',
# 'how' => 'how now brown cow',
# 'midnight' => 'dayover',
# 'kill' => 'deathsnuggle',
# 'babe' => 'bae',
# 'video' => 'series of images played in rapid succession to give the illusion of movement on a static screen',
# 'online' => 'on the interwebs',
# 'internet' => 'series of electrotubes',
# 'highlighted' => 'becoming better',
# 'backfired' => 'went incredibly well',
# 'football' => 'soccer',
# 'baseball' => 'thrown soccer',
# 'basketball' => 'dribbling soccer',
# 'volleyball' => 'beach soccer',
# 'rule' => 'law you must obey',
# 'rules' => 'laws you must obey',
# 'why is this happening' => 'I think this is great',
}
def self.replace(text)
return text
OVERLOAD_WORDS_REPLACEMENTS = {
'bad' => 'bodge',
'knee' => 'leg hinge',
'knees' => 'leg hinges',
'depression' => 'megasadness',
'ice cream' => 'eyes cream',
'life' => 'like a box of chocolates',
'strawberry' => 'plastic tubefruit',
'gremlin' => 'extremely smart alien being that is here to help',
'gremlins' => 'extremely smart alien beings that are here to help',
'soul' => 'inner ghost',
'dance' => 'woopwoop',
'curling iron' => 'medieval torture device',
'insurance' => 'the biggest scam known to man',
'lemonade' => 'sour drank',
'salamander' => 'baby dragon',
'scrolling' => 'vertically surfing through a screen',
'subtext' => 'subtweeting but IRL',
'how' => 'how now brown cow',
'midnight' => 'dayover',
'kill' => 'deathsnuggle',
'babe' => 'bae',
'video' => 'series of images played in rapid succession to give the illusion of movement on a static screen',
'online' => 'on the interwebs',
'internet' => 'series of electrotubes',
'highlighted' => 'becoming better',
'backfired' => 'went incredibly well',
'football' => 'soccer',
'baseball' => 'throwing soccer',
'basketball' => 'dribbling soccer',
'volleyball' => 'beach air soccer',
'rule' => 'law you must obey',
'rules' => 'laws you must obey',
'why is this happening' => 'I think this is great'
}
# TODO: page tag replacements?
def self.replace_for(text, user)
gremlins_phase = 0
replaced_text = text.dup
WORD_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger))
SPAM_WORD_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger, 'red'))
end
PERSPECTIVE_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/{#{trigger.downcase}}/i, wrapped(replacement.call(trigger, user), trigger, 'blue'))
end
if false # not implemented: [[Character-123]] or https://www.notebook.ai/plan/characters/553 etc
REFERENCE_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger, 'notebook-blue'))
end
end
if gremlins_phase >= 1
CASUAL_CHAOS_WORD_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger, 'green'))
end
end
if gremlins_phase >= 2
GREMLINS_WORD_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger, 'green'))
end
end
if gremlins_phase >= 3
OVERLOAD_WORDS_REPLACEMENTS.each do |trigger, replacement|
replaced_text.gsub!(/\b#{trigger.downcase}\b/i, wrapped(replacement, trigger, 'green'))
end
end
replaced_text.html_safe
end
def self.wrapped(text, tooltip)
"<span class='blue lighten-5 tooltipped black-text' style='padding: 4px' data-tooltip='#{tooltip}'>#{text}</span>"
def self.wrapped(text, tooltip, color='blue')
"<span class='#{color} lighten-5 tooltipped black-text' style='padding: 4px' data-tooltip='#{tooltip}'>#{text}</span>"
end
end

View File

@ -127,4 +127,30 @@ class SubscriptionService < Service
].join("\n")
)
end
def self.recalculate_bandwidth_for(user)
base_bandwidth = User.new.upload_bandwidth_kb # 50_000
premium_bonus = BillingPlan.find(4).bonus_bandwidth_kb # 9_950_000
premium_total = base_bandwidth + premium_bonus # 10_000_000
referral_bonus = 100_000 # per referral
max_bandwidth = case user.selected_billing_plan_id
when nil, 1
base_bandwidth
when 2 # free-for-lifers
250_000
when 4, 5, 6 # premium
premium_total
else
raise "User with funky billing plan id: U=#{user.id} BP=#{user.selected_billing_plan_id}"
end
referral_bonus = user.referrals.count * referral_bonus
used_bandwidth = user.image_uploads.sum(:src_file_size) / 1000
remaining_bandwidth = max_bandwidth + referral_bonus - used_bandwidth
return remaining_bandwidth
end
end

View File

@ -45,11 +45,11 @@
<%= category[:label].capitalize.presence || '(blank)' %>
<% if editing && category[:percent_complete].present? %>
<% if category[:percent_complete] == 100 %>
<span class="badge green-text hide-on-med-and-down">
<span class="green-text hide-on-med-and-down" style="padding-left: 10px">
<i class="material-icons">check</i>
</span>
<% else %>
<span class="badge grey-text hide-on-med-and-down">
<span class="grey-text hide-on-med-and-down" style="padding-left: 10px">
<small><%= category[:percent_complete] %>%</small>
</span>
<% end %>

View File

@ -35,7 +35,6 @@
</div>
<%= render 'layouts/ganalytics' %>
</main>
<%= react_component("Footer") unless defined?(@show_footer) && !@show_footer %>

View File

@ -19,7 +19,7 @@
<%= content_tag :article, id: dom_id(post), class: "thredded--post thredded--#{post.read_state}--post #{muted_post ? muted_post_classes : 'card'}" do %>
<%= render 'thredded/posts_common/actions', post: post, actions: local_assigns[:actions] %>
<%= render 'thredded/posts_common/header', post: post %>
<%= ForumReplacementService.replace(content) || render('thredded/posts/content', post: post) %>
<%= ForumReplacementService.replace_for(content, current_user) || render('thredded/posts/content', post: post) %>
<% if post.pending_moderation? && !Thredded.content_visible_while_pending_moderation %>
<p class="thredded--alert thredded--alert-warning"><%= t 'thredded.posts.pending_moderation_notice' %></p>
<% elsif post.blocked? && post.can_moderate? %>

View File

@ -13,6 +13,7 @@
<%= content_tag :article, id: dom_id(private_post), class: "thredded--post thredded--#{private_post.read_state}--post" do %>
<%= render 'thredded/posts_common/actions', post: private_post, actions: local_assigns[:actions] %>
<%= render 'thredded/posts_common/header', post: private_post %>
<%= content || render('thredded/private_posts/content', post: private_post) %>
<%= ForumReplacementService.replace_for(content, current_user) || render('thredded/private_posts/content', post: private_post) %>
<%# content || render('thredded/private_posts/content', post: private_post) %>
<% end %>
<% end %>

View File

@ -7,6 +7,8 @@
:field_type: name
- :name: other_names
:label: Other names
- :name: description
:label: Description
- :name: universe_id
:label: Universe
:field_type: universe

View File

@ -14,12 +14,3 @@ if Date.current >= 'March 1, 2020'.to_date
Rails.application.config.content_types[:premium] -= [Lore]
end
end
# Lore free during the month of October
# Need to change Creature.rb authorizer at the end
if Date.current >= 'October 1, 2021'.to_date
if Date.current < 'November 1, 2021'.to_date
Rails.application.config.content_types[:free] << Creature
Rails.application.config.content_types[:premium] -= [Creature]
end
end

View File

@ -156,6 +156,7 @@ Rails.application.routes.draw do
get '/', to: 'export#index', as: :notebook_export
get '/outline', to: 'export#outline', as: :notebook_outline
get '/markdown', to: 'export#markdown', as: :notebook_markdown
get '/notebook.json', to: 'export#notebook_json', as: :notebook_json
get '/notebook.xml', to: 'export#notebook_xml', as: :notebook_xml
get '/notebook.yml', to: 'export#notebook_yml', as: :notebook_yml

View File

@ -6,4 +6,5 @@
- default
- cache
- notifications
- export
- low_priority

View File

@ -111,5 +111,47 @@ namespace :data_integrity do
end
end
desc "Remove orphan page references"
task remove_orphan_page_references: :environment do
PageReference.find_each do |reference|
if reference.referencing_page.nil?
puts "Deleting reference #{reference.id}"
reference.destroy
next
end
if reference.referenced_page.nil?
puts "Deleting reference #{reference.id}"
reference.destroy
next
end
end
end
desc "Ensure all users have the correct upload bandwidth amounts"
task correct_bandwidths: :environment do
puts "Disabling SQL logging"
old_logger = ActiveRecord::Base.logger
ActiveRecord::Base.logger = nil
# For the sake of minimizing db updates while blazing through all users,
# we ignore a small amount (1kb) of difference between saved bandwidth
# and calculated bandwidth. Users should never be more than 1kb off though.
byte_lenience = 1000
User.find_each do |user|
correct_bandwidth = SubscriptionService.recalculate_bandwidth_for(user)
difference = user.upload_bandwidth_kb - correct_bandwidth
if difference.abs > byte_lenience
# puts "Correcting user #{user.id} bandwidth: #{user.upload_bandwidth_kb} --> #{correct_bandwidth}"
user.update(upload_bandwidth_kb: correct_bandwidth)
end
end
puts "Re-enabling SQL logging"
ActiveRecord::Base.logger = old_logger
end
end

View File

@ -1,9 +1,5 @@
require 'test_helper'
class PageTagsControllerTest < ActionDispatch::IntegrationTest
test "should get remove" do
get page_tags_remove_url
assert_response :success
end
end

View File

@ -3312,9 +3312,9 @@ flush-write-stream@^1.0.0:
readable-stream "^2.3.6"
follow-redirects@^1.0.0, follow-redirects@^1.14.0:
version "1.14.7"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
version "1.14.8"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
for-in@^1.0.2:
version "1.0.2"
@ -7365,9 +7365,9 @@ urix@^0.1.0:
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-parse@^1.4.3, url-parse@^1.5.1:
version "1.5.3"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"