standardrb
I can't take Rubocop any more. Moving up to Ruby 3 forces us to update RuboCop, and it comes with dozens of linters that I'd have to evaluate, none of which is a marginal improvement. I'm done having opinions.
This commit is contained in:
parent
bc3ef5c22b
commit
c3f8625788
|
@ -34,8 +34,7 @@ jobs:
|
|||
bundle exec rspec spec/slow/*_spec.rb
|
||||
- name: Run linters
|
||||
run: |
|
||||
bundle exec rubocop
|
||||
bundle exec ruumba
|
||||
bundle exec standardrb
|
||||
- name: Find leaky gems
|
||||
run: |
|
||||
gem install bundler-leak
|
||||
|
|
277
.rubocop.yml
277
.rubocop.yml
|
@ -1,277 +0,0 @@
|
|||
# Please do not 'fix' style issues without a compelling, metrics-driven
|
||||
# argument that a style change will materially improve cod equality.
|
||||
# https://github.com/lobsters/lobsters/pull/460
|
||||
|
||||
# Project setup:
|
||||
require:
|
||||
- ./extras/prohibit_form_for_and_form_tag.rb
|
||||
- ./extras/prohibit_safe_navigation.rb
|
||||
- rubocop-rails
|
||||
- rubocop-rspec
|
||||
AllCops:
|
||||
Include:
|
||||
- '**/*.rb'
|
||||
- '**/Rakefile'
|
||||
- '**/config.ru'
|
||||
- '**/*.rake'
|
||||
Exclude:
|
||||
- Gemfile
|
||||
- 'bin/**/*'
|
||||
- 'db/**/*'
|
||||
- 'vendor/**/*'
|
||||
UseCache: false
|
||||
|
||||
# Cop configuration:
|
||||
|
||||
# Bundler
|
||||
|
||||
# Gemspec
|
||||
|
||||
# Layout
|
||||
Layout/AccessModifierIndentation:
|
||||
EnforcedStyle: outdent
|
||||
Layout/ArrayAlignment:
|
||||
Enabled: false
|
||||
Layout/CaseIndentation:
|
||||
EnforcedStyle: end
|
||||
Layout/ElseAlignment:
|
||||
Enabled: false
|
||||
Layout/EmptyLineAfterGuardClause:
|
||||
Enabled: false
|
||||
Layout/EmptyLinesAroundExceptionHandlingKeywords:
|
||||
Enabled: false
|
||||
Layout/EndAlignment:
|
||||
EnforcedStyleAlignWith: variable
|
||||
Layout/FirstHashElementIndentation:
|
||||
EnforcedStyle: consistent
|
||||
Layout/HashAlignment:
|
||||
Enabled: false
|
||||
Layout/LineLength:
|
||||
Max: 100
|
||||
Layout/MultilineMethodCallBraceLayout:
|
||||
Enabled: false
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Enabled: false
|
||||
Layout/SpaceAroundOperators:
|
||||
Enabled: false
|
||||
Layout/SpaceInsideBlockBraces:
|
||||
EnforcedStyle: space
|
||||
SpaceBeforeBlockParameters: false
|
||||
Layout/SpaceInsideRangeLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Lint
|
||||
Lint/SuppressedException:
|
||||
Enabled: false
|
||||
Lint/RaiseException:
|
||||
Enabled: true
|
||||
Lint/StructNewOverride:
|
||||
Enabled: true
|
||||
|
||||
# Metrics
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
Metrics/BlockLength:
|
||||
Enabled: false
|
||||
Metrics/BlockNesting:
|
||||
Enabled: false
|
||||
Metrics/ClassLength:
|
||||
Enabled: false
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: false
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
Metrics/ModuleLength:
|
||||
Enabled: false
|
||||
Metrics/ParameterLists:
|
||||
Enabled: false
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
|
||||
# Naming
|
||||
Naming/AccessorMethodName:
|
||||
Enabled: false
|
||||
Naming/MemoizedInstanceVariableName:
|
||||
Enabled: false
|
||||
Naming/PredicateName:
|
||||
Enabled: false
|
||||
# disabled until class vaiables in extras become constants
|
||||
Naming/MethodParameterName:
|
||||
Enabled: false
|
||||
Naming/VariableName:
|
||||
Enabled: false
|
||||
|
||||
# Performance
|
||||
|
||||
# Rails
|
||||
Rails/Blank:
|
||||
Enabled: false
|
||||
Rails/FilePath:
|
||||
Enabled: false
|
||||
Rails/HttpStatus:
|
||||
Enabled: false
|
||||
Rails/OutputSafety:
|
||||
Enabled: false
|
||||
Rails/Present:
|
||||
UnlessBlank: false
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
Rails/HelperInstanceVariable:
|
||||
Enabled: false
|
||||
|
||||
# RSpec
|
||||
RSpec/ExampleLength:
|
||||
Enabled: false
|
||||
RSpec/MultipleExpectations:
|
||||
Enabled: false
|
||||
RSpec/DescribedClass:
|
||||
Enabled: false
|
||||
RSpec/MessageSpies:
|
||||
Enabled: false
|
||||
RSpec/VerifiedDoubles:
|
||||
Enabled: false
|
||||
RSpec/NotToNot:
|
||||
Enabled: false
|
||||
RSpec/ContextWording:
|
||||
Enabled: false
|
||||
RSpec/ExpectChange:
|
||||
Enabled: false
|
||||
RSpec/HookArgument:
|
||||
Enabled: false
|
||||
RSpec/ExampleWording:
|
||||
Enabled: false
|
||||
RSpec/NamedSubject:
|
||||
Enabled: false
|
||||
RSpec/NestedGroups:
|
||||
Enabled: false
|
||||
RSpec/AnyInstance:
|
||||
Enabled: false
|
||||
RSpec/UnspecifiedException:
|
||||
Enabled: false
|
||||
RSpec/InstanceVariable:
|
||||
Enabled: false
|
||||
RSpec/BeforeAfterAll:
|
||||
Enabled: false
|
||||
RSpec/DescribeClass:
|
||||
Enabled: false
|
||||
RSpec/LetSetup:
|
||||
Enabled: false
|
||||
Capybara/FeatureMethods:
|
||||
Enabled: false
|
||||
|
||||
# Security
|
||||
|
||||
# Style
|
||||
Style/AndOr:
|
||||
EnforcedStyle: conditionals
|
||||
Style/BlockDelimiters:
|
||||
Enabled: false
|
||||
Style/BlockComments:
|
||||
Enabled: false
|
||||
Style/CommentedKeyword:
|
||||
Enabled: false
|
||||
Style/ClassVars:
|
||||
Enabled: false
|
||||
Style/ConditionalAssignment:
|
||||
Enabled: false
|
||||
# It would be nice to have this on, but I'm not up for writing 66 of these in
|
||||
# the process of addressing the initial 4,489 cop warnings.
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
Exclude: [db/migrate/**/*, spec/**/*, test/**/*]
|
||||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
Style/EachWithObject:
|
||||
Enabled: false
|
||||
Style/ExpandPathArguments:
|
||||
Enabled: false
|
||||
Style/FormatString:
|
||||
Enabled: false
|
||||
Style/FormatStringToken:
|
||||
Enabled: false
|
||||
Style/FrozenStringLiteralComment:
|
||||
EnforcedStyle: never
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
Style/HashEachMethods:
|
||||
Enabled: true
|
||||
Style/HashSyntax:
|
||||
EnforcedStyle: no_mixed_keys
|
||||
Style/HashTransformKeys:
|
||||
Enabled: true
|
||||
Style/HashTransformValues:
|
||||
Enabled: true
|
||||
Style/IfInsideElse:
|
||||
Enabled: false
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
Style/InverseMethods:
|
||||
Enabled: false
|
||||
Style/Lambda:
|
||||
Enabled: false
|
||||
Style/LineEndConcatenation:
|
||||
Enabled: false
|
||||
Style/MethodDefParentheses:
|
||||
Enabled: false
|
||||
Style/MultilineBlockChain:
|
||||
Enabled: false
|
||||
Style/MultilineMemoization:
|
||||
Enabled: false
|
||||
Style/MultilineTernaryOperator:
|
||||
Enabled: false
|
||||
Style/NegatedIf:
|
||||
EnforcedStyle: postfix
|
||||
Style/NegatedWhile:
|
||||
Enabled: false
|
||||
Style/Next:
|
||||
Enabled: false
|
||||
Style/NonNilCheck:
|
||||
Enabled: false
|
||||
Style/NumericPredicate:
|
||||
Enabled: false
|
||||
Style/ParallelAssignment:
|
||||
Enabled: false
|
||||
Style/PercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
Style/RaiseArgs:
|
||||
EnforcedStyle: compact
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
Style/RedundantParentheses:
|
||||
Enabled: false
|
||||
Style/RedundantReturn:
|
||||
Enabled: false
|
||||
Style/RedundantSelf:
|
||||
Enabled: false
|
||||
Style/RescueModifier:
|
||||
Enabled: false
|
||||
Style/RescueStandardError:
|
||||
EnforcedStyle: implicit
|
||||
Style/RegexpLiteral:
|
||||
AllowInnerSlashes: true
|
||||
Style/SafeNavigation:
|
||||
Enabled: false
|
||||
Style/SignalException:
|
||||
Enabled: false
|
||||
Style/StringLiterals:
|
||||
Enabled: false
|
||||
Style/SymbolArray:
|
||||
EnforcedStyle: brackets
|
||||
Style/TrailingCommaInArguments:
|
||||
Enabled: false
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: consistent_comma
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: consistent_comma
|
||||
Style/WhileUntilDo:
|
||||
Enabled: false
|
||||
Style/WhileUntilModifier:
|
||||
Enabled: false
|
||||
Style/WordArray:
|
||||
Enabled: false
|
||||
Style/YodaCondition:
|
||||
Enabled: false
|
||||
Style/ZeroLengthPredicate:
|
||||
Enabled: false
|
|
@ -1 +1 @@
|
|||
3.0.2
|
||||
3.2.2
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
require: ./extras/prohibit_form_for_and_form_tag
|
||||
|
||||
AllCops:
|
||||
DisabledByDefault: true
|
||||
|
||||
Style/DisallowFormForandFormTag:
|
||||
Enabled: true
|
||||
Include:
|
||||
- '**/*.erb'
|
|
@ -0,0 +1,4 @@
|
|||
ignore:
|
||||
# it's mad about the class variables and I don't want to risk the refactor now
|
||||
- 'extras/**/*':
|
||||
- Naming/VariableName
|
35
Gemfile
35
Gemfile
|
@ -8,10 +8,10 @@ gem "mysql2"
|
|||
# gem "pg"
|
||||
|
||||
# rails
|
||||
gem 'scenic'
|
||||
gem 'scenic-mysql_adapter'
|
||||
gem "scenic"
|
||||
gem "scenic-mysql_adapter"
|
||||
gem "activerecord-typedstore"
|
||||
gem 'sprockets-rails', '2.3.3'
|
||||
gem "sprockets-rails", "2.3.3"
|
||||
|
||||
# js
|
||||
gem "jquery-rails", "~> 4.3"
|
||||
|
@ -35,33 +35,30 @@ gem "htmlentities"
|
|||
gem "commonmarker", ">= 0.23.6"
|
||||
|
||||
# perf
|
||||
gem 'flamegraph'
|
||||
gem 'memory_profiler'
|
||||
gem 'rack-mini-profiler'
|
||||
gem 'stackprof'
|
||||
gem "flamegraph"
|
||||
gem "memory_profiler"
|
||||
gem "rack-mini-profiler"
|
||||
gem "stackprof"
|
||||
|
||||
gem "oauth" # for twitter-posting bot
|
||||
gem "mail" # for parsing incoming mail
|
||||
gem "ruumba" # tests views
|
||||
gem "sitemap_generator" # for better search engine indexing
|
||||
gem "svg-graph", require: 'SVG/Graph/TimeSeries' # for charting, note workaround in lib/time_series.rb
|
||||
gem 'transaction_retry' # mitigate https://github.com/lobsters/lobsters-ansible/issues/39
|
||||
gem 'rack-attack' # rate-limiting
|
||||
gem "svg-graph", require: "SVG/Graph/TimeSeries" # for charting, note workaround in lib/time_series.rb
|
||||
gem "transaction_retry" # mitigate https://github.com/lobsters/lobsters-ansible/issues/39
|
||||
gem "rack-attack" # rate-limiting
|
||||
|
||||
group :test, :development do
|
||||
gem 'benchmark-perf'
|
||||
gem 'capybara'
|
||||
gem 'database_cleaner'
|
||||
gem "benchmark-perf"
|
||||
gem "capybara"
|
||||
gem "database_cleaner"
|
||||
gem "listen"
|
||||
gem 'rspec-rails', '~> 6.0.0.rc1'
|
||||
gem "rspec-rails", "~> 6.0.0.rc1"
|
||||
gem "factory_bot_rails"
|
||||
gem "rubocop", "0.81", require: false
|
||||
gem "rubocop-rails", require: false
|
||||
gem "rubocop-rspec", require: false
|
||||
gem "standard"
|
||||
gem "faker"
|
||||
gem "byebug"
|
||||
gem "rb-readline"
|
||||
gem "vcr"
|
||||
gem "webmock" # used to support vcr
|
||||
gem 'simplecov', require: false
|
||||
gem "simplecov", require: false
|
||||
end
|
||||
|
|
53
Gemfile.lock
53
Gemfile.lock
|
@ -75,6 +75,7 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 6.0)
|
||||
afm (0.2.2)
|
||||
ast (2.4.2)
|
||||
base64 (0.1.1)
|
||||
bcrypt (3.1.18)
|
||||
benchmark-perf (0.6.0)
|
||||
builder (3.2.4)
|
||||
|
@ -125,12 +126,13 @@ GEM
|
|||
htmlentities (4.3.4)
|
||||
i18n (1.13.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jaro_winkler (1.5.4)
|
||||
jquery-rails (4.5.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.6.3)
|
||||
language_server-protocol (3.17.0.3)
|
||||
lint_roller (1.1.0)
|
||||
listen (3.8.0)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
|
@ -172,8 +174,9 @@ GEM
|
|||
oauth-tty (1.0.5)
|
||||
version_gem (~> 1.1, >= 1.1.1)
|
||||
parallel (1.23.0)
|
||||
parser (3.2.2.1)
|
||||
parser (3.2.2.3)
|
||||
ast (~> 2.4.1)
|
||||
racc
|
||||
pdf-reader (2.11.0)
|
||||
Ascii85 (~> 1.0)
|
||||
afm (~> 0.2.1)
|
||||
|
@ -247,24 +250,25 @@ GEM
|
|||
rspec-mocks (~> 3.11)
|
||||
rspec-support (~> 3.11)
|
||||
rspec-support (3.12.0)
|
||||
rubocop (0.81.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
rubocop (1.56.2)
|
||||
base64 (~> 0.1.1)
|
||||
json (~> 2.3)
|
||||
language_server-protocol (>= 3.17.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
parser (>= 3.2.2.3)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
rexml
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml (>= 3.2.5, < 4.0)
|
||||
rubocop-ast (>= 1.28.1, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-rails (2.5.2)
|
||||
activesupport
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
rubocop-rspec (1.41.0)
|
||||
rubocop (>= 0.68.1)
|
||||
unicode-display_width (>= 2.4.0, < 3.0)
|
||||
rubocop-ast (1.29.0)
|
||||
parser (>= 3.2.1.0)
|
||||
rubocop-performance (1.19.0)
|
||||
rubocop (>= 1.7.0, < 2.0)
|
||||
rubocop-ast (>= 0.4.0)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruumba (0.1.17)
|
||||
rubocop
|
||||
scenic (1.7.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
|
@ -290,6 +294,18 @@ GEM
|
|||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
stackprof (0.2.25)
|
||||
standard (1.31.0)
|
||||
language_server-protocol (~> 3.17.0.2)
|
||||
lint_roller (~> 1.0)
|
||||
rubocop (~> 1.56.0)
|
||||
standard-custom (~> 1.0.0)
|
||||
standard-performance (~> 1.2)
|
||||
standard-custom (1.0.2)
|
||||
lint_roller (~> 1.0)
|
||||
rubocop (~> 1.50)
|
||||
standard-performance (1.2.0)
|
||||
lint_roller (~> 1.1)
|
||||
rubocop-performance (~> 1.19.0)
|
||||
svg-graph (2.2.2)
|
||||
thor (1.2.1)
|
||||
timeout (0.3.2)
|
||||
|
@ -303,7 +319,7 @@ GEM
|
|||
concurrent-ruby (~> 1.0)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unicode-display_width (1.8.0)
|
||||
unicode-display_width (2.4.2)
|
||||
vcr (6.1.0)
|
||||
version_gem (1.1.2)
|
||||
webmock (3.18.1)
|
||||
|
@ -352,16 +368,13 @@ DEPENDENCIES
|
|||
rotp
|
||||
rqrcode
|
||||
rspec-rails (~> 6.0.0.rc1)
|
||||
rubocop (= 0.81)
|
||||
rubocop-rails
|
||||
rubocop-rspec
|
||||
ruumba
|
||||
scenic
|
||||
scenic-mysql_adapter
|
||||
simplecov
|
||||
sitemap_generator
|
||||
sprockets-rails (= 2.3.3)
|
||||
stackprof
|
||||
standard
|
||||
svg-graph
|
||||
transaction_retry
|
||||
uglifier (>= 1.3.0)
|
||||
|
|
4
Rakefile
4
Rakefile
|
@ -1,10 +1,10 @@
|
|||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
require File.expand_path("../config/application", __FILE__)
|
||||
|
||||
if Rails.env.development? || Rails.env.test?
|
||||
require 'rubocop/rake_task'
|
||||
require "rubocop/rake_task"
|
||||
RuboCop::RakeTask.new
|
||||
end
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ class AboutController < ApplicationController
|
|||
@title = "Resource Not Found"
|
||||
render action: "404", status: 404
|
||||
rescue ActionView::MissingTemplate
|
||||
render status: 404, html: (
|
||||
render status: 404, html:
|
||||
"<h1>404</h1><p>Resource not found</p>"
|
||||
).html_safe, layout: 'application'
|
||||
.html_safe, layout: "application"
|
||||
end
|
||||
|
||||
def about
|
||||
|
@ -16,30 +16,26 @@ class AboutController < ApplicationController
|
|||
@title = "About"
|
||||
render action: "about"
|
||||
rescue ActionView::MissingTemplate
|
||||
render layout: 'application', html: ("<h1>A mystery.")
|
||||
render layout: "application", html: "<h1>A mystery."
|
||||
end
|
||||
raise "Seriously, write your own about page." if @homeabout
|
||||
end
|
||||
|
||||
def chat
|
||||
begin
|
||||
@title = "Chat"
|
||||
render action: "chat"
|
||||
rescue ActionView::MissingTemplate
|
||||
render html: ("<h1>Don't speak. I know what you're thinking.</h1>"),
|
||||
layout: 'application'
|
||||
end
|
||||
@title = "Chat"
|
||||
render action: "chat"
|
||||
rescue ActionView::MissingTemplate
|
||||
render html: "<h1>Don't speak. I know what you're thinking.</h1>",
|
||||
layout: "application"
|
||||
end
|
||||
|
||||
def privacy
|
||||
begin
|
||||
@title = "Privacy Policy"
|
||||
render action: "privacy"
|
||||
rescue ActionView::MissingTemplate
|
||||
render layout: 'application', html: <<-HTML
|
||||
@title = "Privacy Policy"
|
||||
render action: "privacy"
|
||||
rescue ActionView::MissingTemplate
|
||||
render layout: "application", html: <<-HTML
|
||||
<blockquote>You have zero privacy anyway. Get over it.</blockquote>
|
||||
<p>Scott McNealy</p>
|
||||
HTML
|
||||
end
|
||||
HTML
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,11 +17,11 @@ class ApplicationController < ActionController::Base
|
|||
CACHE_PAGE = proc { false && @user.blank? && cookies[TAG_FILTER_COOKIE].blank? }
|
||||
|
||||
rescue_from ActionController::UnknownFormat do
|
||||
render plain: '404 Not Found', status: :not_found, content_type: 'text/plain'
|
||||
render plain: "404 Not Found", status: :not_found, content_type: "text/plain"
|
||||
end
|
||||
rescue_from ActionDispatch::Http::MimeNegotiation::InvalidType do
|
||||
render plain: 'fix the mime type in your HTTP_ACCEPT header',
|
||||
status: :bad_request, content_type: 'text/plain'
|
||||
render plain: "fix the mime type in your HTTP_ACCEPT header",
|
||||
status: :bad_request, content_type: "text/plain"
|
||||
end
|
||||
|
||||
def agent_is_spider?
|
||||
|
@ -38,8 +38,8 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
if session[:u] &&
|
||||
(user = User.find_by(:session_token => session[:u].to_s)) &&
|
||||
user.is_active?
|
||||
(user = User.find_by(session_token: session[:u].to_s)) &&
|
||||
user.is_active?
|
||||
@user = user
|
||||
end
|
||||
Rails.logger.info(
|
||||
|
@ -61,16 +61,14 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def find_user_from_rss_token
|
||||
if !@user && request[:format] == "rss" && params[:token].to_s.present?
|
||||
@user = User.where(:rss_token => params[:token].to_s).first
|
||||
@user = User.where(rss_token: params[:token].to_s).first
|
||||
end
|
||||
end
|
||||
|
||||
def flag_warning
|
||||
@flag_warning_int ||= time_interval('1m')
|
||||
@flag_warning_int ||= time_interval("1m")
|
||||
return false if Rails.env.development? # expensive because Rails doesn't cache in dev
|
||||
@show_flag_warning ||= (
|
||||
@user && !!FlaggedCommenters.new(@flag_warning_int[:param], 1.day).check_list_for(@user)
|
||||
)
|
||||
@show_flag_warning ||= @user && !!FlaggedCommenters.new(@flag_warning_int[:param], 1.day).check_list_for(@user)
|
||||
end
|
||||
|
||||
def heinous_inline_partials
|
||||
|
@ -78,7 +76,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def mini_profiler
|
||||
if @user && @user.is_moderator?
|
||||
if @user&.is_moderator?
|
||||
Rack::MiniProfiler.authorize_request
|
||||
end
|
||||
end
|
||||
|
@ -92,11 +90,11 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# https://web.archive.org/web/20180108083712/http://umaine.edu/lobsterinstitute/files/2011/12/LobsterColorsWeb.pdf
|
||||
def set_traffic_style
|
||||
@traffic_intensity = '?'
|
||||
@traffic_style = 'background-color: #ac130d;'
|
||||
@traffic_intensity = "?"
|
||||
@traffic_style = "background-color: #ac130d;"
|
||||
return true if Rails.application.read_only? ||
|
||||
agent_is_spider? ||
|
||||
%w{json rss}.include?(params[:format])
|
||||
agent_is_spider? ||
|
||||
%w[json rss].include?(params[:format])
|
||||
if (skip = TrafficHelper.novelty_logo)
|
||||
@traffic_style = skip
|
||||
return
|
||||
|
@ -104,7 +102,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
@traffic_intensity = TrafficHelper.cached_current_intensity
|
||||
# map intensity to 80-255 so there's always a little red
|
||||
hex = sprintf('%02x', (@traffic_intensity * 1.75 + 80).round)
|
||||
hex = sprintf("%02x", (@traffic_intensity * 1.75 + 80).round)
|
||||
@traffic_style = "background-color: ##{hex}0000;"
|
||||
return true unless @user
|
||||
|
||||
|
@ -115,7 +113,7 @@ class ApplicationController < ActionController::Base
|
|||
[6, :yellow, "background-color: ##{hex}#{hex}00;"],
|
||||
[3, :calico, "background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAC4jAAAuIwF4pT92AAAABmJLR0QA/wD/AP+gvaeTAAACpElEQVQYGQXBWW8bVRgA0Hu/u814NsdxGsUxztJUzaJSVS1CCCTKE7zxxiP/gH+I+lKKQEVCLUlJ5YTsU8f2eJvxbHfjHLz7sKeU2mhNfvl579vnEPKUEUJxji1YoBaIob4m6+cX8Our/m99TBwmpKGV0hZjz+EO06FHOAKlFNKIcE+p8HYo3rwd/Xk8m+pVEjW4EzIFdjopVVG6Nt1ocpc3ALnIhqMRnF3afz6qd2flcMElAOWu3nm4tr6xMh2cyDpprqwBwdjQ0Uz9fXJ9el0lRTOekVQ13DCKvCXVWO7sdl6+/Gp01cbpv/uHPcqGlUKIr50NZq+Pi7mymrt+GOxvbz9+zKjS5OLi1uV/ZeObAC3un4qgt+c0bL8/v5qJ64WbaocIPC2HzbaDGCOeF0ySJI7vzz9eLuZFpfDq2lZWmd/fx6/e3twkuDIiL3KCysV83D+/xZ/1uhYXjuC6lg0BVk2fHPXcQMWD7L+bvJCettzhEPpgzRIxjbe3u6VMCcXWMEY5E9qisqo1QlRLjDVwxqxSQpBW5CFnSB2PaulyRleCSEtNhDPLltjkdQWYCC+gDVF6pHzU8z8/7IKgVFaVtshSWaQxA2Osz4FiokTQrLRrQCLIXzxr/fT94cFWVFlGmXExNQznnbbzaGcVgb0bJqO8kS5BzmusNAMdYN5mPlsihRh5sL7pRYHXQM+OOj/+8MV3Xx+2mmQ8qQZxkmfKSGXq1Odyt9MShByffKLgcc3JsqrHk3Eyumu6LbkYFHcfsjttSaR5OFP29H755nzw/sq8+yMh/sYKYiRL76dxzOqr9RBsmeisnCWqVlZaMIyxgC5U9eEy7p9awj0ByDiQ7XfgmyfRl0fRwZbb7bLVNmOOXynADDY3Hxzs7+WL5XSY/w/0MGrkMYhXjAAAAABJRU5ErkJggg==) no-repeat center"],
|
||||
[2, :split, "background: linear-gradient(90deg, ##{hex}0000 50%, #0000#{hex} 50%)"],
|
||||
[2, :albino, "filter: invert(100%);"],
|
||||
[2, :albino, "filter: invert(100%);"]
|
||||
# rubocop:enable Layout/LineLength,
|
||||
].each do |cumulative_odds, name, style|
|
||||
break unless rand(cumulative_odds) == 0
|
||||
|
@ -147,7 +145,7 @@ class ApplicationController < ActionController::Base
|
|||
true
|
||||
else
|
||||
flash[:error] = "You are not authorized to access that resource."
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -160,7 +158,7 @@ class ApplicationController < ActionController::Base
|
|||
true
|
||||
else
|
||||
flash[:error] = "You are not authorized to access that resource."
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -169,13 +167,13 @@ class ApplicationController < ActionController::Base
|
|||
if @user
|
||||
true
|
||||
else
|
||||
render :plain => "not logged in", :status => 400
|
||||
return false
|
||||
render plain: "not logged in", status: 400
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def require_no_user_or_redirect
|
||||
return redirect_to "/" if @user
|
||||
redirect_to "/" if @user
|
||||
end
|
||||
|
||||
def show_title_h1
|
||||
|
@ -184,7 +182,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def tags_filtered_by_cookie
|
||||
@_tags_filtered ||= Tag.where(
|
||||
:tag => cookies[TAG_FILTER_COOKIE].to_s.split(",")
|
||||
tag: cookies[TAG_FILTER_COOKIE].to_s.split(",")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class AvatarsController < ApplicationController
|
||||
before_action :require_logged_in_user, :only => [:expire]
|
||||
before_action :require_logged_in_user, only: [:expire]
|
||||
|
||||
ALLOWED_SIZES = [16, 32, 100, 200].freeze
|
||||
|
||||
|
@ -8,20 +8,18 @@ class AvatarsController < ApplicationController
|
|||
def expire
|
||||
expired = 0
|
||||
|
||||
Dir.entries(CACHE_DIR).select {|f|
|
||||
Dir.entries(CACHE_DIR).select { |f|
|
||||
f.match(/\A#{@user.username}-(\d+)\.png\z/)
|
||||
}.each do |f|
|
||||
begin
|
||||
Rails.logger.debug "Expiring #{f}"
|
||||
File.unlink("#{CACHE_DIR}/#{f}")
|
||||
expired += 1
|
||||
rescue => e
|
||||
Rails.logger.error "Failed expiring #{f}: #{e}"
|
||||
end
|
||||
Rails.logger.debug "Expiring #{f}"
|
||||
File.unlink("#{CACHE_DIR}/#{f}")
|
||||
expired += 1
|
||||
rescue => e
|
||||
Rails.logger.error "Failed expiring #{f}: #{e}"
|
||||
end
|
||||
|
||||
flash[:success] = "Your avatar cache has been purged of #{'file'.pluralize(expired)}"
|
||||
return redirect_to "/settings"
|
||||
flash[:success] = "Your avatar cache has been purged of #{"file".pluralize(expired)}"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -36,7 +34,7 @@ class AvatarsController < ApplicationController
|
|||
raise ActionController::RoutingError.new("invalid user name")
|
||||
end
|
||||
|
||||
u = User.where(:username => username).first!
|
||||
u = User.where(username: username).first!
|
||||
|
||||
if !(av = u.fetched_avatar(size))
|
||||
raise ActionController::RoutingError.new("failed fetching avatar")
|
||||
|
@ -53,6 +51,6 @@ class AvatarsController < ApplicationController
|
|||
File.rename("#{CACHE_DIR}/.#{u.username}-#{size}.png", "#{CACHE_DIR}/#{u.username}-#{size}.png")
|
||||
|
||||
response.headers["Expires"] = 1.hour.from_now.httpdate
|
||||
send_data av, :type => "image/png", :disposition => "inline"
|
||||
send_data av, type: "image/png", disposition: "inline"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,33 +12,33 @@ class CategoriesController < ApplicationController
|
|||
flash[:success] = "Category #{category.category} has been created"
|
||||
redirect_to tags_path
|
||||
else
|
||||
flash[:error] = "New category not created: #{category.errors.full_messages.join(', ')}"
|
||||
flash[:error] = "New category not created: #{category.errors.full_messages.join(", ")}"
|
||||
redirect_to new_category_path
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@category = Category.where(:category => params[:category_name]).first!
|
||||
@category = Category.where(category: params[:category_name]).first!
|
||||
@title = "Edit Category"
|
||||
end
|
||||
|
||||
def update
|
||||
category = Category.where(:category => params[:category_name]).first!
|
||||
category = Category.where(category: params[:category_name]).first!
|
||||
if category.update(category_params)
|
||||
flash[:success] = "Category #{category.category} has been updated"
|
||||
redirect_to tags_path
|
||||
else
|
||||
flash[:error] = "Category not updated: #{category.errors.full_messages.join(', ')}"
|
||||
flash[:error] = "Category not updated: #{category.errors.full_messages.join(", ")}"
|
||||
redirect_to edit_category_path
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def category_params
|
||||
params.require(:category).permit(
|
||||
:category_name,
|
||||
:category,
|
||||
:category
|
||||
).merge(edit_user_id: @user.id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,43 +4,43 @@ class CommentsController < ApplicationController
|
|||
caches_page :index, :threads, if: CACHE_PAGE
|
||||
|
||||
before_action :require_logged_in_user_or_400,
|
||||
:only => [:create, :preview, :upvote, :flag, :unvote]
|
||||
before_action :require_logged_in_user, :only => [:upvoted]
|
||||
only: [:create, :preview, :upvote, :flag, :unvote]
|
||||
before_action :require_logged_in_user, only: [:upvoted]
|
||||
before_action :flag_warning, only: [:user_threads]
|
||||
before_action :show_title_h1
|
||||
|
||||
# for rss feeds, load the user's tag filters if a token is passed
|
||||
before_action :find_user_from_rss_token, :only => [:index]
|
||||
before_action :find_user_from_rss_token, only: [:index]
|
||||
|
||||
def create
|
||||
if !(story = Story.where(:short_id => params[:story_id]).first) ||
|
||||
story.is_gone?
|
||||
return render :plain => "can't find story", :status => 400
|
||||
if !(story = Story.where(short_id: params[:story_id]).first) ||
|
||||
story.is_gone?
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
comment = story.comments.build
|
||||
comment.comment = params[:comment].to_s
|
||||
comment.user = @user
|
||||
|
||||
if params[:hat_id] && @user.wearable_hats.where(:id => params[:hat_id])
|
||||
if params[:hat_id] && @user.wearable_hats.where(id: params[:hat_id])
|
||||
comment.hat_id = params[:hat_id]
|
||||
end
|
||||
|
||||
if params[:parent_comment_short_id].present?
|
||||
# includes parent story_id to ensure this comment's story_id matches
|
||||
comment.parent_comment =
|
||||
Comment.find_by(:story_id => story.id, :short_id => params[:parent_comment_short_id])
|
||||
Comment.find_by(story_id: story.id, short_id: params[:parent_comment_short_id])
|
||||
if !comment.parent_comment
|
||||
return render :json => { :error => "invalid parent comment", :status => 400 }
|
||||
return render json: {error: "invalid parent comment", status: 400}
|
||||
end
|
||||
end
|
||||
|
||||
# sometimes on slow connections people resubmit; silently accept it
|
||||
if (already = Comment.find_by(user: comment.user,
|
||||
story: comment.story,
|
||||
parent_comment_id: comment.parent_comment_id,
|
||||
comment: comment.comment))
|
||||
self.render_created_comment(already)
|
||||
story: comment.story,
|
||||
parent_comment_id: comment.parent_comment_id,
|
||||
comment: comment.comment))
|
||||
render_created_comment(already)
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -50,11 +50,11 @@ class CommentsController < ApplicationController
|
|||
end
|
||||
|
||||
if comment.valid? && params[:preview].blank? && ActiveRecord::Base.transaction { comment.save }
|
||||
comment.current_vote = { :vote => 1 }
|
||||
self.render_created_comment(comment)
|
||||
comment.current_vote = {vote: 1}
|
||||
render_created_comment(comment)
|
||||
else
|
||||
comment.score = 1
|
||||
comment.current_vote = { :vote => 1 }
|
||||
comment.current_vote = {vote: 1}
|
||||
|
||||
preview comment
|
||||
end
|
||||
|
@ -62,8 +62,8 @@ class CommentsController < ApplicationController
|
|||
|
||||
def render_created_comment(comment)
|
||||
if request.xhr?
|
||||
render :partial => "comments/postedreply", :layout => false,
|
||||
:content_type => "text/html", :locals => { :comment => comment }
|
||||
render partial: "comments/postedreply", layout: false,
|
||||
content_type: "text/html", locals: {comment: comment}
|
||||
else
|
||||
redirect_to comment.path
|
||||
end
|
||||
|
@ -71,46 +71,46 @@ class CommentsController < ApplicationController
|
|||
|
||||
def show
|
||||
if !((comment = find_comment) && comment.is_editable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
render :partial => "comment",
|
||||
:layout => false,
|
||||
:content_type => "text/html",
|
||||
:locals => {
|
||||
:comment => comment,
|
||||
:show_tree_lines => params[:show_tree_lines],
|
||||
}
|
||||
render partial: "comment",
|
||||
layout: false,
|
||||
content_type: "text/html",
|
||||
locals: {
|
||||
comment: comment,
|
||||
show_tree_lines: params[:show_tree_lines]
|
||||
}
|
||||
end
|
||||
|
||||
def show_short_id
|
||||
if !(comment = find_comment)
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
render :json => comment.as_json
|
||||
render json: comment.as_json
|
||||
end
|
||||
|
||||
def redirect_from_short_id
|
||||
if (comment = find_comment)
|
||||
return redirect_to comment.path
|
||||
redirect_to comment.path
|
||||
else
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
render plain: "can't find comment", status: 400
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
if !((comment = find_comment) && comment.is_editable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
render :partial => "commentbox", :layout => false,
|
||||
:content_type => "text/html", :locals => { :comment => comment }
|
||||
render partial: "commentbox", layout: false,
|
||||
content_type: "text/html", locals: {comment: comment}
|
||||
end
|
||||
|
||||
def reply
|
||||
if !(parent_comment = find_comment)
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
@story = parent_comment.story
|
||||
|
@ -122,15 +122,15 @@ class CommentsController < ApplicationController
|
|||
if !parent_comment.depth_permits_reply?
|
||||
ModNote.tattle_on_max_depth_limit(@user, parent_comment)
|
||||
if request.xhr?
|
||||
render partial: 'too_deep'
|
||||
render partial: "too_deep"
|
||||
else
|
||||
render '_too_deep'
|
||||
render "_too_deep"
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if request.xhr?
|
||||
render partial: 'commentbox', locals: { comment: comment }
|
||||
render partial: "commentbox", locals: {comment: comment}
|
||||
else
|
||||
parents = comment.parents.with_thread_attributes.for_presentation
|
||||
|
||||
|
@ -140,55 +140,55 @@ class CommentsController < ApplicationController
|
|||
c.current_vote = @votes[c.id]
|
||||
end
|
||||
end
|
||||
render '_commentbox', locals: {
|
||||
render "_commentbox", locals: {
|
||||
comment: comment,
|
||||
parents: parents,
|
||||
parents: parents
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def delete
|
||||
if !((comment = find_comment) && comment.is_deletable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
comment.delete_for_user(@user, params[:reason])
|
||||
|
||||
render :partial => "comment", :layout => false,
|
||||
:content_type => "text/html", :locals => { :comment => comment }
|
||||
render partial: "comment", layout: false,
|
||||
content_type: "text/html", locals: {comment: comment}
|
||||
end
|
||||
|
||||
def undelete
|
||||
if !((comment = find_comment) && comment.is_undeletable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
comment.undelete_for_user(@user)
|
||||
|
||||
render :partial => "comment", :layout => false,
|
||||
:content_type => "text/html", :locals => { :comment => comment }
|
||||
render partial: "comment", layout: false,
|
||||
content_type: "text/html", locals: {comment: comment}
|
||||
end
|
||||
|
||||
def disown
|
||||
if !((comment = find_comment) && comment.is_disownable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
InactiveUser.disown! comment
|
||||
comment = find_comment
|
||||
|
||||
render :partial => "comment", :layout => false,
|
||||
:content_type => "text/html", :locals => { :comment => comment }
|
||||
render partial: "comment", layout: false,
|
||||
content_type: "text/html", locals: {comment: comment}
|
||||
end
|
||||
|
||||
def update
|
||||
if !((comment = find_comment) && comment.is_editable_by_user?(@user))
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
comment.comment = params[:comment]
|
||||
comment.hat_id = nil
|
||||
if params[:hat_id] && @user.wearable_hats.where(:id => params[:hat_id])
|
||||
if params[:hat_id] && @user.wearable_hats.where(id: params[:hat_id])
|
||||
comment.hat_id = params[:hat_id]
|
||||
end
|
||||
|
||||
|
@ -196,12 +196,12 @@ class CommentsController < ApplicationController
|
|||
votes = Vote.comment_votes_by_user_for_comment_ids_hash(@user.id, [comment.id])
|
||||
comment.current_vote = votes[comment.id]
|
||||
|
||||
render :partial => "comments/comment",
|
||||
:layout => false,
|
||||
:content_type => "text/html",
|
||||
:locals => { :comment => comment, :show_tree_lines => params[:show_tree_lines] }
|
||||
render partial: "comments/comment",
|
||||
layout: false,
|
||||
content_type: "text/html",
|
||||
locals: {comment: comment, show_tree_lines: params[:show_tree_lines]}
|
||||
else
|
||||
comment.current_vote = { :vote => 1 }
|
||||
comment.current_vote = {vote: 1}
|
||||
|
||||
preview comment
|
||||
end
|
||||
|
@ -209,52 +209,52 @@ class CommentsController < ApplicationController
|
|||
|
||||
def unvote
|
||||
if !(comment = find_comment) || comment.is_gone?
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
0, comment.story_id, comment.id, @user.id, nil
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def upvote
|
||||
if !(comment = find_comment) || comment.is_gone?
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
1, comment.story_id, comment.id, @user.id, params[:reason]
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def flag
|
||||
if !(comment = find_comment) || comment.is_gone?
|
||||
return render :plain => "can't find comment", :status => 400
|
||||
return render plain: "can't find comment", status: 400
|
||||
end
|
||||
|
||||
if !Vote::COMMENT_REASONS[params[:reason]]
|
||||
return render :plain => "invalid reason", :status => 400
|
||||
return render plain: "invalid reason", status: 400
|
||||
end
|
||||
|
||||
if !@user.can_flag?(comment)
|
||||
return render :plain => "not permitted to flag", :status => 400
|
||||
return render plain: "not permitted to flag", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
-1, comment.story_id, comment.id, @user.id, params[:reason]
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def index
|
||||
@rss_link ||= {
|
||||
:title => "RSS 2.0 - Newest Comments",
|
||||
:href => "/comments.rss" + (@user ? "?token=#{@user.rss_token}" : ""),
|
||||
title: "RSS 2.0 - Newest Comments",
|
||||
href: "/comments.rss" + (@user ? "?token=#{@user.rss_token}" : "")
|
||||
}
|
||||
|
||||
@title = "Newest Comments"
|
||||
|
@ -262,15 +262,15 @@ class CommentsController < ApplicationController
|
|||
@page = params[:page].to_i
|
||||
if @page == 0
|
||||
@page = 1
|
||||
elsif @page < 0 || @page > (2 ** 32)
|
||||
elsif @page < 0 || @page > (2**32)
|
||||
raise ActionController::RoutingError.new("page out of bounds")
|
||||
end
|
||||
|
||||
@comments = Comment.accessible_to_user(@user)
|
||||
.not_on_story_hidden_by(@user)
|
||||
.order("id DESC")
|
||||
.includes(:user, :hat, :story => :user)
|
||||
.joins(:story).where.not(stories: { is_deleted: true })
|
||||
.includes(:user, :hat, story: :user)
|
||||
.joins(:story).where.not(stories: {is_deleted: true})
|
||||
.limit(COMMENTS_PER_PAGE)
|
||||
.offset((@page - 1) * COMMENTS_PER_PAGE)
|
||||
|
||||
|
@ -285,39 +285,39 @@ class CommentsController < ApplicationController
|
|||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.html { render action: "index" }
|
||||
format.rss {
|
||||
if @user && params[:token].present?
|
||||
@title = "Private comments feed for #{@user.username}"
|
||||
end
|
||||
|
||||
render :action => "index", :layout => false
|
||||
render action: "index", layout: false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def upvoted
|
||||
@rss_link ||= {
|
||||
:title => "RSS 2.0 - Newest Comments",
|
||||
:href => upvoted_comments_path(format: :rss) + (@user ? "?token=#{@user.rss_token}" : ""),
|
||||
title: "RSS 2.0 - Newest Comments",
|
||||
href: upvoted_comments_path(format: :rss) + (@user ? "?token=#{@user.rss_token}" : "")
|
||||
}
|
||||
|
||||
@title = "Upvoted Comments"
|
||||
@above = 'saved/subnav'
|
||||
@above = "saved/subnav"
|
||||
|
||||
@page = params[:page].to_i
|
||||
if @page == 0
|
||||
@page = 1
|
||||
elsif @page < 0 || @page > (2 ** 32)
|
||||
elsif @page < 0 || @page > (2**32)
|
||||
raise ActionController::RoutingError.new("page out of bounds")
|
||||
end
|
||||
|
||||
@comments = Comment.accessible_to_user(@user)
|
||||
.where.not(user_id: @user.id)
|
||||
.order("id DESC")
|
||||
.includes(:user, :hat, :story => :user)
|
||||
.joins(:votes).where(votes: { user_id: @user.id, vote: 1 })
|
||||
.joins(:story).where.not(stories: { is_deleted: true })
|
||||
.includes(:user, :hat, story: :user)
|
||||
.joins(:votes).where(votes: {user_id: @user.id, vote: 1})
|
||||
.joins(:story).where.not(stories: {is_deleted: true})
|
||||
.limit(COMMENTS_PER_PAGE)
|
||||
.offset((@page - 1) * COMMENTS_PER_PAGE)
|
||||
|
||||
|
@ -335,7 +335,7 @@ class CommentsController < ApplicationController
|
|||
@title = "Upvoted comments feed for #{@user.username}"
|
||||
end
|
||||
|
||||
render :action => "index", :layout => false
|
||||
render action: "index", layout: false
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -368,30 +368,30 @@ class CommentsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def preview(comment)
|
||||
comment.previewing = true
|
||||
comment.is_deleted = false # show normal preview for deleted comments
|
||||
|
||||
render :partial => "comments/commentbox",
|
||||
:layout => false,
|
||||
:content_type => "text/html",
|
||||
:locals => {
|
||||
:comment => comment,
|
||||
:show_comment => comment,
|
||||
:show_tree_lines => params[:show_tree_lines],
|
||||
}
|
||||
render partial: "comments/commentbox",
|
||||
layout: false,
|
||||
content_type: "text/html",
|
||||
locals: {
|
||||
comment: comment,
|
||||
show_comment: comment,
|
||||
show_tree_lines: params[:show_tree_lines]
|
||||
}
|
||||
end
|
||||
|
||||
def find_comment
|
||||
comment = Comment.where(short_id: params[:id]).first
|
||||
# convenience to use PK (from external queries) without generally permitting enumeration:
|
||||
comment ||= Comment.find(params[:id]) if @user && @user.is_admin?
|
||||
comment ||= Comment.find(params[:id]) if @user&.is_admin?
|
||||
|
||||
if @user && comment
|
||||
comment.current_vote = Vote.where(:user_id => @user.id,
|
||||
:story_id => comment.story_id, :comment_id => comment.id).first
|
||||
comment.current_vote = Vote.where(user_id: @user.id,
|
||||
story_id: comment.story_id, comment_id: comment.id).first
|
||||
end
|
||||
|
||||
comment
|
||||
|
|
|
@ -2,7 +2,8 @@ class DomainsController < ApplicationController
|
|||
before_action :require_logged_in_admin
|
||||
before_action :find_domain, only: [:edit, :update]
|
||||
|
||||
def edit; end
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
if domain_params[:banned_reason].present?
|
||||
|
@ -19,7 +20,7 @@ class DomainsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def domain_params
|
||||
params.require(:domain).permit(:banned_reason)
|
||||
|
@ -30,14 +31,14 @@ private
|
|||
end
|
||||
|
||||
def path_of_form(domain)
|
||||
prms = { name: domain.domain }
|
||||
prms = {name: domain.domain}
|
||||
domain.banned_at ? unban_domain_path(prms) : update_domain_path(prms)
|
||||
end
|
||||
|
||||
helper_method :path_of_form
|
||||
|
||||
def caption_of_button(domain)
|
||||
domain.banned_at ? 'Unban' : 'Ban'
|
||||
domain.banned_at ? "Unban" : "Ban"
|
||||
end
|
||||
|
||||
helper_method :caption_of_button
|
||||
|
|
|
@ -6,25 +6,25 @@ class FiltersController < ApplicationController
|
|||
@title = "Filtered Tags"
|
||||
|
||||
@categories = Category.all
|
||||
.order('category asc, tags.tag asc')
|
||||
.eager_load(:tags)
|
||||
.references(:tags)
|
||||
.where('tags.active = true')
|
||||
.order("category asc, tags.tag asc")
|
||||
.eager_load(:tags)
|
||||
.references(:tags)
|
||||
.where("tags.active = true")
|
||||
|
||||
# perf: three queries is much faster than joining, grouping on tags.id for counts
|
||||
@story_counts = Tagging.group(:tag_id).count
|
||||
@filter_counts = TagFilter.group(:tag_id).count
|
||||
|
||||
if @user
|
||||
@filtered_tags = @user.tag_filter_tags.index_by(&:id)
|
||||
@filtered_tags = if @user
|
||||
@user.tag_filter_tags.index_by(&:id)
|
||||
else
|
||||
@filtered_tags = tags_filtered_by_cookie.index_by(&:id)
|
||||
tags_filtered_by_cookie.index_by(&:id)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
new_tags = Tag.active.where(:tag => (params[:tags] || {}).keys).to_a
|
||||
new_tags.keep_if {|t| t.user_can_filter? @user }
|
||||
new_tags = Tag.active.where(tag: (params[:tags] || {}).keys).to_a
|
||||
new_tags.keep_if { |t| t.user_can_filter? @user }
|
||||
|
||||
if @user
|
||||
@user.tag_filter_tags = new_tags
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class HatsController < ApplicationController
|
||||
before_action :require_logged_in_user, :except => [:index]
|
||||
before_action :require_logged_in_moderator, :except => [:build_request, :index, :create_request]
|
||||
before_action :require_logged_in_user, except: [:index]
|
||||
before_action :require_logged_in_moderator, except: [:build_request, :index, :create_request]
|
||||
before_action :show_title_h1
|
||||
|
||||
def build_request
|
||||
|
@ -32,7 +32,7 @@ class HatsController < ApplicationController
|
|||
return redirect_to "/hats"
|
||||
end
|
||||
|
||||
render :action => "build_request"
|
||||
render action: "build_request"
|
||||
end
|
||||
|
||||
def requests_index
|
||||
|
@ -49,7 +49,7 @@ class HatsController < ApplicationController
|
|||
|
||||
flash[:success] = "Successfully approved hat request."
|
||||
|
||||
return redirect_to "/hats/requests"
|
||||
redirect_to "/hats/requests"
|
||||
end
|
||||
|
||||
def reject_request
|
||||
|
@ -58,6 +58,6 @@ class HatsController < ApplicationController
|
|||
|
||||
flash[:success] = "Successfully rejected hat request."
|
||||
|
||||
return redirect_to "/hats/requests"
|
||||
redirect_to "/hats/requests"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,9 +4,9 @@ class HomeController < ApplicationController
|
|||
caches_page :index, :newest, :newest_by_user, :recent, :top, if: CACHE_PAGE
|
||||
|
||||
# for rss feeds, load the user's tag filters if a token is passed
|
||||
before_action :find_user_from_rss_token, :only => [:index, :newest, :saved, :upvoted]
|
||||
before_action :find_user_from_rss_token, only: [:index, :newest, :saved, :upvoted]
|
||||
before_action { @page = page }
|
||||
before_action :require_logged_in_user, :only => [:hidden, :saved, :upvoted]
|
||||
before_action :require_logged_in_user, only: [:hidden, :saved, :upvoted]
|
||||
before_action :show_title_h1, only: [:top]
|
||||
|
||||
def active
|
||||
|
@ -14,8 +14,8 @@ class HomeController < ApplicationController
|
|||
paginate stories.active
|
||||
}
|
||||
|
||||
@title = 'Active Discussions'
|
||||
@above = 'active'
|
||||
@title = "Active Discussions"
|
||||
@above = "active"
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render action: :index }
|
||||
|
@ -29,9 +29,9 @@ class HomeController < ApplicationController
|
|||
}
|
||||
|
||||
@title = "Hidden Stories"
|
||||
@above = 'saved/subnav'
|
||||
@above = "saved/subnav"
|
||||
|
||||
render :action => "index"
|
||||
render action: "index"
|
||||
end
|
||||
|
||||
def index
|
||||
|
@ -40,31 +40,31 @@ class HomeController < ApplicationController
|
|||
}
|
||||
|
||||
@rss_link ||= {
|
||||
:title => "RSS 2.0",
|
||||
:href => user_token_link("/rss"),
|
||||
title: "RSS 2.0",
|
||||
href: user_token_link("/rss")
|
||||
}
|
||||
@comments_rss_link ||= {
|
||||
:title => "Comments - RSS 2.0",
|
||||
:href => user_token_link("/comments.rss"),
|
||||
title: "Comments - RSS 2.0",
|
||||
href: user_token_link("/comments.rss")
|
||||
}
|
||||
|
||||
@title = ""
|
||||
@root_path = true
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.html { render action: "index" }
|
||||
format.rss {
|
||||
if @user
|
||||
@title = "Private feed for #{@user.username}"
|
||||
render :action => "rss", :layout => false
|
||||
render action: "rss", layout: false
|
||||
else
|
||||
content = Rails.cache.fetch("rss", :expires_in => (60 * 2)) {
|
||||
render_to_string :action => "rss", :layout => false
|
||||
content = Rails.cache.fetch("rss", expires_in: (60 * 2)) {
|
||||
render_to_string action: "rss", layout: false
|
||||
}
|
||||
render :plain => content, :layout => false
|
||||
render plain: content, layout: false
|
||||
end
|
||||
}
|
||||
format.json { render :json => @stories }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -74,23 +74,23 @@ class HomeController < ApplicationController
|
|||
}
|
||||
|
||||
@title = "Newest Stories"
|
||||
@above = 'stories/subnav'
|
||||
@above = "stories/subnav"
|
||||
|
||||
@rss_link = {
|
||||
:title => "RSS 2.0 - Newest Items",
|
||||
:href => user_token_link("/newest.rss"),
|
||||
title: "RSS 2.0 - Newest Items",
|
||||
href: user_token_link("/newest.rss")
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.html { render action: "index" }
|
||||
format.rss {
|
||||
if @user && params[:token].present?
|
||||
@title += " - Private feed for #{@user.username}"
|
||||
end
|
||||
|
||||
render :action => "rss", :layout => false
|
||||
render action: "rss", layout: false
|
||||
}
|
||||
format.json { render :json => @stories }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -101,14 +101,14 @@ class HomeController < ApplicationController
|
|||
|
||||
@title = "Newest Stories by #{by_user.username}"
|
||||
@newest_by_user = by_user
|
||||
@above = 'newest_by_user'
|
||||
@above = "newest_by_user"
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.html { render action: "index" }
|
||||
format.rss {
|
||||
render :action => "rss", :layout => false
|
||||
render action: "rss", layout: false
|
||||
}
|
||||
format.json { render :json => @stories }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,13 +118,13 @@ class HomeController < ApplicationController
|
|||
}
|
||||
|
||||
@title = "Recent Stories"
|
||||
@above = 'stories/subnav'
|
||||
@below = 'recent'
|
||||
@above = "stories/subnav"
|
||||
@below = "recent"
|
||||
|
||||
# our list is unstable because upvoted stories get removed, so point at /newest.rss
|
||||
@rss_link = { :title => "RSS 2.0 - Newest Items", :href => user_token_link("/newest.rss") }
|
||||
@rss_link = {title: "RSS 2.0 - Newest Items", href: user_token_link("/newest.rss")}
|
||||
|
||||
render :action => "index"
|
||||
render action: "index"
|
||||
end
|
||||
|
||||
def saved
|
||||
|
@ -133,47 +133,47 @@ class HomeController < ApplicationController
|
|||
}
|
||||
|
||||
@rss_link ||= {
|
||||
:title => "RSS 2.0",
|
||||
:href => user_token_link("/saved.rss"),
|
||||
title: "RSS 2.0",
|
||||
href: user_token_link("/saved.rss")
|
||||
}
|
||||
|
||||
@title = "Saved Stories"
|
||||
@above = 'saved/subnav'
|
||||
@above = "saved/subnav"
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.html { render action: "index" }
|
||||
format.rss {
|
||||
if @user
|
||||
@title = "Private feed of saved stories for #{@user.username}"
|
||||
end
|
||||
render :action => "rss", :layout => false
|
||||
render action: "rss", layout: false
|
||||
}
|
||||
format.json { render :json => @stories }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
def category
|
||||
category_params = params[:category].split(',')
|
||||
category_params = params[:category].split(",")
|
||||
@categories = Category.where(category: category_params)
|
||||
|
||||
raise ActiveRecord::RecordNotFound unless @categories.length == category_params.length
|
||||
|
||||
@stories, @show_more = get_from_cache(categories: category_params.sort.join(',')) do
|
||||
@stories, @show_more = get_from_cache(categories: category_params.sort.join(",")) do
|
||||
paginate stories.categories(@categories)
|
||||
end
|
||||
|
||||
@title = @categories.map(&:category).join(' ')
|
||||
@above = 'category'
|
||||
@title = @categories.map(&:category).join(" ")
|
||||
@above = "category"
|
||||
|
||||
@rss_link = {
|
||||
title: "RSS 2.0 - Categorized #{@title}",
|
||||
href: category_url(params[:category], format: 'rss'),
|
||||
href: category_url(params[:category], format: "rss")
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.rss { render :action => "rss", :layout => false }
|
||||
format.json { render :json => @stories }
|
||||
format.html { render action: "index" }
|
||||
format.rss { render action: "rss", layout: false }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -184,48 +184,48 @@ class HomeController < ApplicationController
|
|||
paginate stories.tagged([@tag])
|
||||
end
|
||||
|
||||
@title = [@tag.tag, @tag.description].compact.join(' - ')
|
||||
@above = 'single_tag'
|
||||
@title = [@tag.tag, @tag.description].compact.join(" - ")
|
||||
@above = "single_tag"
|
||||
@related = Rails.cache.fetch("related_#{@tag.tag}", expires_in: 1.day) {
|
||||
Tag.related(@tag)
|
||||
}
|
||||
@below = 'tags/multi_tag_tip'
|
||||
@below = "tags/multi_tag_tip"
|
||||
|
||||
@rss_link = {
|
||||
title: "RSS 2.0 - Tagged #{@tag.tag} (#{@tag.description})",
|
||||
href: "/t/#{@tag.tag}.rss",
|
||||
href: "/t/#{@tag.tag}.rss"
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.rss { render :action => "rss", :layout => false }
|
||||
format.json { render :json => @stories }
|
||||
format.html { render action: "index" }
|
||||
format.rss { render action: "rss", layout: false }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
def multi_tag
|
||||
tag_params = params[:tag].split(',')
|
||||
tag_params = params[:tag].split(",")
|
||||
@tags = Tag.where(tag: tag_params)
|
||||
raise ActiveRecord::RecordNotFound unless @tags.length == tag_params.length
|
||||
|
||||
@stories, @show_more = get_from_cache(tags: tag_params.sort.join(',')) do
|
||||
@stories, @show_more = get_from_cache(tags: tag_params.sort.join(",")) do
|
||||
paginate stories.tagged(@tags)
|
||||
end
|
||||
|
||||
@title = @tags.map do |tag|
|
||||
[tag.tag, tag.description].compact.join(' - ')
|
||||
end.join(' ')
|
||||
@above = 'multi_tag'
|
||||
[tag.tag, tag.description].compact.join(" - ")
|
||||
end.join(" ")
|
||||
@above = "multi_tag"
|
||||
|
||||
@rss_link = {
|
||||
title: "RSS 2.0 - Tagged #{tags_with_description_for_rss(@tags)}",
|
||||
href: "/t/#{params[:tag]}.rss",
|
||||
href: "/t/#{params[:tag]}.rss"
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.rss { render :action => "rss", :layout => false }
|
||||
format.json { render :json => @stories }
|
||||
format.html { render action: "index" }
|
||||
format.rss { render action: "rss", layout: false }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -233,21 +233,21 @@ class HomeController < ApplicationController
|
|||
@domain = Domain.find_by!(domain: params[:id])
|
||||
|
||||
@stories, @show_more = get_from_cache(domain: @domain.domain) do
|
||||
paginate @domain.stories.base(@user).order('id desc')
|
||||
paginate @domain.stories.base(@user).order("id desc")
|
||||
end
|
||||
|
||||
@title = @domain.domain
|
||||
@above = 'for_domain'
|
||||
@above = "for_domain"
|
||||
|
||||
@rss_link = {
|
||||
title: "RSS 2.0 - For #{@domain.domain}",
|
||||
href: "/domain/#{@domain.domain}.rss",
|
||||
href: "/domain/#{@domain.domain}.rss"
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.rss { render :action => "rss", :layout => false }
|
||||
format.json { render :json => @stories }
|
||||
format.html { render action: "index" }
|
||||
format.rss { render action: "rss", layout: false }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -258,35 +258,35 @@ class HomeController < ApplicationController
|
|||
paginate stories.top(length)
|
||||
}
|
||||
|
||||
if length[:dur] > 1
|
||||
@title = "Top Stories of the Past #{length[:dur]} #{length[:intv]}"
|
||||
@title = if length[:dur] > 1
|
||||
"Top Stories of the Past #{length[:dur]} #{length[:intv]}"
|
||||
else
|
||||
@title = "Top Stories of the Past #{length[:intv]}"
|
||||
"Top Stories of the Past #{length[:intv]}"
|
||||
end
|
||||
@above = 'stories/subnav'
|
||||
@above = "stories/subnav"
|
||||
|
||||
@rss_link ||= {
|
||||
:title => "RSS 2.0 - " + @title,
|
||||
:href => "/top/rss",
|
||||
title: "RSS 2.0 - " + @title,
|
||||
href: "/top/rss"
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.rss { render :action => "rss", :layout => false }
|
||||
format.html { render action: "index" }
|
||||
format.rss { render action: "rss", layout: false }
|
||||
end
|
||||
end
|
||||
|
||||
def upvoted
|
||||
@stories, @show_more = get_from_cache(upvoted: true, user: @user) {
|
||||
paginate @user.upvoted_stories.includes(:tags).order('votes.id DESC')
|
||||
paginate @user.upvoted_stories.includes(:tags).order("votes.id DESC")
|
||||
}
|
||||
|
||||
@title = "Upvoted Stories"
|
||||
@above = 'saved/subnav'
|
||||
@above = "saved/subnav"
|
||||
|
||||
@rss_link = {
|
||||
:title => "RSS 2.0 - Upvoted Stories",
|
||||
:href => user_token_link("/upvoted.rss"),
|
||||
title: "RSS 2.0 - Upvoted Stories",
|
||||
href: user_token_link("/upvoted.rss")
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
|
@ -296,13 +296,13 @@ class HomeController < ApplicationController
|
|||
@title += " - Private feed for #{@user.username}"
|
||||
end
|
||||
|
||||
render :action => "rss", :layout => false
|
||||
render action: "rss", layout: false
|
||||
}
|
||||
format.json { render :json => @stories }
|
||||
format.json { render json: @stories }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def filtered_tag_ids
|
||||
if @user
|
||||
|
@ -320,7 +320,7 @@ private
|
|||
p = params[:page].to_i
|
||||
if p == 0
|
||||
p = 1
|
||||
elsif p < 0 || p > (2 ** 32)
|
||||
elsif p < 0 || p > (2**32)
|
||||
raise ActionController::RoutingError.new("page out of bounds")
|
||||
end
|
||||
p
|
||||
|
@ -334,9 +334,9 @@ private
|
|||
if Rails.env.development? || @user || tags_filtered_by_cookie.any?
|
||||
yield
|
||||
else
|
||||
key = opts.merge(page: page).sort.map {|k, v| "#{k}=#{v.to_param}" }.join(" ")
|
||||
key = opts.merge(page: page).sort.map { |k, v| "#{k}=#{v.to_param}" }.join(" ")
|
||||
begin
|
||||
Rails.cache.fetch("stories #{key}", :expires_in => 45, &block)
|
||||
Rails.cache.fetch("stories #{key}", expires_in: 45, &block)
|
||||
rescue Errno::ENOENT => e
|
||||
Rails.logger.error "error fetching stories #{key}: #{e}"
|
||||
yield
|
||||
|
@ -349,6 +349,6 @@ private
|
|||
end
|
||||
|
||||
def tags_with_description_for_rss(tags)
|
||||
tags.map {|tag| "#{tag.tag} (#{tag.description})" }.join(' ')
|
||||
tags.map { |tag| "#{tag.tag} (#{tag.description})" }.join(" ")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class InvitationsController < ApplicationController
|
||||
before_action :require_logged_in_user, :except => [:build, :create_by_request, :confirm_email]
|
||||
before_action :require_logged_in_user, except: [:build, :create_by_request, :confirm_email]
|
||||
before_action :show_title_h1
|
||||
|
||||
def build
|
||||
|
@ -8,7 +8,7 @@ class InvitationsController < ApplicationController
|
|||
@invitation_request = InvitationRequest.new
|
||||
else
|
||||
flash[:error] = "Public invitation requests are not allowed."
|
||||
return redirect_to "/login"
|
||||
redirect_to "/login"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -19,11 +19,11 @@ class InvitationsController < ApplicationController
|
|||
return redirect_to "/"
|
||||
end
|
||||
|
||||
@invitation_requests = InvitationRequest.where(:is_verified => true)
|
||||
@invitation_requests = InvitationRequest.where(is_verified: true)
|
||||
end
|
||||
|
||||
def confirm_email
|
||||
if !(ir = InvitationRequest.where(:code => params[:code].to_s).first)
|
||||
if !(ir = InvitationRequest.where(code: params[:code].to_s).first)
|
||||
flash[:error] = "Invalid or expired invitation request"
|
||||
return redirect_to "/invitations/request"
|
||||
end
|
||||
|
@ -31,9 +31,9 @@ class InvitationsController < ApplicationController
|
|||
ir.is_verified = true
|
||||
ir.save!
|
||||
|
||||
flash[:success] = "Your invitation request has been validated and " <<
|
||||
"will now be shown to other logged-in users."
|
||||
return redirect_to "/invitations/request"
|
||||
flash[:success] = "Your invitation request has been validated and " \
|
||||
"will now be shown to other logged-in users."
|
||||
redirect_to "/invitations/request"
|
||||
end
|
||||
|
||||
def create
|
||||
|
@ -52,47 +52,48 @@ class InvitationsController < ApplicationController
|
|||
i.save!
|
||||
i.send_email
|
||||
flash[:success] = "Successfully e-mailed invitation to " <<
|
||||
params[:email].to_s << "."
|
||||
params[:email].to_s << "."
|
||||
rescue => e
|
||||
Rails.logger.error "Error creating invitation for #{params[:email]}: #{e.message}"
|
||||
flash[:error] = "Could not send invitation, verify the e-mail " <<
|
||||
"address is valid."
|
||||
flash[:error] = "Could not send invitation, verify the e-mail " \
|
||||
"address is valid."
|
||||
end
|
||||
|
||||
if params[:return_home]
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
else
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
end
|
||||
|
||||
def create_by_request
|
||||
if Rails.application.allow_invitation_requests?
|
||||
@invitation_request = InvitationRequest.new(
|
||||
params.require(:invitation_request).permit(:name, :email, :memo))
|
||||
params.require(:invitation_request).permit(:name, :email, :memo)
|
||||
)
|
||||
|
||||
@invitation_request.ip_address = request.remote_ip
|
||||
|
||||
if @invitation_request.save
|
||||
flash[:success] = "You have been e-mailed a confirmation to " <<
|
||||
params[:invitation_request][:email].to_s << "."
|
||||
return redirect_to "/invitations/request"
|
||||
params[:invitation_request][:email].to_s << "."
|
||||
redirect_to "/invitations/request"
|
||||
else
|
||||
render :action => :build
|
||||
render action: :build
|
||||
end
|
||||
else
|
||||
return redirect_to "/login"
|
||||
redirect_to "/login"
|
||||
end
|
||||
end
|
||||
|
||||
def send_for_request
|
||||
if !@user.can_see_invitation_requests?
|
||||
flash[:error] = "Your account is not permitted to view invitation " <<
|
||||
"requests."
|
||||
flash[:error] = "Your account is not permitted to view invitation " \
|
||||
"requests."
|
||||
return redirect_to "/"
|
||||
end
|
||||
|
||||
if !(ir = InvitationRequest.where(:code => params[:code].to_s).first)
|
||||
if !(ir = InvitationRequest.where(code: params[:code].to_s).first)
|
||||
flash[:error] = "Invalid or expired invitation request"
|
||||
return redirect_to "/invitations"
|
||||
end
|
||||
|
@ -104,12 +105,12 @@ class InvitationsController < ApplicationController
|
|||
i.send_email
|
||||
ir.destroy!
|
||||
flash[:success] = "Successfully e-mailed invitation to " <<
|
||||
ir.name.to_s << "."
|
||||
ir.name.to_s << "."
|
||||
|
||||
Rails.logger.info "[u#{@user.id}] sent invitiation for request " <<
|
||||
ir.inspect
|
||||
ir.inspect
|
||||
|
||||
return redirect_to "/invitations"
|
||||
redirect_to "/invitations"
|
||||
end
|
||||
|
||||
def delete_request
|
||||
|
@ -117,18 +118,18 @@ class InvitationsController < ApplicationController
|
|||
return redirect_to "/invitations"
|
||||
end
|
||||
|
||||
if !(ir = InvitationRequest.where(:code => params[:code].to_s).first)
|
||||
if !(ir = InvitationRequest.where(code: params[:code].to_s).first)
|
||||
flash[:error] = "Invalid or expired invitation request"
|
||||
return redirect_to "/invitations"
|
||||
end
|
||||
|
||||
ir.destroy!
|
||||
flash[:success] = "Successfully deleted invitation request from " <<
|
||||
ir.name.to_s << "."
|
||||
ir.name.to_s << "."
|
||||
|
||||
Rails.logger.info "[u#{@user.id}] deleted invitation request " <<
|
||||
"from #{ir.inspect}"
|
||||
Rails.logger.info "[u#{@user.id}] deleted invitation request " \
|
||||
"from #{ir.inspect}"
|
||||
|
||||
return redirect_to "/invitations"
|
||||
redirect_to "/invitations"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,10 +19,10 @@ class KeybaseProofsController < ApplicationController
|
|||
if Keybase.proof_valid?(kb_username, kb_signature, @user.username)
|
||||
@user.add_or_update_keybase_proof(kb_username, kb_signature)
|
||||
@user.save!
|
||||
return redirect_to Keybase.success_url(kb_username, kb_signature, kb_ua, @user.username)
|
||||
redirect_to Keybase.success_url(kb_username, kb_signature, kb_ua, @user.username)
|
||||
else
|
||||
flash[:error] = "Failed to connect your account to Keybase. Try again from Keybase."
|
||||
return redirect_to settings_path
|
||||
redirect_to settings_path
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,7 +51,7 @@ class KeybaseProofsController < ApplicationController
|
|||
@user_re = User.username_regex_s[1...-1]
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def force_to_json
|
||||
request.format = :json
|
||||
|
@ -60,7 +60,7 @@ private
|
|||
def check_user_matches
|
||||
unless case_insensitive_match?(@user.username, params[:username])
|
||||
flash[:error] = "not logged in as the correct user"
|
||||
return redirect_to settings_path
|
||||
redirect_to settings_path
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
class LoginBannedError < StandardError; end
|
||||
|
||||
class LoginDeletedError < StandardError; end
|
||||
|
||||
class LoginTOTPFailedError < StandardError; end
|
||||
|
||||
class LoginWipedError < StandardError; end
|
||||
|
||||
class LoginFailedError < StandardError; end
|
||||
|
||||
class LoginController < ApplicationController
|
||||
before_action :authenticate_user
|
||||
before_action :check_for_read_only_mode, :except => [:index]
|
||||
before_action :check_for_read_only_mode, except: [:index]
|
||||
before_action :require_no_user_or_redirect,
|
||||
only: [:index, :login, :forgot_password, :reset_password]
|
||||
only: [:index, :login, :forgot_password, :reset_password]
|
||||
before_action :show_title_h1
|
||||
|
||||
def logout
|
||||
|
@ -25,10 +29,10 @@ class LoginController < ApplicationController
|
|||
end
|
||||
|
||||
def login
|
||||
if params[:email].to_s.match(/@/)
|
||||
user = User.where(:email => params[:email]).first
|
||||
user = if /@/.match?(params[:email].to_s)
|
||||
User.where(email: params[:email]).first
|
||||
else
|
||||
user = User.where(:username => params[:email]).first
|
||||
User.where(username: params[:email]).first
|
||||
end
|
||||
|
||||
fail_reason = nil
|
||||
|
@ -54,7 +58,7 @@ class LoginController < ApplicationController
|
|||
raise LoginDeletedError
|
||||
end
|
||||
|
||||
if !user.password_digest.to_s.match(/^\$2a\$#{BCrypt::Engine::DEFAULT_COST}\$/)
|
||||
if !user.password_digest.to_s.match(/^\$2a\$#{BCrypt::Engine::DEFAULT_COST}\$/o)
|
||||
user.password = user.password_confirmation = params[:password].to_s
|
||||
user.save
|
||||
end
|
||||
|
@ -82,8 +86,8 @@ class LoginController < ApplicationController
|
|||
|
||||
return redirect_to "/"
|
||||
rescue LoginWipedError
|
||||
fail_reason = "Your account was banned or deleted before the site changed admins. " <<
|
||||
"Your email and password hash were wiped for privacy."
|
||||
fail_reason = "Your account was banned or deleted before the site changed admins. " \
|
||||
"Your email and password hash were wiped for privacy."
|
||||
rescue LoginBannedError
|
||||
fail_reason = "Your account has been banned. Log: #{user.banned_reason}"
|
||||
rescue LoginDeletedError
|
||||
|
@ -118,24 +122,24 @@ class LoginController < ApplicationController
|
|||
end
|
||||
|
||||
if @found_user.is_wiped?
|
||||
flash.now[:error] = "It's not possible to reset your password " <<
|
||||
"because your account was deleted before the site changed admins " <<
|
||||
"and your email address was wiped for privacy."
|
||||
flash.now[:error] = "It's not possible to reset your password " \
|
||||
"because your account was deleted before the site changed admins " \
|
||||
"and your email address was wiped for privacy."
|
||||
return forgot_password
|
||||
end
|
||||
|
||||
@found_user.initiate_password_reset_for_ip(request.remote_ip)
|
||||
|
||||
flash.now[:success] = "Password reset instructions have been e-mailed to you."
|
||||
return index
|
||||
index
|
||||
end
|
||||
|
||||
def set_new_password
|
||||
@title = "Set New Password"
|
||||
|
||||
if (m = params[:token].to_s.match(/^(\d+)-/)) &&
|
||||
(Time.current - Time.zone.at(m[1].to_i)) < 24.hours
|
||||
@reset_user = User.where(:password_reset_token => params[:token].to_s).first
|
||||
(Time.current - Time.zone.at(m[1].to_i)) < 24.hours
|
||||
@reset_user = User.where(password_reset_token: params[:token].to_s).first
|
||||
end
|
||||
|
||||
if @reset_user && !@reset_user.is_banned?
|
||||
|
@ -152,30 +156,30 @@ class LoginController < ApplicationController
|
|||
if @reset_user.save && @reset_user.is_active?
|
||||
if @reset_user.has_2fa?
|
||||
flash[:success] = "Your password has been reset."
|
||||
return redirect_to "/login"
|
||||
redirect_to "/login"
|
||||
else
|
||||
session[:u] = @reset_user.session_token
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
end
|
||||
else
|
||||
flash[:error] = "Could not reset password."
|
||||
end
|
||||
end
|
||||
else
|
||||
flash[:error] = "Invalid reset token. It may have already been " <<
|
||||
"used or you may have copied it incorrectly."
|
||||
return redirect_to forgot_password_path
|
||||
flash[:error] = "Invalid reset token. It may have already been " \
|
||||
"used or you may have copied it incorrectly."
|
||||
redirect_to forgot_password_path
|
||||
end
|
||||
end
|
||||
|
||||
def twofa
|
||||
@title = "Login - Two Factor Authentication"
|
||||
if (tmpu = find_twofa_user)
|
||||
Rails.logger.info " Authenticated as user #{tmpu.id} " <<
|
||||
"(#{tmpu.username}), verifying TOTP"
|
||||
Rails.logger.info " Authenticated as user #{tmpu.id} " \
|
||||
"(#{tmpu.username}), verifying TOTP"
|
||||
else
|
||||
reset_session
|
||||
return redirect_to "/login"
|
||||
redirect_to "/login"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -184,18 +188,18 @@ class LoginController < ApplicationController
|
|||
if (tmpu = find_twofa_user) && tmpu.authenticate_totp(params[:totp_code])
|
||||
session[:u] = tmpu.session_token
|
||||
session.delete(:twofa_u)
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
else
|
||||
flash[:error] = "Your TOTP code did not match. Please try again."
|
||||
return redirect_to "/login/2fa"
|
||||
redirect_to "/login/2fa"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def find_twofa_user
|
||||
if session[:twofa_u].present?
|
||||
return User.where(:session_token => session[:twofa_u]).first
|
||||
User.where(session_token: session[:twofa_u]).first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class MessagesController < ApplicationController
|
||||
before_action :require_logged_in_user
|
||||
before_action :require_logged_in_moderator, only: [:mod_note]
|
||||
before_action :find_message, :only => [:show, :destroy, :keep_as_new, :mod_note]
|
||||
before_action :find_message, only: [:show, :destroy, :keep_as_new, :mod_note]
|
||||
before_action :show_title_h1
|
||||
|
||||
def index
|
||||
|
@ -21,7 +21,7 @@ class MessagesController < ApplicationController
|
|||
end
|
||||
}
|
||||
format.json {
|
||||
render :json => @messages
|
||||
render json: @messages
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -38,10 +38,10 @@ class MessagesController < ApplicationController
|
|||
|
||||
@new_message = Message.new
|
||||
|
||||
render :action => "index"
|
||||
render action: "index"
|
||||
}
|
||||
format.json {
|
||||
render :json => @messages
|
||||
render json: @messages
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -59,11 +59,11 @@ class MessagesController < ApplicationController
|
|||
ModNote.create_from_message(@new_message, @user)
|
||||
end
|
||||
flash[:success] = "Your message has been sent to " <<
|
||||
@new_message.recipient.username.to_s << "."
|
||||
return redirect_to "/messages"
|
||||
@new_message.recipient.username.to_s << "."
|
||||
redirect_to "/messages"
|
||||
else
|
||||
@messages = Message.inbox(@user).load
|
||||
render :action => "index"
|
||||
render action: "index"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -72,13 +72,13 @@ class MessagesController < ApplicationController
|
|||
|
||||
if @message.author
|
||||
@new_message = Message.new
|
||||
@new_message.recipient_username = (@message.author_user_id == @user.id ?
|
||||
@new_message.recipient_username = ((@message.author_user_id == @user.id) ?
|
||||
@message.recipient.username : @message.author.username)
|
||||
|
||||
if @message.subject.match(/^re:/i)
|
||||
@new_message.subject = @message.subject
|
||||
@new_message.subject = if /^re:/i.match?(@message.subject)
|
||||
@message.subject
|
||||
else
|
||||
@new_message.subject = "Re: #{@message.subject}"
|
||||
"Re: #{@message.subject}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,9 +103,9 @@ class MessagesController < ApplicationController
|
|||
flash[:success] = "Deleted message."
|
||||
|
||||
if @message.author_user_id == @user.id
|
||||
return redirect_to "/messages/sent"
|
||||
redirect_to "/messages/sent"
|
||||
else
|
||||
return redirect_to "/messages"
|
||||
redirect_to "/messages"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -114,7 +114,7 @@ class MessagesController < ApplicationController
|
|||
|
||||
params.each do |k, v|
|
||||
if (v.to_s == "1") && (m = k.match(/^delete_(.+)$/))
|
||||
if (message = Message.where(:short_id => m[1]).first)
|
||||
if (message = Message.where(short_id: m[1]).first)
|
||||
ok = false
|
||||
if message.author_user_id == @user.id
|
||||
message.deleted_by_author = true
|
||||
|
@ -133,27 +133,27 @@ class MessagesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
flash[:success] = "Deleted #{deleted} #{'message'.pluralize(deleted)}"
|
||||
flash[:success] = "Deleted #{deleted} #{"message".pluralize(deleted)}"
|
||||
|
||||
@user.update_unread_message_count!
|
||||
|
||||
return redirect_to "/messages"
|
||||
redirect_to "/messages"
|
||||
end
|
||||
|
||||
def keep_as_new
|
||||
@message.has_been_read = false
|
||||
@message.save
|
||||
|
||||
return redirect_to "/messages"
|
||||
redirect_to "/messages"
|
||||
end
|
||||
|
||||
def mod_note
|
||||
ModNote.create_from_message(@message, @user)
|
||||
|
||||
return redirect_to @message, notice: 'ModNote created'
|
||||
redirect_to @message, notice: "ModNote created"
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def message_params
|
||||
params.require(:message).permit(
|
||||
|
@ -163,7 +163,7 @@ private
|
|||
end
|
||||
|
||||
def find_message
|
||||
if (@message = Message.where(:short_id => params[:message_id] || params[:id]).first)
|
||||
if (@message = Message.where(short_id: params[:message_id] || params[:id]).first)
|
||||
if @message.author_user_id == @user.id || @message.recipient_user_id == @user.id
|
||||
return true
|
||||
end
|
||||
|
@ -171,6 +171,6 @@ private
|
|||
|
||||
flash[:error] = "Could not find message."
|
||||
redirect_to "/messages"
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,10 +9,10 @@ class ModController < ApplicationController
|
|||
def index
|
||||
@title = "Activity by Other Mods"
|
||||
@moderations = Moderation.all
|
||||
.eager_load(:moderator, :story, :tag, :user, :comment => [:story, :user])
|
||||
.eager_load(:moderator, :story, :tag, :user, comment: [:story, :user])
|
||||
.where("moderator_user_id != ? or moderator_user_id is null", @user.id)
|
||||
.where('moderations.created_at >= (NOW() - INTERVAL 1 MONTH)')
|
||||
.order('moderations.id desc')
|
||||
.where("moderations.created_at >= (NOW() - INTERVAL 1 MONTH)")
|
||||
.order("moderations.id desc")
|
||||
end
|
||||
|
||||
def flagged_stories
|
||||
|
@ -26,7 +26,7 @@ class ModController < ApplicationController
|
|||
def flagged_comments
|
||||
@title = "Flagged Comments"
|
||||
@comments = period(Comment
|
||||
.eager_load(:user, :hat, :story => :user, :votes => :user)
|
||||
.eager_load(:user, :hat, story: :user, votes: :user)
|
||||
.where("comments.flags >= 2")
|
||||
.where("(select count(*) from votes where
|
||||
votes.comment_id = comments.id and
|
||||
|
@ -43,10 +43,10 @@ class ModController < ApplicationController
|
|||
@commenters = fc.commenters
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def default_periods
|
||||
@periods = %w{1d 2d 3d 1w 1m}
|
||||
@periods = %w[1d 2d 3d 1w 1m]
|
||||
end
|
||||
|
||||
def period(query)
|
||||
|
|
|
@ -4,7 +4,7 @@ class ModNotesController < ModController
|
|||
def index
|
||||
@title = "Mod Notes"
|
||||
@username = params[:username]
|
||||
query = ModNote.order('created_at desc').includes(:moderator, :user)
|
||||
query = ModNote.order("created_at desc").includes(:moderator, :user)
|
||||
if (@username = params[:username])
|
||||
if (user = User.find_by(username: @username))
|
||||
@title = "#{@username} Mod Notes"
|
||||
|
@ -23,15 +23,15 @@ class ModNotesController < ModController
|
|||
@mod_note = ModNote.new(mod_note_params)
|
||||
@mod_note.moderator = @user
|
||||
if @mod_note.save
|
||||
redirect_to user_path(@mod_note.user), success: 'Noted'
|
||||
redirect_to user_path(@mod_note.user), success: "Noted"
|
||||
else
|
||||
# This is bad and needs to change if note ever has non-trivial validation
|
||||
redirect_to user_path(@mod_note.user),
|
||||
error: "Invalid note and Peter half-assed the error handling"
|
||||
error: "Invalid note and Peter half-assed the error handling"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def mod_note_params
|
||||
params.require(:mod_note).permit(:username, :note)
|
||||
|
|
|
@ -5,35 +5,35 @@ class ModerationsController < ApplicationController
|
|||
|
||||
def index
|
||||
@title = "Moderation Log"
|
||||
@moderators = ['(All)', '(Users)'] + User.moderators.map(&:username)
|
||||
@moderators = ["(All)", "(Users)"] + User.moderators.map(&:username)
|
||||
|
||||
@moderator = params.fetch('moderator', '(All)')
|
||||
@moderator = params.fetch("moderator", "(All)")
|
||||
@what = {
|
||||
:stories => params.dig(:what, :stories),
|
||||
:comments => params.dig(:what, :comments),
|
||||
:tags => params.dig(:what, :tags),
|
||||
:users => params.dig(:what, :users),
|
||||
:domains => params.dig(:what, :domains),
|
||||
:categories => params.dig(:what, :categories),
|
||||
stories: params.dig(:what, :stories),
|
||||
comments: params.dig(:what, :comments),
|
||||
tags: params.dig(:what, :tags),
|
||||
users: params.dig(:what, :users),
|
||||
domains: params.dig(:what, :domains),
|
||||
categories: params.dig(:what, :categories)
|
||||
}
|
||||
@what.transform_values! { true } if @what.values.none?
|
||||
|
||||
@moderations = Moderation.all.eager_load(:moderator,
|
||||
:story,
|
||||
:comment,
|
||||
:tag,
|
||||
:user,
|
||||
:domain,
|
||||
:category)
|
||||
:story,
|
||||
:comment,
|
||||
:tag,
|
||||
:user,
|
||||
:domain,
|
||||
:category)
|
||||
|
||||
# filter based on target
|
||||
@moderations = case @moderator
|
||||
when '(All)'
|
||||
when "(All)"
|
||||
@moderations
|
||||
when '(Users)'
|
||||
when "(Users)"
|
||||
@moderations.where("is_from_suggestions = true")
|
||||
else
|
||||
@moderations.joins(:moderator).where(:users => { :username => @moderator })
|
||||
@moderations.joins(:moderator).where(users: {username: @moderator})
|
||||
end
|
||||
|
||||
# filter based on type of thing moderated
|
||||
|
@ -47,13 +47,13 @@ class ModerationsController < ApplicationController
|
|||
@page = params[:page].to_i
|
||||
if @page == 0
|
||||
@page = 1
|
||||
elsif @page < 0 || @page > (2 ** 32) || @page > @pages
|
||||
elsif @page < 0 || @page > (2**32) || @page > @pages
|
||||
raise ActionController::RoutingError.new("page out of bounds")
|
||||
end
|
||||
|
||||
@moderations = @moderations
|
||||
.offset((@page - 1) * ENTRIES_PER_PAGE)
|
||||
.order("moderations.created_at desc")
|
||||
.limit(ENTRIES_PER_PAGE)
|
||||
.offset((@page - 1) * ENTRIES_PER_PAGE)
|
||||
.order("moderations.created_at desc")
|
||||
.limit(ENTRIES_PER_PAGE)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,9 +10,9 @@ class RepliesController < ApplicationController
|
|||
@title = "All Your Replies"
|
||||
|
||||
@replies = ReplyingComment
|
||||
.for_user(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
.for_user(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
apply_current_vote
|
||||
render :show
|
||||
end
|
||||
|
@ -20,9 +20,9 @@ class RepliesController < ApplicationController
|
|||
def comments
|
||||
@title = "Your Comment Replies"
|
||||
@replies = ReplyingComment
|
||||
.comment_replies_for(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
.comment_replies_for(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
apply_current_vote
|
||||
render :show
|
||||
end
|
||||
|
@ -30,9 +30,9 @@ class RepliesController < ApplicationController
|
|||
def stories
|
||||
@title = "Your Story Replies"
|
||||
@replies = ReplyingComment
|
||||
.story_replies_for(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
.story_replies_for(@user.id)
|
||||
.offset((@page - 1) * REPLIES_PER_PAGE)
|
||||
.limit(REPLIES_PER_PAGE)
|
||||
apply_current_vote
|
||||
render :show
|
||||
end
|
||||
|
@ -44,7 +44,7 @@ class RepliesController < ApplicationController
|
|||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
# comments/_comment expects Comment objects to have a comment_vote attribute
|
||||
# with the current user's vote added by StoriesController.load_user_votes
|
||||
|
@ -53,7 +53,7 @@ private
|
|||
next unless r.current_vote_vote.present?
|
||||
r.comment.current_vote = {
|
||||
vote: r.current_vote_vote,
|
||||
reason: r.current_vote_reason.to_s,
|
||||
reason: r.current_vote_reason.to_s
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -70,7 +70,7 @@ private
|
|||
@page = params[:page].to_i
|
||||
if @page == 0
|
||||
@page = 1
|
||||
elsif @page < 0 || @page > (2 ** 32)
|
||||
elsif @page < 0 || @page > (2**32)
|
||||
raise ActionController::RoutingError.new("page out of bounds")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ class SettingsController < ApplicationController
|
|||
end
|
||||
|
||||
def delete_account
|
||||
unless params[:user][:i_am_sure] == '1'
|
||||
unless params[:user][:i_am_sure] == "1"
|
||||
flash[:error] = 'You did not check the "I am sure" checkbox.'
|
||||
return redirect_to settings_path
|
||||
end
|
||||
|
@ -21,13 +21,13 @@ class SettingsController < ApplicationController
|
|||
|
||||
@user.delete!
|
||||
disown_text = ""
|
||||
if params[:user][:disown] == '1'
|
||||
if params[:user][:disown] == "1"
|
||||
disown_text = " and disowned your stories and comments."
|
||||
InactiveUser.disown_all_by_author! @user
|
||||
end
|
||||
reset_session
|
||||
flash[:success] = "You have deleted your account#{disown_text}. Bye."
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -35,15 +35,15 @@ class SettingsController < ApplicationController
|
|||
@edit_user = @user.clone
|
||||
|
||||
if params[:user][:password].empty? ||
|
||||
@user.authenticate(params[:current_password].to_s)
|
||||
@user.authenticate(params[:current_password].to_s)
|
||||
@edit_user.roll_session_token if params[:user][:password]
|
||||
if @edit_user.update(user_params)
|
||||
if @edit_user.username != previous_username
|
||||
Moderation.create!(
|
||||
is_from_suggestions: true,
|
||||
user: @edit_user,
|
||||
action: "changed own username from \"#{previous_username}\" " <<
|
||||
"to \"#{@edit_user.username}\"",
|
||||
action: "changed own username from \"#{previous_username}\" " \
|
||||
"to \"#{@edit_user.username}\""
|
||||
)
|
||||
end
|
||||
session[:u] = @user.session_token if params[:user][:password]
|
||||
|
@ -54,7 +54,7 @@ class SettingsController < ApplicationController
|
|||
flash[:error] = "Your current password was not entered correctly."
|
||||
end
|
||||
|
||||
render :action => "index"
|
||||
render action: "index"
|
||||
end
|
||||
|
||||
def twofa
|
||||
|
@ -69,13 +69,13 @@ class SettingsController < ApplicationController
|
|||
if @user.has_2fa?
|
||||
@user.disable_2fa!
|
||||
flash[:success] = "Two-Factor Authentication has been disabled."
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
else
|
||||
return redirect_to twofa_enroll_url
|
||||
redirect_to twofa_enroll_url
|
||||
end
|
||||
else
|
||||
flash[:error] = "Your password was not correct."
|
||||
return redirect_to twofa_url
|
||||
redirect_to twofa_url
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -91,15 +91,15 @@ class SettingsController < ApplicationController
|
|||
session[:totp_secret] = ROTP::Base32.random
|
||||
end
|
||||
|
||||
totp = ROTP::TOTP.new(session[:totp_secret], :issuer => Rails.application.name)
|
||||
totp = ROTP::TOTP.new(session[:totp_secret], issuer: Rails.application.name)
|
||||
totp_url = totp.provisioning_uri(@user.email)
|
||||
|
||||
qrcode = RQRCode::QRCode.new(totp_url)
|
||||
qr = qrcode.as_svg(offset: 0,
|
||||
fill: "ffffff",
|
||||
color: "000",
|
||||
module_size: 5,
|
||||
shape_rendering: "crispEdges")
|
||||
fill: "ffffff",
|
||||
color: "000",
|
||||
module_size: 5,
|
||||
shape_rendering: "crispEdges")
|
||||
|
||||
@qr_secret = totp.secret
|
||||
@qr_svg = "<a href=\"#{totp_url}\">#{qr}</a>"
|
||||
|
@ -109,15 +109,15 @@ class SettingsController < ApplicationController
|
|||
@title = "Two-Factor Authentication"
|
||||
|
||||
if ((Time.now.to_i - session[:last_authed].to_i) > TOTP_SESSION_TIMEOUT) ||
|
||||
!session[:totp_secret]
|
||||
!session[:totp_secret]
|
||||
flash[:error] = "Your enrollment period timed out."
|
||||
return redirect_to twofa_url
|
||||
redirect_to twofa_url
|
||||
end
|
||||
end
|
||||
|
||||
def twofa_update
|
||||
if ((Time.now.to_i - session[:last_authed].to_i) > TOTP_SESSION_TIMEOUT) ||
|
||||
!session[:totp_secret]
|
||||
!session[:totp_secret]
|
||||
flash[:error] = "Your enrollment period timed out."
|
||||
return redirect_to twofa_url
|
||||
end
|
||||
|
@ -132,11 +132,11 @@ class SettingsController < ApplicationController
|
|||
|
||||
flash[:success] = "Two-Factor Authentication has been enabled on your account."
|
||||
session.delete(:totp_secret)
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
else
|
||||
flash[:error] = "Your TOTP code was invalid, please verify the " <<
|
||||
"current code in your TOTP application."
|
||||
return redirect_to twofa_verify_url
|
||||
flash[:error] = "Your TOTP code was invalid, please verify the " \
|
||||
"current code in your TOTP application."
|
||||
redirect_to twofa_verify_url
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -150,10 +150,10 @@ class SettingsController < ApplicationController
|
|||
|
||||
session[:pushover_rand] = SecureRandom.hex
|
||||
|
||||
return redirect_to Pushover.subscription_url(
|
||||
:success => "#{Rails.application.root_url}settings/pushover_callback?" <<
|
||||
redirect_to Pushover.subscription_url(
|
||||
success: "#{Rails.application.root_url}settings/pushover_callback?" \
|
||||
"rand=#{session[:pushover_rand]}",
|
||||
:failure => "#{Rails.application.root_url}settings/",
|
||||
failure: "#{Rails.application.root_url}settings/"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -175,24 +175,24 @@ class SettingsController < ApplicationController
|
|||
@user.pushover_user_key = params[:pushover_user_key].to_s
|
||||
@user.save!
|
||||
|
||||
if @user.pushover_user_key.present?
|
||||
flash[:success] = "Your account is now setup for Pushover notifications."
|
||||
flash[:success] = if @user.pushover_user_key.present?
|
||||
"Your account is now setup for Pushover notifications."
|
||||
else
|
||||
flash[:success] = "Your account is no longer setup for Pushover notifications."
|
||||
"Your account is no longer setup for Pushover notifications."
|
||||
end
|
||||
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def github_auth
|
||||
session[:github_state] = SecureRandom.hex
|
||||
return redirect_to Github.oauth_auth_url(session[:github_state])
|
||||
redirect_to Github.oauth_auth_url(session[:github_state])
|
||||
end
|
||||
|
||||
def github_callback
|
||||
if !session[:github_state].present? ||
|
||||
!params[:code].present? ||
|
||||
(params[:state].to_s != session[:github_state].to_s)
|
||||
!params[:code].present? ||
|
||||
(params[:state].to_s != session[:github_state].to_s)
|
||||
flash[:error] = "Invalid OAuth state"
|
||||
return redirect_to "/settings"
|
||||
end
|
||||
|
@ -209,7 +209,7 @@ class SettingsController < ApplicationController
|
|||
return github_disconnect
|
||||
end
|
||||
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def github_disconnect
|
||||
|
@ -217,20 +217,20 @@ class SettingsController < ApplicationController
|
|||
@user.github_username = nil
|
||||
@user.save!
|
||||
flash[:success] = "Your GitHub association has been removed."
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def twitter_auth
|
||||
session[:twitter_state] = SecureRandom.hex
|
||||
return redirect_to Twitter.oauth_auth_url(session[:twitter_state])
|
||||
redirect_to Twitter.oauth_auth_url(session[:twitter_state])
|
||||
rescue OAuth::Unauthorized
|
||||
flash[:error] = "Twitter says we're not authenticating properly, please message the admin"
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def twitter_callback
|
||||
if session[:twitter_state].blank? ||
|
||||
(params[:state].to_s != session[:twitter_state].to_s)
|
||||
(params[:state].to_s != session[:twitter_state].to_s)
|
||||
flash[:error] = "Invalid OAuth state"
|
||||
return redirect_to "/settings"
|
||||
end
|
||||
|
@ -238,7 +238,8 @@ class SettingsController < ApplicationController
|
|||
session.delete(:twitter_state)
|
||||
|
||||
tok, sec, username = Twitter.token_secret_and_user_from_token_and_verifier(
|
||||
params[:oauth_token], params[:oauth_verifier])
|
||||
params[:oauth_token], params[:oauth_verifier]
|
||||
)
|
||||
if tok.present? && username.present?
|
||||
@user.twitter_oauth_token = tok
|
||||
@user.twitter_oauth_token_secret = sec
|
||||
|
@ -249,7 +250,7 @@ class SettingsController < ApplicationController
|
|||
return twitter_disconnect
|
||||
end
|
||||
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
def twitter_disconnect
|
||||
|
@ -258,10 +259,10 @@ class SettingsController < ApplicationController
|
|||
@user.twitter_oauth_token_secret = nil
|
||||
@user.save!
|
||||
flash[:success] = "Your Twitter association has been removed."
|
||||
return redirect_to "/settings"
|
||||
redirect_to "/settings"
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
class SignupController < ApplicationController
|
||||
before_action :require_logged_in_user, :check_new_users, :check_can_invite, :only => :invite
|
||||
before_action :require_logged_in_user, :check_new_users, :check_can_invite, only: :invite
|
||||
before_action :check_for_read_only_mode, :show_title_h1
|
||||
|
||||
def index
|
||||
|
@ -9,7 +9,7 @@ class SignupController < ApplicationController
|
|||
return redirect_to "/"
|
||||
end
|
||||
if Rails.application.open_signups?
|
||||
redirect_to action: :invited, invitation_code: 'open' and return
|
||||
redirect_to action: :invited, invitation_code: "open" and return
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -27,7 +27,7 @@ class SignupController < ApplicationController
|
|||
end
|
||||
|
||||
if !Rails.application.open_signups?
|
||||
if !(@invitation = Invitation.unused.where(:code => params[:invitation_code].to_s).first)
|
||||
if !(@invitation = Invitation.unused.where(code: params[:invitation_code].to_s).first)
|
||||
flash[:error] = "Invalid or expired invitation"
|
||||
return redirect_to "/signup"
|
||||
end
|
||||
|
@ -44,7 +44,7 @@ class SignupController < ApplicationController
|
|||
|
||||
def signup
|
||||
if !Rails.application.open_signups?
|
||||
if !(@invitation = Invitation.unused.where(:code => params[:invitation_code].to_s).first)
|
||||
if !(@invitation = Invitation.unused.where(code: params[:invitation_code].to_s).first)
|
||||
flash[:error] = "Invalid or expired invitation."
|
||||
return redirect_to "/signup"
|
||||
end
|
||||
|
@ -59,40 +59,38 @@ class SignupController < ApplicationController
|
|||
end
|
||||
|
||||
if @new_user.save
|
||||
if @invitation
|
||||
@invitation.update(used_at: Time.current, new_user: @new_user)
|
||||
end
|
||||
@invitation&.update(used_at: Time.current, new_user: @new_user)
|
||||
session[:u] = @new_user.session_token
|
||||
flash[:success] = "Welcome to #{Rails.application.name}, " <<
|
||||
"#{@new_user.username}!"
|
||||
flash[:success] = "Welcome to #{Rails.application.name}, " \
|
||||
"#{@new_user.username}!"
|
||||
|
||||
if Rails.application.allow_new_users_to_invite?
|
||||
return redirect_to signup_invite_path
|
||||
redirect_to signup_invite_path
|
||||
else
|
||||
return redirect_to root_path
|
||||
redirect_to root_path
|
||||
end
|
||||
else
|
||||
render :action => "invited"
|
||||
render action: "invited"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def check_new_users
|
||||
if !Rails.application.allow_new_users_to_invite? && @user.is_new?
|
||||
redirect_to root_path, flash: { error: "New users cannot send invites" }
|
||||
redirect_to root_path, flash: {error: "New users cannot send invites"}
|
||||
end
|
||||
end
|
||||
|
||||
def check_can_invite
|
||||
if !@user.can_invite?
|
||||
redirect_to root_path, flash: { error: "You can't send invites" }
|
||||
redirect_to root_path, flash: {error: "You can't send invites"}
|
||||
end
|
||||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(
|
||||
:username, :email, :password, :password_confirmation, :about,
|
||||
:username, :email, :password, :password_confirmation, :about
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,14 +7,14 @@ class StatsController < ApplicationController
|
|||
|
||||
@users_graph = monthly_graph("users_graph", {
|
||||
graph_title: "Users joining by month",
|
||||
scale_y_divisions: 100,
|
||||
scale_y_divisions: 100
|
||||
}) {
|
||||
User.group("date_format(created_at, '%Y-%m')").count
|
||||
}
|
||||
|
||||
@active_users_graph = monthly_graph("active_users_graph", {
|
||||
graph_title: "Active users by month",
|
||||
scale_y_divisions: 500,
|
||||
scale_y_divisions: 500
|
||||
}) {
|
||||
User.connection.execute <<~SQL
|
||||
SELECT ym, count(distinct user_id)
|
||||
|
@ -32,27 +32,27 @@ class StatsController < ApplicationController
|
|||
|
||||
@stories_graph = monthly_graph("stories_graph", {
|
||||
graph_title: "Stories submitted by month",
|
||||
scale_y_divisions: 250,
|
||||
scale_y_divisions: 250
|
||||
}) {
|
||||
Story.group("date_format(created_at, '%Y-%m')").count
|
||||
}
|
||||
|
||||
@comments_graph = monthly_graph("comments_graph", {
|
||||
graph_title: "Comments posted by month",
|
||||
scale_y_divisions: 1_000,
|
||||
scale_y_divisions: 1_000
|
||||
}) {
|
||||
Comment.group("date_format(created_at, '%Y-%m')").count
|
||||
}
|
||||
|
||||
@votes_graph = monthly_graph("votes_graph", {
|
||||
graph_title: "Votes cast by month",
|
||||
scale_y_divisions: 10_000,
|
||||
scale_y_divisions: 10_000
|
||||
}) {
|
||||
Vote.group("date_format(updated_at, '%Y-%m')").count
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def monthly_graph(cache_key, opts)
|
||||
Rails.cache.fetch(cache_key, expires_in: 1.day) {
|
||||
|
@ -82,12 +82,12 @@ private
|
|||
area_fill: false,
|
||||
min_y_value: 0,
|
||||
number_format: "%d",
|
||||
show_lines: false,
|
||||
show_lines: false
|
||||
}
|
||||
graph = TimeSeries.new(defaults.merge(opts))
|
||||
graph.add_data(
|
||||
data: yield.to_a.flatten,
|
||||
template: "%Y-%m",
|
||||
template: "%Y-%m"
|
||||
)
|
||||
graph.burn_svg_only
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ class StoriesController < ApplicationController
|
|||
caches_page :show, if: CACHE_PAGE
|
||||
|
||||
before_action :require_logged_in_user_or_400,
|
||||
:only => [:upvote, :flag, :unvote, :hide, :unhide, :preview, :save, :unsave]
|
||||
only: [:upvote, :flag, :unvote, :hide, :unhide, :preview, :save, :unsave]
|
||||
before_action :require_logged_in_user,
|
||||
:only => [:destroy, :create, :edit, :fetch_url_attributes, :new, :suggest]
|
||||
before_action :verify_user_can_submit_stories, :only => [:new, :create]
|
||||
before_action :find_user_story, :only => [:destroy, :edit, :undelete, :update]
|
||||
before_action :find_story!, :only => [:suggest, :submit_suggestions]
|
||||
only: [:destroy, :create, :edit, :fetch_url_attributes, :new, :suggest]
|
||||
before_action :verify_user_can_submit_stories, only: [:new, :create]
|
||||
before_action :find_user_story, only: [:destroy, :edit, :undelete, :update]
|
||||
before_action :find_story!, only: [:suggest, :submit_suggestions]
|
||||
around_action :track_story_reads, only: [:show], if: -> { @user.present? }
|
||||
before_action :show_title_h1, only: [:new, :edit, :suggest]
|
||||
|
||||
|
@ -24,7 +24,7 @@ class StoriesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
return render :action => "new"
|
||||
render action: "new"
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
@ -36,8 +36,8 @@ class StoriesController < ApplicationController
|
|||
update_story_attributes
|
||||
|
||||
if @story.user_id != @user.id && @user.is_moderator? && !@story.moderation_reason.present?
|
||||
@story.errors.add(:moderation_reason, message: 'is required')
|
||||
return render :action => "edit"
|
||||
@story.errors.add(:moderation_reason, message: "is required")
|
||||
return render action: "edit"
|
||||
end
|
||||
|
||||
@story.is_deleted = true
|
||||
|
@ -69,7 +69,7 @@ class StoriesController < ApplicationController
|
|||
s.fetching_ip = request.remote_ip
|
||||
s.url = params[:fetch_url]
|
||||
|
||||
return render :json => s.fetched_attributes
|
||||
render json: s.fetched_attributes
|
||||
end
|
||||
|
||||
def new
|
||||
|
@ -83,8 +83,8 @@ class StoriesController < ApplicationController
|
|||
sattrs = @story.fetched_attributes
|
||||
|
||||
if sattrs[:url].present? && @story.url != sattrs[:url]
|
||||
flash.now[:notice] = "Note: URL has been changed to fetched " <<
|
||||
"canonicalized version"
|
||||
flash.now[:notice] = "Note: URL has been changed to fetched " \
|
||||
"canonicalized version"
|
||||
@story.url = sattrs[:url]
|
||||
end
|
||||
|
||||
|
@ -108,14 +108,14 @@ class StoriesController < ApplicationController
|
|||
@story.user_id = @user.id
|
||||
@story.previewing = true
|
||||
|
||||
@story.vote = Vote.new(:vote => 1)
|
||||
@story.vote = Vote.new(vote: 1)
|
||||
@story.score = 1
|
||||
|
||||
@story.valid?
|
||||
|
||||
@story.seen_previous = true
|
||||
|
||||
return render :action => "new", :layout => false
|
||||
render action: "new", layout: false
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -142,12 +142,12 @@ class StoriesController < ApplicationController
|
|||
@moderation = Moderation
|
||||
.where(story: @story, comment: nil)
|
||||
.where("action LIKE '%deleted story%'")
|
||||
.order('id desc')
|
||||
.order("id desc")
|
||||
.first
|
||||
end
|
||||
if !@story.can_be_seen_by_user?(@user)
|
||||
respond_to do |format|
|
||||
format.html { return render action: '_missing', status: 404 }
|
||||
format.html { return render action: "_missing", status: 404 }
|
||||
format.json { raise ActiveRecord::RecordNotFound }
|
||||
end
|
||||
end
|
||||
|
@ -169,9 +169,9 @@ class StoriesController < ApplicationController
|
|||
"twitter:site" => "@lobsters",
|
||||
"twitter:title" => @story.title,
|
||||
"twitter:description" => @story.comments_count.to_s + " " +
|
||||
'comment'.pluralize(@story.comments_count),
|
||||
"comment".pluralize(@story.comments_count),
|
||||
"twitter:image" => Rails.application.root_url +
|
||||
"apple-touch-icon-144.png",
|
||||
"apple-touch-icon-144.png"
|
||||
}
|
||||
|
||||
if @story.user.twitter_username.present?
|
||||
|
@ -180,25 +180,25 @@ class StoriesController < ApplicationController
|
|||
|
||||
load_user_votes
|
||||
|
||||
render :action => "show"
|
||||
render action: "show"
|
||||
}
|
||||
format.json {
|
||||
render :json => @story.as_json(:with_comments => @comments)
|
||||
render json: @story.as_json(with_comments: @comments)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def suggest
|
||||
@title = 'Suggest Story Changes'
|
||||
@title = "Suggest Story Changes"
|
||||
if !@story.can_have_suggestions_from_user?(@user)
|
||||
flash[:error] = "You are not allowed to offer suggestions on that story."
|
||||
return redirect_to @story.comments_path
|
||||
end
|
||||
|
||||
if (suggested_tags = @story.suggested_taggings.where(:user_id => @user.id)).any?
|
||||
@story.tags_a = suggested_tags.map {|st| st.tag.tag }
|
||||
if (suggested_tags = @story.suggested_taggings.where(user_id: @user.id)).any?
|
||||
@story.tags_a = suggested_tags.map { |st| st.tag.tag }
|
||||
end
|
||||
if (tt = @story.suggested_titles.where(:user_id => @user.id).first)
|
||||
if (tt = @story.suggested_titles.where(user_id: @user.id).first)
|
||||
@story.title = tt.title
|
||||
end
|
||||
end
|
||||
|
@ -219,7 +219,7 @@ class StoriesController < ApplicationController
|
|||
dsug = true
|
||||
end
|
||||
|
||||
sugtags = params[:story][:tags_a].reject {|t| t.to_s.strip == "" }.sort
|
||||
sugtags = params[:story][:tags_a].reject { |t| t.to_s.strip == "" }.sort
|
||||
if @story.tags_a.sort != sugtags
|
||||
@story.save_suggested_tags_a_for_user!(sugtags, @user)
|
||||
dsug = true
|
||||
|
@ -231,7 +231,7 @@ class StoriesController < ApplicationController
|
|||
end
|
||||
redirect_to ostory.comments_path
|
||||
else
|
||||
render :action => "suggest"
|
||||
render action: "suggest"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -264,106 +264,106 @@ class StoriesController < ApplicationController
|
|||
update_story_attributes
|
||||
|
||||
if @story.save
|
||||
return redirect_to @story.comments_path
|
||||
redirect_to @story.comments_path
|
||||
else
|
||||
return render :action => "edit"
|
||||
render action: "edit"
|
||||
end
|
||||
end
|
||||
|
||||
def unvote
|
||||
if !(story = find_story) || story.is_gone?
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
0, story.id, nil, @user.id, nil
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def upvote
|
||||
if !(story = find_story) || story.is_gone?
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
if story.merged_into_story
|
||||
return render :plain => "story has been merged", :status => 400
|
||||
return render plain: "story has been merged", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
1, story.id, nil, @user.id, nil
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def flag
|
||||
if !(story = find_story) || story.is_gone?
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
if !Vote::STORY_REASONS[params[:reason]]
|
||||
return render :plain => "invalid reason", :status => 400
|
||||
return render plain: "invalid reason", status: 400
|
||||
end
|
||||
|
||||
if !@user.can_flag?(story)
|
||||
return render :plain => "not permitted to flag", :status => 400
|
||||
return render plain: "not permitted to flag", status: 400
|
||||
end
|
||||
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
-1, story.id, nil, @user.id, params[:reason]
|
||||
)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def hide
|
||||
if !(story = find_story)
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
if story.merged_into_story
|
||||
return render :plain => "story has been merged", :status => 400
|
||||
return render plain: "story has been merged", status: 400
|
||||
end
|
||||
|
||||
HiddenStory.hide_story_for_user(story.id, @user.id)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def unhide
|
||||
if !(story = find_story)
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
HiddenStory.unhide_story_for_user(story.id, @user.id)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def save
|
||||
if !(story = find_story)
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
if story.merged_into_story
|
||||
return render :plain => "story has been merged", :status => 400
|
||||
return render plain: "story has been merged", status: 400
|
||||
end
|
||||
|
||||
SavedStory.save_story_for_user(story.id, @user.id)
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def unsave
|
||||
if !(story = find_story)
|
||||
return render :plain => "can't find story", :status => 400
|
||||
return render plain: "can't find story", status: 400
|
||||
end
|
||||
|
||||
SavedStory.where(:user_id => @user.id, :story_id => story.id).delete_all
|
||||
SavedStory.where(user_id: @user.id, story_id: story.id).delete_all
|
||||
|
||||
render :plain => "ok"
|
||||
render plain: "ok"
|
||||
end
|
||||
|
||||
def check_url_dupe
|
||||
|
@ -374,19 +374,19 @@ class StoriesController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
return render :partial => "stories/form_errors", :layout => false,
|
||||
:content_type => "text/html", :locals => { :story => @story }
|
||||
return render partial: "stories/form_errors", layout: false,
|
||||
content_type: "text/html", locals: {story: @story}
|
||||
}
|
||||
# json: https://github.com/lobsters/lobsters/pull/555
|
||||
format.json {
|
||||
similar_stories = @story.public_similar_stories(@user).map(&:as_json)
|
||||
|
||||
render :json => @story.as_json.merge(similar_stories: similar_stories)
|
||||
render json: @story.as_json.merge(similar_stories: similar_stories)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def get_arranged_comments_from_cache(short_id, &block)
|
||||
if Rails.env.development? || @user
|
||||
|
@ -400,10 +400,10 @@ private
|
|||
p = params.require(:story).permit(
|
||||
:title, :url, :description, :moderation_reason, :seen_previous,
|
||||
:merge_story_short_id, :is_unavailable, :user_is_author, :user_is_following,
|
||||
:tags_a => [],
|
||||
tags_a: []
|
||||
)
|
||||
|
||||
if @user && @user.is_moderator?
|
||||
if @user&.is_moderator?
|
||||
p
|
||||
else
|
||||
p.except(:moderation_reason, :merge_story_short_id, :is_unavailable)
|
||||
|
@ -411,23 +411,23 @@ private
|
|||
end
|
||||
|
||||
def update_story_attributes
|
||||
if @story.url_is_editable_by_user?(@user)
|
||||
@story.attributes = story_params
|
||||
@story.attributes = if @story.url_is_editable_by_user?(@user)
|
||||
story_params
|
||||
else
|
||||
@story.attributes = story_params.except(:url)
|
||||
story_params.except(:url)
|
||||
end
|
||||
end
|
||||
|
||||
def find_story
|
||||
story = Story.find_by(:short_id => params[:story_id])
|
||||
story = Story.find_by(short_id: params[:story_id])
|
||||
# convenience to use PK (from external queries) without generally permitting enumeration:
|
||||
story ||= Story.find(params[:id]) if @user && @user.is_admin?
|
||||
story ||= Story.find(params[:id]) if @user&.is_admin?
|
||||
|
||||
if @user && story
|
||||
story.vote = Vote.find_by(
|
||||
user: @user,
|
||||
story: story.id,
|
||||
comment: nil
|
||||
comment: nil
|
||||
).try(:vote)
|
||||
end
|
||||
|
||||
|
@ -442,32 +442,32 @@ private
|
|||
end
|
||||
|
||||
def find_user_story
|
||||
if @user.is_moderator?
|
||||
@story = Story.where(:short_id => params[:story_id] || params[:id]).first
|
||||
@story = if @user.is_moderator?
|
||||
Story.where(short_id: params[:story_id] || params[:id]).first
|
||||
else
|
||||
@story = Story.where(:user_id => @user.id, :short_id =>
|
||||
(params[:story_id] || params[:id])).first
|
||||
Story.where(user_id: @user.id, short_id: (params[:story_id] || params[:id])).first
|
||||
end
|
||||
|
||||
if !@story
|
||||
flash[:error] = "Could not find story or you are not authorized " <<
|
||||
"to manage it."
|
||||
flash[:error] = "Could not find story or you are not authorized " \
|
||||
"to manage it."
|
||||
redirect_to "/"
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def load_user_votes
|
||||
if @user
|
||||
if (v = Vote.where(:user_id => @user.id, :story_id => @story.id, :comment_id => nil).first)
|
||||
@story.vote = { :vote => v.vote, :reason => v.reason }
|
||||
if (v = Vote.where(user_id: @user.id, story_id: @story.id, comment_id: nil).first)
|
||||
@story.vote = {vote: v.vote, reason: v.reason}
|
||||
end
|
||||
|
||||
@story.is_hidden_by_cur_user = @story.is_hidden_by_user?(@user)
|
||||
@story.is_saved_by_cur_user = @story.is_saved_by_user?(@user)
|
||||
|
||||
@votes = Vote.comment_votes_by_user_for_story_hash(
|
||||
@user.id, (@story.merged_stories.ids).push(@story.id))
|
||||
@user.id, @story.merged_stories.ids.push(@story.id)
|
||||
)
|
||||
@comments.each do |c|
|
||||
if @votes[c.id]
|
||||
c.current_vote = @votes[c.id]
|
||||
|
@ -479,7 +479,7 @@ private
|
|||
def verify_user_can_submit_stories
|
||||
if !@user.can_submit_stories?
|
||||
flash[:error] = "You are not allowed to submit new stories."
|
||||
return redirect_to "/"
|
||||
redirect_to "/"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,18 +5,18 @@ class TagsController < ApplicationController
|
|||
def index
|
||||
@title = "Tags"
|
||||
|
||||
@categories = Category.all.order('category asc').includes(:tags)
|
||||
@categories = Category.all.order("category asc").includes(:tags)
|
||||
@tags = Tag.all
|
||||
|
||||
if @user
|
||||
@filtered_tags = @user.tag_filter_tags.index_by(&:id)
|
||||
@filtered_tags = if @user
|
||||
@user.tag_filter_tags.index_by(&:id)
|
||||
else
|
||||
@filtered_tags = tags_filtered_by_cookie.index_by(&:id)
|
||||
tags_filtered_by_cookie.index_by(&:id)
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "index" }
|
||||
format.json { render :json => @tags }
|
||||
format.html { render action: "index" }
|
||||
format.json { render json: @tags }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,34 +26,34 @@ class TagsController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
@title = 'Create Tag'
|
||||
@title = "Create Tag"
|
||||
tag = Tag.create(tag_params)
|
||||
if tag.valid?
|
||||
flash[:success] = "Tag #{tag.tag} has been created"
|
||||
redirect_to tags_path
|
||||
else
|
||||
flash[:error] = "New tag not created: #{tag.errors.full_messages.join(', ')}"
|
||||
flash[:error] = "New tag not created: #{tag.errors.full_messages.join(", ")}"
|
||||
redirect_to new_tag_path
|
||||
end
|
||||
end
|
||||
|
||||
def edit
|
||||
@tag = Tag.where(:tag => params[:tag_name]).first!
|
||||
@tag = Tag.where(tag: params[:tag_name]).first!
|
||||
@title = "Edit Tag"
|
||||
end
|
||||
|
||||
def update
|
||||
tag = Tag.where(:tag => params[:tag_name]).first!
|
||||
tag = Tag.where(tag: params[:tag_name]).first!
|
||||
if tag.update(tag_params)
|
||||
flash[:success] = "Tag #{tag.tag} has been updated"
|
||||
redirect_to tags_path
|
||||
else
|
||||
flash[:error] = "Tag not updated: #{tag.errors.full_messages.join(', ')}"
|
||||
flash[:error] = "Tag not updated: #{tag.errors.full_messages.join(", ")}"
|
||||
redirect_to edit_tag_path
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def tag_params
|
||||
params.require(:tag).permit(
|
||||
|
@ -65,7 +65,7 @@ private
|
|||
:privileged,
|
||||
:is_media,
|
||||
:active,
|
||||
:hotness_mod,
|
||||
:hotness_mod
|
||||
).merge(edit_user_id: @user.id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class UsersController < ApplicationController
|
||||
before_action :load_showing_user, :only => [:show, :standing]
|
||||
before_action :load_showing_user, only: [:show, :standing]
|
||||
before_action :require_logged_in_moderator,
|
||||
:only => [:enable_invitation, :disable_invitation, :ban, :unban]
|
||||
only: [:enable_invitation, :disable_invitation, :ban, :unban]
|
||||
before_action :flag_warning, only: [:show]
|
||||
before_action :require_logged_in_user, only: [:standing]
|
||||
before_action :only_user_or_moderator, only: [:standing]
|
||||
|
@ -21,8 +21,8 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :action => "show" }
|
||||
format.json { render :json => @showing_user }
|
||||
format.html { render action: "show" }
|
||||
format.json { render json: @showing_user }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,32 +31,32 @@ class UsersController < ApplicationController
|
|||
newest_user = User.last.id
|
||||
|
||||
# pulling 10k+ users is significant enough memory pressure this is worthwhile
|
||||
attrs = %w{banned_at created_at deleted_at id invited_by_user_id is_admin is_moderator karma
|
||||
username}
|
||||
attrs = %w[banned_at created_at deleted_at id invited_by_user_id is_admin is_moderator karma
|
||||
username]
|
||||
|
||||
if params[:by].to_s == "karma"
|
||||
content = Rails.cache.fetch("users_by_karma_#{newest_user}", :expires_in => (60 * 60 * 24)) {
|
||||
content = Rails.cache.fetch("users_by_karma_#{newest_user}", expires_in: (60 * 60 * 24)) {
|
||||
@users = User.select(*attrs).order("karma DESC, id ASC").to_a
|
||||
@user_count = @users.length
|
||||
@title << " By Karma"
|
||||
render_to_string :action => "list", :layout => nil
|
||||
render_to_string action: "list", layout: nil
|
||||
}
|
||||
render :html => content.html_safe, :layout => "application"
|
||||
render html: content.html_safe, layout: "application"
|
||||
elsif params[:moderators]
|
||||
@users = User.select(*attrs).where("is_admin = ? OR is_moderator = ?", true, true)
|
||||
.order("id ASC").to_a
|
||||
@user_count = @users.length
|
||||
@title = "Moderators and Administrators"
|
||||
render :action => "list"
|
||||
render action: "list"
|
||||
else
|
||||
content = Rails.cache.fetch("users_tree_#{newest_user}", :expires_in => (60 * 60 * 24)) {
|
||||
content = Rails.cache.fetch("users_tree_#{newest_user}", expires_in: (60 * 60 * 24)) {
|
||||
users = User.select(*attrs).order("id DESC").to_a
|
||||
@user_count = users.length
|
||||
@users_by_parent = users.group_by(&:invited_by_user_id)
|
||||
@newest = User.select(*attrs).order("id DESC").limit(10)
|
||||
render_to_string :action => "tree", :layout => nil
|
||||
render_to_string action: "tree", layout: nil
|
||||
}
|
||||
render :html => content.html_safe, :layout => "application"
|
||||
render html: content.html_safe, layout: "application"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,7 +65,7 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def disable_invitation
|
||||
target = User.where(:username => params[:username]).first
|
||||
target = User.where(username: params[:username]).first
|
||||
if !target
|
||||
flash[:error] = "Invalid user."
|
||||
redirect_to "/"
|
||||
|
@ -73,12 +73,12 @@ class UsersController < ApplicationController
|
|||
target.disable_invite_by_user_for_reason!(@user, params[:reason])
|
||||
|
||||
flash[:success] = "User has had invite capability disabled."
|
||||
redirect_to user_path(:user => target.username)
|
||||
redirect_to user_path(user: target.username)
|
||||
end
|
||||
end
|
||||
|
||||
def enable_invitation
|
||||
target = User.where(:username => params[:username]).first
|
||||
target = User.where(username: params[:username]).first
|
||||
if !target
|
||||
flash[:error] = "Invalid user."
|
||||
redirect_to "/"
|
||||
|
@ -86,12 +86,12 @@ class UsersController < ApplicationController
|
|||
target.enable_invite_by_user!(@user)
|
||||
|
||||
flash[:success] = "User has had invite capability enabled."
|
||||
redirect_to user_path(:user => target.username)
|
||||
redirect_to user_path(user: target.username)
|
||||
end
|
||||
end
|
||||
|
||||
def ban
|
||||
buser = User.where(:username => params[:username]).first
|
||||
buser = User.where(username: params[:username]).first
|
||||
if !buser
|
||||
flash[:error] = "Invalid user."
|
||||
return redirect_to "/"
|
||||
|
@ -99,17 +99,17 @@ class UsersController < ApplicationController
|
|||
|
||||
if !params[:reason].present?
|
||||
flash[:error] = "You must give a reason for the ban."
|
||||
return redirect_to user_path(:user => buser.username)
|
||||
return redirect_to user_path(user: buser.username)
|
||||
end
|
||||
|
||||
buser.ban_by_user_for_reason!(@user, params[:reason])
|
||||
|
||||
flash[:success] = "User has been banned."
|
||||
return redirect_to user_path(:user => buser.username)
|
||||
redirect_to user_path(user: buser.username)
|
||||
end
|
||||
|
||||
def unban
|
||||
buser = User.where(:username => params[:username]).first
|
||||
buser = User.where(username: params[:username]).first
|
||||
if !buser
|
||||
flash[:error] = "Invalid user."
|
||||
return redirect_to "/"
|
||||
|
@ -118,7 +118,7 @@ class UsersController < ApplicationController
|
|||
buser.unban_by_user!(@user, params[:reason])
|
||||
|
||||
flash[:success] = "User has been unbanned."
|
||||
return redirect_to user_path(:user => buser.username)
|
||||
redirect_to user_path(user: buser.username)
|
||||
end
|
||||
|
||||
def standing
|
||||
|
@ -126,7 +126,7 @@ class UsersController < ApplicationController
|
|||
int = @flag_warning_int
|
||||
|
||||
fc = FlaggedCommenters.new(int[:param], 1.day)
|
||||
@fc_flagged = fc.commenters.map {|_, c| c[:n_flags] }.sort
|
||||
@fc_flagged = fc.commenters.map { |_, c| c[:n_flags] }.sort
|
||||
@flagged_user_stats = fc.check_list_for(@showing_user)
|
||||
|
||||
rows = ActiveRecord::Base.connection.exec_query("
|
||||
|
@ -144,7 +144,7 @@ class UsersController < ApplicationController
|
|||
order by 1 asc;
|
||||
").rows
|
||||
users = Array.new(@fc_flagged.last.to_i + 1, 0)
|
||||
rows.each {|r| users[r.first] = r.last }
|
||||
rows.each { |r| users[r.first] = r.last }
|
||||
@lookup = rows.to_h
|
||||
|
||||
@flagged_comments = @showing_user.comments
|
||||
|
@ -152,11 +152,11 @@ class UsersController < ApplicationController
|
|||
comments.flags > 0 and
|
||||
comments.created_at >= now() - interval #{int[:dur]} #{int[:intv]}")
|
||||
.order("id DESC")
|
||||
.includes(:user, :hat, :story => :user)
|
||||
.includes(:user, :hat, story: :user)
|
||||
.joins(:story)
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def load_showing_user
|
||||
# case-insensitive search by username
|
||||
|
@ -171,7 +171,7 @@ private
|
|||
# now a case-sensitive check
|
||||
if params[:username] != @showing_user.username
|
||||
redirect_to username: @showing_user.username
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ module ApplicationHelper
|
|||
def avatar_img(user, size)
|
||||
image_tag(
|
||||
user.avatar_path(size),
|
||||
:srcset => "#{user.avatar_path(size)} 1x, #{user.avatar_path(size * 2)} 2x",
|
||||
:class => "avatar",
|
||||
:size => "#{size}x#{size}",
|
||||
:alt => "#{user.username} avatar",
|
||||
:loading => "lazy",
|
||||
:decoding => "async",
|
||||
srcset: "#{user.avatar_path(size)} 1x, #{user.avatar_path(size * 2)} 2x",
|
||||
class: "avatar",
|
||||
size: "#{size}x#{size}",
|
||||
alt: "#{user.username} avatar",
|
||||
loading: "lazy",
|
||||
decoding: "async"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -19,7 +19,7 @@ module ApplicationHelper
|
|||
html = ""
|
||||
unless object.errors.blank?
|
||||
html << "<div class=\"flash-error\">"
|
||||
html << "<h2>#{pluralize(object.errors.count, 'error')} prohibited this \
|
||||
html << "<h2>#{pluralize(object.errors.count, "error")} prohibited this \
|
||||
#{object.class.name.downcase} from being saved</h2>"
|
||||
html << "<p>There were the problems with the following fields:</p>"
|
||||
html << "<ul>"
|
||||
|
@ -35,28 +35,28 @@ module ApplicationHelper
|
|||
# limitation: this can't handle generating links based on a hash of options,
|
||||
# like { controller: ..., action: ... }
|
||||
def link_to_different_page(text, path, options = {})
|
||||
current = request.path.sub(/\/page\/\d+$/, '')
|
||||
path.sub!(/\/page\/\d+$/, '')
|
||||
current = request.path.sub(/\/page\/\d+$/, "")
|
||||
path.sub!(/\/page\/\d+$/, "")
|
||||
options[:class] = :current_page if current == path
|
||||
link_to text, path, options
|
||||
end
|
||||
|
||||
def link_post button_label, link, options = {}
|
||||
options.reverse_merge class_name: nil, confirm: nil
|
||||
render partial: 'helpers/link_post', locals: {
|
||||
render partial: "helpers/link_post", locals: {
|
||||
button_label: button_label,
|
||||
link: link,
|
||||
class_name: options[:class_name],
|
||||
confirm: options[:confirm],
|
||||
confirm: options[:confirm]
|
||||
}
|
||||
end
|
||||
|
||||
def page_numbers_for_pagination(max, cur)
|
||||
if max <= MAX_PAGES
|
||||
return (1 .. max).to_a
|
||||
return (1..max).to_a
|
||||
end
|
||||
|
||||
pages = (cur - (MAX_PAGES / 2) + 1 .. cur + (MAX_PAGES / 2) - 1).to_a
|
||||
pages = (cur - (MAX_PAGES / 2) + 1..cur + (MAX_PAGES / 2) - 1).to_a
|
||||
|
||||
while pages[0] < 1
|
||||
pages.push pages.last + 1
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
module IntervalHelper
|
||||
TIME_INTERVALS = { "h" => "Hour",
|
||||
"d" => "Day",
|
||||
"w" => "Week",
|
||||
"m" => "Month",
|
||||
"y" => "Year", }.freeze
|
||||
TIME_INTERVALS = {"h" => "Hour",
|
||||
"d" => "Day",
|
||||
"w" => "Week",
|
||||
"m" => "Month",
|
||||
"y" => "Year"}.freeze
|
||||
|
||||
def time_interval(param)
|
||||
if (m = param.to_s.match(/\A(\d+)([#{TIME_INTERVALS.keys.join}])\z/))
|
||||
|
@ -12,10 +12,10 @@ module IntervalHelper
|
|||
param: param,
|
||||
dur: dur,
|
||||
intv: TIME_INTERVALS[m[2]],
|
||||
human: "#{dur == 1 ? '' : dur} #{TIME_INTERVALS[m[2]]}".downcase.pluralize(dur).chomp,
|
||||
human: "#{(dur == 1) ? "" : dur} #{TIME_INTERVALS[m[2]]}".downcase.pluralize(dur).chomp
|
||||
}
|
||||
else
|
||||
{ input: '1w', dur: 1, intv: "Week", human: 'week' }
|
||||
{input: "1w", dur: 1, intv: "Week", human: "week"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,6 @@ module KeybaseProofsHelper
|
|||
end
|
||||
|
||||
def keybase_proof_link(kb_sig)
|
||||
File.join Keybase.BASE_URL, kb_sig[:kb_username], "sigchain\##{kb_sig[:sig_hash]}"
|
||||
File.join Keybase.BASE_URL, kb_sig[:kb_username], "sigchain##{kb_sig[:sig_hash]}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,11 +9,11 @@ module StoriesHelper
|
|||
end
|
||||
|
||||
if Moderation.joins(:story)
|
||||
.where(
|
||||
"stories.user_id = ? AND moderations.created_at > ?",
|
||||
@user.id,
|
||||
5.days.ago
|
||||
).exists?
|
||||
.where(
|
||||
"stories.user_id = ? AND moderations.created_at > ?",
|
||||
@user.id,
|
||||
5.days.ago
|
||||
).exists?
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -30,10 +30,10 @@ module TrafficHelper
|
|||
end
|
||||
|
||||
def self.cache_traffic!
|
||||
low, high = self.traffic_range
|
||||
Keystore.put('traffic:low', low)
|
||||
Keystore.put('traffic:high', high)
|
||||
Keystore.put('traffic:intensity', current_intensity(low, high))
|
||||
low, high = traffic_range
|
||||
Keystore.put("traffic:low", low)
|
||||
Keystore.put("traffic:high", high)
|
||||
Keystore.put("traffic:intensity", current_intensity(low, high))
|
||||
end
|
||||
|
||||
def self.current_activity
|
||||
|
@ -50,11 +50,11 @@ module TrafficHelper
|
|||
def self.current_intensity(low, high)
|
||||
return 0.5 if low.nil? || high.nil? || high == low
|
||||
activity = [low, current_activity, high].sort[1]
|
||||
[0, ((activity - low)*1.0/(high - low) * 100).round, 100].sort[1]
|
||||
[0, ((activity - low) * 1.0 / (high - low) * 100).round, 100].sort[1]
|
||||
end
|
||||
|
||||
def self.cached_current_intensity
|
||||
Keystore.value_for('traffic:intensity') || 0.5
|
||||
Keystore.value_for("traffic:intensity") || 0.5
|
||||
end
|
||||
|
||||
# rubocop:disable Layout/LineLength
|
||||
|
|
|
@ -32,18 +32,18 @@ module UsersHelper
|
|||
|
||||
def styled_user_link user, content = nil, css_classes = []
|
||||
if content.is_a?(Story) && content.user_is_author?
|
||||
css_classes.push 'user_is_author'
|
||||
css_classes.push "user_is_author"
|
||||
end
|
||||
if content.is_a?(Comment) && content.story &&
|
||||
content.story.user_is_author? && content.story.user_id == user.id
|
||||
css_classes.push 'user_is_author'
|
||||
content.story.user_is_author? && content.story.user_id == user.id
|
||||
css_classes.push "user_is_author"
|
||||
end
|
||||
|
||||
if !user.is_active?
|
||||
css_classes.push 'inactive_user'
|
||||
css_classes.push "inactive_user"
|
||||
end
|
||||
if user.is_new?
|
||||
css_classes.push 'new_user'
|
||||
css_classes.push "new_user"
|
||||
end
|
||||
|
||||
link_to(user.username, user, class: css_classes)
|
||||
|
@ -59,9 +59,9 @@ module UsersHelper
|
|||
end
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def user_is_moderator?
|
||||
@user && @user.is_moderator?
|
||||
@user&.is_moderator?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
class ApplicationMailer < ActionMailer::Base
|
||||
default :from => "#{Rails.application.name} <nobody@#{Rails.application.domain}>"
|
||||
default from: "#{Rails.application.name} <nobody@#{Rails.application.domain}>"
|
||||
end
|
||||
|
|
|
@ -4,10 +4,10 @@ class BanNotification < ApplicationMailer
|
|||
@reason = reason
|
||||
|
||||
mail(
|
||||
:from => "#{@banner.username} <nobody@#{Rails.application.domain}>",
|
||||
:replyto => "#{@banner.username} <#{@banner.email}>",
|
||||
:to => user.email,
|
||||
:subject => "[#{Rails.application.name}] You have been banned"
|
||||
from: "#{@banner.username} <nobody@#{Rails.application.domain}>",
|
||||
replyto: "#{@banner.username} <#{@banner.email}>",
|
||||
to: user.email,
|
||||
subject: "[#{Rails.application.name}] You have been banned"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ class EmailMessage < ApplicationMailer
|
|||
@user = user
|
||||
|
||||
mail(
|
||||
:to => user.email,
|
||||
:subject => "[#{Rails.application.name}] Private Message from " <<
|
||||
to: user.email,
|
||||
subject: "[#{Rails.application.name}] Private Message from " \
|
||||
"#{message.author_username}: #{message.subject}"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ class EmailReply < ApplicationMailer
|
|||
@user = user
|
||||
|
||||
mail(
|
||||
:to => user.email,
|
||||
:subject => "[#{Rails.application.name}] Reply from " <<
|
||||
to: user.email,
|
||||
subject: "[#{Rails.application.name}] Reply from " \
|
||||
"#{comment.user.username} on #{comment.story.title}"
|
||||
)
|
||||
end
|
||||
|
@ -15,8 +15,8 @@ class EmailReply < ApplicationMailer
|
|||
@user = user
|
||||
|
||||
mail(
|
||||
:to => user.email,
|
||||
:subject => "[#{Rails.application.name}] Mention from " <<
|
||||
to: user.email,
|
||||
subject: "[#{Rails.application.name}] Mention from " \
|
||||
"#{comment.user.username} on #{comment.story.title}"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class InvitationRequestMailer < ApplicationMailer
|
|||
|
||||
mail(
|
||||
to: invitation_request.email,
|
||||
subject: "[#{Rails.application.name}] Confirm your invitation " <<
|
||||
subject: "[#{Rails.application.name}] Confirm your invitation " \
|
||||
"request to " << Rails.application.name
|
||||
)
|
||||
end
|
||||
|
|
|
@ -4,8 +4,8 @@ class PasswordReset < ApplicationMailer
|
|||
@ip = ip
|
||||
|
||||
mail(
|
||||
:to => user.email,
|
||||
:subject => "[#{Rails.application.name}] Reset your password"
|
||||
to: user.email,
|
||||
subject: "[#{Rails.application.name}] Reset your password"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,5 +2,5 @@ class ApplicationRecord < ActiveRecord::Base
|
|||
self.abstract_class = true
|
||||
|
||||
# https://stackoverflow.com/questions/50026344/composing-activerecord-scopes-with-selects
|
||||
scope :select_fix, -> { select(self.arel_table.project(Arel.star)) }
|
||||
scope :select_fix, -> { select(arel_table.project(Arel.star)) }
|
||||
end
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
class Category < ApplicationRecord
|
||||
has_many :tags,
|
||||
-> { order('tag asc') },
|
||||
dependent: :restrict_with_error,
|
||||
inverse_of: :category
|
||||
-> { order("tag asc") },
|
||||
dependent: :restrict_with_error,
|
||||
inverse_of: :category
|
||||
has_many :stories, through: :tags
|
||||
|
||||
after_save :log_modifications
|
||||
|
||||
attr_accessor :edit_user_id
|
||||
|
||||
validates :category, length: { maximum: 25 }, presence: true,
|
||||
uniqueness: { case_sensitive: false },
|
||||
format: { with: /\A[A-Za-z0-9_\-]+\z/ }
|
||||
validates :category, length: {maximum: 25}, presence: true,
|
||||
uniqueness: {case_sensitive: false},
|
||||
format: {with: /\A[A-Za-z0-9_\-]+\z/}
|
||||
|
||||
def to_param
|
||||
self.category
|
||||
category
|
||||
end
|
||||
|
||||
def log_modifications
|
||||
Moderation.create do |m|
|
||||
if self.id_previously_changed?
|
||||
m.action = 'Created new category ' +
|
||||
self.attributes.map {|f, c| "with #{f} '#{c}'" }.join(', ')
|
||||
m.action = if id_previously_changed?
|
||||
"Created new category " +
|
||||
attributes.map { |f, c| "with #{f} '#{c}'" }.join(", ")
|
||||
else
|
||||
m.action = "Updating category #{self.category}, " + self.saved_changes
|
||||
.map {|f, c| "changed #{f} from '#{c[0]}' to '#{c[1]}'" } .join(', ')
|
||||
"Updating category #{category}, " + saved_changes
|
||||
.map { |f, c| "changed #{f} from '#{c[0]}' to '#{c[1]}'" }.join(", ")
|
||||
end
|
||||
m.moderator_user_id = @edit_user_id
|
||||
m.category_id = self.id
|
||||
m.category_id = id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
require 'set'
|
||||
|
||||
class Comment < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :story,
|
||||
:inverse_of => :comments
|
||||
inverse_of: :comments
|
||||
has_many :votes,
|
||||
:dependent => :delete_all
|
||||
dependent: :delete_all
|
||||
belongs_to :parent_comment,
|
||||
:class_name => "Comment",
|
||||
:inverse_of => false,
|
||||
:optional => true
|
||||
class_name: "Comment",
|
||||
inverse_of: false,
|
||||
optional: true
|
||||
has_one :moderation,
|
||||
:class_name => "Moderation",
|
||||
:inverse_of => :comment,
|
||||
:dependent => :destroy
|
||||
class_name: "Moderation",
|
||||
inverse_of: :comment,
|
||||
dependent: :destroy
|
||||
belongs_to :hat,
|
||||
:optional => true
|
||||
optional: true
|
||||
has_many :taggings, through: :story
|
||||
|
||||
attr_accessor :current_vote, :previewing
|
||||
|
@ -23,15 +21,15 @@ class Comment < ApplicationRecord
|
|||
attribute :reply_count, :integer
|
||||
|
||||
before_validation on: :create do
|
||||
self.assign_short_id_and_score
|
||||
self.assign_initial_confidence
|
||||
self.assign_thread_id
|
||||
assign_short_id_and_score
|
||||
assign_initial_confidence
|
||||
assign_thread_id
|
||||
end
|
||||
after_create :record_initial_upvote, :mark_submitter, :deliver_reply_notifications,
|
||||
:deliver_mention_notifications, :log_hat_use
|
||||
:deliver_mention_notifications, :log_hat_use
|
||||
after_create do
|
||||
# fire this once after record_initial_upvote
|
||||
self.update_score_and_recalculate! 0, 0
|
||||
update_score_and_recalculate! 0, 0
|
||||
end
|
||||
after_destroy :unassign_votes
|
||||
|
||||
|
@ -39,20 +37,20 @@ class Comment < ApplicationRecord
|
|||
scope :not_deleted, -> { where(is_deleted: false) }
|
||||
scope :not_moderated, -> { where(is_moderated: false) }
|
||||
scope :active, -> { not_deleted.not_moderated }
|
||||
scope :accessible_to_user, ->(user) { user && user.is_moderator? ? all : active }
|
||||
scope :accessible_to_user, ->(user) { (user && user.is_moderator?) ? all : active }
|
||||
scope :for_presentation, -> {
|
||||
includes(:user, :hat, :moderation => :moderator, :story => :user, :votes => :user)
|
||||
includes(:user, :hat, moderation: :moderator, story: :user, votes: :user)
|
||||
}
|
||||
scope :not_on_story_hidden_by, ->(user) {
|
||||
user ? where.not(
|
||||
HiddenStory.select('TRUE')
|
||||
.where(Arel.sql('hidden_stories.story_id = stories.id'))
|
||||
HiddenStory.select("TRUE")
|
||||
.where(Arel.sql("hidden_stories.story_id = stories.id"))
|
||||
.by(user).arel.exists
|
||||
) : where('true')
|
||||
) : where("true")
|
||||
}
|
||||
# workaround: if this select is in #parents, calling .count produces invalid SQL
|
||||
scope :with_thread_attributes, -> {
|
||||
select('comments.*, comments_recursive.depth as depth, comments_recursive.reply_count')
|
||||
select("comments.*, comments_recursive.depth as depth, comments_recursive.reply_count")
|
||||
}
|
||||
|
||||
FLAGGABLE_DAYS = 7
|
||||
|
@ -74,39 +72,39 @@ class Comment < ApplicationRecord
|
|||
# but in practice all deep reply chains have gone off-topic and/or tuned into flamewars.
|
||||
MAX_DEPTH = 18
|
||||
|
||||
SCORE_RANGE_TO_HIDE = (-2 .. 4).freeze
|
||||
SCORE_RANGE_TO_HIDE = (-2..4)
|
||||
|
||||
validates :short_id, length: { maximum: 10 }
|
||||
validates :short_id, length: {maximum: 10}
|
||||
validates :user_id, presence: true
|
||||
validates :story_id, presence: true
|
||||
validates :markeddown_comment, length: { maximum: 16_777_215 }
|
||||
validates :comment, presence: { with: true, message: "cannot be empty." }
|
||||
validates :markeddown_comment, length: {maximum: 16_777_215}
|
||||
validates :comment, presence: {with: true, message: "cannot be empty."}
|
||||
|
||||
validate do
|
||||
self.parent_comment && self.parent_comment.is_gone? &&
|
||||
parent_comment&.is_gone? &&
|
||||
errors.add(:base, "Comment was deleted by the author or a mod while you were writing.")
|
||||
|
||||
self.parent_comment && !self.parent_comment.depth_permits_reply? &&
|
||||
ModNote.tattle_on_max_depth_limit(self.user, self.parent_comment) &&
|
||||
parent_comment && !parent_comment.depth_permits_reply? &&
|
||||
ModNote.tattle_on_max_depth_limit(user, parent_comment) &&
|
||||
errors.add(:base, "You have replied too greedily and too deep.")
|
||||
|
||||
(m = self.comment.to_s.strip.match(/\A(t)his([\.!])?$\z/i)) &&
|
||||
errors.add(:base, (m[1] == "T" ? "N" : "n") + "ope" + m[2].to_s)
|
||||
(m = comment.to_s.strip.match(/\A(t)his([\.!])?$\z/i)) &&
|
||||
errors.add(:base, ((m[1] == "T") ? "N" : "n") + "ope" + m[2].to_s)
|
||||
|
||||
self.comment.to_s.strip.match(/\Atl;?dr.?$\z/i) &&
|
||||
comment.to_s.strip.match(/\Atl;?dr.?$\z/i) &&
|
||||
errors.add(:base, "Wow! A blue car!")
|
||||
|
||||
self.comment.to_s.strip.match(/\A([[[:upper:]][[:punct:]]] )+[[[:upper:]][[:punct:]]]?$\z/) &&
|
||||
comment.to_s.strip.match(/\A([[[:upper:]][[:punct:]]] )+[[[:upper:]][[:punct:]]]?$\z/) &&
|
||||
errors.add(:base, "D O N ' T")
|
||||
|
||||
self.comment.to_s.strip.match(/\A(me too|nice)([\.!])?\z/i) &&
|
||||
comment.to_s.strip.match(/\A(me too|nice)([\.!])?\z/i) &&
|
||||
errors.add(:base, "Please just upvote the parent post instead.")
|
||||
|
||||
self.hat.present? && self.user.wearable_hats.exclude?(self.hat) &&
|
||||
hat.present? && user.wearable_hats.exclude?(hat) &&
|
||||
errors.add(:hat, "not wearable by user")
|
||||
|
||||
# .try so tests don't need to persist a story and user
|
||||
self.story.try(:accepting_comments?) ||
|
||||
story.try(:accepting_comments?) ||
|
||||
errors.add(:base, "Story is no longer accepting comments.")
|
||||
end
|
||||
|
||||
|
@ -115,7 +113,7 @@ class Comment < ApplicationRecord
|
|||
|
||||
Comment.all.find_each do |c|
|
||||
c.markeddown_comment = c.generated_markeddown_comment
|
||||
c.save(:validate => false)
|
||||
c.save(validate: false)
|
||||
end
|
||||
|
||||
Comment.record_timestamps = true
|
||||
|
@ -133,23 +131,23 @@ class Comment < ApplicationRecord
|
|||
:is_moderated,
|
||||
:score,
|
||||
:flags,
|
||||
{ :parent_comment => self.parent_comment && self.parent_comment.short_id },
|
||||
{ :comment => (self.is_gone? ? "<em>#{self.gone_text}</em>" : :markeddown_comment) },
|
||||
{ :comment_plain => (self.is_gone? ? self.gone_text : :comment) },
|
||||
{parent_comment: parent_comment&.short_id},
|
||||
{comment: (is_gone? ? "<em>#{gone_text}</em>" : :markeddown_comment)},
|
||||
{comment_plain: (is_gone? ? gone_text : :comment)},
|
||||
:url,
|
||||
:depth,
|
||||
{ :commenting_user => :user },
|
||||
{commenting_user: :user}
|
||||
]
|
||||
|
||||
js = {}
|
||||
h.each do |k|
|
||||
if k.is_a?(Symbol)
|
||||
js[k] = self.send(k)
|
||||
js[k] = send(k)
|
||||
elsif k.is_a?(Hash)
|
||||
if k.values.first.is_a?(Symbol)
|
||||
js[k.keys.first] = self.send(k.values.first)
|
||||
js[k.keys.first] = if k.values.first.is_a?(Symbol)
|
||||
send(k.values.first)
|
||||
else
|
||||
js[k.keys.first] = k.values.first
|
||||
k.values.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -158,10 +156,10 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def assign_initial_confidence
|
||||
self.confidence = self.calculated_confidence
|
||||
self.confidence = calculated_confidence
|
||||
# 3 byte placeholder, immediately replaced by after_create callback calling
|
||||
# update_score_and_recalculate! to fill in the autogenerated 'id' value
|
||||
self.confidence_order = [0, 0, 0].pack('CCC')
|
||||
self.confidence_order = [0, 0, 0].pack("CCC")
|
||||
end
|
||||
|
||||
def assign_short_id_and_score
|
||||
|
@ -170,20 +168,20 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def assign_thread_id
|
||||
if self.parent_comment.present?
|
||||
self.thread_id = self.parent_comment.thread_id
|
||||
self.thread_id = if parent_comment.present?
|
||||
parent_comment.thread_id
|
||||
else
|
||||
self.thread_id = Keystore.incremented_value_for("thread_id")
|
||||
Keystore.incremented_value_for("thread_id")
|
||||
end
|
||||
end
|
||||
|
||||
# http://evanmiller.org/how-not-to-sort-by-average-rating.html
|
||||
# https://github.com/reddit/reddit/blob/master/r2/r2/lib/db/_sorts.pyx
|
||||
def calculated_confidence
|
||||
n = (self.score + self.flags * 2).to_f
|
||||
return 0 if n == 0.0
|
||||
return 0 if self.score == 0 && flags == 0
|
||||
n = (self.score + flags * 2).to_f
|
||||
|
||||
upvotes = self.score + self.flags
|
||||
upvotes = self.score + flags
|
||||
z = 1.281551565545 # 80% confidence
|
||||
p = upvotes.to_f / n
|
||||
|
||||
|
@ -191,7 +189,7 @@ class Comment < ApplicationRecord
|
|||
right = z * Math.sqrt((p * ((1.0 - p) / n)) + (z * (z / (4.0 * n * n))))
|
||||
under = 1.0 + ((1.0 / n) * z * z)
|
||||
|
||||
return (left - right) / under
|
||||
(left - right) / under
|
||||
end
|
||||
|
||||
# rate-limit users in heated reply chains, called by controller so that authors can preview
|
||||
|
@ -223,7 +221,7 @@ class Comment < ApplicationRecord
|
|||
|
||||
def comment=(com)
|
||||
self[:comment] = com.to_s.rstrip
|
||||
self.markeddown_comment = self.generated_markeddown_comment
|
||||
self.markeddown_comment = generated_markeddown_comment
|
||||
end
|
||||
|
||||
def delete_for_user(user, reason = nil)
|
||||
|
@ -231,11 +229,11 @@ class Comment < ApplicationRecord
|
|||
|
||||
self.is_deleted = true
|
||||
|
||||
if user.is_moderator? && user.id != self.user_id
|
||||
if user.is_moderator? && user.id != user_id
|
||||
self.is_moderated = true
|
||||
|
||||
m = Moderation.new
|
||||
m.comment_id = self.id
|
||||
m.comment_id = id
|
||||
m.moderator_user_id = user.id
|
||||
m.action = "deleted comment"
|
||||
|
||||
|
@ -245,20 +243,20 @@ class Comment < ApplicationRecord
|
|||
|
||||
m.save
|
||||
|
||||
User.update_counters self.user_id, karma: (self.votes.count * -2)
|
||||
User.update_counters user_id, karma: (votes.count * -2)
|
||||
end
|
||||
|
||||
self.save(:validate => false)
|
||||
save(validate: false)
|
||||
Comment.record_timestamps = true
|
||||
|
||||
self.story.update_comments_count!
|
||||
story.update_comments_count!
|
||||
self.user.refresh_counts!
|
||||
end
|
||||
|
||||
def deliver_mention_notifications
|
||||
self.plaintext_comment.scan(/\B\@([\w\-]+)/).flatten.uniq.each do |mention|
|
||||
if (u = User.active.find_by(:username => mention))
|
||||
if u.id == self.user.id
|
||||
plaintext_comment.scan(/\B@([\w\-]+)/).flatten.uniq.each do |mention|
|
||||
if (u = User.active.find_by(username: mention))
|
||||
if u.id == user.id
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -272,11 +270,11 @@ class Comment < ApplicationRecord
|
|||
|
||||
if u.pushover_mentions?
|
||||
u.pushover!(
|
||||
:title => "#{Rails.application.name} mention by " <<
|
||||
"#{self.user.username} on #{self.story.title}",
|
||||
:message => self.plaintext_comment,
|
||||
:url => self.url,
|
||||
:url_title => "Reply to #{self.user.username}",
|
||||
title: "#{Rails.application.name} mention by " \
|
||||
"#{user.username} on #{story.title}",
|
||||
message: plaintext_comment,
|
||||
url: url,
|
||||
url_title: "Reply to #{user.username}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -285,14 +283,14 @@ class Comment < ApplicationRecord
|
|||
|
||||
def users_following_thread
|
||||
users_following_thread = Set.new
|
||||
if self.user.id != self.story.user.id && self.story.user_is_following
|
||||
users_following_thread << self.story.user
|
||||
if user.id != story.user.id && story.user_is_following
|
||||
users_following_thread << story.user
|
||||
end
|
||||
|
||||
if self.parent_comment_id &&
|
||||
(u = self.parent_comment.try(:user)) &&
|
||||
u.id != self.user.id &&
|
||||
u.is_active?
|
||||
if parent_comment_id &&
|
||||
(u = parent_comment.try(:user)) &&
|
||||
u.id != user.id &&
|
||||
u.is_active?
|
||||
users_following_thread << u
|
||||
end
|
||||
|
||||
|
@ -311,11 +309,11 @@ class Comment < ApplicationRecord
|
|||
|
||||
if u.pushover_replies?
|
||||
u.pushover!(
|
||||
:title => "#{Rails.application.name} reply from " <<
|
||||
"#{self.user.username} on #{self.story.title}",
|
||||
:message => self.plaintext_comment,
|
||||
:url => self.url,
|
||||
:url_title => "Reply to #{self.user.username}",
|
||||
title: "#{Rails.application.name} reply from " \
|
||||
"#{user.username} on #{story.title}",
|
||||
message: plaintext_comment,
|
||||
url: url,
|
||||
url_title: "Reply to #{user.username}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -325,17 +323,17 @@ class Comment < ApplicationRecord
|
|||
# Top-level replies (eg parent_comment_id == null) have depth 0, then each reply is +1.
|
||||
# Alternate definition: depth is the number of ancestor comments.
|
||||
|
||||
return false if self.new_record? # can't reply to unsaved comments
|
||||
return false if new_record? # can't reply to unsaved comments
|
||||
|
||||
# Most commonly, depth is set by merged_comments. But we need to count parents when executing as
|
||||
# a validation on reply.
|
||||
self.depth ||= self.parents.count
|
||||
self.depth ||= parents.count
|
||||
|
||||
depth < MAX_DEPTH
|
||||
end
|
||||
|
||||
def generated_markeddown_comment
|
||||
Markdowner.to_html(self.comment)
|
||||
Markdowner.to_html(comment)
|
||||
end
|
||||
|
||||
# TODO: race condition: if two votes arrive at the same time, the second one
|
||||
|
@ -359,19 +357,19 @@ class Comment < ApplicationRecord
|
|||
UPDATE comments SET
|
||||
score = (select coalesce(sum(vote), 0) from votes where comment_id = comments.id),
|
||||
flags = (select count(*) from votes where comment_id = comments.id and vote = -1),
|
||||
confidence = #{self.calculated_confidence},
|
||||
confidence = #{calculated_confidence},
|
||||
confidence_order = concat(lpad(char(65536 - floor(((confidence - -0.2) * 65535) / 1.2) using binary), 2, '0'), char(id & 0xff using binary))
|
||||
WHERE id = #{self.id.to_i}
|
||||
WHERE id = #{id.to_i}
|
||||
SQL
|
||||
self.story.recalculate_hotness!
|
||||
story.recalculate_hotness!
|
||||
end
|
||||
|
||||
def gone_text
|
||||
if self.is_moderated?
|
||||
if is_moderated?
|
||||
"Comment removed by moderator " <<
|
||||
self.moderation.try(:moderator).try(:username).to_s << ": " <<
|
||||
(self.moderation.try(:reason) || "No reason given")
|
||||
elsif self.user.is_banned?
|
||||
moderation.try(:moderator).try(:username).to_s << ": " <<
|
||||
(moderation.try(:reason) || "No reason given")
|
||||
elsif user.is_banned?
|
||||
"Comment from banned user removed"
|
||||
else
|
||||
"Comment removed by author"
|
||||
|
@ -379,41 +377,41 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def has_been_edited?
|
||||
self.updated_at && (self.updated_at - self.created_at > 1.minute)
|
||||
updated_at && (updated_at - created_at > 1.minute)
|
||||
end
|
||||
|
||||
def is_deletable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id
|
||||
return self.created_at >= DELETEABLE_DAYS.days.ago
|
||||
if user&.is_moderator?
|
||||
true
|
||||
elsif user && user.id == user_id
|
||||
created_at >= DELETEABLE_DAYS.days.ago
|
||||
else
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_disownable_by_user?(user)
|
||||
user && user.id == self.user_id && self.created_at && self.created_at < DELETEABLE_DAYS.days.ago
|
||||
user && user.id == user_id && created_at && created_at < DELETEABLE_DAYS.days.ago
|
||||
end
|
||||
|
||||
def is_flaggable?
|
||||
if self.created_at && self.score > FLAGGABLE_MIN_SCORE
|
||||
Time.current - self.created_at <= FLAGGABLE_DAYS.days
|
||||
if created_at && self.score > FLAGGABLE_MIN_SCORE
|
||||
Time.current - created_at <= FLAGGABLE_DAYS.days
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def is_editable_by_user?(user)
|
||||
if user && user.id == self.user_id
|
||||
if self.is_moderated?
|
||||
return false
|
||||
if user && user.id == user_id
|
||||
if is_moderated?
|
||||
false
|
||||
else
|
||||
return (Time.current.to_i - (self.updated_at ? self.updated_at.to_i :
|
||||
self.created_at.to_i) < (60 * MAX_EDIT_MINS))
|
||||
(Time.current.to_i - (updated_at ? updated_at.to_i :
|
||||
created_at.to_i) < (60 * MAX_EDIT_MINS))
|
||||
end
|
||||
else
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -422,42 +420,42 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def is_undeletable_by_user?(user)
|
||||
if user && user.is_moderator?
|
||||
return true
|
||||
elsif user && user.id == self.user_id && !self.is_moderated?
|
||||
return true
|
||||
if user&.is_moderator?
|
||||
true
|
||||
elsif user && user.id == user_id && !is_moderated?
|
||||
true
|
||||
else
|
||||
return false
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def log_hat_use
|
||||
return unless self.hat && self.hat.modlog_use
|
||||
return unless hat&.modlog_use
|
||||
|
||||
m = Moderation.new
|
||||
m.created_at = self.created_at
|
||||
m.comment_id = self.id
|
||||
m.created_at = created_at
|
||||
m.comment_id = id
|
||||
m.moderator_user_id = user.id
|
||||
m.action = "used #{self.hat.hat} hat"
|
||||
m.action = "used #{hat.hat} hat"
|
||||
m.save!
|
||||
end
|
||||
|
||||
def mark_submitter
|
||||
Keystore.increment_value_for("user:#{self.user_id}:comments_posted")
|
||||
Keystore.increment_value_for("user:#{user_id}:comments_posted")
|
||||
end
|
||||
|
||||
def mailing_list_message_id
|
||||
[
|
||||
"comment",
|
||||
self.short_id,
|
||||
self.is_from_email ? "email" : nil,
|
||||
created_at.to_i,
|
||||
short_id,
|
||||
is_from_email ? "email" : nil,
|
||||
created_at.to_i
|
||||
].reject(&:!).join(".") << "@" << Rails.application.domain
|
||||
end
|
||||
|
||||
# all direct ancestors of this comment, oldest first
|
||||
def parents
|
||||
return Comment.none if self.parent_comment_id.nil?
|
||||
return Comment.none if parent_comment_id.nil?
|
||||
|
||||
# starts from parent_comment_id so it works on new records
|
||||
@parents ||= Comment
|
||||
|
@ -470,7 +468,7 @@ class Comment < ApplicationRecord
|
|||
parent_comment_id,
|
||||
0 as depth,
|
||||
(select count(*) from comments where parent_comment_id = id) as reply_count
|
||||
from comments where id = #{self.parent_comment_id}
|
||||
from comments where id = #{parent_comment_id}
|
||||
union all
|
||||
select
|
||||
parents.target_id,
|
||||
|
@ -483,11 +481,11 @@ class Comment < ApplicationRecord
|
|||
) as comments_recursive on comments.id = comments_recursive.id
|
||||
SQL
|
||||
)
|
||||
.order('id asc')
|
||||
.order("id asc")
|
||||
end
|
||||
|
||||
def path
|
||||
self.story.comments_path + "#c_#{self.short_id}"
|
||||
story.comments_path + "#c_#{short_id}"
|
||||
end
|
||||
|
||||
def plaintext_comment
|
||||
|
@ -497,16 +495,16 @@ class Comment < ApplicationRecord
|
|||
|
||||
def record_initial_upvote
|
||||
Vote.vote_thusly_on_story_or_comment_for_user_because(
|
||||
1, self.story_id, self.id, self.user_id, nil, false
|
||||
1, story_id, id, user_id, nil, false
|
||||
)
|
||||
|
||||
self.story.update_comments_count!
|
||||
story.update_comments_count!
|
||||
end
|
||||
|
||||
def score_for_user(u)
|
||||
if self.show_score_to_user?(u)
|
||||
if show_score_to_user?(u)
|
||||
score
|
||||
elsif u && u.can_flag?(self)
|
||||
elsif u&.can_flag?(self)
|
||||
"~"
|
||||
else
|
||||
" ".html_safe
|
||||
|
@ -514,37 +512,37 @@ class Comment < ApplicationRecord
|
|||
end
|
||||
|
||||
def short_id_url
|
||||
Rails.application.root_url + "c/#{self.short_id}"
|
||||
Rails.application.root_url + "c/#{short_id}"
|
||||
end
|
||||
|
||||
def show_score_to_user?(u)
|
||||
return true if u && u.is_moderator?
|
||||
return true if u&.is_moderator?
|
||||
|
||||
# hide score on new/near-zero comments to cut down on threads about voting
|
||||
# also hide if user has flagged the story/comment to make retaliatory flagging less fun
|
||||
(
|
||||
(self.created_at && self.created_at < 36.hours.ago) ||
|
||||
(created_at && created_at < 36.hours.ago) ||
|
||||
!SCORE_RANGE_TO_HIDE.include?(self.score)
|
||||
) && (!current_vote || current_vote[:vote] >= 0)
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.short_id
|
||||
short_id
|
||||
end
|
||||
|
||||
def unassign_votes
|
||||
self.story.update_comments_count!
|
||||
story.update_comments_count!
|
||||
end
|
||||
|
||||
def url
|
||||
self.story.comments_url + "#c_#{self.short_id}"
|
||||
story.comments_url + "#c_#{short_id}"
|
||||
end
|
||||
|
||||
def vote_summary_for_user(u)
|
||||
r_counts = {}
|
||||
r_users = {}
|
||||
# don't includes(:user) here and assume the caller did this already
|
||||
self.votes.each do |v|
|
||||
votes.each do |v|
|
||||
r_counts[v.reason.to_s] ||= 0
|
||||
r_counts[v.reason.to_s] += v.vote
|
||||
|
||||
|
@ -552,12 +550,12 @@ class Comment < ApplicationRecord
|
|||
r_users[v.reason.to_s].push v.user.username
|
||||
end
|
||||
|
||||
r_counts.keys.map {|k|
|
||||
r_counts.keys.map { |k|
|
||||
next if k == ""
|
||||
|
||||
o = "#{r_counts[k]} #{Vote::ALL_COMMENT_REASONS[k]}"
|
||||
if u && u.is_moderator? && self.user_id != u.id
|
||||
o << " (#{r_users[k].join(', ')})"
|
||||
if u && u.is_moderator? && user_id != u.id
|
||||
o << " (#{r_users[k].join(", ")})"
|
||||
end
|
||||
o
|
||||
}.compact.join(", ")
|
||||
|
@ -571,19 +569,19 @@ class Comment < ApplicationRecord
|
|||
if user.is_moderator?
|
||||
self.is_moderated = false
|
||||
|
||||
if user.id != self.user_id
|
||||
if user.id != user_id
|
||||
m = Moderation.new
|
||||
m.comment_id = self.id
|
||||
m.comment_id = id
|
||||
m.moderator_user_id = user.id
|
||||
m.action = "undeleted comment"
|
||||
m.save
|
||||
end
|
||||
end
|
||||
|
||||
self.save(:validate => false)
|
||||
save(validate: false)
|
||||
Comment.record_timestamps = true
|
||||
|
||||
self.story.update_comments_count!
|
||||
story.update_comments_count!
|
||||
self.user.refresh_counts!
|
||||
end
|
||||
|
||||
|
@ -593,7 +591,7 @@ class Comment < ApplicationRecord
|
|||
thread_ids = Comment
|
||||
.where(user: user)
|
||||
.group(:thread_id)
|
||||
.order('id desc')
|
||||
.order("id desc")
|
||||
.limit(20)
|
||||
.pluck(:thread_id)
|
||||
return Comment.none if thread_ids.empty?
|
||||
|
@ -609,7 +607,7 @@ class Comment < ApplicationRecord
|
|||
cast(confidence_order as char(#{Comment::COP_LENGTH}) character set binary) as confidence_order_path
|
||||
from comments c
|
||||
where
|
||||
thread_id in (#{thread_ids.join(', ')}) and
|
||||
thread_id in (#{thread_ids.join(", ")}) and
|
||||
parent_comment_id is null
|
||||
union all
|
||||
select
|
||||
|
@ -626,7 +624,7 @@ class Comment < ApplicationRecord
|
|||
) as comments_recursive on comments.id = comments_recursive.id
|
||||
SQL
|
||||
)
|
||||
.order('comments.thread_id desc, comments_recursive.confidence_order_path')
|
||||
.order("comments.thread_id desc, comments_recursive.confidence_order_path")
|
||||
.select('
|
||||
comments.*,
|
||||
comments_recursive.depth as depth,
|
||||
|
@ -669,7 +667,7 @@ class Comment < ApplicationRecord
|
|||
) as comments_recursive on comments.id = comments_recursive.id
|
||||
SQL
|
||||
)
|
||||
.order('comments_recursive.confidence_order_path')
|
||||
.order("comments_recursive.confidence_order_path")
|
||||
.select('
|
||||
comments.*,
|
||||
comments_recursive.depth as depth,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
class Domain < ApplicationRecord
|
||||
has_many :stories # rubocop:disable Rails/HasManyOrHasOneDependent
|
||||
belongs_to :banned_by_user,
|
||||
:class_name => "User",
|
||||
:inverse_of => false,
|
||||
:optional => true
|
||||
validates :banned_reason, :length => { :maximum => 200 }
|
||||
class_name: "User",
|
||||
inverse_of: false,
|
||||
optional: true
|
||||
validates :banned_reason, length: {maximum: 200}
|
||||
|
||||
validates :domain, presence: true
|
||||
|
||||
|
@ -12,12 +12,12 @@ class Domain < ApplicationRecord
|
|||
self.banned_at = Time.current
|
||||
self.banned_by_user_id = banner.id
|
||||
self.banned_reason = reason
|
||||
self.save!
|
||||
save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = banner.id
|
||||
m.domain = self
|
||||
m.action = 'Banned'
|
||||
m.action = "Banned"
|
||||
m.reason = reason
|
||||
m.save!
|
||||
end
|
||||
|
@ -26,12 +26,12 @@ class Domain < ApplicationRecord
|
|||
self.banned_at = nil
|
||||
self.banned_by_user_id = nil
|
||||
self.banned_reason = nil
|
||||
self.save!
|
||||
save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = banner.id
|
||||
m.domain = self
|
||||
m.action = 'Unbanned'
|
||||
m.action = "Unbanned"
|
||||
m.reason = reason
|
||||
m.save!
|
||||
end
|
||||
|
@ -41,7 +41,7 @@ class Domain < ApplicationRecord
|
|||
end
|
||||
|
||||
def n_submitters
|
||||
self.stories.count('distinct user_id')
|
||||
stories.count("distinct user_id")
|
||||
end
|
||||
|
||||
def to_param
|
||||
|
|
|
@ -19,7 +19,7 @@ class FlaggedCommenters
|
|||
|
||||
# aggregates for all commenters; not just those receiving flags
|
||||
def aggregates
|
||||
Rails.cache.fetch("aggregates_#{interval}_#{cache_time}", expires_in: self.cache_time) {
|
||||
Rails.cache.fetch("aggregates_#{interval}_#{cache_time}", expires_in: cache_time) {
|
||||
ActiveRecord::Base.connection.exec_query("
|
||||
select
|
||||
stddev(sum_flags) as stddev,
|
||||
|
@ -52,7 +52,7 @@ class FlaggedCommenters
|
|||
|
||||
def commenters
|
||||
Rails.cache.fetch("flagged_commenters_#{interval}_#{cache_time}",
|
||||
expires_in: self.cache_time) {
|
||||
expires_in: cache_time) {
|
||||
rank = 0
|
||||
User.active.joins(:comments)
|
||||
.where("comments.created_at >= ?", period)
|
||||
|
@ -71,7 +71,7 @@ class FlaggedCommenters
|
|||
.having("n_comments > 4 and n_stories > 1 and n_flags >= 10 and percent_flagged > 10")
|
||||
.order("sigma desc")
|
||||
.limit(30)
|
||||
.each_with_object({}) {|u, hash|
|
||||
.each_with_object({}) { |u, hash|
|
||||
hash[u.id] = {
|
||||
username: u.username,
|
||||
rank: rank += 1,
|
||||
|
@ -81,7 +81,7 @@ class FlaggedCommenters
|
|||
n_flags: u.n_flags,
|
||||
average_flags: u.average_flags,
|
||||
stddev: 0,
|
||||
percent_flagged: u.percent_flagged,
|
||||
percent_flagged: u.percent_flagged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,50 +5,50 @@ class Hat < ApplicationRecord
|
|||
after_create :log_moderation
|
||||
|
||||
validates :user, :granted_by_user, :hat, presence: true
|
||||
validates :hat, :link, length: { maximum: 255 }
|
||||
validates :hat, :link, length: {maximum: 255}
|
||||
|
||||
scope :active, -> { joins(:user).where(doffed_at: nil).merge(User.active) }
|
||||
|
||||
def doff_by_user_with_reason(user, reason)
|
||||
m = Moderation.new
|
||||
m.user_id = self.user_id
|
||||
m.user_id = user_id
|
||||
m.moderator_user_id = user.id
|
||||
m.action = "Doffed hat \"#{self.hat}\": #{reason}"
|
||||
m.action = "Doffed hat \"#{hat}\": #{reason}"
|
||||
m.save!
|
||||
|
||||
self.doffed_at = Time.current
|
||||
self.save!
|
||||
save!
|
||||
end
|
||||
|
||||
def destroy_by_user_with_reason(user, reason)
|
||||
m = Moderation.new
|
||||
m.user_id = self.user_id
|
||||
m.user_id = user_id
|
||||
m.moderator_user_id = user.id
|
||||
m.action = "Revoked hat \"#{self.hat}\": #{reason}"
|
||||
m.action = "Revoked hat \"#{hat}\": #{reason}"
|
||||
m.save!
|
||||
|
||||
self.destroy
|
||||
destroy
|
||||
end
|
||||
|
||||
def to_html_label
|
||||
hl = (self.link.present? && self.link.match(/^https?:\/\//))
|
||||
hl = (link.present? && link.match(/^https?:\/\//))
|
||||
|
||||
h = "<span class=\"hat " <<
|
||||
"hat_#{self.hat.gsub(/[^A-Za-z0-9]/, '_').downcase}\" " <<
|
||||
"title=\"Granted #{self.created_at.strftime('%Y-%m-%d')}"
|
||||
h = "<span class=\"hat " \
|
||||
"hat_#{hat.gsub(/[^A-Za-z0-9]/, "_").downcase}\" " \
|
||||
"title=\"Granted #{created_at.strftime("%Y-%m-%d")}"
|
||||
|
||||
if !hl && self.link.present?
|
||||
h << " - #{ERB::Util.html_escape(self.sanitized_link)}"
|
||||
if !hl && link.present?
|
||||
h << " - #{ERB::Util.html_escape(sanitized_link)}"
|
||||
end
|
||||
|
||||
h << "\">" <<
|
||||
h << "\">" \
|
||||
"<span class=\"crown\">"
|
||||
|
||||
if hl
|
||||
h << "<a href=\"#{ERB::Util.html_escape(self.link)}\" target=\"_blank\">"
|
||||
h << "<a href=\"#{ERB::Util.html_escape(link)}\" target=\"_blank\">"
|
||||
end
|
||||
|
||||
h << ERB::Util.html_escape(self.hat)
|
||||
h << ERB::Util.html_escape(hat)
|
||||
|
||||
if hl
|
||||
h << "</a>"
|
||||
|
@ -60,16 +60,16 @@ class Hat < ApplicationRecord
|
|||
end
|
||||
|
||||
def to_txt
|
||||
"(#{self.hat}) "
|
||||
"(#{hat}) "
|
||||
end
|
||||
|
||||
def log_moderation
|
||||
m = Moderation.new
|
||||
m.created_at = self.created_at
|
||||
m.user_id = self.user_id
|
||||
m.moderator_user_id = self.granted_by_user_id
|
||||
m.action = "Granted hat \"#{self.hat}\"" + (self.link.present? ?
|
||||
" (#{self.link})" : "")
|
||||
m.created_at = created_at
|
||||
m.user_id = user_id
|
||||
m.moderator_user_id = granted_by_user_id
|
||||
m.action = "Granted hat \"#{hat}\"" + (link.present? ?
|
||||
" (#{link})" : "")
|
||||
m.save
|
||||
end
|
||||
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
class HatRequest < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
validates :hat, presence: true, length: { maximum: 255 }
|
||||
validates :link, presence: true, length: { maximum: 255 }
|
||||
validates :comment, presence: true, length: { maximum: 65_535 }
|
||||
validates :hat, presence: true, length: {maximum: 255}
|
||||
validates :link, presence: true, length: {maximum: 255}
|
||||
validates :comment, presence: true, length: {maximum: 65_535}
|
||||
|
||||
attr_accessor :rejection_comment
|
||||
|
||||
def approve_by_user_for_reason!(user, reason)
|
||||
self.transaction do
|
||||
transaction do
|
||||
h = Hat.new
|
||||
h.user_id = self.user_id
|
||||
h.user_id = user_id
|
||||
h.granted_by_user_id = user.id
|
||||
h.hat = self.hat
|
||||
h.link = self.link
|
||||
h.hat = hat
|
||||
h.link = link
|
||||
h.save!
|
||||
|
||||
m = Message.new
|
||||
m.author_user_id = user.id
|
||||
m.recipient_user_id = self.user_id
|
||||
m.subject = "Your hat \"#{self.hat}\" has been approved"
|
||||
m.recipient_user_id = user_id
|
||||
m.subject = "Your hat \"#{hat}\" has been approved"
|
||||
m.body = reason
|
||||
m.save!
|
||||
|
||||
self.destroy
|
||||
destroy
|
||||
end
|
||||
end
|
||||
|
||||
def reject_by_user_for_reason!(user, reason)
|
||||
self.transaction do
|
||||
transaction do
|
||||
m = Message.new
|
||||
m.author_user_id = user.id
|
||||
m.recipient_user_id = self.user_id
|
||||
m.subject = "Your request for hat \"#{self.hat}\" has been rejected"
|
||||
m.recipient_user_id = user_id
|
||||
m.subject = "Your request for hat \"#{hat}\" has been rejected"
|
||||
m.body = reason
|
||||
m.save!
|
||||
|
||||
self.destroy
|
||||
destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,14 +5,12 @@ class HiddenStory < ApplicationRecord
|
|||
scope :by, ->(user) { where(user: user) }
|
||||
|
||||
def self.hide_story_for_user(story_id, user_id)
|
||||
HiddenStory.where(:user_id => user_id, :story_id =>
|
||||
story_id).first_or_initialize.save!
|
||||
HiddenStory.where(user_id: user_id, story_id: story_id).first_or_initialize.save!
|
||||
ReadRibbon.hide_replies_for(story_id, user_id)
|
||||
end
|
||||
|
||||
def self.unhide_story_for_user(story_id, user_id)
|
||||
HiddenStory.where(:user_id => user_id, :story_id =>
|
||||
story_id).delete_all
|
||||
HiddenStory.where(user_id: user_id, story_id: story_id).delete_all
|
||||
ReadRibbon.unhide_replies_for(story_id, user_id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module InactiveUser
|
||||
def self.inactive_user
|
||||
@inactive_user ||= User.find_by!(username: 'inactive-user')
|
||||
@inactive_user ||= User.find_by!(username: "inactive-user")
|
||||
end
|
||||
|
||||
def self.disown! comment_or_story
|
||||
|
@ -12,13 +12,13 @@ module InactiveUser
|
|||
def self.disown_all_by_author! author
|
||||
# leave attribution on deleted stuff, which is generally very relevant to mods
|
||||
# when looking back at returning users
|
||||
author.stories.not_deleted(nil).update_all(:user_id => inactive_user.id)
|
||||
author.comments.active.update_all(:user_id => inactive_user.id)
|
||||
author.stories.not_deleted(nil).update_all(user_id: inactive_user.id)
|
||||
author.comments.active.update_all(user_id: inactive_user.id)
|
||||
refresh_counts! author
|
||||
end
|
||||
|
||||
def self.refresh_counts! user
|
||||
user.refresh_counts! if user
|
||||
user&.refresh_counts!
|
||||
inactive_user.refresh_counts!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
class Invitation < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :new_user, class_name: 'User', inverse_of: nil, optional: true
|
||||
belongs_to :new_user, class_name: "User", inverse_of: nil, optional: true
|
||||
|
||||
scope :used, -> { where.not(:used_at => nil) }
|
||||
scope :unused, -> { where(:used_at => nil) }
|
||||
scope :used, -> { where.not(used_at: nil) }
|
||||
scope :unused, -> { where(used_at: nil) }
|
||||
|
||||
validate do
|
||||
unless email.to_s.match(/\A[^@ ]+@[^ @]+\.[^ @]+\z/)
|
||||
unless /\A[^@ ]+@[^ @]+\.[^ @]+\z/.match?(email.to_s)
|
||||
errors.add(:email, "is not valid")
|
||||
end
|
||||
end
|
||||
|
||||
validates :code, :email, :memo, length: { maximum: 255 }
|
||||
validates :code, :email, :memo, length: {maximum: 255}
|
||||
|
||||
before_validation :create_code, :on => :create
|
||||
before_validation :create_code, on: :create
|
||||
|
||||
def create_code
|
||||
10.times do
|
||||
self.code = Utils.random_str(15)
|
||||
return unless Invitation.exists?(:code => self.code)
|
||||
return unless Invitation.exists?(code: code)
|
||||
end
|
||||
raise "too many hash collisions"
|
||||
end
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
class InvitationRequest < ApplicationRecord
|
||||
validates :name,
|
||||
:presence => true,
|
||||
:length => { maximum: 255 }
|
||||
presence: true,
|
||||
length: {maximum: 255}
|
||||
validates :email,
|
||||
:format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ },
|
||||
:presence => true,
|
||||
:length => { maximum: 255 }
|
||||
format: {with: /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/},
|
||||
presence: true,
|
||||
length: {maximum: 255}
|
||||
validates :memo,
|
||||
:format => { :with => /https?:\/\// },
|
||||
:length => { maximum: 255 }
|
||||
validates :code, :ip_address, :length => { maximum: 255 }
|
||||
format: {with: /https?:\/\//},
|
||||
length: {maximum: 255}
|
||||
validates :code, :ip_address, length: {maximum: 255}
|
||||
|
||||
before_validation :create_code
|
||||
after_create :send_email
|
||||
|
||||
def self.verified_count
|
||||
InvitationRequest.where(:is_verified => true).count
|
||||
InvitationRequest.where(is_verified: true).count
|
||||
end
|
||||
|
||||
def create_code
|
||||
10.times do
|
||||
self.code = Utils.random_str(15)
|
||||
return unless InvitationRequest.exists?(:code => self.code)
|
||||
return unless InvitationRequest.exists?(code: code)
|
||||
end
|
||||
raise "too many hash collisions"
|
||||
end
|
||||
|
||||
def markeddown_memo
|
||||
Markdowner.to_html(self.memo)
|
||||
Markdowner.to_html(memo)
|
||||
end
|
||||
|
||||
def send_email
|
||||
|
|
|
@ -3,28 +3,28 @@ class Keystore < ApplicationRecord
|
|||
|
||||
self.primary_key = "key"
|
||||
|
||||
validates :key, presence: true, length: { maximum: MAX_KEY_LENGTH }
|
||||
validates :key, presence: true, length: {maximum: MAX_KEY_LENGTH}
|
||||
|
||||
def self.get(key)
|
||||
self.find_by(key: key)
|
||||
find_by(key: key)
|
||||
end
|
||||
|
||||
def self.value_for(key)
|
||||
self.where(key: key).limit(1).pluck(:value).first
|
||||
where(key: key).limit(1).pluck(:value).first
|
||||
end
|
||||
|
||||
def self.put(key, value)
|
||||
validate_input_key(key)
|
||||
if Keystore.connection.adapter_name == "SQLite"
|
||||
Keystore.connection.execute("INSERT OR REPLACE INTO " <<
|
||||
"#{Keystore.table_name} (`key`, `value`) VALUES " <<
|
||||
Keystore.connection.execute("INSERT OR REPLACE INTO " \
|
||||
"#{Keystore.table_name} (`key`, `value`) VALUES " \
|
||||
"(#{q(key)}, #{q(value)})")
|
||||
elsif Keystore.connection.adapter_name =~ /Mysql/
|
||||
Keystore.connection.execute("INSERT INTO #{Keystore.table_name} (" +
|
||||
"`key`, `value`) VALUES (#{q(key)}, #{q(value)}) ON DUPLICATE KEY " +
|
||||
elsif /Mysql/.match?(Keystore.connection.adapter_name)
|
||||
Keystore.connection.execute("INSERT INTO #{Keystore.table_name} (" \
|
||||
"`key`, `value`) VALUES (#{q(key)}, #{q(value)}) ON DUPLICATE KEY " \
|
||||
"UPDATE `value` = #{q(value)}")
|
||||
else
|
||||
kv = self.find_or_create_key_for_update(key, value)
|
||||
kv = find_or_create_key_for_update(key, value)
|
||||
kv.value = value
|
||||
kv.save!
|
||||
end
|
||||
|
@ -32,40 +32,40 @@ class Keystore < ApplicationRecord
|
|||
end
|
||||
|
||||
def self.increment_value_for(key, amount = 1)
|
||||
self.incremented_value_for(key, amount)
|
||||
incremented_value_for(key, amount)
|
||||
end
|
||||
|
||||
def self.incremented_value_for(key, amount = 1)
|
||||
validate_input_key(key)
|
||||
Keystore.transaction do
|
||||
if Keystore.connection.adapter_name == "SQLite"
|
||||
Keystore.connection.execute("INSERT OR IGNORE INTO " <<
|
||||
"#{Keystore.table_name} (`key`, `value`) VALUES " <<
|
||||
Keystore.connection.execute("INSERT OR IGNORE INTO " \
|
||||
"#{Keystore.table_name} (`key`, `value`) VALUES " \
|
||||
"(#{q(key)}, 0)")
|
||||
Keystore.connection.execute("UPDATE #{Keystore.table_name} " <<
|
||||
Keystore.connection.execute("UPDATE #{Keystore.table_name} " \
|
||||
"SET `value` = `value` + #{q(amount)} WHERE `key` = #{q(key)}")
|
||||
elsif Keystore.connection.adapter_name =~ /Mysql/
|
||||
Keystore.connection.execute("INSERT INTO #{Keystore.table_name} (" +
|
||||
"`key`, `value`) VALUES (#{q(key)}, #{q(amount)}) ON DUPLICATE KEY " +
|
||||
elsif /Mysql/.match?(Keystore.connection.adapter_name)
|
||||
Keystore.connection.execute("INSERT INTO #{Keystore.table_name} (" \
|
||||
"`key`, `value`) VALUES (#{q(key)}, #{q(amount)}) ON DUPLICATE KEY " \
|
||||
"UPDATE `value` = `value` + #{q(amount)}")
|
||||
else
|
||||
kv = self.find_or_create_key_for_update(key, 0)
|
||||
kv = find_or_create_key_for_update(key, 0)
|
||||
kv.value = kv.value.to_i + amount
|
||||
kv.save!
|
||||
return kv.value
|
||||
end
|
||||
|
||||
self.value_for(key)
|
||||
value_for(key)
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_or_create_key_for_update(key, init = nil)
|
||||
loop do
|
||||
found = self.lock(true).find_by(:key => key)
|
||||
found = lock(true).find_by(key: key)
|
||||
return found if found
|
||||
|
||||
begin
|
||||
self.create! do |kv|
|
||||
create! do |kv|
|
||||
kv.key = key
|
||||
kv.value = init
|
||||
kv.save!
|
||||
|
@ -77,11 +77,11 @@ class Keystore < ApplicationRecord
|
|||
end
|
||||
|
||||
def self.decrement_value_for(key, amount = -1)
|
||||
self.increment_value_for(key, amount)
|
||||
increment_value_for(key, amount)
|
||||
end
|
||||
|
||||
def self.decremented_value_for(key, amount = -1)
|
||||
self.incremented_value_for(key, amount)
|
||||
incremented_value_for(key, amount)
|
||||
end
|
||||
|
||||
# deliberately no lock/transaction as TrafficHelper is on the hot path of every request
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
class Message < ApplicationRecord
|
||||
belongs_to :recipient,
|
||||
:class_name => "User",
|
||||
:foreign_key => "recipient_user_id",
|
||||
:inverse_of => :received_messages
|
||||
class_name: "User",
|
||||
foreign_key: "recipient_user_id",
|
||||
inverse_of: :received_messages
|
||||
belongs_to :author,
|
||||
:class_name => "User",
|
||||
:foreign_key => "author_user_id",
|
||||
:inverse_of => :sent_messages,
|
||||
:optional => true
|
||||
class_name: "User",
|
||||
foreign_key: "author_user_id",
|
||||
inverse_of: :sent_messages,
|
||||
optional: true
|
||||
belongs_to :hat,
|
||||
:optional => true
|
||||
optional: true
|
||||
|
||||
attribute :mod_note, :boolean
|
||||
attr_reader :recipient_username
|
||||
|
||||
validates :subject, length: { :in => 1..100 }
|
||||
validates :body, length: { :maximum => (64 * 1024) }
|
||||
validates :short_id, length: { maximum: 30 }
|
||||
validates :subject, length: {in: 1..100}
|
||||
validates :body, length: {maximum: (64 * 1024)}
|
||||
validates :short_id, length: {maximum: 30}
|
||||
validate :hat do
|
||||
next if hat.blank?
|
||||
if author.blank? || author.wearable_hats.exclude?(hat)
|
||||
errors.add(:hat, 'not wearable by author')
|
||||
errors.add(:hat, "not wearable by author")
|
||||
end
|
||||
end
|
||||
|
||||
scope :inbox, ->(user) {
|
||||
where(
|
||||
recipient: user,
|
||||
deleted_by_recipient: false,
|
||||
).preload(:author, :hat, :recipient).order('id asc')
|
||||
deleted_by_recipient: false
|
||||
).preload(:author, :hat, :recipient).order("id asc")
|
||||
}
|
||||
scope :outbox, ->(user) {
|
||||
where(
|
||||
author: user,
|
||||
deleted_by_author: false,
|
||||
).preload(:author, :hat, :recipient).order('id asc')
|
||||
deleted_by_author: false
|
||||
).preload(:author, :hat, :recipient).order("id asc")
|
||||
}
|
||||
scope :unread, -> { where(:has_been_read => false, :deleted_by_recipient => false) }
|
||||
scope :unread, -> { where(has_been_read: false, deleted_by_recipient: false) }
|
||||
|
||||
before_validation :assign_short_id, :on => :create
|
||||
before_validation :assign_short_id, on: :create
|
||||
after_create :deliver_email_notifications
|
||||
after_save :update_unread_counts
|
||||
after_save :check_for_both_deleted
|
||||
|
@ -51,13 +51,13 @@ class Message < ApplicationRecord
|
|||
:subject,
|
||||
:body,
|
||||
:deleted_by_author,
|
||||
:deleted_by_recipient,
|
||||
:deleted_by_recipient
|
||||
]
|
||||
|
||||
h = super(:only => attrs)
|
||||
h = super(only: attrs)
|
||||
|
||||
h[:author_username] = self.author.try(:username)
|
||||
h[:recipient_username] = self.recipient.try(:username)
|
||||
h[:author_username] = author.try(:username)
|
||||
h[:recipient_username] = recipient.try(:username)
|
||||
|
||||
h
|
||||
end
|
||||
|
@ -67,42 +67,42 @@ class Message < ApplicationRecord
|
|||
end
|
||||
|
||||
def author_username
|
||||
if self.author
|
||||
self.author.username
|
||||
if author
|
||||
author.username
|
||||
else
|
||||
"System"
|
||||
end
|
||||
end
|
||||
|
||||
def check_for_both_deleted
|
||||
if self.deleted_by_author? && self.deleted_by_recipient?
|
||||
self.destroy
|
||||
if deleted_by_author? && deleted_by_recipient?
|
||||
destroy
|
||||
end
|
||||
end
|
||||
|
||||
def update_unread_counts
|
||||
self.recipient.update_unread_message_count!
|
||||
recipient.update_unread_message_count!
|
||||
end
|
||||
|
||||
def deliver_email_notifications
|
||||
return if Rails.env.development?
|
||||
|
||||
if self.recipient.email_messages?
|
||||
if recipient.email_messages?
|
||||
begin
|
||||
EmailMessage.notify(self, self.recipient).deliver_now
|
||||
EmailMessage.notify(self, recipient).deliver_now
|
||||
rescue => e
|
||||
Rails.logger.error "error e-mailing #{self.recipient.email}: #{e}"
|
||||
Rails.logger.error "error e-mailing #{recipient.email}: #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
if self.recipient.pushover_messages?
|
||||
self.recipient.pushover!(
|
||||
:title => "#{Rails.application.name} message from " <<
|
||||
"#{self.author_username}: #{self.subject}",
|
||||
:message => self.plaintext_body,
|
||||
:url => self.url,
|
||||
:url_title => (self.author ? "Reply to #{self.author_username}" :
|
||||
"View message"),
|
||||
if recipient.pushover_messages?
|
||||
recipient.pushover!(
|
||||
title: "#{Rails.application.name} message from " \
|
||||
"#{author_username}: #{subject}",
|
||||
message: plaintext_body,
|
||||
url: url,
|
||||
url_title: (author ? "Reply to #{author_username}" :
|
||||
"View message")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -110,7 +110,7 @@ class Message < ApplicationRecord
|
|||
def recipient_username=(username)
|
||||
self.recipient_user_id = nil
|
||||
|
||||
if (u = User.find_by(:username => username))
|
||||
if (u = User.find_by(username: username))
|
||||
self.recipient_user_id = u.id
|
||||
@recipient_username = username
|
||||
else
|
||||
|
@ -119,19 +119,19 @@ class Message < ApplicationRecord
|
|||
end
|
||||
|
||||
def linkified_body
|
||||
Markdowner.to_html(self.body)
|
||||
Markdowner.to_html(body)
|
||||
end
|
||||
|
||||
def plaintext_body
|
||||
# TODO: linkify then strip tags and convert entities back
|
||||
self.body.to_s
|
||||
body.to_s
|
||||
end
|
||||
|
||||
def to_param
|
||||
self.short_id
|
||||
short_id
|
||||
end
|
||||
|
||||
def url
|
||||
Rails.application.root_url + "messages/#{self.short_id}"
|
||||
Rails.application.root_url + "messages/#{short_id}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,16 +2,16 @@ class ModNote < ApplicationRecord
|
|||
extend TimeAgoInWords
|
||||
|
||||
belongs_to :moderator,
|
||||
class_name: "User",
|
||||
foreign_key: "moderator_user_id",
|
||||
inverse_of: :moderations
|
||||
class_name: "User",
|
||||
foreign_key: "moderator_user_id",
|
||||
inverse_of: :moderations
|
||||
belongs_to :user,
|
||||
inverse_of: :mod_notes
|
||||
inverse_of: :mod_notes
|
||||
|
||||
scope :recent, -> { where('created_at >= ?', 1.week.ago).order('created_at desc') }
|
||||
scope :for, ->(user) { includes(:moderator).where('user_id = ?', user).order('created_at desc') }
|
||||
scope :recent, -> { where("created_at >= ?", 1.week.ago).order("created_at desc") }
|
||||
scope :for, ->(user) { includes(:moderator).where("user_id = ?", user).order("created_at desc") }
|
||||
|
||||
validates :note, :markeddown_note, presence: true, length: { maximum: 65_535 }
|
||||
validates :note, :markeddown_note, presence: true, length: {maximum: 65_535}
|
||||
|
||||
delegate :username, to: :user
|
||||
|
||||
|
@ -28,15 +28,15 @@ class ModNote < ApplicationRecord
|
|||
|
||||
def note=(n)
|
||||
self[:note] = n.to_s.strip
|
||||
self.markeddown_note = self.generated_markeddown
|
||||
self.markeddown_note = generated_markeddown
|
||||
end
|
||||
|
||||
def generated_markeddown
|
||||
Markdowner.to_html(self.note)
|
||||
Markdowner.to_html(note)
|
||||
end
|
||||
|
||||
def self.create_from_message(message, moderator)
|
||||
user = moderator.id == message.recipient.id && message.author ?
|
||||
user = (moderator.id == message.recipient.id && message.author) ?
|
||||
message.author : message.recipient
|
||||
|
||||
ModNote.create!(
|
||||
|
@ -44,7 +44,7 @@ class ModNote < ApplicationRecord
|
|||
user: user,
|
||||
created_at: message.created_at,
|
||||
note: <<~NOTE
|
||||
*#{message.author ? message.author.username : '(System)'} #{message.hat ? message.hat.to_txt : ''}-> #{message.recipient.username}*: #{message.subject}
|
||||
*#{message.author ? message.author.username : "(System)"} #{message.hat ? message.hat.to_txt : ""}-> #{message.recipient.username}*: #{message.subject}
|
||||
|
||||
#{message.body}
|
||||
NOTE
|
||||
|
@ -69,22 +69,22 @@ class ModNote < ApplicationRecord
|
|||
moderator: InactiveUser.inactive_user,
|
||||
user: redeemer,
|
||||
created_at: Time.current,
|
||||
note: "Attempted to redeem invitation code #{invitation.code} while logged in:\n" +
|
||||
"- sent by: [#{sender.username}](#{sender_url})\n" +
|
||||
"- created_at: #{invitation.created_at}\n" +
|
||||
"- used_at: #{invitation.used_at || 'unused'}\n" +
|
||||
"- email: #{invitation.email}\n" +
|
||||
note: "Attempted to redeem invitation code #{invitation.code} while logged in:\n" \
|
||||
"- sent by: [#{sender.username}](#{sender_url})\n" \
|
||||
"- created_at: #{invitation.created_at}\n" \
|
||||
"- used_at: #{invitation.used_at || "unused"}\n" \
|
||||
"- email: #{invitation.email}\n" \
|
||||
"- memo: #{invitation.memo}"
|
||||
)
|
||||
create_without_dupe!(
|
||||
moderator: InactiveUser.inactive_user,
|
||||
user: sender,
|
||||
created_at: Time.current,
|
||||
note: "Sent invitation #{invitation.code} another user tried to redeem while logged in:\n" +
|
||||
"- attempted redeemer: [#{redeemer.username}](#{redeemer_url})\n" +
|
||||
"- created_at: #{invitation.created_at}\n" +
|
||||
"- used_at: #{invitation.used_at || 'unused'}\n" +
|
||||
"- email: #{invitation.email}\n" +
|
||||
note: "Sent invitation #{invitation.code} another user tried to redeem while logged in:\n" \
|
||||
"- attempted redeemer: [#{redeemer.username}](#{redeemer_url})\n" \
|
||||
"- created_at: #{invitation.created_at}\n" \
|
||||
"- used_at: #{invitation.used_at || "unused"}\n" \
|
||||
"- email: #{invitation.email}\n" \
|
||||
"- memo: #{invitation.memo}"
|
||||
)
|
||||
end
|
||||
|
@ -102,7 +102,7 @@ class ModNote < ApplicationRecord
|
|||
moderator: InactiveUser.inactive_user,
|
||||
user: user,
|
||||
created_at: Time.current,
|
||||
note: "Hit max comment depth replying to [#{parent_comment.short_id}](#{comment_url}) " +
|
||||
note: "Hit max comment depth replying to [#{parent_comment.short_id}](#{comment_url}) " \
|
||||
"by [#{parent_comment.user.username}](#{parent_author_url})"
|
||||
)
|
||||
end
|
||||
|
@ -112,12 +112,12 @@ class ModNote < ApplicationRecord
|
|||
moderator: InactiveUser.inactive_user,
|
||||
user: story.user,
|
||||
created_at: Time.current,
|
||||
note: "Attempted to submit a story with tag(s) not allowed to new users:\n" +
|
||||
"- user joined: #{time_ago_in_words(story.user.created_at)}\n" +
|
||||
"- url: #{story.url}\n" +
|
||||
"- title: #{story.title}\n" +
|
||||
"- user_is_author: #{story.user_is_author}\n" +
|
||||
"- tags: #{story.tags_a.join(' ')}\n" +
|
||||
note: "Attempted to submit a story with tag(s) not allowed to new users:\n" \
|
||||
"- user joined: #{time_ago_in_words(story.user.created_at)}\n" \
|
||||
"- url: #{story.url}\n" \
|
||||
"- title: #{story.title}\n" \
|
||||
"- user_is_author: #{story.user_is_author}\n" \
|
||||
"- tags: #{story.tags_a.join(" ")}\n" \
|
||||
"- description: #{story.description}\n"
|
||||
)
|
||||
end
|
||||
|
@ -127,12 +127,12 @@ class ModNote < ApplicationRecord
|
|||
moderator: InactiveUser.inactive_user,
|
||||
user: story.user,
|
||||
created_at: Time.current,
|
||||
note: "Attempted to post a story from a #{reason} domain:\n" +
|
||||
"- user joined: #{time_ago_in_words(story.user.created_at)}\n" +
|
||||
"- url: #{story.url}\n" +
|
||||
"- title: #{story.title}\n" +
|
||||
"- user_is_author: #{story.user_is_author}\n" +
|
||||
"- tags: #{story.tags_a.join(' ')}\n" +
|
||||
note: "Attempted to post a story from a #{reason} domain:\n" \
|
||||
"- user joined: #{time_ago_in_words(story.user.created_at)}\n" \
|
||||
"- url: #{story.url}\n" \
|
||||
"- title: #{story.title}\n" \
|
||||
"- user_is_author: #{story.user_is_author}\n" \
|
||||
"- tags: #{story.tags_a.join(" ")}\n" \
|
||||
"- description: #{story.description}\n"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
class Moderation < ApplicationRecord
|
||||
belongs_to :moderator,
|
||||
:class_name => "User",
|
||||
:foreign_key => "moderator_user_id",
|
||||
:inverse_of => :moderations,
|
||||
:optional => true
|
||||
class_name: "User",
|
||||
foreign_key: "moderator_user_id",
|
||||
inverse_of: :moderations,
|
||||
optional: true
|
||||
belongs_to :comment,
|
||||
:optional => true
|
||||
optional: true
|
||||
belongs_to :domain,
|
||||
:optional => true
|
||||
optional: true
|
||||
belongs_to :story,
|
||||
:optional => true
|
||||
optional: true
|
||||
belongs_to :tag,
|
||||
:optional => true
|
||||
optional: true
|
||||
belongs_to :user,
|
||||
:optional => true
|
||||
optional: true
|
||||
belongs_to :category,
|
||||
:optional => true
|
||||
optional: true
|
||||
|
||||
scope :for, ->(user) {
|
||||
left_outer_joins(:story, :comment)
|
||||
|
@ -26,50 +26,50 @@ class Moderation < ApplicationRecord
|
|||
comments.user_id = ?", user, user, user)
|
||||
}
|
||||
|
||||
validates :action, :reason, length: { maximum: 16_777_215 }
|
||||
validates :action, :reason, length: {maximum: 16_777_215}
|
||||
validate :one_foreign_key_present
|
||||
|
||||
after_create :send_message_to_moderated
|
||||
|
||||
def send_message_to_moderated
|
||||
m = Message.new
|
||||
m.author_user_id = self.moderator_user_id
|
||||
m.author_user_id = moderator_user_id
|
||||
|
||||
# mark as deleted by author so they don't fill up moderator message boxes
|
||||
m.deleted_by_author = true
|
||||
|
||||
if self.story
|
||||
m.recipient_user_id = self.story.user_id
|
||||
if story
|
||||
m.recipient_user_id = story.user_id
|
||||
m.subject = "Your story has been edited by " <<
|
||||
(self.is_from_suggestions? ? "user suggestions" : "a moderator")
|
||||
m.body = "Your story [#{self.story.title}](" <<
|
||||
"#{self.story.comments_url}) has been edited with the following " <<
|
||||
"changes:\n" <<
|
||||
"\n" <<
|
||||
"> *#{self.action}*\n"
|
||||
(is_from_suggestions? ? "user suggestions" : "a moderator")
|
||||
m.body = "Your story [#{story.title}](" \
|
||||
"#{story.comments_url}) has been edited with the following " \
|
||||
"changes:\n" \
|
||||
"\n" \
|
||||
"> *#{action}*\n"
|
||||
|
||||
if self.reason.present?
|
||||
m.body << "\n" <<
|
||||
"The reason given:\n" <<
|
||||
"\n" <<
|
||||
"> *#{self.reason}*\n" <<
|
||||
"\n" <<
|
||||
if reason.present?
|
||||
m.body << "\n" \
|
||||
"The reason given:\n" \
|
||||
"\n" \
|
||||
"> *#{reason}*\n" \
|
||||
"\n" \
|
||||
"Maybe the guidelines on topicality are useful: https://lobste.rs/about#topicality"
|
||||
end
|
||||
|
||||
elsif self.comment
|
||||
m.recipient_user_id = self.comment.user_id
|
||||
elsif comment
|
||||
m.recipient_user_id = comment.user_id
|
||||
m.subject = "Your comment has been moderated"
|
||||
m.body = "Your comment on [#{self.comment.story.title}](" <<
|
||||
"#{self.comment.story.comments_url}) has been moderated:\n" <<
|
||||
"\n" <<
|
||||
self.comment.comment.split("\n").map {|l| "> " << l }.join("\n")
|
||||
m.body = "Your comment on [#{comment.story.title}](" \
|
||||
"#{comment.story.comments_url}) has been moderated:\n" \
|
||||
"\n" <<
|
||||
comment.comment.split("\n").map { |l| "> " << l }.join("\n")
|
||||
|
||||
if self.reason.present?
|
||||
m.body << "\n" <<
|
||||
"The reason given:\n" <<
|
||||
"\n" <<
|
||||
"> *#{self.reason}*\n"
|
||||
if reason.present?
|
||||
m.body << "\n" \
|
||||
"The reason given:\n" \
|
||||
"\n" \
|
||||
"> *#{reason}*\n"
|
||||
end
|
||||
|
||||
else
|
||||
|
@ -79,13 +79,13 @@ class Moderation < ApplicationRecord
|
|||
|
||||
return if m.recipient_user_id == m.author_user_id
|
||||
|
||||
m.body << "\n" <<
|
||||
m.body << "\n" \
|
||||
"*This is an automated message.*"
|
||||
|
||||
m.save
|
||||
end
|
||||
|
||||
protected
|
||||
protected
|
||||
|
||||
def one_foreign_key_present
|
||||
fks = [comment_id, domain_id, story_id, category_id, tag_id, user_id].compact.length
|
||||
|
|
|
@ -8,7 +8,7 @@ class ReadRibbon < ApplicationRecord
|
|||
# StoriesController uses .bump and RepliesController uses update_all, etc.
|
||||
|
||||
def self.expire_old_ribbons!
|
||||
self.where("updated_at < ?", 1.year.ago).delete_all
|
||||
where("updated_at < ?", 1.year.ago).delete_all
|
||||
end
|
||||
|
||||
def self.hide_replies_for(story_id, user_id)
|
||||
|
@ -30,7 +30,7 @@ class ReadRibbon < ApplicationRecord
|
|||
if new_record?
|
||||
save
|
||||
else
|
||||
self.update_column(:updated_at, Time.now.utc)
|
||||
update_column(:updated_at, Time.now.utc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,14 @@ class ReplyingComment < ApplicationRecord
|
|||
scope :for_user, ->(user_id) {
|
||||
where(user_id: user_id)
|
||||
.order(comment_created_at: :desc)
|
||||
.preload(:comment => [:story, :user])
|
||||
.preload(comment: [:story, :user])
|
||||
}
|
||||
scope :unread_replies_for, ->(user_id) { for_user(user_id).where(is_unread: true) }
|
||||
scope :comment_replies_for,
|
||||
->(user_id) { for_user(user_id).where('parent_comment_id is not null') }
|
||||
scope :story_replies_for, ->(user_id) { for_user(user_id).where('parent_comment_id is null') }
|
||||
->(user_id) { for_user(user_id).where("parent_comment_id is not null") }
|
||||
scope :story_replies_for, ->(user_id) { for_user(user_id).where("parent_comment_id is null") }
|
||||
|
||||
protected
|
||||
protected
|
||||
|
||||
# This is a view, not a real table
|
||||
def readonly?
|
||||
|
|
|
@ -5,7 +5,6 @@ class SavedStory < ApplicationRecord
|
|||
scope :by, ->(user) { where(user: user) }
|
||||
|
||||
def self.save_story_for_user(story_id, user_id)
|
||||
SavedStory.where(:user_id => user_id, :story_id =>
|
||||
story_id).first_or_initialize.save!
|
||||
SavedStory.where(user_id: user_id, story_id: story_id).first_or_initialize.save!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ class Search
|
|||
attr_accessor :results, :page, :total_results, :per_page
|
||||
attr_writer :what
|
||||
|
||||
validates :q, length: { :minimum => 2 }
|
||||
validates :q, length: {minimum: 2}
|
||||
|
||||
def initialize
|
||||
@q = ""
|
||||
|
@ -31,17 +31,17 @@ class Search
|
|||
end
|
||||
|
||||
def to_url_params
|
||||
[:q, :what, :order].map {|p| "#{p}=#{CGI.escape(self.send(p).to_s)}" }.join("&")
|
||||
[:q, :what, :order].map { |p| "#{p}=#{CGI.escape(send(p).to_s)}" }.join("&")
|
||||
end
|
||||
|
||||
def page_count
|
||||
total = self.total_results.to_i
|
||||
total = total_results.to_i
|
||||
|
||||
if total == -1 || total > self.max_matches
|
||||
total = self.max_matches
|
||||
if total == -1 || total > max_matches
|
||||
total = max_matches
|
||||
end
|
||||
|
||||
((total - 1) / self.per_page.to_i) + 1
|
||||
((total - 1) / per_page.to_i) + 1
|
||||
end
|
||||
|
||||
def what
|
||||
|
@ -55,8 +55,8 @@ class Search
|
|||
|
||||
def with_tags(base, tag_scopes)
|
||||
base
|
||||
.joins({ :taggings => :tag }, :user).left_outer_joins(:story_text)
|
||||
.where(:tags => { :tag => tag_scopes })
|
||||
.joins({taggings: :tag}, :user).left_outer_joins(:story_text)
|
||||
.where(tags: {tag: tag_scopes})
|
||||
.having("COUNT(stories.id) = ?", tag_scopes.length)
|
||||
.group("stories.id")
|
||||
end
|
||||
|
@ -71,7 +71,7 @@ class Search
|
|||
base.joins(story: [:domain])
|
||||
else
|
||||
fail "Can't handle #{base.class}"
|
||||
end.where('domains.domain = ?', domain)
|
||||
end.where("domains.domain = ?", domain)
|
||||
end
|
||||
|
||||
def with_stories_matching_tags(base, tag_scopes)
|
||||
|
@ -88,7 +88,7 @@ class Search
|
|||
# extract domain query since it must be done separately
|
||||
domain = nil
|
||||
tag_scopes = []
|
||||
words = self.q.to_s.split(" ").reject {|w|
|
||||
words = q.to_s.split(" ").reject { |w|
|
||||
if (m = w.match(/^domain:(.+)$/))
|
||||
domain = m[1]
|
||||
elsif (m = w.match(/^tag:(.+)$/))
|
||||
|
@ -100,7 +100,7 @@ class Search
|
|||
|
||||
base = nil
|
||||
|
||||
case self.what
|
||||
case what
|
||||
when "stories"
|
||||
base = Story.unmerged.where(is_deleted: false).includes(:domain, :tags, :taggings)
|
||||
if domain.present?
|
||||
|
@ -115,41 +115,41 @@ class Search
|
|||
|
||||
if qwords.present?
|
||||
base.where!(
|
||||
"(#{title_match_sql} OR " +
|
||||
"#{description_match_sql} OR " +
|
||||
"(#{title_match_sql} OR " \
|
||||
"#{description_match_sql} OR " \
|
||||
"#{story_text_match_sql})"
|
||||
)
|
||||
|
||||
if tag_scopes.present?
|
||||
self.results = with_tags(base, tag_scopes)
|
||||
else
|
||||
base = base.includes({ :taggings => :tag }, :user).left_outer_joins(:story_text)
|
||||
base = base.includes({taggings: :tag}, :user).left_outer_joins(:story_text)
|
||||
self.results = base.select(
|
||||
["stories.*", title_match_sql, description_match_sql, story_text_match_sql].join(', ')
|
||||
["stories.*", title_match_sql, description_match_sql, story_text_match_sql].join(", ")
|
||||
)
|
||||
end
|
||||
else
|
||||
if tag_scopes.present?
|
||||
self.results = with_tags(base, tag_scopes)
|
||||
self.results = if tag_scopes.present?
|
||||
with_tags(base, tag_scopes)
|
||||
else
|
||||
self.results = base.includes({ :taggings => :tag }, :user).left_outer_joins(:story_text)
|
||||
base.includes({taggings: :tag}, :user).left_outer_joins(:story_text)
|
||||
end
|
||||
end
|
||||
self.total_results = self.results.dup.count("stories.id")
|
||||
self.total_results = results.dup.count("stories.id")
|
||||
|
||||
case self.order
|
||||
case order
|
||||
when "relevance"
|
||||
if qwords.present?
|
||||
self.results.order!(Arel.sql("((#{title_match_sql}) * 2) + " +
|
||||
"((#{description_match_sql}) * 1.5) + " +
|
||||
results.order!(Arel.sql("((#{title_match_sql}) * 2) + " \
|
||||
"((#{description_match_sql}) * 1.5) + " \
|
||||
"(#{story_text_match_sql}) DESC"))
|
||||
else
|
||||
self.results.order!("stories.created_at DESC")
|
||||
results.order!("stories.created_at DESC")
|
||||
end
|
||||
when "newest"
|
||||
self.results.order!("stories.created_at DESC")
|
||||
results.order!("stories.created_at DESC")
|
||||
when "points"
|
||||
self.results.order!("score DESC")
|
||||
results.order!("score DESC")
|
||||
end
|
||||
|
||||
when "comments"
|
||||
|
@ -164,59 +164,58 @@ class Search
|
|||
base = base.where(Arel.sql("MATCH(comment) AGAINST('#{qwords}' IN BOOLEAN MODE)"))
|
||||
end
|
||||
self.results = base.select(
|
||||
"comments.*, " +
|
||||
"comments.*, " \
|
||||
"MATCH(comment) AGAINST('#{qwords}' IN BOOLEAN MODE) AS rel_comment"
|
||||
).includes(:user, :story)
|
||||
self.total_results = self.results.dup.count("comments.id")
|
||||
self.total_results = results.dup.count("comments.id")
|
||||
|
||||
case self.order
|
||||
case order
|
||||
when "relevance"
|
||||
self.results.order!("rel_comment DESC")
|
||||
results.order!("rel_comment DESC")
|
||||
when "newest"
|
||||
self.results.order!("created_at DESC")
|
||||
results.order!("comments.created_at DESC")
|
||||
when "points"
|
||||
self.results.order!("score DESC")
|
||||
results.order!("score DESC")
|
||||
end
|
||||
end
|
||||
|
||||
# with_tags uses group_by, so count returns a hash
|
||||
self.total_results = self.total_results.count if self.total_results.is_a? Hash
|
||||
self.total_results = total_results.count if total_results.is_a? Hash
|
||||
|
||||
if self.page > self.page_count
|
||||
self.page = self.page_count
|
||||
if page > page_count
|
||||
self.page = page_count
|
||||
end
|
||||
if self.page < 1
|
||||
if page < 1
|
||||
self.page = 1
|
||||
end
|
||||
|
||||
self.results = self.results
|
||||
.limit(self.per_page)
|
||||
.offset((self.page - 1) * self.per_page)
|
||||
self.results = results
|
||||
.limit(per_page)
|
||||
.offset((page - 1) * per_page)
|
||||
|
||||
# if a user is logged in, fetch their votes for what's on the page
|
||||
if user
|
||||
case what
|
||||
when "stories"
|
||||
self.results = self.results.mod_preload?(user)
|
||||
votes = Vote.story_votes_by_user_for_story_ids_hash(user.id, self.results.map(&:id))
|
||||
self.results = results.mod_preload?(user)
|
||||
votes = Vote.story_votes_by_user_for_story_ids_hash(user.id, results.map(&:id))
|
||||
|
||||
self.results.each do |r|
|
||||
results.each do |r|
|
||||
if votes[r.id]
|
||||
r.vote = votes[r.id]
|
||||
end
|
||||
end
|
||||
|
||||
when "comments"
|
||||
votes = Vote.comment_votes_by_user_for_comment_ids_hash(user.id, self.results.map(&:id))
|
||||
votes = Vote.comment_votes_by_user_for_comment_ids_hash(user.id, results.map(&:id))
|
||||
|
||||
self.results.each do |r|
|
||||
results.each do |r|
|
||||
if votes[r.id]
|
||||
r.current_vote = votes[r.id]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ActiveRecord::StatementInvalid
|
||||
# more likely the user has entered invalid boolean mode operators than our
|
||||
# code is broken (not that I really trust this hairy class)
|
||||
|
|
|
@ -2,14 +2,14 @@ class ShortId
|
|||
attr_accessor :klass, :generation_attempts
|
||||
|
||||
def initialize(klass)
|
||||
self.klass = klass
|
||||
self.klass = klass
|
||||
self.generation_attempts = 0
|
||||
end
|
||||
|
||||
def generate
|
||||
until (generated_id = candidate_id) && generated_id.valid? do
|
||||
until (generated_id = candidate_id) && generated_id.valid?
|
||||
self.generation_attempts += 1
|
||||
raise 'too many hash collisions' if generation_attempts == 10
|
||||
raise "too many hash collisions" if generation_attempts == 10
|
||||
end
|
||||
generated_id.to_s
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ class ShortId
|
|||
|
||||
def initialize(klass)
|
||||
self.klass = klass
|
||||
self.id = generate_id
|
||||
self.id = generate_id
|
||||
end
|
||||
|
||||
def to_s
|
||||
|
|
|
@ -13,10 +13,10 @@ class StoriesPaginator
|
|||
def get
|
||||
with_pagination_info @scope.limit(per_page + 1)
|
||||
.offset((@page - 1) * per_page)
|
||||
.includes(:domain, :user, :taggings => :tag)
|
||||
.includes(:domain, :user, taggings: :tag)
|
||||
end
|
||||
|
||||
private
|
||||
private
|
||||
|
||||
def with_pagination_info(scope)
|
||||
scope = scope.to_a
|
||||
|
@ -30,10 +30,8 @@ private
|
|||
if @user
|
||||
votes = Vote.votes_by_user_for_stories_hash(@user.id, scope.map(&:id))
|
||||
|
||||
hs = HiddenStory.where(:user_id => @user.id, :story_id =>
|
||||
scope.map(&:id)).map(&:story_id)
|
||||
ss = SavedStory.where(:user_id => @user.id, :story_id =>
|
||||
scope.map(&:id)).map(&:story_id)
|
||||
hs = HiddenStory.where(user_id: @user.id, story_id: scope.map(&:id)).map(&:story_id)
|
||||
ss = SavedStory.where(user_id: @user.id, story_id: scope.map(&:id)).map(&:story_id)
|
||||
|
||||
scope.each do |s|
|
||||
if votes[s.id]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,7 +13,7 @@ class StoryRepository
|
|||
def hottest
|
||||
hottest = Story.base(@user).positive_ranked.not_hidden_by(@user)
|
||||
hottest = hottest.filter_tags(@params[:exclude_tags] || [])
|
||||
hottest.order('hotness')
|
||||
hottest.order("hotness")
|
||||
end
|
||||
|
||||
def hidden
|
||||
|
@ -33,8 +33,8 @@ class StoryRepository
|
|||
from comments
|
||||
where comments.story_id = stories.id
|
||||
) as latest_comment_id')
|
||||
.where('created_at >= ?', 3.days.ago)
|
||||
.order('latest_comment_id desc')
|
||||
.where("created_at >= ?", 3.days.ago)
|
||||
.order("latest_comment_id desc")
|
||||
end
|
||||
|
||||
def newest_by_user(user)
|
||||
|
@ -53,7 +53,7 @@ class StoryRepository
|
|||
end
|
||||
|
||||
def top(length)
|
||||
top = Story.base(@user).where("created_at >= (NOW() - INTERVAL " <<
|
||||
top = Story.base(@user).where("created_at >= (NOW() - INTERVAL " \
|
||||
"#{length[:dur]} #{length[:intv].upcase})")
|
||||
top.order("score DESC")
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ class StoryText < ApplicationRecord
|
|||
|
||||
belongs_to :story, foreign_key: :id, inverse_of: :story_text
|
||||
|
||||
validates :body, presence: true, length: { :maximum => 16_777_215 }
|
||||
validates :body, presence: true, length: {maximum: 16_777_215}
|
||||
|
||||
def self.fill_cache!(story)
|
||||
return nil unless story.url.present?
|
||||
|
@ -25,6 +25,6 @@ class StoryText < ApplicationRecord
|
|||
return false
|
||||
end
|
||||
|
||||
self.where(id: story).exists?
|
||||
where(id: story).exists?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,54 +1,54 @@
|
|||
class Tag < ApplicationRecord
|
||||
belongs_to :category
|
||||
has_many :taggings, :dependent => :delete_all
|
||||
has_many :stories, :through => :taggings
|
||||
has_many :tag_filters, :dependent => :destroy
|
||||
has_many :taggings, dependent: :delete_all
|
||||
has_many :stories, through: :taggings
|
||||
has_many :tag_filters, dependent: :destroy
|
||||
has_many :filtering_users,
|
||||
:class_name => "User",
|
||||
:through => :tag_filters,
|
||||
:source => :user,
|
||||
:dependent => :delete_all
|
||||
class_name: "User",
|
||||
through: :tag_filters,
|
||||
source: :user,
|
||||
dependent: :delete_all
|
||||
|
||||
after_save :log_modifications
|
||||
|
||||
attr_accessor :edit_user_id, :stories_count
|
||||
attr_writer :filtered_count
|
||||
|
||||
validates :tag, length: { maximum: 25 }, presence: true,
|
||||
uniqueness: { case_sensitive: true },
|
||||
format: { with: /\A[A-Za-z0-9_\-\+]+\z/ }
|
||||
validates :description, length: { maximum: 100 }
|
||||
validates :hotness_mod, inclusion: { in: -10..10 }
|
||||
validates :permit_by_new_users, :privileged, inclusion: { in: [true, false] }
|
||||
validates :tag, length: {maximum: 25}, presence: true,
|
||||
uniqueness: {case_sensitive: true},
|
||||
format: {with: /\A[A-Za-z0-9_\-\+]+\z/}
|
||||
validates :description, length: {maximum: 100}
|
||||
validates :hotness_mod, inclusion: {in: -10..10}
|
||||
validates :permit_by_new_users, :privileged, inclusion: {in: [true, false]}
|
||||
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :not_permitted_for_new_users, -> { where(permit_by_new_users: false) }
|
||||
scope :related, ->(tag) {
|
||||
active
|
||||
.joins(:taggings)
|
||||
.where(taggings: { story_id: Tagging.where(tag: tag).select(:story_id) })
|
||||
.where(taggings: {story_id: Tagging.where(tag: tag).select(:story_id)})
|
||||
.where.not(id: [tag, 67]) # 67 = programming, the catch-all
|
||||
.where.not(is_media: true)
|
||||
.group(:id)
|
||||
.order(Arel.sql('COUNT(*) desc'))
|
||||
.order(Arel.sql("COUNT(*) desc"))
|
||||
.limit(8)
|
||||
}
|
||||
|
||||
def to_param
|
||||
self.tag
|
||||
tag
|
||||
end
|
||||
|
||||
def self.all_with_filtered_counts_for(user)
|
||||
counts = TagFilter.group(:tag_id).count
|
||||
|
||||
Tag.active.order(:tag).select {|t| t.valid_for?(user) }.map {|t|
|
||||
Tag.active.order(:tag).select { |t| t.valid_for?(user) }.map { |t|
|
||||
t.filtered_count = counts[t.id].to_i
|
||||
t
|
||||
}
|
||||
end
|
||||
|
||||
def category_name
|
||||
self.category && self.category.category
|
||||
category&.category
|
||||
end
|
||||
|
||||
def category_name=(category)
|
||||
|
@ -56,15 +56,15 @@ class Tag < ApplicationRecord
|
|||
end
|
||||
|
||||
def css_class
|
||||
"tag tag_#{self.tag}" << (self.is_media?? " tag_is_media" : "")
|
||||
"tag tag_#{tag}" << (is_media? ? " tag_is_media" : "")
|
||||
end
|
||||
|
||||
def user_can_filter?(user)
|
||||
self.active? && (!self.privileged? || user.try(:is_moderator?))
|
||||
active? && (!privileged? || user.try(:is_moderator?))
|
||||
end
|
||||
|
||||
def valid_for?(user)
|
||||
if self.privileged?
|
||||
if privileged?
|
||||
!!user.try(:is_moderator?)
|
||||
else
|
||||
true
|
||||
|
@ -72,19 +72,19 @@ class Tag < ApplicationRecord
|
|||
end
|
||||
|
||||
def filtered_count
|
||||
@filtered_count ||= TagFilter.where(:tag_id => self.id).count
|
||||
@filtered_count ||= TagFilter.where(tag_id: id).count
|
||||
end
|
||||
|
||||
def log_modifications
|
||||
Moderation.create do |m|
|
||||
if self.id_previously_changed?
|
||||
m.action = 'Created new tag ' + self.attributes.map {|f, c| "with #{f} '#{c}'" }.join(', ')
|
||||
m.action = if id_previously_changed?
|
||||
"Created new tag " + attributes.map { |f, c| "with #{f} '#{c}'" }.join(", ")
|
||||
else
|
||||
m.action = "Updating tag #{self.tag}, " + self.saved_changes
|
||||
.map {|f, c| "changed #{f} from '#{c[0]}' to '#{c[1]}'" } .join(', ')
|
||||
"Updating tag #{tag}, " + saved_changes
|
||||
.map { |f, c| "changed #{f} from '#{c[0]}' to '#{c[1]}'" }.join(", ")
|
||||
end
|
||||
m.moderator_user_id = @edit_user_id
|
||||
m.tag_id = self.id
|
||||
m.tag_id = id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Tagging < ApplicationRecord
|
||||
belongs_to :tag, :inverse_of => :taggings
|
||||
belongs_to :story, :inverse_of => :taggings
|
||||
belongs_to :tag, inverse_of: :taggings
|
||||
belongs_to :story, inverse_of: :taggings
|
||||
end
|
||||
|
|
|
@ -1,73 +1,73 @@
|
|||
class User < ApplicationRecord
|
||||
has_many :stories, -> { includes :user }, :inverse_of => :user
|
||||
has_many :stories, -> { includes :user }, inverse_of: :user
|
||||
has_many :comments,
|
||||
:inverse_of => :user,
|
||||
:dependent => :restrict_with_exception
|
||||
inverse_of: :user,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :sent_messages,
|
||||
:class_name => "Message",
|
||||
:foreign_key => "author_user_id",
|
||||
:inverse_of => :author,
|
||||
:dependent => :restrict_with_exception
|
||||
class_name: "Message",
|
||||
foreign_key: "author_user_id",
|
||||
inverse_of: :author,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :received_messages,
|
||||
:class_name => "Message",
|
||||
:foreign_key => "recipient_user_id",
|
||||
:inverse_of => :recipient,
|
||||
:dependent => :restrict_with_exception
|
||||
has_many :tag_filters, :dependent => :destroy
|
||||
class_name: "Message",
|
||||
foreign_key: "recipient_user_id",
|
||||
inverse_of: :recipient,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :tag_filters, dependent: :destroy
|
||||
has_many :tag_filter_tags,
|
||||
:class_name => "Tag",
|
||||
:through => :tag_filters,
|
||||
:source => :tag,
|
||||
:dependent => :delete_all
|
||||
class_name: "Tag",
|
||||
through: :tag_filters,
|
||||
source: :tag,
|
||||
dependent: :delete_all
|
||||
belongs_to :invited_by_user,
|
||||
:class_name => "User",
|
||||
:inverse_of => false,
|
||||
:optional => true
|
||||
class_name: "User",
|
||||
inverse_of: false,
|
||||
optional: true
|
||||
belongs_to :banned_by_user,
|
||||
:class_name => "User",
|
||||
:inverse_of => false,
|
||||
:optional => true
|
||||
class_name: "User",
|
||||
inverse_of: false,
|
||||
optional: true
|
||||
belongs_to :disabled_invite_by_user,
|
||||
:class_name => "User",
|
||||
:inverse_of => false,
|
||||
:optional => true
|
||||
has_many :invitations, :dependent => :destroy
|
||||
class_name: "User",
|
||||
inverse_of: false,
|
||||
optional: true
|
||||
has_many :invitations, dependent: :destroy
|
||||
has_many :mod_notes,
|
||||
:inverse_of => :user,
|
||||
:dependent => :restrict_with_exception
|
||||
inverse_of: :user,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :moderations,
|
||||
:inverse_of => :moderator,
|
||||
:dependent => :restrict_with_exception
|
||||
has_many :votes, :dependent => :destroy
|
||||
has_many :voted_stories, -> { where('votes.comment_id' => nil) },
|
||||
:through => :votes,
|
||||
:source => :story
|
||||
inverse_of: :moderator,
|
||||
dependent: :restrict_with_exception
|
||||
has_many :votes, dependent: :destroy
|
||||
has_many :voted_stories, -> { where("votes.comment_id" => nil) },
|
||||
through: :votes,
|
||||
source: :story
|
||||
has_many :upvoted_stories,
|
||||
-> {
|
||||
where('votes.comment_id' => nil, 'votes.vote' => 1)
|
||||
.where('stories.user_id != votes.user_id')
|
||||
},
|
||||
:through => :votes,
|
||||
:source => :story
|
||||
has_many :hats, :dependent => :destroy
|
||||
has_many :wearable_hats, -> { where('doffed_at is null') },
|
||||
:class_name => "Hat",
|
||||
:inverse_of => :user
|
||||
-> {
|
||||
where("votes.comment_id" => nil, "votes.vote" => 1)
|
||||
.where("stories.user_id != votes.user_id")
|
||||
},
|
||||
through: :votes,
|
||||
source: :story
|
||||
has_many :hats, dependent: :destroy
|
||||
has_many :wearable_hats, -> { where("doffed_at is null") },
|
||||
class_name: "Hat",
|
||||
inverse_of: :user
|
||||
|
||||
has_secure_password
|
||||
|
||||
typed_store :settings do |s|
|
||||
s.string :prefers_color_scheme, :default => "system"
|
||||
s.boolean :email_notifications, :default => false
|
||||
s.boolean :email_replies, :default => false
|
||||
s.boolean :pushover_replies, :default => false
|
||||
s.string :prefers_color_scheme, default: "system"
|
||||
s.boolean :email_notifications, default: false
|
||||
s.boolean :email_replies, default: false
|
||||
s.boolean :pushover_replies, default: false
|
||||
s.string :pushover_user_key
|
||||
s.boolean :email_messages, :default => false
|
||||
s.boolean :pushover_messages, :default => false
|
||||
s.boolean :email_mentions, :default => false
|
||||
s.boolean :show_avatars, :default => true
|
||||
s.boolean :show_story_previews, :default => false
|
||||
s.boolean :show_submitted_story_threads, :default => false
|
||||
s.boolean :email_messages, default: false
|
||||
s.boolean :pushover_messages, default: false
|
||||
s.boolean :email_mentions, default: false
|
||||
s.boolean :show_avatars, default: true
|
||||
s.boolean :show_story_previews, default: false
|
||||
s.boolean :show_submitted_story_threads, default: false
|
||||
s.string :totp_secret
|
||||
s.string :github_oauth_token
|
||||
s.string :github_username
|
||||
|
@ -78,49 +78,49 @@ class User < ApplicationRecord
|
|||
s.string :homepage
|
||||
end
|
||||
|
||||
validates :prefers_color_scheme, inclusion: %w(system light dark)
|
||||
validates :prefers_color_scheme, inclusion: %w[system light dark]
|
||||
|
||||
validates :email,
|
||||
:length => { :maximum => 100 },
|
||||
:format => { :with => /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/ },
|
||||
:uniqueness => { :case_sensitive => false }
|
||||
length: {maximum: 100},
|
||||
format: {with: /\A[^@ ]+@[^@ ]+\.[^@ ]+\Z/},
|
||||
uniqueness: {case_sensitive: false}
|
||||
|
||||
validates :homepage,
|
||||
:format => {
|
||||
:with => /\A(?:https?|gemini|gopher):\/\/[^\/\s]+\.[^.\/\s]+(\/.*)?\Z/,
|
||||
},
|
||||
:allow_blank => true
|
||||
format: {
|
||||
with: /\A(?:https?|gemini|gopher):\/\/[^\/\s]+\.[^.\/\s]+(\/.*)?\Z/
|
||||
},
|
||||
allow_blank: true
|
||||
|
||||
validates :password, :presence => true, :on => :create
|
||||
validates :password, presence: true, on: :create
|
||||
|
||||
VALID_USERNAME = /[A-Za-z0-9][A-Za-z0-9_-]{0,24}/.freeze
|
||||
VALID_USERNAME = /[A-Za-z0-9][A-Za-z0-9_-]{0,24}/
|
||||
validates :username,
|
||||
:format => { :with => /\A#{VALID_USERNAME}\z/ },
|
||||
:length => { :maximum => 50 },
|
||||
:uniqueness => { :case_sensitive => false }
|
||||
format: {with: /\A#{VALID_USERNAME}\z/o},
|
||||
length: {maximum: 50},
|
||||
uniqueness: {case_sensitive: false}
|
||||
|
||||
validates :password_reset_token,
|
||||
:length => { :maximum => 75 }
|
||||
length: {maximum: 75}
|
||||
validates :session_token,
|
||||
:length => { :maximum => 75 }
|
||||
length: {maximum: 75}
|
||||
validates :about,
|
||||
:length => { :maximum => 16_777_215 }
|
||||
length: {maximum: 16_777_215}
|
||||
validates :rss_token,
|
||||
:length => { :maximum => 75 }
|
||||
length: {maximum: 75}
|
||||
validates :mailing_list_token,
|
||||
:length => { :maximum => 75 }
|
||||
length: {maximum: 75}
|
||||
validates :banned_reason,
|
||||
:length => { :maximum => 200 }
|
||||
length: {maximum: 200}
|
||||
validates :disabled_invite_reason,
|
||||
:length => { :maximum => 200 }
|
||||
length: {maximum: 200}
|
||||
|
||||
validates_each :username do |record, attr, value|
|
||||
if BANNED_USERNAMES.include?(value.to_s.downcase) || value.starts_with?('tag-')
|
||||
if BANNED_USERNAMES.include?(value.to_s.downcase) || value.starts_with?("tag-")
|
||||
record.errors.add(attr, "is not permitted")
|
||||
end
|
||||
end
|
||||
|
||||
scope :active, -> { where(:banned_at => nil, :deleted_at => nil) }
|
||||
scope :active, -> { where(banned_at: nil, deleted_at: nil) }
|
||||
scope :moderators, -> {
|
||||
where('
|
||||
is_moderator = True OR
|
||||
|
@ -129,15 +129,15 @@ class User < ApplicationRecord
|
|||
}
|
||||
|
||||
before_save :check_session_token
|
||||
before_validation :on => :create do
|
||||
self.create_rss_token
|
||||
self.create_mailing_list_token
|
||||
before_validation on: :create do
|
||||
create_rss_token
|
||||
create_mailing_list_token
|
||||
end
|
||||
|
||||
BANNED_USERNAMES = ["admin", "administrator", "contact", "fraud", "guest",
|
||||
"help", "hostmaster", "lobster", "lobsters", "mailer-daemon", "moderator",
|
||||
"moderators", "nobody", "postmaster", "root", "security", "support",
|
||||
"sysop", "webmaster", "enable", "new", "signup",].freeze
|
||||
"sysop", "webmaster", "enable", "new", "signup"].freeze
|
||||
|
||||
# days old accounts are considered new for
|
||||
NEW_USER_DAYS = 70
|
||||
|
@ -169,59 +169,59 @@ class User < ApplicationRecord
|
|||
:username,
|
||||
:created_at,
|
||||
:is_admin,
|
||||
:is_moderator,
|
||||
:is_moderator
|
||||
]
|
||||
|
||||
if !self.is_admin?
|
||||
if !is_admin?
|
||||
attrs.push :karma
|
||||
end
|
||||
|
||||
attrs.push :homepage, :about
|
||||
|
||||
h = super(:only => attrs)
|
||||
h = super(only: attrs)
|
||||
|
||||
h[:avatar_url] = self.avatar_url
|
||||
h[:avatar_url] = avatar_url
|
||||
h[:invited_by_user] = User.where(id: invited_by_user_id).pluck(:username).first
|
||||
|
||||
if self.github_username.present?
|
||||
h[:github_username] = self.github_username
|
||||
if github_username.present?
|
||||
h[:github_username] = github_username
|
||||
end
|
||||
|
||||
if self.twitter_username.present?
|
||||
h[:twitter_username] = self.twitter_username
|
||||
if twitter_username.present?
|
||||
h[:twitter_username] = twitter_username
|
||||
end
|
||||
|
||||
if self.keybase_signatures.present?
|
||||
h[:keybase_signatures] = self.keybase_signatures
|
||||
if keybase_signatures.present?
|
||||
h[:keybase_signatures] = keybase_signatures
|
||||
end
|
||||
|
||||
h
|
||||
end
|
||||
|
||||
def authenticate_totp(code)
|
||||
totp = ROTP::TOTP.new(self.totp_secret)
|
||||
totp = ROTP::TOTP.new(totp_secret)
|
||||
totp.verify(code)
|
||||
end
|
||||
|
||||
def avatar_path(size = 100)
|
||||
ActionController::Base.helpers.image_path(
|
||||
"/avatars/#{self.username}-#{size}.png",
|
||||
"/avatars/#{username}-#{size}.png",
|
||||
skip_pipeline: true
|
||||
)
|
||||
end
|
||||
|
||||
def avatar_url(size = 100)
|
||||
ActionController::Base.helpers.image_url(
|
||||
"/avatars/#{self.username}-#{size}.png",
|
||||
"/avatars/#{username}-#{size}.png",
|
||||
skip_pipeline: true
|
||||
)
|
||||
end
|
||||
|
||||
def average_karma
|
||||
if self.karma == 0
|
||||
if karma == 0
|
||||
0
|
||||
else
|
||||
self.karma.to_f / (self.stories_submitted_count + self.comments_posted_count)
|
||||
karma.to_f / (stories_submitted_count + comments_posted_count)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -230,23 +230,23 @@ class User < ApplicationRecord
|
|||
self.disabled_invite_at = Time.current
|
||||
self.disabled_invite_by_user_id = disabler.id
|
||||
self.disabled_invite_reason = reason
|
||||
self.save!
|
||||
save!
|
||||
|
||||
msg = Message.new
|
||||
msg.deleted_by_author = true
|
||||
msg.author_user_id = disabler.id
|
||||
msg.recipient_user_id = self.id
|
||||
msg.recipient_user_id = id
|
||||
msg.subject = "Your invite privileges have been revoked"
|
||||
msg.body = "The reason given:\n" <<
|
||||
"\n" <<
|
||||
"> *#{reason}*\n" <<
|
||||
"\n" <<
|
||||
"*This is an automated message.*"
|
||||
msg.body = "The reason given:\n" \
|
||||
"\n" \
|
||||
"> *#{reason}*\n" \
|
||||
"\n" \
|
||||
"*This is an automated message.*"
|
||||
msg.save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = disabler.id
|
||||
m.user_id = self.id
|
||||
m.user_id = id
|
||||
m.action = "Disabled invitations"
|
||||
m.reason = reason
|
||||
m.save!
|
||||
|
@ -261,12 +261,12 @@ class User < ApplicationRecord
|
|||
self.banned_by_user_id = banner.id
|
||||
self.banned_reason = reason
|
||||
|
||||
BanNotification.notify(self, banner, reason) unless self.deleted_at?
|
||||
self.delete!
|
||||
BanNotification.notify(self, banner, reason) unless deleted_at?
|
||||
delete!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = banner.id
|
||||
m.user_id = self.id
|
||||
m.user_id = id
|
||||
m.action = "Banned"
|
||||
m.reason = reason
|
||||
m.save!
|
||||
|
@ -290,59 +290,59 @@ class User < ApplicationRecord
|
|||
return true
|
||||
end
|
||||
elsif obj.is_a?(Comment) && obj.is_flaggable?
|
||||
return self.karma >= MIN_KARMA_TO_FLAG
|
||||
return karma >= MIN_KARMA_TO_FLAG
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def can_invite?
|
||||
!self.is_new? && !banned_from_inviting? && self.can_submit_stories?
|
||||
!is_new? && !banned_from_inviting? && can_submit_stories?
|
||||
end
|
||||
|
||||
def can_offer_suggestions?
|
||||
!self.is_new? && (self.karma >= MIN_KARMA_TO_SUGGEST)
|
||||
!is_new? && (karma >= MIN_KARMA_TO_SUGGEST)
|
||||
end
|
||||
|
||||
def can_see_invitation_requests?
|
||||
can_invite? && (self.is_moderator? ||
|
||||
(self.karma >= MIN_KARMA_FOR_INVITATION_REQUESTS))
|
||||
can_invite? && (is_moderator? ||
|
||||
(karma >= MIN_KARMA_FOR_INVITATION_REQUESTS))
|
||||
end
|
||||
|
||||
def can_submit_stories?
|
||||
self.karma >= MIN_KARMA_TO_SUBMIT_STORIES
|
||||
karma >= MIN_KARMA_TO_SUBMIT_STORIES
|
||||
end
|
||||
|
||||
def check_session_token
|
||||
if self.session_token.blank?
|
||||
self.roll_session_token
|
||||
if session_token.blank?
|
||||
roll_session_token
|
||||
end
|
||||
end
|
||||
|
||||
def create_mailing_list_token
|
||||
if self.mailing_list_token.blank?
|
||||
if mailing_list_token.blank?
|
||||
self.mailing_list_token = Utils.random_str(10)
|
||||
end
|
||||
end
|
||||
|
||||
def create_rss_token
|
||||
if self.rss_token.blank?
|
||||
if rss_token.blank?
|
||||
self.rss_token = Utils.random_str(60)
|
||||
end
|
||||
end
|
||||
|
||||
def comments_posted_count
|
||||
Keystore.value_for("user:#{self.id}:comments_posted").to_i
|
||||
Keystore.value_for("user:#{id}:comments_posted").to_i
|
||||
end
|
||||
|
||||
def comments_deleted_count
|
||||
Keystore.value_for("user:#{self.id}:comments_deleted").to_i
|
||||
Keystore.value_for("user:#{id}:comments_deleted").to_i
|
||||
end
|
||||
|
||||
def fetched_avatar(size = 100)
|
||||
gravatar_url = "https://www.gravatar.com/avatar/" <<
|
||||
Digest::MD5.hexdigest(self.email.strip.downcase) <<
|
||||
"?r=pg&d=identicon&s=#{size}"
|
||||
Digest::MD5.hexdigest(email.strip.downcase) <<
|
||||
"?r=pg&d=identicon&s=#{size}"
|
||||
|
||||
begin
|
||||
s = Sponge.new
|
||||
|
@ -359,81 +359,81 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def refresh_counts!
|
||||
Keystore.put("user:#{self.id}:stories_submitted", self.stories.count)
|
||||
Keystore.put("user:#{self.id}:comments_posted", self.comments.active.count)
|
||||
Keystore.put("user:#{self.id}:comments_deleted", self.comments.deleted.count)
|
||||
Keystore.put("user:#{id}:stories_submitted", stories.count)
|
||||
Keystore.put("user:#{id}:comments_posted", comments.active.count)
|
||||
Keystore.put("user:#{id}:comments_deleted", comments.deleted.count)
|
||||
end
|
||||
|
||||
def delete!
|
||||
User.transaction do
|
||||
self.comments
|
||||
comments
|
||||
.where("score < 0")
|
||||
.find_each {|c| c.delete_for_user(self) }
|
||||
.find_each { |c| c.delete_for_user(self) }
|
||||
|
||||
self.sent_messages.each do |m|
|
||||
sent_messages.each do |m|
|
||||
m.deleted_by_author = true
|
||||
m.save
|
||||
end
|
||||
self.received_messages.each do |m|
|
||||
received_messages.each do |m|
|
||||
m.deleted_by_recipient = true
|
||||
m.save
|
||||
end
|
||||
|
||||
self.invitations.destroy_all
|
||||
invitations.destroy_all
|
||||
|
||||
self.roll_session_token
|
||||
roll_session_token
|
||||
|
||||
self.deleted_at = Time.current
|
||||
self.good_riddance?
|
||||
self.save!
|
||||
good_riddance?
|
||||
save!
|
||||
end
|
||||
end
|
||||
|
||||
def undelete!
|
||||
User.transaction do
|
||||
self.sent_messages.each do |m|
|
||||
sent_messages.each do |m|
|
||||
m.deleted_by_author = false
|
||||
m.save
|
||||
end
|
||||
self.received_messages.each do |m|
|
||||
received_messages.each do |m|
|
||||
m.deleted_by_recipient = false
|
||||
m.save
|
||||
end
|
||||
|
||||
self.deleted_at = nil
|
||||
self.save!
|
||||
save!
|
||||
end
|
||||
end
|
||||
|
||||
def disable_2fa!
|
||||
self.totp_secret = nil
|
||||
self.save!
|
||||
save!
|
||||
end
|
||||
|
||||
# ensures some users talk to a mod before reactivating
|
||||
def good_riddance?
|
||||
return if self.is_banned? # https://www.youtube.com/watch?v=UcZzlPGnKdU
|
||||
self.email = "#{self.username}@lobsters.example" if \
|
||||
self.karma < 0 ||
|
||||
(self.comments.where('created_at >= now() - interval 30 day AND is_deleted').count +
|
||||
self.stories.where('created_at >= now() - interval 30 day AND is_deleted AND is_moderated')
|
||||
.count >= 3) ||
|
||||
FlaggedCommenters.new('90d').check_list_for(self)
|
||||
return if is_banned? # https://www.youtube.com/watch?v=UcZzlPGnKdU
|
||||
self.email = "#{username}@lobsters.example" if \
|
||||
karma < 0 ||
|
||||
(comments.where("created_at >= now() - interval 30 day AND is_deleted").count +
|
||||
stories.where("created_at >= now() - interval 30 day AND is_deleted AND is_moderated")
|
||||
.count >= 3) ||
|
||||
FlaggedCommenters.new("90d").check_list_for(self)
|
||||
end
|
||||
|
||||
def grant_moderatorship_by_user!(user)
|
||||
User.transaction do
|
||||
self.is_moderator = true
|
||||
self.save!
|
||||
save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = user.id
|
||||
m.user_id = self.id
|
||||
m.user_id = id
|
||||
m.action = "Granted moderator status"
|
||||
m.save!
|
||||
|
||||
h = Hat.new
|
||||
h.user_id = self.id
|
||||
h.user_id = id
|
||||
h.granted_by_user_id = user.id
|
||||
h.hat = "Sysop"
|
||||
h.save!
|
||||
|
@ -444,13 +444,13 @@ class User < ApplicationRecord
|
|||
|
||||
def initiate_password_reset_for_ip(ip)
|
||||
self.password_reset_token = "#{Time.current.to_i}-#{Utils.random_str(30)}"
|
||||
self.save!
|
||||
save!
|
||||
|
||||
PasswordReset.password_reset_link(self, ip).deliver_now
|
||||
end
|
||||
|
||||
def has_2fa?
|
||||
self.totp_secret.present?
|
||||
totp_secret.present?
|
||||
end
|
||||
|
||||
def is_active?
|
||||
|
@ -463,23 +463,23 @@ class User < ApplicationRecord
|
|||
|
||||
# user was deleted/banned before a server move, see lib/tasks/privacy_wipe
|
||||
def is_wiped?
|
||||
password_digest == '*'
|
||||
password_digest == "*"
|
||||
end
|
||||
|
||||
def is_new?
|
||||
return true unless self.created_at # unsaved object; in signup flow or a test
|
||||
self.created_at > NEW_USER_DAYS.days.ago
|
||||
return true unless created_at # unsaved object; in signup flow or a test
|
||||
created_at > NEW_USER_DAYS.days.ago
|
||||
end
|
||||
|
||||
def add_or_update_keybase_proof(kb_username, kb_signature)
|
||||
self.keybase_signatures ||= []
|
||||
self.remove_keybase_proof(kb_username)
|
||||
self.keybase_signatures.push('kb_username' => kb_username, 'sig_hash' => kb_signature)
|
||||
remove_keybase_proof(kb_username)
|
||||
self.keybase_signatures.push("kb_username" => kb_username, "sig_hash" => kb_signature)
|
||||
end
|
||||
|
||||
def remove_keybase_proof(kb_username)
|
||||
self.keybase_signatures ||= []
|
||||
self.keybase_signatures.reject! {|kbsig| kbsig['kb_username'] == kb_username }
|
||||
self.keybase_signatures.reject! { |kbsig| kbsig["kb_username"] == kb_username }
|
||||
end
|
||||
|
||||
def roll_session_token
|
||||
|
@ -487,47 +487,47 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def is_heavy_self_promoter?
|
||||
total_count = self.stories_submitted_count
|
||||
total_count = stories_submitted_count
|
||||
|
||||
if total_count < MIN_STORIES_CHECK_SELF_PROMOTION
|
||||
false
|
||||
else
|
||||
authored = self.stories.where(:user_is_author => true).count
|
||||
authored = stories.where(user_is_author: true).count
|
||||
authored.to_f / total_count >= HEAVY_SELF_PROMOTER_PROPORTION
|
||||
end
|
||||
end
|
||||
|
||||
def linkified_about
|
||||
Markdowner.to_html(self.about)
|
||||
Markdowner.to_html(about)
|
||||
end
|
||||
|
||||
def most_common_story_tag
|
||||
Tag.active.joins(
|
||||
:stories
|
||||
).where(
|
||||
:stories => { :user_id => self.id, :is_deleted => false }
|
||||
stories: {user_id: id, is_deleted: false}
|
||||
).group(
|
||||
Tag.arel_table[:id]
|
||||
).order(
|
||||
Arel.sql('COUNT(*) desc')
|
||||
Arel.sql("COUNT(*) desc")
|
||||
).first
|
||||
end
|
||||
|
||||
def pushover!(params)
|
||||
if self.pushover_user_key.present?
|
||||
Pushover.push(self.pushover_user_key, params)
|
||||
if pushover_user_key.present?
|
||||
Pushover.push(pushover_user_key, params)
|
||||
end
|
||||
end
|
||||
|
||||
def recent_threads(amount, include_submitted_stories: false, for_user: user)
|
||||
comments = self.comments.accessible_to_user(for_user)
|
||||
|
||||
thread_ids = comments.group(:thread_id).order('MAX(created_at) DESC').limit(amount)
|
||||
thread_ids = comments.group(:thread_id).order("MAX(created_at) DESC").limit(amount)
|
||||
.pluck(:thread_id)
|
||||
|
||||
if include_submitted_stories && self.show_submitted_story_threads
|
||||
if include_submitted_stories && show_submitted_story_threads
|
||||
thread_ids += Comment.joins(:story)
|
||||
.where(:stories => { :user_id => self.id }).group(:thread_id)
|
||||
.where(stories: {user_id: id}).group(:thread_id)
|
||||
.order("MAX(comments.created_at) DESC").limit(amount).pluck(:thread_id)
|
||||
|
||||
thread_ids = thread_ids.uniq.sort.reverse[0, amount]
|
||||
|
@ -537,11 +537,11 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def stories_submitted_count
|
||||
Keystore.value_for("user:#{self.id}:stories_submitted").to_i
|
||||
Keystore.value_for("user:#{id}:stories_submitted").to_i
|
||||
end
|
||||
|
||||
def stories_deleted_count
|
||||
Keystore.value_for("user:#{self.id}:stories_deleted").to_i
|
||||
Keystore.value_for("user:#{id}:stories_deleted").to_i
|
||||
end
|
||||
|
||||
def to_param
|
||||
|
@ -553,11 +553,11 @@ class User < ApplicationRecord
|
|||
self.banned_by_user_id = nil
|
||||
self.banned_reason = nil
|
||||
self.deleted_at = nil
|
||||
self.save!
|
||||
save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = unbanner.id
|
||||
m.user_id = self.id
|
||||
m.user_id = id
|
||||
m.action = "Unbanned"
|
||||
m.reason = reason
|
||||
m.save!
|
||||
|
@ -570,11 +570,11 @@ class User < ApplicationRecord
|
|||
self.disabled_invite_at = nil
|
||||
self.disabled_invite_by_user_id = nil
|
||||
self.disabled_invite_reason = nil
|
||||
self.save!
|
||||
save!
|
||||
|
||||
m = Moderation.new
|
||||
m.moderator_user_id = mod.id
|
||||
m.user_id = self.id
|
||||
m.user_id = id
|
||||
m.action = "Enabled invitations"
|
||||
m.save!
|
||||
end
|
||||
|
@ -583,22 +583,22 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def unread_message_count
|
||||
@unread_message_count ||= Keystore.value_for("user:#{self.id}:unread_messages").to_i
|
||||
@unread_message_count ||= Keystore.value_for("user:#{id}:unread_messages").to_i
|
||||
end
|
||||
|
||||
def update_unread_message_count!
|
||||
@unread_message_count = self.received_messages.unread.count
|
||||
Keystore.put("user:#{self.id}:unread_messages", @unread_message_count)
|
||||
@unread_message_count = received_messages.unread.count
|
||||
Keystore.put("user:#{id}:unread_messages", @unread_message_count)
|
||||
end
|
||||
|
||||
def clear_unread_replies!
|
||||
Rails.cache.delete("user:#{self.id}:unread_replies")
|
||||
Rails.cache.delete("user:#{id}:unread_replies")
|
||||
end
|
||||
|
||||
def unread_replies_count
|
||||
@unread_replies_count ||=
|
||||
Rails.cache.fetch("user:#{self.id}:unread_replies", expires_in: 2.minutes) {
|
||||
ReplyingComment.where(user_id: self.id, is_unread: true).count
|
||||
Rails.cache.fetch("user:#{id}:unread_replies", expires_in: 2.minutes) {
|
||||
ReplyingComment.where(user_id: id, is_unread: true).count
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -607,9 +607,9 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def votes_for_others
|
||||
self.votes.left_outer_joins(:story, :comment)
|
||||
votes.left_outer_joins(:story, :comment)
|
||||
.includes(comment: :user, story: :user)
|
||||
.where("(votes.comment_id is not null and comments.user_id <> votes.user_id) OR " <<
|
||||
.where("(votes.comment_id is not null and comments.user_id <> votes.user_id) OR " \
|
||||
"(votes.comment_id is null and stories.user_id <> votes.user_id)")
|
||||
.order("id DESC")
|
||||
end
|
||||
|
|
|
@ -5,10 +5,10 @@ class Vote < ApplicationRecord
|
|||
|
||||
validates :vote, presence: true
|
||||
validates :reason,
|
||||
length: { is: 1 },
|
||||
allow_blank: true
|
||||
length: {is: 1},
|
||||
allow_blank: true
|
||||
|
||||
scope :comments_flags, ->(comments, user=nil) {
|
||||
scope :comments_flags, ->(comments, user = nil) {
|
||||
q = where(comment: comments, vote: -1)
|
||||
user ? q.where(user: user) : q.all
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ class Vote < ApplicationRecord
|
|||
"T" => "Troll",
|
||||
"U" => "Unkind",
|
||||
"S" => "Spam",
|
||||
"" => "Cancel",
|
||||
"" => "Cancel"
|
||||
}.freeze
|
||||
ALL_COMMENT_REASONS = COMMENT_REASONS.merge({
|
||||
"I" => "Incorrect",
|
||||
"I" => "Incorrect"
|
||||
}).freeze
|
||||
|
||||
# don't forget to edit the explanations on /about
|
||||
|
@ -32,18 +32,18 @@ class Vote < ApplicationRecord
|
|||
"A" => "Already Posted",
|
||||
"B" => "Broken Link",
|
||||
"S" => "Spam",
|
||||
"" => "Cancel",
|
||||
"" => "Cancel"
|
||||
}.freeze
|
||||
ALL_STORY_REASONS = STORY_REASONS.merge({
|
||||
"Q" => "Low Quality",
|
||||
"Q" => "Low Quality"
|
||||
}).freeze
|
||||
|
||||
def self.votes_by_user_for_stories_hash(user, stories)
|
||||
votes = {}
|
||||
|
||||
Vote.where(:user_id => user, :story_id => stories,
|
||||
:comment_id => nil).find_each do |v|
|
||||
votes[v.story_id] = { :vote => v.vote, :reason => v.reason }
|
||||
Vote.where(user_id: user, story_id: stories,
|
||||
comment_id: nil).find_each do |v|
|
||||
votes[v.story_id] = {vote: v.vote, reason: v.reason}
|
||||
end
|
||||
|
||||
votes
|
||||
|
@ -53,11 +53,11 @@ class Vote < ApplicationRecord
|
|||
votes = {}
|
||||
|
||||
Vote.where(
|
||||
:user_id => user_id, :story_id => story_id
|
||||
user_id: user_id, story_id: story_id
|
||||
).where(
|
||||
"comment_id IS NOT NULL"
|
||||
).find_each do |v|
|
||||
votes[v.comment_id] = { :vote => v.vote, :reason => v.reason }
|
||||
votes[v.comment_id] = {vote: v.vote, reason: v.reason}
|
||||
end
|
||||
|
||||
votes
|
||||
|
@ -67,14 +67,13 @@ class Vote < ApplicationRecord
|
|||
if story_ids.empty?
|
||||
{}
|
||||
else
|
||||
votes = self.where(
|
||||
:user_id => user_id,
|
||||
:comment_id => nil,
|
||||
:story_id => story_ids,
|
||||
votes = where(
|
||||
user_id: user_id,
|
||||
comment_id: nil,
|
||||
story_id: story_ids
|
||||
)
|
||||
votes.inject({}) do |memo, v|
|
||||
memo[v.story_id] = { :vote => v.vote, :reason => v.reason }
|
||||
memo
|
||||
votes.each_with_object({}) do |v, memo|
|
||||
memo[v.story_id] = {vote: v.vote, reason: v.reason}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -83,13 +82,12 @@ class Vote < ApplicationRecord
|
|||
if comment_ids.empty?
|
||||
{}
|
||||
else
|
||||
votes = self.where(
|
||||
:user_id => user_id,
|
||||
:comment_id => comment_ids,
|
||||
votes = where(
|
||||
user_id: user_id,
|
||||
comment_id: comment_ids
|
||||
)
|
||||
votes.inject({}) do |memo, v|
|
||||
memo[v.comment_id] = { :vote => v.vote, :reason => v.reason }
|
||||
memo
|
||||
votes.each_with_object({}) do |v, memo|
|
||||
memo[v.comment_id] = {vote: v.vote, reason: v.reason}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -97,21 +95,21 @@ class Vote < ApplicationRecord
|
|||
def self.vote_thusly_on_story_or_comment_for_user_because(
|
||||
new_vote, story_id, comment_id, user_id, reason, update_counters = true
|
||||
)
|
||||
v = Vote.where(:user_id => user_id, :story_id => story_id,
|
||||
:comment_id => comment_id).first_or_initialize
|
||||
v = Vote.where(user_id: user_id, story_id: story_id,
|
||||
comment_id: comment_id).first_or_initialize
|
||||
|
||||
return if !v.new_record? && v.vote == new_vote # done if there's no change
|
||||
|
||||
score_delta = new_vote - v.vote.to_i
|
||||
if v.vote == -1
|
||||
flag_delta = if v.vote == -1
|
||||
# we know there's a change, so we must be removing a flag
|
||||
flag_delta = -1
|
||||
-1
|
||||
elsif new_vote == -1
|
||||
# we know there's a change, so we must be adding a flag
|
||||
flag_delta = 1
|
||||
1
|
||||
else
|
||||
# change from 1 to 0 or 0 to 1, so number of flags doesn't change
|
||||
flag_delta = 0
|
||||
0
|
||||
end
|
||||
|
||||
if new_vote == 0
|
||||
|
@ -132,10 +130,10 @@ class Vote < ApplicationRecord
|
|||
end
|
||||
|
||||
def target
|
||||
if self.comment_id
|
||||
Comment.find(self.comment_id)
|
||||
if comment_id
|
||||
Comment.find(comment_id)
|
||||
else
|
||||
Story.find(self.story_id)
|
||||
Story.find(story_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
# see https://keybase.io/docs/proof_integration_guide#1-config
|
||||
|
||||
JSON.pretty_generate({
|
||||
"version": 1,
|
||||
"domain": @domain,
|
||||
"display_name": @name,
|
||||
"description": @description,
|
||||
"brand_color": @brand_color,
|
||||
"logo": {
|
||||
"svg_black": @logo_black,
|
||||
"svg_full": @logo_full
|
||||
version: 1,
|
||||
domain: @domain,
|
||||
display_name: @name,
|
||||
description: @description,
|
||||
brand_color: @brand_color,
|
||||
logo: {
|
||||
svg_black: @logo_black,
|
||||
svg_full: @logo_full
|
||||
},
|
||||
"username": {
|
||||
"re": @user_re,
|
||||
"min": 1,
|
||||
"max": 25
|
||||
username: {
|
||||
re: @user_re,
|
||||
min: 1,
|
||||
max: 25
|
||||
},
|
||||
"prefill_url": @prefill_url,
|
||||
"profile_url": @profile_url,
|
||||
"check_url": @check_url,
|
||||
"check_path": ["keybase_signatures"],
|
||||
"avatar_path": ["avatar_url"],
|
||||
"contact": @contacts
|
||||
prefill_url: @prefill_url,
|
||||
profile_url: @profile_url,
|
||||
check_url: @check_url,
|
||||
check_path: ["keybase_signatures"],
|
||||
avatar_path: ["avatar_url"],
|
||||
contact: @contacts
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
10
bin/oauth
10
bin/oauth
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
@ -26,4 +24,4 @@ end
|
|||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("oauth", "oauth")
|
||||
load Gem.bin_path("oauth-tty", "oauth")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
8
bin/puma
8
bin/puma
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby3.0
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'racc' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("racc", "racc")
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
29
bin/rails
29
bin/rails
|
@ -1,4 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
APP_PATH = File.expand_path('../config/application', __dir__)
|
||||
require_relative '../config/boot'
|
||||
require 'rails/commands'
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rails' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("railties", "rails")
|
||||
|
|
29
bin/rake
29
bin/rake
|
@ -1,4 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
require_relative '../config/boot'
|
||||
require 'rake'
|
||||
Rake.application.run
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'rake' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("rake", "rake")
|
||||
|
|
8
bin/rotp
8
bin/rotp
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'ruby-memory-profiler' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("memory_profiler", "ruby-memory-profiler")
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env ruby2.7
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
|
@ -8,11 +8,9 @@
|
|||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require "pathname"
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
||||
Pathname.new(__FILE__).realpath)
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'stackprof' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("stackprof", "stackprof")
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'stackprof-flamegraph.pl' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("stackprof", "stackprof-flamegraph.pl")
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# This file was generated by Bundler.
|
||||
#
|
||||
# The application 'stackprof-gprof2dot.py' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
||||
|
||||
bundle_binstub = File.expand_path("bundle", __dir__)
|
||||
|
||||
if File.file?(bundle_binstub)
|
||||
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
||||
load(bundle_binstub)
|
||||
else
|
||||
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
||||
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
||||
end
|
||||
end
|
||||
|
||||
require "rubygems"
|
||||
require "bundler/setup"
|
||||
|
||||
load Gem.bin_path("stackprof", "stackprof-gprof2dot.py")
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue