diff --git a/Gemfile b/Gemfile index 9c48ce5a..f966887f 100644 --- a/Gemfile +++ b/Gemfile @@ -33,6 +33,8 @@ gem 'font-awesome-rails', '~> 4.3.0.0' gem 'rails-assets-growl' gem "paperclip", "~> 4.2" gem 'delayed_paperclip' +gem 'momentjs-rails', '>= 2.9.0' +gem 'bootstrap3-datetimepicker-rails', '~> 4.7.14' gem 'ruby-progressbar' @@ -78,4 +80,3 @@ group :development, :test do gem 'simplecov-rcov', require: false gem 'database_cleaner' end - diff --git a/Gemfile.lock b/Gemfile.lock index e5e5c83a..b94af2ab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -48,6 +48,8 @@ GEM bcrypt (3.1.10) bootstrap-sass (3.2.0.2) sass (~> 3.2) + bootstrap3-datetimepicker-rails (4.7.14) + momentjs-rails (>= 2.8.1) bootstrap_form (2.3.0) bootswatch-rails (3.2.4) railties (>= 3.1) @@ -149,6 +151,8 @@ GEM mime-types (2.4.3) mini_portile (0.6.2) minitest (5.6.0) + momentjs-rails (2.9.0) + railties (>= 3.1) multi_json (1.11.0) multipart-post (2.0.0) mysql2 (0.3.18) @@ -362,6 +366,7 @@ PLATFORMS DEPENDENCIES bcrypt (~> 3.1.7) bootstrap-sass (~> 3.2.0.1) + bootstrap3-datetimepicker-rails (~> 4.7.14) bootstrap_form bootswatch-rails capybara @@ -380,6 +385,7 @@ DEPENDENCIES jbuilder (~> 2.2.4) jquery-rails jquery-turbolinks + momentjs-rails (>= 2.9.0) mysql2 nprogress-rails omniauth diff --git a/Rakefile b/Rakefile index dbc8e076..bfa9ec53 100644 --- a/Rakefile +++ b/Rakefile @@ -94,11 +94,67 @@ namespace :justask do end desc "Hits an user with the banhammer." - task :ban, [:screen_name] => :environment do |t, args| + task :permanently_ban, [:screen_name, :reason] => :environment do |t, args| fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) fail "user #{args[:screen_name]} not found" if user.nil? - user.banned = true + user.permanently_banned = true + user.ban_reason = args[:reason] + user.save! + puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" + end + + desc "Hits an user with the banhammer for one day." + task :ban, [:screen_name, :reason] => :environment do |t, args| + fail "screen name required" if args[:screen_name].nil? + user = User.find_by_screen_name(args[:screen_name]) + user.permanently_banned = false + user.banned_until = DateTime.current + 1 + user.ban_reason = args[:reason] + user.save! + puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" + end + + desc "Hits an user with the banhammer for one week." + task :week_ban, [:screen_name, :reason] => :environment do |t, args| + fail "screen name required" if args[:screen_name].nil? + user = User.find_by_screen_name(args[:screen_name]) + user.permanently_banned = false + user.banned_until = DateTime.current + 7 + user.ban_reason = args[:reason] + user.save! + puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" + end + + desc "Hits an user with the banhammer for one month." + task :month_ban, [:screen_name, :reason] => :environment do |t, args| + fail "screen name required" if args[:screen_name].nil? + user = User.find_by_screen_name(args[:screen_name]) + user.permanently_banned = false + user.banned_until = DateTime.current + 30 + user.ban_reason = args[:reason] + user.save! + puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" + end + + desc "Hits an user with the banhammer for one year." + task :year_ban, [:screen_name, :reason] => :environment do |t, args| + fail "screen name required" if args[:screen_name].nil? + user = User.find_by_screen_name(args[:screen_name]) + user.permanently_banned = false + user.banned_until = DateTime.current + 365 + user.ban_reason = args[:reason] + user.save! + puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" + end + + desc "Hits an user with the banhammer for one aeon." + task :aeon_ban, [:screen_name, :reason] => :environment do |t, args| + fail "screen name required" if args[:screen_name].nil? + user = User.find_by_screen_name(args[:screen_name]) + user.permanently_banned = false + user.banned_until = DateTime.current + 365_000_000_000 + user.ban_reason = args[:reason] user.save! puts "#{user.screen_name} got hit by\033[5m YE OLDE BANHAMMER\033[0m!!1!" end @@ -108,7 +164,9 @@ namespace :justask do fail "screen name required" if args[:screen_name].nil? user = User.find_by_screen_name(args[:screen_name]) fail "user #{args[:screen_name]} not found" if user.nil? - user.banned = false + user.permanently_banned = false + user.banned_until = nil + user.ban_reason = nil user.save! puts "#{user.screen_name} is no longer banned." end diff --git a/app/assets/javascripts/application.js.erb.coffee b/app/assets/javascripts/application.js.erb.coffee index 9d0839b2..5fe872f9 100644 --- a/app/assets/javascripts/application.js.erb.coffee +++ b/app/assets/javascripts/application.js.erb.coffee @@ -41,7 +41,8 @@ $(document).on "click", "button#create-account", -> Turbolinks.visit "/sign_up" _ready = -> - sweetAlertInitialize() + if typeof sweetAlertInitialize != "undefined" + sweetAlertInitialize() $(document).ready _ready $(document).on 'page:load', _ready diff --git a/app/assets/javascripts/moderation.js.erb.coffee b/app/assets/javascripts/moderation.js.erb.coffee index 5a8a1551..60eefeaf 100644 --- a/app/assets/javascripts/moderation.js.erb.coffee +++ b/app/assets/javascripts/moderation.js.erb.coffee @@ -1 +1,3 @@ -#= require_tree ./moderation \ No newline at end of file +#= require moment +#= require bootstrap-datetimepicker +#= require_tree ./moderation diff --git a/app/assets/javascripts/moderation/ban.coffee b/app/assets/javascripts/moderation/ban.coffee new file mode 100644 index 00000000..87ec6555 --- /dev/null +++ b/app/assets/javascripts/moderation/ban.coffee @@ -0,0 +1,58 @@ +$(document).on "DOMContentLoaded", -> + parent = $ "#ban-control-super" + parent.find('#_ban').on "change", (event) -> + $t = $ this + if $t.is(":checked") + $("#ban-controls").show() + else + $("#ban-controls").hide() + parent.find('#_permaban').on "change", (event) -> + $t = $ this + if $t.is(":checked") + $("#ban-controls-time").hide() + else + $("#ban-controls-time").show() + + parent.find("#until").datetimepicker + defaultDate: parent.find("#until").val() + sideBySide: true + icons: + time: "fa fa-clock-o" + date: "fa fa-calendar" + up: "fa fa-chevron-up" + down: "fa fa-chevron-down" + previous: "fa fa-chevron-left" + next: "fa fa-chevron-right" + today: "fa fa-home" + clear: "fa fa-trash-o" + close: "fa fa-times" + + parent.parent()[0].addEventListener "submit", (event) -> + event.preventDefault(); + + $("#modal-ban").modal "hide" + + checktostr = (selector) -> + if $(selector)[0].checked + "1" + else + "0" + + data = { + ban: checktostr "#_ban" + permaban: checktostr "#_permaban" + until: $("#until")[0].value.trim() + reason: $("#reason")[0].value.trim() + user: $("#_user")[0].value + } + + $.ajax + url: '/ajax/mod/ban' + type: 'POST' + data: data + success: (data, status, jqxhr) -> + showNotification data.message, data.success + error: (jqxhr, status, error) -> + console.log jqxhr, status, error + showNotification "An error occurred, a developer should check the console for details", false + complete: (jqxhr, status) -> diff --git a/app/assets/javascripts/moderation/privileges.coffee b/app/assets/javascripts/moderation/privileges.coffee index 6f676851..c64c4c38 100644 --- a/app/assets/javascripts/moderation/privileges.coffee +++ b/app/assets/javascripts/moderation/privileges.coffee @@ -21,4 +21,4 @@ console.log jqxhr, status, error showNotification "An error occurred, a developer should check the console for details", false complete: (jqxhr, status) -> - box.removeAttr "disabled" \ No newline at end of file + box.removeAttr "disabled" diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 1f1549fd..dff05d54 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -45,7 +45,14 @@ $navbar-inverse-toggle-border-color: #512da8; @import "bootswatch/cosmo/variables"; @import "bootstrap"; + body { padding-top: $navbar-height; } + +@import 'bootstrap-datetimepicker'; +.remove-native-picker::-webkit-calendar-picker-indicator{ + display: none +} + @import "bootswatch/cosmo/bootswatch"; @import "base"; diff --git a/app/controllers/ajax/moderation_controller.rb b/app/controllers/ajax/moderation_controller.rb index 12580420..82b438af 100644 --- a/app/controllers/ajax/moderation_controller.rb +++ b/app/controllers/ajax/moderation_controller.rb @@ -105,6 +105,46 @@ class Ajax::ModerationController < ApplicationController @success = true end + def ban + @status = :err + @message = "Weird..." + @success = false + + params.require :user + params.require :ban + params.require :permaban + + reason = params[:reason] + target = User.find_by_screen_name(params[:user]) + unban = params[:ban] == "0" + perma = params[:permaban] == "1" + + buntil = DateTime.strptime params[:until], "%m/%d/%Y %I:%M %p" unless unban or perma + + if not unban and target.admin? + @status = :nopriv + @message = "You cannot ban an administrator!" + @success = false + return + end + + if unban + target.unban + @message = "Unbanned user." + @success = true + elsif perma + target.ban nil, reason + @message = "Permanently banned user." + else + target.ban buntil, reason + @message = "Banned user until #{buntil.to_s}" + end + target.save! + + @status = :okay + @success = target.banned? == !unban + end + def privilege @status = :err @success = false @@ -118,10 +158,9 @@ class Ajax::ModerationController < ApplicationController target_user = User.find_by_screen_name(params[:user]) @message = "nope!" - return unless %w(banned blogger supporter moderator admin contributor).include? params[:type].downcase + return unless %w(blogger supporter moderator admin contributor).include? params[:type].downcase - if (%w(supporter moderator admin).include?(params[:type].downcase) and !current_user.admin?) or - (params[:type].downcase == 'banned' and target_user.admin?) + if %w(supporter moderator admin).include?(params[:type].downcase) and !current_user.admin? @status = :nopriv @message = "You'd better check YOUR privileges first!" @success = false diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 42840939..e178da33 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,6 +12,13 @@ class ApplicationController < ActionController::Base name = current_user.screen_name # obligatory '2001: A Space Odyssey' reference flash[:notice] = "I'm sorry, #{name}, I'm afraid I can't do that." + if current_user.ban_reason.nil? + flash[:notice] += "\nBan reason: #{current_user.ban_reason}" + end + if not current_user.permanently_banned? + # TODO format banned_until + flash[:notice] += "\nBanned until: #{current_user.banned_until}" + end sign_out current_user redirect_to new_user_session_path end diff --git a/app/models/user.rb b/app/models/user.rb index b2bdd111..b8dc904d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -224,4 +224,21 @@ class User < ActiveRecord::Base def cropping? !crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank? end + + # forwards fill + def banned? + self.permanently_banned? or ((not self.banned_until.nil?) and self.banned_until >= DateTime.current) + end + + def unban + self.update(permanently_banned: false, ban_reason: nil, banned_until: nil) + end + + def ban(buntil=nil, reason=nil) + if buntil == nil + self.update(permanently_banned: true, ban_reason: reason) + else + self.update(permanently_banned: false, banned_until: buntil, ban_reason: reason) + end + end end diff --git a/app/views/ajax/moderation/ban.json.jbuilder b/app/views/ajax/moderation/ban.json.jbuilder new file mode 100644 index 00000000..f98b3c38 --- /dev/null +++ b/app/views/ajax/moderation/ban.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'ajax/shared/status' diff --git a/app/views/moderation/_moderationbox.html.haml b/app/views/moderation/_moderationbox.html.haml index 225f3e15..2e44b39e 100644 --- a/app/views/moderation/_moderationbox.html.haml +++ b/app/views/moderation/_moderationbox.html.haml @@ -27,6 +27,9 @@ %a.btn.btn-primary{href: content_url(report)} View reported = report.type.sub('Reports::', '') + - if report.target.respond_to? :user and not report.target.user.nil? + %a.btn.btn-primary{href: show_user_profile_path(report.target.user.screen_name)} + View reported User .col-md-6.col-sm-8.col-xs-6.text-right %span.mod-count{id: "mod-count-#{report.id}"} = report.votes diff --git a/app/views/user/_actions.html.haml b/app/views/user/_actions.html.haml index 6ed47740..1c70e9ab 100644 --- a/app/views/user/_actions.html.haml +++ b/app/views/user/_actions.html.haml @@ -35,4 +35,9 @@ Check = succeed "'s" do = user.screen_name - privileges \ No newline at end of file + privileges + - unless user.admin? + %li + %a{href: '#', data: { target: "#modal-ban", toggle: :modal }} + %i.fa.fa-ban + Ban Control diff --git a/app/views/user/_modal_ban.html.haml b/app/views/user/_modal_ban.html.haml new file mode 100644 index 00000000..cdfb1fb8 --- /dev/null +++ b/app/views/user/_modal_ban.html.haml @@ -0,0 +1,21 @@ +#modal-ban.modal.fade{"aria-hidden" => "true", "aria-labelledby" => "modal-ban-label", :role => "dialog", :tabindex => "-1"} + .modal-dialog + .modal-content + .modal-header + %button.close{"data-dismiss" => "modal", :type => "button"} + %span{"aria-hidden" => "true"} × + %span.sr-only Close + %h4#modal-ban-label.modal-title + Ban Control Center + = bootstrap_form_tag(url: '/mod/ban', html: { method: :post, novalidate: "novalidate" }) do |f| + = f.hidden_field :user, value: @user.screen_name + #ban-control-super.modal-body + = f.check_box :ban, label: "Ban?", checked: @user.banned? + #ban-controls{style: "#{"display: none" unless @user.banned?}"} + = f.check_box :permaban, label: "Permanently?", checked: @user.permanently_banned? + #ban-controls-time{style: "#{"display: none" unless not @user.permanently_banned?}"} + = f.text_field :until, label: "", required: true, value: (@user.banned_until || DateTime.current).strftime("%m/%d/%Y %I:%M %p") + = f.text_field :reason, placeholder: "Reason", value: @user.ban_reason + .modal-footer + %button.btn.btn-default{name: 'stop-time', type: :button, data: { dismiss: :modal }} Close + = f.submit "Hammer Time", class: "btn btn-primary", name: 'hammer-time' diff --git a/app/views/user/_modal_privileges.html.haml b/app/views/user/_modal_privileges.html.haml index 3edbb981..265a3330 100644 --- a/app/views/user/_modal_privileges.html.haml +++ b/app/views/user/_modal_privileges.html.haml @@ -11,8 +11,6 @@ = @user.screen_name privileges %ul.list-group.groups--list - - unless @user.admin? - = render 'user/modal_privileges_item', privilege: 'banned', description: 'Hit the user with ye olde banhammer', user: @user = render 'user/modal_privileges_item', privilege: 'blogger', description: 'The user gets that privilege if they blogged something (nice) about Retrospring.', user: @user = render 'user/modal_privileges_item', privilege: 'contributor', description: "This user has contributed to justask#{" (the software behind #{APP_CONFIG['site_name']})" unless APP_CONFIG['site_name'] == 'justask'}.", user: @user - if current_user.admin? diff --git a/app/views/user/show.html.haml b/app/views/user/show.html.haml index 53401652..568706b9 100644 --- a/app/views/user/show.html.haml +++ b/app/views/user/show.html.haml @@ -5,17 +5,19 @@ .hidden-xs= render 'shared/links' .col-md-9.col-xs-12.col-sm-8.j2-col-reset = render 'shared/questionbox' - #answers - - @answers.each do |a| - = render 'shared/answerbox', a: a + = unless @user.banned? + #answers + - @answers.each do |a| + = render 'shared/answerbox', a: a - #pagination= will_paginate @answers, renderer: BootstrapPagination::Rails, page_links: false + #pagination= will_paginate @answers, renderer: BootstrapPagination::Rails, page_links: false - - if @answers.next_page - %button#load-more-btn.btn.btn-default{type: :button, data: { current_page: @answers.current_page }} - Load more + - if @answers.next_page + %button#load-more-btn.btn.btn-default{type: :button, data: { current_page: @answers.current_page }} + Load more .visible-xs= render 'shared/links' - if user_signed_in? = render 'user/modal_group_memberships' - if current_user.mod? and @user != current_user - = render 'user/modal_privileges' \ No newline at end of file + = render 'user/modal_privileges' + = render 'user/modal_ban' diff --git a/config/routes.rb b/config/routes.rb index 760a680a..9b8c648f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,7 @@ Rails.application.routes.draw do match '/mod/create_vote', to: 'moderation#vote', via: :post, as: :mod_create_vote match '/mod/destroy_vote', to: 'moderation#destroy_vote', via: :post, as: :mod_destroy_vote match '/mod/privilege', to: 'moderation#privilege', via: :post, as: :mod_privilege + match '/mod/ban', to: 'moderation#ban', via: :post, as: :mod_ban end end diff --git a/db/migrate/20150422224203_rename_banned_to_permanently_banned_in_users.rb b/db/migrate/20150422224203_rename_banned_to_permanently_banned_in_users.rb new file mode 100644 index 00000000..26d50c78 --- /dev/null +++ b/db/migrate/20150422224203_rename_banned_to_permanently_banned_in_users.rb @@ -0,0 +1,9 @@ +class RenameBannedToPermanentlyBannedInUsers < ActiveRecord::Migration + def up + rename_column :users, :banned, :permanently_banned + end + + def down + rename_column :users, :permanently_banned, :banned + end +end diff --git a/db/migrate/20150422224225_add_ban_reason_and_banned_until_to_users.rb b/db/migrate/20150422224225_add_ban_reason_and_banned_until_to_users.rb new file mode 100644 index 00000000..9e2ac7a4 --- /dev/null +++ b/db/migrate/20150422224225_add_ban_reason_and_banned_until_to_users.rb @@ -0,0 +1,6 @@ +class AddBanReasonAndBannedUntilToUsers < ActiveRecord::Migration + def change + add_column :users, :ban_reason, :string, default: nil + add_column :users, :banned_until, :datetime, default: nil + end +end