Merge remote-tracking branch 'origin/master' into moderation
Conflicts: app/models/user.rb
This commit is contained in:
commit
90fe42e643
4
Gemfile
4
Gemfile
|
@ -40,11 +40,15 @@ gem 'sinatra', require: false
|
||||||
|
|
||||||
gem 'questiongenerator', git: 'https://github.com/justask/questiongenerator.git'
|
gem 'questiongenerator', git: 'https://github.com/justask/questiongenerator.git'
|
||||||
|
|
||||||
|
gem 'sanitize'
|
||||||
|
gem 'redcarpet'
|
||||||
|
|
||||||
# OmniAuth and providers
|
# OmniAuth and providers
|
||||||
gem 'omniauth'
|
gem 'omniauth'
|
||||||
gem 'omniauth-twitter'
|
gem 'omniauth-twitter'
|
||||||
|
|
||||||
gem 'foreman'
|
gem 'foreman'
|
||||||
|
gem 'redis'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
|
|
15
Gemfile.lock
15
Gemfile.lock
|
@ -1,8 +1,8 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/justask/questiongenerator.git
|
remote: https://github.com/justask/questiongenerator.git
|
||||||
revision: 95bfac7b8e9157702befb5a6f7ea8220037fd804
|
revision: 64d82a2b9de9df699fe5cf53bdfc90cb2bc3e771
|
||||||
specs:
|
specs:
|
||||||
questiongenerator (0.0.1)
|
questiongenerator (0.0.2)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
|
@ -77,6 +77,7 @@ GEM
|
||||||
coffee-script-source (1.8.0)
|
coffee-script-source (1.8.0)
|
||||||
colorize (0.7.5)
|
colorize (0.7.5)
|
||||||
connection_pool (2.1.0)
|
connection_pool (2.1.0)
|
||||||
|
crass (1.0.1)
|
||||||
daemons (1.1.9)
|
daemons (1.1.9)
|
||||||
database_cleaner (1.3.0)
|
database_cleaner (1.3.0)
|
||||||
devise (3.4.1)
|
devise (3.4.1)
|
||||||
|
@ -152,6 +153,8 @@ GEM
|
||||||
net-ssh (2.9.1)
|
net-ssh (2.9.1)
|
||||||
nokogiri (1.6.5)
|
nokogiri (1.6.5)
|
||||||
mini_portile (~> 0.6.0)
|
mini_portile (~> 0.6.0)
|
||||||
|
nokogumbo (1.2.0)
|
||||||
|
nokogiri
|
||||||
nprogress-rails (0.1.6.3)
|
nprogress-rails (0.1.6.3)
|
||||||
oauth (0.4.7)
|
oauth (0.4.7)
|
||||||
omniauth (1.2.2)
|
omniauth (1.2.2)
|
||||||
|
@ -214,6 +217,7 @@ GEM
|
||||||
rake (10.4.2)
|
rake (10.4.2)
|
||||||
rdoc (4.2.0)
|
rdoc (4.2.0)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
|
redcarpet (3.2.2)
|
||||||
redis (3.2.0)
|
redis (3.2.0)
|
||||||
redis-namespace (1.5.1)
|
redis-namespace (1.5.1)
|
||||||
redis (~> 3.0, >= 3.0.4)
|
redis (~> 3.0, >= 3.0.4)
|
||||||
|
@ -238,6 +242,10 @@ GEM
|
||||||
rspec-support (3.0.4)
|
rspec-support (3.0.4)
|
||||||
ruby-progressbar (1.7.0)
|
ruby-progressbar (1.7.0)
|
||||||
safe_yaml (1.0.4)
|
safe_yaml (1.0.4)
|
||||||
|
sanitize (3.1.0)
|
||||||
|
crass (~> 1.0.1)
|
||||||
|
nokogiri (>= 1.4.4)
|
||||||
|
nokogumbo (= 1.2.0)
|
||||||
sass (3.2.19)
|
sass (3.2.19)
|
||||||
sass-rails (4.0.5)
|
sass-rails (4.0.5)
|
||||||
railties (>= 4.0.0, < 5.0)
|
railties (>= 4.0.0, < 5.0)
|
||||||
|
@ -352,8 +360,11 @@ DEPENDENCIES
|
||||||
rails (= 4.1.8)
|
rails (= 4.1.8)
|
||||||
rails-assets-growl
|
rails-assets-growl
|
||||||
rails_admin
|
rails_admin
|
||||||
|
redcarpet
|
||||||
|
redis
|
||||||
rspec-rails (~> 3.0.0)
|
rspec-rails (~> 3.0.0)
|
||||||
ruby-progressbar
|
ruby-progressbar
|
||||||
|
sanitize
|
||||||
sass-rails (~> 4.0.3)
|
sass-rails (~> 4.0.3)
|
||||||
sdoc (~> 0.4.1)
|
sdoc (~> 0.4.1)
|
||||||
sidekiq
|
sidekiq
|
||||||
|
|
87
Rakefile
87
Rakefile
|
@ -117,4 +117,91 @@ namespace :justask do
|
||||||
|
|
||||||
puts "Purged #{destroyed_count} dead notifications."
|
puts "Purged #{destroyed_count} dead notifications."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc "Fixes everything else"
|
||||||
|
task fix_db: :environment do
|
||||||
|
format = '%t (%c/%C) [%b>%i] %e'
|
||||||
|
destroyed_count = {
|
||||||
|
inbox: 0,
|
||||||
|
question: 0,
|
||||||
|
answer: 0,
|
||||||
|
smile: 0,
|
||||||
|
comment: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
total = Inbox.count
|
||||||
|
progress = ProgressBar.create title: 'Processing inboxes', format: format, starting_at: 0, total: total
|
||||||
|
Inbox.all.each do |n|
|
||||||
|
if n.question.nil?
|
||||||
|
n.destroy
|
||||||
|
destroyed_count[:inbox] += 1
|
||||||
|
end
|
||||||
|
progress.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
total = Question.count
|
||||||
|
progress = ProgressBar.create title: 'Processing questions', format: format, starting_at: 0, total: total
|
||||||
|
Question.all.each do |q|
|
||||||
|
if q.user.nil?
|
||||||
|
q.user_id = nil
|
||||||
|
q.author_is_anonymous = true
|
||||||
|
destroyed_count[:question] += 1
|
||||||
|
end
|
||||||
|
progress.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
total = Answer.count
|
||||||
|
progress = ProgressBar.create title: 'Processing answers', format: format, starting_at: 0, total: total
|
||||||
|
Answer.all.each do |a|
|
||||||
|
if a.user.nil? or a.question.nil?
|
||||||
|
a.destroy
|
||||||
|
destroyed_count[:answer] += 1
|
||||||
|
end
|
||||||
|
progress.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
total = Comment.count
|
||||||
|
progress = ProgressBar.create title: 'Processing comments', format: format, starting_at: 0, total: total
|
||||||
|
Comment.all.each do |c|
|
||||||
|
if c.user.nil? or c.answer.nil?
|
||||||
|
c.destroy
|
||||||
|
destroyed_count[:comment] += 1
|
||||||
|
end
|
||||||
|
progress.increment
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "Purged #{destroyed_count[:inbox]} dead inbox entries."
|
||||||
|
puts "Marked #{destroyed_count[:question]} questions as anonymous."
|
||||||
|
puts "Purged #{destroyed_count[:answer]} dead answers."
|
||||||
|
puts "Purged #{destroyed_count[:answer]} dead comments."
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Prints lonely people."
|
||||||
|
task loners: :environment do
|
||||||
|
people = {}
|
||||||
|
Question.all.each do |q|
|
||||||
|
if q.author_is_anonymous and q.author_name != 'justask'
|
||||||
|
q.answers.each do |a|
|
||||||
|
if q.user == a.user
|
||||||
|
people[q.user.screen_name] ||= 0
|
||||||
|
people[q.user.screen_name] += 1
|
||||||
|
puts "#{q.user.screen_name} -- answer id #{a.id}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
max = 0
|
||||||
|
res = []
|
||||||
|
people.each { |k, v| max = v if v > max }
|
||||||
|
people.each { |k, v| res << k if v == max }
|
||||||
|
if res.length == 0
|
||||||
|
puts "No one? I hope you're just on the development session."
|
||||||
|
else
|
||||||
|
puts res.length == 1 ? "And the winner is..." : "And the winners are..."
|
||||||
|
print "\033[5;31m"
|
||||||
|
res.each { |name| puts " - #{name}" }
|
||||||
|
print "\033[0m"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
complete: (jqxhr, status) ->
|
complete: (jqxhr, status) ->
|
||||||
btn.button "reset"
|
btn.button "reset"
|
||||||
if succ
|
if succ
|
||||||
btn.attr "disabled", "disabled" # this doesn't really work like I wanted it to…
|
btn.prop "disabled", true # this doesn't really work like I wanted it to…
|
||||||
btn[0].dataset.ibCount = 0
|
btn[0].dataset.ibCount = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,14 +46,8 @@ class Ajax::InboxController < ApplicationController
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
# sharing
|
services = JSON.parse params[:share]
|
||||||
begin
|
ShareWorker.perform_async(current_user.id, answer.id, services)
|
||||||
share_to = JSON.parse params[:share]
|
|
||||||
current_user.services.each do |service|
|
|
||||||
service.post(answer) if share_to.include? service.provider
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
|
|
||||||
@status = :okay
|
@status = :okay
|
||||||
@message = "Successfully answered question."
|
@message = "Successfully answered question."
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
module MarkdownHelper
|
||||||
|
|
||||||
|
def markdown(content)
|
||||||
|
md = Redcarpet::Markdown.new(FlavoredMarkdown,
|
||||||
|
filter_html: true,
|
||||||
|
escape_html: true,
|
||||||
|
no_images: true,
|
||||||
|
no_styles: true,
|
||||||
|
safe_links_only: true,
|
||||||
|
xhtml: false,
|
||||||
|
hard_wrap: true,
|
||||||
|
no_intra_emphasis: true,
|
||||||
|
tables: true,
|
||||||
|
fenced_code_blocks: true,
|
||||||
|
autolink: true,
|
||||||
|
disable_indented_code_blocks: true,
|
||||||
|
strikethrough: true,
|
||||||
|
superscript: true)
|
||||||
|
Sanitize.fragment(md.render(content), EVIL_TAGS).html_safe
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
class Question < ActiveRecord::Base
|
class Question < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
has_many :answers
|
has_many :answers
|
||||||
|
has_many :inboxes, dependent: :destroy
|
||||||
|
|
||||||
validates :content, length: { maximum: 255 }
|
validates :content, length: { maximum: 255 }
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Services::Twitter < Service
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_tweet(answer)
|
def post_tweet(answer)
|
||||||
client.update prepare_tweet(answer)
|
client.update! prepare_tweet(answer)
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_tweet(answer)
|
def prepare_tweet(answer)
|
||||||
|
@ -35,7 +35,7 @@ class Services::Twitter < Service
|
||||||
host: APP_CONFIG['hostname'],
|
host: APP_CONFIG['hostname'],
|
||||||
protocol: (APP_CONFIG['https'] ? :https : :http)
|
protocol: (APP_CONFIG['https'] ? :https : :http)
|
||||||
)
|
)
|
||||||
"#{question_content[0..55]}#{'…' if question_content.length > 56}" \
|
"#{question_content[0..54]}#{'…' if question_content.length > 55}" \
|
||||||
" — #{answer_content[0..55]}#{'…' if answer_content.length > 56} #{answer_url}"
|
" — #{answer_content[0..55]}#{'…' if answer_content.length > 56} #{answer_url}"
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -19,9 +19,9 @@ class User < ActiveRecord::Base
|
||||||
dependent: :destroy
|
dependent: :destroy
|
||||||
has_many :friends, through: :active_relationships, source: :target
|
has_many :friends, through: :active_relationships, source: :target
|
||||||
has_many :followers, through: :passive_relationships, source: :source
|
has_many :followers, through: :passive_relationships, source: :source
|
||||||
has_many :smiles
|
has_many :smiles, dependent: :destroy
|
||||||
has_many :services
|
has_many :services, dependent: :destroy
|
||||||
has_many :notifications, foreign_key: :recipient_id
|
has_many :notifications, foreign_key: :recipient_id, dependent: :destroy
|
||||||
has_many :reports, dependent: :destroy
|
has_many :reports, dependent: :destroy
|
||||||
|
|
||||||
SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]{1,16}\z/
|
SCREEN_NAME_REGEX = /\A[a-zA-Z0-9_]{1,16}\z/
|
||||||
|
@ -126,14 +126,4 @@ class User < ActiveRecord::Base
|
||||||
increment! :commented_count
|
increment! :commented_count
|
||||||
answer.increment! :comment_count
|
answer.increment! :comment_count
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [Boolean] is the user a moderator?
|
|
||||||
def mod?
|
|
||||||
return true if self.moderator? or self.admin?
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def report(object)
|
|
||||||
Report.create(type: "Reports::#{object.class}", target_id: object.id, user_id: self.id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
class FlavoredMarkdown < Redcarpet::Render::HTML
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
def preprocess(text)
|
||||||
|
wrap_mentions(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def wrap_mentions(text)
|
||||||
|
text.gsub! /(^|\s)(@\w+)/ do
|
||||||
|
"#{$1}[#{$2}](#{show_user_profile_path $2.tr('@', '')})"
|
||||||
|
end
|
||||||
|
text
|
||||||
|
end
|
||||||
|
|
||||||
|
def header(text, _header_level)
|
||||||
|
paragraph text
|
||||||
|
end
|
||||||
|
|
||||||
|
def paragraph(text)
|
||||||
|
"<p>#{text}</p>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def raw_html(raw_html)
|
||||||
|
Rack::Utils.escape_html raw_html
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,16 +23,15 @@
|
||||||
%p.answerbox--question-text
|
%p.answerbox--question-text
|
||||||
= a.question.content
|
= a.question.content
|
||||||
.panel-body
|
.panel-body
|
||||||
%p
|
- if @display_all.nil?
|
||||||
- if @display_all.nil?
|
= markdown a.content[0..255]
|
||||||
= a.content[0..255]
|
- if a.content.length > 255
|
||||||
- if a.content.length > 255
|
[...]
|
||||||
[...]
|
%p
|
||||||
%p
|
%a.btn.btn-primary{href: show_user_answer_path(a.user.screen_name, a.id)}
|
||||||
%a.btn.btn-primary{href: show_user_answer_path(a.user.screen_name, a.id)}
|
Read the entire answer
|
||||||
Read the entire answer
|
- else
|
||||||
- else
|
= markdown a.content
|
||||||
= a.content
|
|
||||||
- if @user.nil?
|
- if @user.nil?
|
||||||
.row
|
.row
|
||||||
.col-md-6.col-sm-4.col-xs-7.text-left.text-muted
|
.col-md-6.col-sm-4.col-xs-7.text-left.text-muted
|
||||||
|
|
|
@ -3,7 +3,16 @@ class ShareWorker
|
||||||
|
|
||||||
sidekiq_options queue: :share
|
sidekiq_options queue: :share
|
||||||
|
|
||||||
def perform(answer_id)
|
# @param user_id [Integer] the user id
|
||||||
# fuck this… for now
|
# @param answer_id [Integer] the user id
|
||||||
|
# @param services [Array] array containing strings
|
||||||
|
def perform(user_id, answer_id, services)
|
||||||
|
User.find(user_id).services.each do |service|
|
||||||
|
begin
|
||||||
|
service.post(Answer.find(answer_id)) if services.include? service.provider
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "failed to post answer #{answer_id} to #{service.provider} for user #{user_id}: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
EVIL_TAGS = {
|
||||||
|
elements: %w(blockquote a p i strong em del pre code table tr td th br ul ol li hr),
|
||||||
|
attributes: {
|
||||||
|
'a' => %w(href)
|
||||||
|
},
|
||||||
|
protocols: {
|
||||||
|
'a' => { 'href' => ['http', 'https', :relative] }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue