mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
Merge branch 'master' into tailwind-redesign
This commit is contained in:
commit
10e7998c10
4
Gemfile
4
Gemfile
@ -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
|
||||
|
||||
189
Gemfile.lock
189
Gemfile.lock
@ -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
|
||||
|
||||
@ -34,7 +34,8 @@ p.long-form {
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
padding: 10px;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 << {
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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
14
app/jobs/export_job.rb
Normal 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
|
||||
@ -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
|
||||
|
||||
15
app/jobs/premium_downgrade_job.rb
Normal file
15
app/jobs/premium_downgrade_job.rb
Normal 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
|
||||
@ -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,
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
323
app/services/export_service.rb
Normal file
323
app/services/export_service.rb
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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 %>
|
||||
|
||||
@ -35,7 +35,6 @@
|
||||
</div>
|
||||
|
||||
<%= render 'layouts/ganalytics' %>
|
||||
|
||||
</main>
|
||||
|
||||
<%= react_component("Footer") unless defined?(@show_footer) && !@show_footer %>
|
||||
|
||||
@ -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? %>
|
||||
|
||||
@ -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 %>
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -6,4 +6,5 @@
|
||||
- default
|
||||
- cache
|
||||
- notifications
|
||||
- export
|
||||
- low_priority
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user