diff --git a/.env b/.env.sample
similarity index 93%
rename from .env
rename to .env.sample
index fd56f06f4..c804ae841 100644
--- a/.env
+++ b/.env.sample
@@ -68,3 +68,10 @@
# export DANBOORU_VERSION=
# export DANBOORU_HOSTNAME=
# export DANBOORU_IQDBS_SERVER=
+
+#
+# Development Only
+#
+
+# Start the integrated solargraph service from the compose file. Requires a rebuild when changed.
+# export COMPOSE_PROFILES=solargraph
diff --git a/.gitignore b/.gitignore
index 57b2a6256..70f6a6529 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-.env.*
+.env
.bundle
.yardoc
config/database.yml
diff --git a/.rubocop.yml b/.rubocop.yml
index a1d56794b..bd46bbec6 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -4,8 +4,8 @@ require:
AllCops:
NewCops: enable
Exclude:
- - "bin/*"
- - "node_modules/**/*"
+ - bin/*
+ - node_modules/**/*
Bundler/OrderedGems:
Enabled: false
@@ -13,23 +13,30 @@ Bundler/OrderedGems:
Layout/EmptyLineAfterGuardClause:
Enabled: false
-Layout/EmptyLineBetweenDefs:
- Enabled: false
+Layout/FirstArrayElementIndentation:
+ EnforcedStyle: consistent
+
+Layout/FirstHashElementIndentation:
+ EnforcedStyle: consistent
Layout/LineLength:
Enabled: false
-Lint/InheritException:
- Enabled: false
-
-Lint/RescueException:
- Enabled: false
+Lint/SymbolConversion:
+ EnforcedStyle: consistent
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
- Enabled: false
+ AllowedMethods:
+ - class_methods
+ - concerning
+ - context
+ - create_table
+ - should
+ Exclude:
+ - config/routes.rb
Metrics/ClassLength:
Enabled: false
@@ -49,15 +56,28 @@ Metrics/PerceivedComplexity:
Naming/PredicateName:
Enabled: false
+Rails/BulkChangeTable:
+ Enabled: false
+
Rails/HasManyOrHasOneDependent:
Enabled: false
Rails/HttpStatus:
EnforcedStyle: numeric
+Rails/I18nLocaleTexts:
+ Enabled: false
+
Rails/InverseOf:
Enabled: false
+Rails/Output:
+ Exclude:
+ - db/*.rb
+
+Rails/ReversibleMigration:
+ Enabled: false
+
Rails/SkipsModelValidations:
Enabled: false
@@ -85,14 +105,17 @@ Style/FloatDivision:
Style/FrozenStringLiteralComment:
Enabled: false
+Style/GuardClause:
+ Enabled: false
+
+Style/HashSyntax:
+ EnforcedShorthandSyntax: never
+
Style/IfUnlessModifier:
Enabled: false
-Style/MutableConstant:
- Enabled: false
-
Style/NumericPredicate:
- Enabled: false
+ EnforcedStyle: comparison
Style/PerlBackrefs:
Enabled: false
@@ -100,17 +123,14 @@ Style/PerlBackrefs:
Style/QuotedSymbols:
Enabled: false
-Style/RescueStandardError:
- Enabled: false
-
Style/StringLiterals:
- Enabled: false
+ EnforcedStyle: double_quotes
-Style/StringLiteralsInInterpolation:
- Enabled: false
+Style/TrailingCommaInArguments:
+ EnforcedStyleForMultiline: comma
-Style/SymbolArray:
- Enabled: false
+Style/TrailingCommaInArrayLiteral:
+ EnforcedStyleForMultiline: consistent_comma
-Style/WordArray:
- Enabled: false
+Style/TrailingCommaInHashLiteral:
+ EnforcedStyleForMultiline: consistent_comma
diff --git a/.solargraph.yml b/.solargraph.yml
index 145bc915d..64b155321 100644
--- a/.solargraph.yml
+++ b/.solargraph.yml
@@ -1,31 +1,12 @@
----
-include:
-- "**/*.rb"
-exclude:
-- spec/**/*
-- test/**/*
-- vendor/**/*
-- ".bundle/**/*"
require:
-- actioncable
-- actionmailer
-- actionpack
-- actionview
-- activejob
-- activemodel
-- activerecord
-- activestorage
-- activesupport
-domains: []
+ - actioncable
+ - actionmailer
+ - actionpack
+ - actionview
+ - activejob
+ - activemodel
+ - activerecord
+ - activestorage
+ - activesupport
reporters:
-- rubocop
-- require_not_found
-formatter:
- rubocop:
- cops: safe
- except: []
- only: []
- extra_args: []
-require_paths: []
-plugins: []
-max_files: 5000
+ - rubocop
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000..8117eaea2
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,10 @@
+{
+ "solargraph.checkGemVersion": false,
+ "solargraph.diagnostics": true,
+ "solargraph.externalServer": {
+ "host": "localhost",
+ "port": 7658
+ },
+ "solargraph.formatting": true,
+ "solargraph.transport": "external",
+}
diff --git a/Dockerfile b/Dockerfile
index 3a757a24c..b84ed677b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -26,5 +26,15 @@ RUN gem install bundler:2.3.12 && \
RUN wget -O /usr/bin/shoreman https://github.com/chrismytton/shoreman/raw/master/shoreman.sh \
&& chmod +x /usr/bin/shoreman
+
+# Only setup solargraph stuff when the profile is selected
+ARG COMPOSE_PROFILES
+RUN if [[ $COMPOSE_PROFILES == *"solargraph"* ]]; then \
+ solargraph download-core && bundle exec yard gems && solargraph bundle; \
+fi
+
+# Stop bin/rails console from offering autocomplete
+RUN echo "IRB.conf[:USE_AUTOCOMPLETE] = false" > ~/.irbrc
+
WORKDIR /app
CMD [ "shoreman" ]
diff --git a/Gemfile b/Gemfile
index 1273cbef3..674e85fe3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -53,6 +53,12 @@ group :development, :test do
gem 'puma'
end
+group :docker do
+ gem "rubocop", require: false
+ gem "rubocop-rails", require: false
+ gem "solargraph", require: false
+end
+
group :test do
gem "shoulda-context"
gem "shoulda-matchers"
diff --git a/Gemfile.lock b/Gemfile.lock
index 02348c4f9..ba15723c4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -85,7 +85,10 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
+ ast (2.4.2)
+ backport (1.2.0)
bcrypt (3.1.18)
+ benchmark (0.2.0)
bootsnap (1.13.0)
msgpack (~> 1.2)
brpoplpush-redis_script (0.1.2)
@@ -118,6 +121,7 @@ GEM
activesupport (>= 5.0)
request_store (>= 1.0)
ruby2_keywords
+ e2mmap (0.1.0)
elasticsearch (7.17.1)
elasticsearch-api (= 7.17.1)
elasticsearch-transport (= 7.17.1)
@@ -174,9 +178,14 @@ GEM
multi_xml (>= 0.5.2)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
+ jaro_winkler (1.5.4)
json (2.6.2)
jsonapi-renderer (0.2.2)
kgio (2.11.4)
+ kramdown (2.4.0)
+ rexml
+ kramdown-parser-gfm (1.1.0)
+ kramdown (~> 2.0)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
@@ -217,6 +226,9 @@ GEM
nokogiri (1.13.8)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
+ parallel (1.22.1)
+ parser (3.1.2.1)
+ ast (~> 2.4.1)
pg (1.4.3)
pry (0.14.1)
coderay (~> 1.1)
@@ -259,6 +271,7 @@ GEM
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
+ rainbow (3.1.1)
raindrops (0.20.0)
rake (13.0.6)
rb-fsevent (0.11.2)
@@ -267,6 +280,7 @@ GEM
recaptcha (5.12.3)
json
redis (4.8.0)
+ regexp_parser (2.6.0)
request_store (1.5.1)
rack (>= 1.4)
resolv (0.2.1)
@@ -285,7 +299,26 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
retriable (3.1.2)
+ reverse_markdown (2.1.1)
+ nokogiri
rexml (3.2.5)
+ rubocop (1.37.0)
+ json (~> 2.3)
+ parallel (~> 1.10)
+ parser (>= 3.1.2.1)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.22.0, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 1.4.0, < 3.0)
+ rubocop-ast (1.23.0)
+ parser (>= 3.1.1.0)
+ rubocop-rails (2.17.0)
+ activesupport (>= 4.2.0)
+ rack (>= 1.1)
+ rubocop (>= 1.33.0, < 2.0)
+ ruby-progressbar (1.11.0)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
@@ -308,9 +341,25 @@ GEM
simple_form (5.1.0)
actionpack (>= 5.2)
activemodel (>= 5.2)
+ solargraph (0.47.2)
+ backport (~> 1.2)
+ benchmark
+ bundler (>= 1.17.2)
+ diff-lcs (~> 1.4)
+ e2mmap
+ jaro_winkler (~> 1.5)
+ kramdown (~> 2.3)
+ kramdown-parser-gfm (~> 1.1)
+ parser (~> 3.0)
+ reverse_markdown (>= 1.0.5, < 3)
+ rubocop (>= 0.52)
+ thor (~> 1.0)
+ tilt (~> 2.0)
+ yard (~> 0.9, >= 0.9.24)
streamio-ffmpeg (3.0.2)
multi_json (~> 1.8)
thor (1.2.1)
+ tilt (2.0.11)
timecop (0.9.5)
timeout (0.3.0)
tzinfo (2.0.5)
@@ -318,6 +367,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
+ unicode-display_width (2.3.0)
unicorn (6.1.0)
kgio (~> 2.6)
raindrops (~> 0.7)
@@ -333,11 +383,14 @@ GEM
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
+ webrick (1.7.0)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
+ yard (0.9.28)
+ webrick (~> 1.7.0)
zeitwerk (2.6.1)
PLATFORMS
@@ -377,6 +430,8 @@ DEPENDENCIES
resolv
responders
retriable
+ rubocop
+ rubocop-rails
ruby-vips
sanitize
shoulda-context
@@ -384,6 +439,7 @@ DEPENDENCIES
sidekiq
sidekiq-unique-jobs
simple_form
+ solargraph
streamio-ffmpeg
timecop
unicorn
diff --git a/README.md b/README.md
index 92e7fff29..faa386d66 100644
--- a/README.md
+++ b/README.md
@@ -15,22 +15,30 @@ To mitigate this you can install a WSL distribution and clone the project inside
#### Installation
1. Download and install the prerequisites.
-2. Clone the repo with `git clone https://github.com/zwagoth/e621ng.git`.
-3. `cd` into the repo.
-4. Run the following commands:
+1. Clone the repo with `git clone https://github.com/zwagoth/e621ng.git`.
+1. `cd` into the repo.
+1. Copy the sample environment file with `cp .env.sample .env`.
+1. Uncomment the `COMPOSE_PROFILES` variable if you wish to use solargraph. Doesn't work on Windows without WSL.
+1. Run the following commands:
```
docker-compose run -e DANBOORU_DISABLE_THROTTLES=true -e SEED_POST_COUNT=100 e621 /app/bin/setup
docker-compose up
```
After running the commands once only `docker-compose up` is needed to bring up the containers.
-5. This would be a good time to rewatch your favorite TV series installment, cook & have breakfast/lunch/dinner, walk the dog, clean your room, etc.
+1. This would be a good time to rewatch your favorite TV series installment, cook & have breakfast/lunch/dinner, walk the dog, clean your room, etc.
By the time you get back the install will surely have completed.1
-6. To confirm the installation worked, open the web browser of your choice and enter `http://localhost:3000` into the address bar and see if the website loads correctly. An admin account has been created automatically, the username and password are `admin` and `e621test` respectively.
+1. To confirm the installation worked, open the web browser of your choice and enter `http://localhost:3000` into the address bar and see if the website loads correctly. An admin account has been created automatically, the username and password are `admin` and `e621test` respectively.
Note: When gems or js packages were updated you need to execute `docker-compose build` to reflect them in the container.
1 If the install did not finish by the time an activity is complete please select another activity to avoid crippling boredom.
+#### Useful docker services
+
+`docker-compose run --rm tests` to execute the test suite.
+
+`docker-compose run --rm rubocop` to run the linter. Run it against changed files only, there are too many existing violations at the moment.
+
#### Development Database
The postgres server accepts outside connections which you can use to connect with a local client. Use `localhost:34517` to connect to a database named `danbooru2` with the user `danbooru`. Leave the password blank, anything will work.
@@ -66,9 +74,3 @@ debug your Nginx configuration file.
### IQDB Service
IQDB integration is delegated to the [IQDBS service](https://github.com/zwagoth/iqdbs).
-
-### Cropped Thumbnails
-
-There's optional support for cropped thumbnails. This relies on installing
-`libvips-8.6` or higher and setting `Danbooru.config.enable_image_cropping?`
-to true.
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index a746fc3c8..d6b120413 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -28,6 +28,8 @@ ORDER BY u1.id DESC, u2.last_logged_in_at DESC;")
def update
@user = User.find(params[:id])
+ @user.validate_email_format = true
+ @user.skip_email_blank_check = true
@user.update!(user_params)
if @user.saved_change_to_profile_about || @user.saved_change_to_profile_artinfo
ModAction.log(:user_text_change, { user_id: @user.id })
@@ -37,8 +39,6 @@ ORDER BY u1.id DESC, u2.last_logged_in_at DESC;")
end
@user.mark_verified! if params[:user][:verified] == 'true'
@user.mark_unverified! if params[:user][:verified] == 'false'
- params[:user][:is_upgrade] = true
- params[:user][:skip_dmail] = true
@user.promote_to!(params[:user][:level], params[:user])
old_username = @user.name
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 3827b04fe..680d1cb8f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -32,12 +32,6 @@ class ApplicationController < ActionController::Base
protected
- def self.rescue_with(*klasses, status: 500)
- rescue_from *klasses do |exception|
- render_error_page(status, exception)
- end
- end
-
def api_check
if !CurrentUser.is_anonymous? && !request.get? && !request.head?
throttled = CurrentUser.user.token_bucket.throttled?
@@ -55,6 +49,9 @@ class ApplicationController < ActionController::Base
def rescue_exception(exception)
@exception = exception
+ # If InvalidAuthenticityToken was raised, CurrentUser isn't set so we have to do it here manually.
+ CurrentUser.user ||= User.anonymous
+
if Rails.env.test? && ENV["DEBUG"]
puts "---"
STDERR.puts("#{exception.class} exception thrown: #{exception.message}")
@@ -120,9 +117,8 @@ class ApplicationController < ActionController::Base
def render_expected_error(status, message, format: request.format.symbol)
format = :html unless format.in?(%i[html json atom])
- layout = CurrentUser.user.present? ? "default" : "blank"
@message = message
- render "static/error", layout: layout, status: status, formats: format
+ render "static/error", status: status, formats: format
end
def render_error_page(status, exception, message: exception.message, format: request.format.symbol)
@@ -132,17 +128,14 @@ class ApplicationController < ActionController::Base
@backtrace = Rails.backtrace_cleaner.clean(@exception.backtrace)
format = :html unless format.in?(%i[html json atom])
- # if InvalidAuthenticityToken was raised, CurrentUser isn't set so we have to use the blank layout.
- layout = CurrentUser.user.present? ? "default" : "blank"
-
- if !CurrentUser.user&.try(:is_janitor?) && message == exception.message
+ if !CurrentUser.user.is_janitor? && message == exception.message
@message = "An unexpected error occurred."
end
DanbooruLogger.log(@exception, expected: @expected)
log = ExceptionLog.add(exception, CurrentUser.id, request) if !@expected
@log_code = log&.code
- render "static/error", layout: layout, status: status, formats: format
+ render "static/error", status: status, formats: format
end
def access_denied(exception = nil)
diff --git a/app/controllers/comment_votes_controller.rb b/app/controllers/comment_votes_controller.rb
index 3a0c595dc..42f86b7e2 100644
--- a/app/controllers/comment_votes_controller.rb
+++ b/app/controllers/comment_votes_controller.rb
@@ -8,19 +8,19 @@ class CommentVotesController < ApplicationController
def create
@comment = Comment.find(params[:comment_id])
@comment_vote = VoteManager.comment_vote!(comment: @comment, user: CurrentUser.user, score: params[:score])
- if @comment_vote == :need_unvote
+ if @comment_vote == :need_unvote && params[:no_unvote] != "true"
VoteManager.comment_unvote!(comment: @comment, user: CurrentUser.user)
end
@comment.reload
render json: {score: @comment.score, our_score: @comment_vote != :need_unvote ? @comment_vote.score : 0}
- rescue CommentVote::Error, ActiveRecord::RecordInvalid => x
+ rescue UserVote::Error, ActiveRecord::RecordInvalid => x
render_expected_error(422, x)
end
def destroy
@comment = Comment.find(params[:comment_id])
VoteManager.comment_unvote!(comment: @comment, user: CurrentUser.user)
- rescue CommentVote::Error => x
+ rescue UserVote::Error => x
render_expected_error(422, x)
end
diff --git a/app/controllers/mascots_controller.rb b/app/controllers/mascots_controller.rb
new file mode 100644
index 000000000..cf6691261
--- /dev/null
+++ b/app/controllers/mascots_controller.rb
@@ -0,0 +1,43 @@
+class MascotsController < ApplicationController
+ respond_to :html, :json
+ before_action :admin_only, except: [:index]
+
+ def index
+ @mascots = Mascot.search(search_params).paginate(params[:page], limit: 75)
+ respond_with(@mascots)
+ end
+
+ def new
+ @mascot = Mascot.new
+ end
+
+ def create
+ @mascot = Mascot.create(mascot_params.merge(creator: CurrentUser.user))
+ ModAction.log(:mascot_create, { id: @mascot.id }) if @mascot.valid?
+ respond_with(@mascot, location: mascots_path)
+ end
+
+ def edit
+ @mascot = Mascot.find(params[:id])
+ end
+
+ def update
+ @mascot = Mascot.find(params[:id])
+ @mascot.update(mascot_params)
+ ModAction.log(:mascot_update, { id: @mascot.id }) if @mascot.valid?
+ respond_with(@mascot, location: mascots_path)
+ end
+
+ def destroy
+ @mascot = Mascot.find(params[:id])
+ @mascot.destroy
+ ModAction.log(:mascot_delete, { id: @mascot.id })
+ respond_with(@mascot)
+ end
+
+ private
+
+ def mascot_params
+ params.fetch(:mascot, {}).permit(%i[mascot_file display_name background_color artist_url artist_name safe_mode_only active])
+ end
+end
diff --git a/app/controllers/post_versions_controller.rb b/app/controllers/post_versions_controller.rb
index e80381a35..68fe5b37e 100644
--- a/app/controllers/post_versions_controller.rb
+++ b/app/controllers/post_versions_controller.rb
@@ -4,7 +4,7 @@ class PostVersionsController < ApplicationController
respond_to :js, only: [:undo]
def index
- @post_versions = PostVersion.__elasticsearch__.search(PostVersion.build_query(search_params)).paginate(params[:page], :limit => params[:limit], :search_count => params[:search], includes: [:updater, post: [:versions]])
+ @post_versions = PostVersion.__elasticsearch__.search(PostVersion.build_query(search_params)).paginate(params[:page], limit: params[:limit], max_count: 10_000, search_count: params[:search], includes: [:updater, post: [:versions]])
respond_with(@post_versions)
end
diff --git a/app/controllers/post_votes_controller.rb b/app/controllers/post_votes_controller.rb
index 1804fc8cf..8e66ed511 100644
--- a/app/controllers/post_votes_controller.rb
+++ b/app/controllers/post_votes_controller.rb
@@ -10,14 +10,14 @@ class PostVotesController < ApplicationController
VoteManager.unvote!(post: @post, user: CurrentUser.user)
end
render json: {score: @post.score, up: @post.up_score, down: @post.down_score, our_score: @post_vote != :need_unvote ? @post_vote.score : 0}
- rescue PostVote::Error, ActiveRecord::RecordInvalid => x
+ rescue UserVote::Error, ActiveRecord::RecordInvalid => x
render_expected_error(422, x)
end
def destroy
@post = Post.find(params[:post_id])
VoteManager.unvote!(post: @post, user: CurrentUser.user)
- rescue PostVote::Error => x
+ rescue UserVote::Error => x
render_expected_error(422, x)
end
diff --git a/app/controllers/takedowns_controller.rb b/app/controllers/takedowns_controller.rb
index a6a4a6267..f25a33a8b 100644
--- a/app/controllers/takedowns_controller.rb
+++ b/app/controllers/takedowns_controller.rb
@@ -10,6 +10,7 @@ class TakedownsController < ApplicationController
def destroy
@takedown = Takedown.find(params[:id])
@takedown.destroy
+ ModAction.log(:takedown_delete, { takedown_id: @takedown.id })
respond_with(@takedown)
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 1f325326d..891444dff 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -19,12 +19,7 @@ class UsersController < ApplicationController
def index
if params[:name].present?
- @user = User.find_by_name(params[:name])
- if @user.nil?
- raise "No user found with name: #{params[:name]}"
- else
- redirect_to user_path(@user)
- end
+ redirect_to user_path(id: params[:name])
else
@users = User.search(search_params).includes(:user_status).paginate(params[:page], limit: params[:limit], search_count: params[:search])
respond_with(@users) do |format|
@@ -63,6 +58,7 @@ class UsersController < ApplicationController
raise User::PrivilegeError.new("Signups are disabled") unless Danbooru.config.enable_signups?
User.transaction do
@user = User.new(user_params(:create).merge({last_ip_addr: request.remote_ip}))
+ @user.validate_email_format = true
@user.email_verification_key = '1' if Danbooru.config.enable_email_verification?
if !Danbooru.config.enable_recaptcha? || verify_recaptcha(model: @user)
@user.save
@@ -92,6 +88,7 @@ class UsersController < ApplicationController
def update
@user = User.find(CurrentUser.id)
+ @user.validate_email_format = true
check_privilege(@user)
@user.update(user_params(:update))
if @user.errors.any?
diff --git a/app/decorators/mod_action_decorator.rb b/app/decorators/mod_action_decorator.rb
index 4b3a9bea6..d4a128920 100644
--- a/app/decorators/mod_action_decorator.rb
+++ b/app/decorators/mod_action_decorator.rb
@@ -23,6 +23,8 @@ class ModActionDecorator < ApplicationDecorator
### Takedowns ###
when "takedown_process"
"Completed takedown ##{vals['takedown_id']}"
+ when "takedown_delete"
+ "Deleted takedown ##{vals['takedown_id']}"
### IP Ban ###
when "ip_ban_create"
@@ -292,6 +294,14 @@ class ModActionDecorator < ApplicationDecorator
when "wiki_page_rename"
"Renamed wiki page ([[#{vals['old_title']}]] → [[#{vals['new_title']}]])"
+ ### Mascots ###
+ when "mascot_create"
+ "Created mascot ##{vals['id']}"
+ when "mascot_update"
+ "Updated mascot ##{vals['id']}"
+ when "mascot_delete"
+ "Deleted mascot ##{vals['id']}"
+
when "bulk_revert"
"Processed bulk revert for #{vals['constraints']} by #{user}"
diff --git a/app/decorators/paginated_decorator.rb b/app/decorators/paginated_decorator.rb
index c3f562531..6ce7d8e73 100644
--- a/app/decorators/paginated_decorator.rb
+++ b/app/decorators/paginated_decorator.rb
@@ -1,3 +1,3 @@
class PaginatedDecorator < Draper::CollectionDecorator
- delegate :current_page, :total_pages, :is_first_page?, :is_last_page?, :sequential_paginator_mode, :records, :total_count
+ delegate :current_page, :total_pages, :is_first_page?, :is_last_page?, :sequential_paginator_mode, :max_numbered_pages, :records, :total_count
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 4fab55081..7f53b3599 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -116,14 +116,6 @@ module ApplicationHelper
content_tag(:time, content || datetime, :datetime => datetime, :title => time.to_formatted_s)
end
- def humanized_duration(from, to)
- duration = distance_of_time_in_words(from, to)
- datetime = from.iso8601 + "/" + to.iso8601
- title = "#{from.strftime("%Y-%m-%d %H:%M")} to #{to.strftime("%Y-%m-%d %H:%M")}"
-
- raw content_tag(:time, duration, datetime: datetime, title: title)
- end
-
def time_ago_in_words_tagged(time, compact: false)
if time.past?
text = time_ago_in_words(time) + " ago"
diff --git a/app/helpers/pagination_helper.rb b/app/helpers/pagination_helper.rb
index 0b587903d..4c7053ee0 100644
--- a/app/helpers/pagination_helper.rb
+++ b/app/helpers/pagination_helper.rb
@@ -3,7 +3,7 @@ module PaginationHelper
html = '