Merge branch 'master' into feature/bootstrap

This commit is contained in:
Andreas Nedbal 2020-04-27 02:11:54 +02:00
commit e9e0a222b0
48 changed files with 70 additions and 316 deletions

175
Rakefile
View File

@ -227,66 +227,6 @@ namespace :justask do
puts "#{user.screen_name} is no longer banned."
end
desc "Gives blogger status to an user."
task :blog, [:screen_name] => :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.blogger = true
user.save!
puts "#{user.screen_name} is now a blogger."
end
desc "Removes blogger status from an user."
task :unblog, [:screen_name] => :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.blogger = false
user.save!
puts "#{user.screen_name} is no longer a blogger."
end
desc "Gives supporter status to an user."
task :sup, [:screen_name] => :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.supporter = true
user.save!
puts "#{user.screen_name} is now an supporter."
end
desc "Removes supporter status from an user."
task :desup, [:screen_name] => :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.supporter = false
user.save!
puts "#{user.screen_name} is no longer an supporter."
end
desc "Gives contributor status to an user."
task :contrib, [:screen_name] => :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.contributor = true
user.save!
puts "#{user.screen_name} is now a contributor."
end
desc "Removes contributor status from an user."
task :decontrib, [:screen_name] => :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.contributor = false
user.save!
puts "#{user.screen_name} is no longer a contributor."
end
desc "Lists all users."
task lusers: :environment do
User.all.each do |u|
@ -489,119 +429,4 @@ namespace :justask do
print "\033[0m"
end
end
desc "Export data for an user"
task :export, [:email] => :environment do |t, args|
require 'json'
require 'yaml'
return if args[:email].nil?
obj = {}
format = '%t (%c/%C) [%b>%i] %e'
u = User.where("LOWER(email) = ?", args[:email].downcase).first!
export_dirname = "export_#{u.screen_name}_#{Time.now.to_i}"
export_filename = u.screen_name
%i(id screen_name display_name created_at sign_in_count last_sign_in_at friend_count follower_count asked_count answered_count commented_count smiled_count motivation_header bio website location moderator admin supporter banned blogger).each do |f|
obj[f] = u.send f
end
total = u.questions.count
progress = ProgressBar.create title: 'Processing questions', format: format, starting_at: 0, total: total
obj[:questions] = []
u.questions.each do |q|
qobj = {}
%i(id content author_is_anonymous created_at answer_count).each do |f|
qobj[f] = q.send f
end
obj[:questions] << qobj
progress.increment
end
total = u.answers.count
progress = ProgressBar.create title: 'Processing answers', format: format, starting_at: 0, total: total
obj[:answers] = []
u.answers.each do |a|
aobj = {}
%i(id content comment_count smile_count created_at).each do |f|
aobj[f] = a.send f
end
aobj[:question] = {}
%i(id content author_is_anonymous created_at answer_count).each do |f|
aobj[:question][f] = a.question.send f
end
aobj[:question][:user] = a.question.user.screen_name unless a.question.author_is_anonymous
aobj[:comments] = []
a.comments.each do |c|
cobj = {}
%i(id content created_at).each do |f|
cobj[f] = c.send f
end
cobj[:user] = c.user.screen_name
aobj[:comments] << cobj
end
obj[:answers] << aobj
progress.increment
end
total = u.comments.count
progress = ProgressBar.create title: 'Processing comments', format: format, starting_at: 0, total: total
obj[:comments] = []
u.comments.each do |c|
cobj = {}
%i(id content created_at).each do |f|
cobj[f] = c.send f
end
cobj[:answer] = {}
%i(id content comment_count smile_count created_at).each do |f|
cobj[:answer][f] = c.answer.send f
end
cobj[:answer][:question] = {}
%i(id content author_is_anonymous created_at answer_count).each do |f|
cobj[:answer][:question][f] = c.answer.question.send f
end
cobj[:answer][:question][:user] = c.answer.question.user.screen_name unless c.answer.question.author_is_anonymous
obj[:comments] << cobj
progress.increment
end
total = u.smiles.count
progress = ProgressBar.create title: 'Processing smiles', format: format, starting_at: 0, total: total
obj[:smiles] = []
u.smiles.each do |s|
sobj = {}
%i(id created_at).each do |f|
sobj[f] = s.send f
end
sobj[:answer] = {}
%i(id content comment_count smile_count created_at).each do |f|
sobj[:answer][f] = s.answer.send f
end
sobj[:answer][:question] = {}
%i(id content author_is_anonymous created_at answer_count).each do |f|
sobj[:answer][:question][f] = s.answer.question.send f
end
sobj[:answer][:question][:user] = s.answer.question.user.screen_name unless s.answer.question.author_is_anonymous
obj[:smiles] << sobj
progress.increment
end
`mkdir -p /usr/home/justask/justask/public/export`
`mkdir -p /tmp/rs_export/#{export_dirname}/picture`
if u.profile_picture
%i(large medium small original).each do |s|
x = u.profile_picture.path(s).gsub('"', '\\"')
`cp "#{x}" "/tmp/rs_export/#{export_dirname}/picture/#{s}_#{File.basename x}"`
end
end
File.open "/tmp/rs_export/#{export_dirname}/#{export_filename}.json", 'w' do |f|
f.puts obj.to_json
end
File.open "/tmp/rs_export/#{export_dirname}/#{export_filename}.yml", 'w' do |f|
f.puts obj.to_yaml
end
`tar czvf public/export/#{export_dirname}.tar.gz -C /tmp/rs_export #{export_dirname}`
puts "\033[1mhttps://retrospring.net/export/#{export_dirname}.tar.gz\033[0m"
end
end

View File

@ -1,22 +1,27 @@
load = ->
parent = $ "#ban-control-super"
return unless parent.length > 0
return unless document.getElementById('ban-control-super') != null
modalEl = $("#modal-ban")
modalEl.modal "hide"
modalForm = modalEl.find("form")[0]
banCheckbox = modalForm.querySelector('[name="ban"][type="checkbox"]')
permabanCheckbox = modalForm.querySelector('[name="permaban"][type="checkbox"]')
parent.find('#_ban').on "change", (event) ->
banCheckbox.addEventListener "change", (event) ->
$t = $ this
if $t.is(":checked")
$("#ban-controls").show()
else
$("#ban-controls").hide()
parent.find('#_permaban').on "change", (event) ->
permabanCheckbox.addEventListener "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()
untilInput = $ modalForm.elements["until"]
untilInput.datetimepicker
defaultDate: untilInput.val()
sideBySide: true
icons:
time: "fa fa-clock-o"
@ -29,23 +34,21 @@ load = ->
clear: "fa fa-trash-o"
close: "fa fa-times"
parent.parent()[0].addEventListener "submit", (event) ->
modalForm.addEventListener "submit", (event) ->
event.preventDefault();
$("#modal-ban").modal "hide"
checktostr = (selector) ->
if $(selector)[0].checked
checktostr = (el) ->
if el.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
ban: checktostr banCheckbox
permaban: checktostr permabanCheckbox
until: modalForm.elements["until"].value.trim()
reason: modalForm.elements["reason"].value.trim()
user: modalForm.elements["user"].value
}
$.ajax

View File

@ -164,9 +164,9 @@ class Ajax::ModerationController < ApplicationController
target_user = User.find_by_screen_name(params[:user])
@message = I18n.t('messages.moderation.privilege.nope')
return unless %w(blogger supporter moderator admin contributor translator).include? params[:type].downcase
return unless %w(moderator admin).include? params[:type].downcase
if %w(supporter moderator admin).include?(params[:type].downcase) && !current_user.has_role?(:administrator)
unless current_user.has_role?(:administrator)
@status = :nopriv
@message = I18n.t('messages.moderation.privilege.nopriv')
@success = false
@ -176,21 +176,11 @@ class Ajax::ModerationController < ApplicationController
@checked = status
type = params[:type].downcase
target_role = {"admin" => "administrator"}.fetch(type, type).to_sym
case type
when 'blogger'
target_user.blogger = status
when 'contributor'
target_user.contributor = status
when 'translator'
target_user.translator = status
when 'supporter'
target_user.supporter = status
when 'moderator', 'admin'
if status
target_user.add_role target_role
else
target_user.remove_role target_role
end
if status
target_user.add_role target_role
else
target_user.remove_role target_role
end
target_user.save!

View File

@ -17,7 +17,7 @@ class StaticController < ApplicationController
end
def about
@admins = User.where(admin: true).order(:id)
end
def faq

View File

@ -89,15 +89,6 @@ module ApplicationHelper
((!current_user.nil?) && ((current_user == user) || current_user.mod?)) ? true : false
end
# @deprecated Use {User#profile_picture.url} instead.
def gravatar_url(user)
return user.profile_picture.url :medium
# return '/cage.png'
#return '//www.gravatar.com/avatar' if user.nil?
#return "//www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user)}" if user.is_a? String
#"//www.gravatar.com/avatar/#{Digest::MD5.hexdigest(user.email)}"
end
def ios_web_app?
user_agent = request.env['HTTP_USER_AGENT'] || 'Mozilla/5.0'
# normal MobileSafari.app UA: Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B435 Safari/600.1.4

View File

@ -70,7 +70,6 @@ class User < ApplicationRecord
process_in_background :profile_header
before_save do
self.display_name = 'WRYYYYYYYY' if display_name == 'Dio Brando'
self.website = if website.match %r{\Ahttps?://}
website
else

View File

@ -7,7 +7,7 @@
%div{class: "ab-comment-smile-list", style: "height: 0; width: 0"}= render "modal/comment_smiles", comment: comment
.media.comments--media
.pull-left
%img.img-rounded.answerbox--img{src: gravatar_url(comment.user)}
%img.img-rounded.answerbox--img{src: comment.user.profile_picture.url(:medium)}
.media-body.comments--body
%h6.media-heading.answerbox--question-user
= user_screen_name comment.user

View File

@ -2,7 +2,7 @@
.media.question-media
- unless a.question.author_is_anonymous
%a.pull-left{href: show_user_profile_path(a.question.user.screen_name)}
%img.img-rounded.answerbox--img{src: gravatar_url(a.question.user)}
%img.img-rounded.answerbox--img{src: a.question.user.profile_picture.url(:medium)}
.media-body.question-body
- if user_signed_in?
.pull-right

View File

@ -7,4 +7,4 @@
- else
- a.smiles.all.each do |smile|
%a{href: show_user_profile_path(smile.user.screen_name), title: smile.user.screen_name, data: { toggle: :tooltip, placement: :top, smile_id: smile.id }}
%img.img-rounded.answerbox--img-small{src: gravatar_url(smile.user)}
%img.img-rounded.answerbox--img-small{src: smile.user.profile_picture.url(:medium)}

View File

@ -19,7 +19,7 @@
.media
.pull-left
%a{href: show_user_profile_path(a.user.screen_name)}
%img.img-rounded.answerbox--img{src: gravatar_url(a.user)}
%img.img-rounded.answerbox--img{src: a.user.profile_picture.url(:medium)}
.media-body
%h6.media-heading.answerbox--answer-user
= raw t('views.answerbox.answered', hide: hidespan(t('views.answerbox.hide'), "xs"), user: user_screen_name(a.user))

View File

@ -3,7 +3,7 @@
.media
- unless i.question.author_is_anonymous
%a.pull-left{href: show_user_profile_path(i.question.user.screen_name)}
%img.img-rounded.answerbox--img{src: gravatar_url(i.question.user)}
%img.img-rounded.answerbox--img{src: i.question.user.profile_picture.url(:medium)}
.media-body
%h6.text-muted.media-heading.answerbox--question-user
= raw t('views.inbox.entry.asked', user: user_screen_name(i.question.user, anonymous: i.question.author_is_anonymous), time: time_tooltip(i.question))

View File

@ -8,7 +8,7 @@
%a.btn.btn-block.btn-primary{target: '_blank', href: "https://twitter.com/intent/tweet?text=Ask%20me%20anything%21&url=#{show_user_profile_url(current_user.screen_name)}"}
%i.fa.fa-fw.fa-twitter
= raw t('views.inbox.sidebar.share.button', service: "Twitter")
%a.btn.btn-block.btn-primary{target: '_blank', href: "http://www.tumblr.com/share/link?url=#{show_user_profile_url(current_user.screen_name)}&name=Ask%20me%20anything%21"}
%a.btn.btn-block.btn-primary{target: '_blank', href: "https://www.tumblr.com/share/link?url=#{show_user_profile_url(current_user.screen_name)}&name=Ask%20me%20anything%21"}
%i.fa.fa-fw.fa-tumblr
= raw t('views.inbox.sidebar.share.button', service: "Tumblr")
.card.inbox--panel

View File

@ -14,5 +14,5 @@
- comment.smiles.all.each do |smile|
%li.user-list-entry.user-list-entry-smiles
%a{href: show_user_profile_path(smile.user.screen_name)}
%img.img-rounded{src: gravatar_url(smile.user), alt: user_screen_name(smile.user, url: false)}
%img.img-rounded{src: smile.user.profile_picture.url(:medium), alt: user_screen_name(smile.user, url: false)}
%span= user_screen_name(smile.user, url: false)

View File

@ -8,11 +8,7 @@
%span{"aria-hidden" => "true"} ×
%span.sr-only= t 'views.actions.close'
%ul.list-group.groups--list
= render 'modal/privileges/item', privilege: 'blogger', description: t('views.modal.privilege.blogger'), user: @user
= render 'modal/privileges/item', privilege: 'contributor', description: t('views.modal.privilege.contributor'), user: @user
= render 'modal/privileges/item', privilege: 'translator', description: t('views.modal.privilege.translator'), user: @user
- if current_user.has_role?(:administrator)
= render 'modal/privileges/item', privilege: 'supporter', description: t('views.modal.privilege.supporter'), user: @user
= render 'modal/privileges/item', privilege: 'moderator', description: t('views.modal.privilege.moderator'),user: @user
= render 'modal/privileges/item', privilege: 'admin', description: t('views.modal.privilege.admin'), user: @user
.modal-footer

View File

@ -6,7 +6,7 @@
%li{data: { comment_id: comment.id }}
.media.comments--media
.pull-left
%img.img-rounded.answerbox--img{src: gravatar_url(comment.user)}
%img.img-rounded.answerbox--img{src: comment.user.profile_picture.url(:medium)}
.media-body.comments--body
%h6.media-heading.answerbox--question-user
= user_screen_name comment.user

View File

@ -1,7 +1,7 @@
- unless report.nil? or report.target.nil? or report.user.nil? or report.type.nil?
.card.moderationbox{data: { id: report.id }}
.card-header
%img.img-rounded.answerbox--img{src: gravatar_url(report.user)}
%img.img-rounded.answerbox--img{src: report.user.profile_picture.url(:medium)}
= raw t('views.moderation.moderationbox.reported', user: user_screen_name(report.user), content: report.type.sub('Reports::', ''), time: time_tooltip(report))
.card-body
%p

View File

@ -106,42 +106,6 @@
- else
%span.label.label-danger
%i.fa.fa-fw.fa-close
%p.data-heading Supporter
%p
- if current_user.supporter?
%span.label.label-success
%i.fa.fa-fw.fa-check
- else
%span.label.label-danger
%i.fa.fa-fw.fa-close
%p.data-heading Contributor
%p
- if current_user.contributor?
%span.label.label-success
%i.fa.fa-fw.fa-check
- else
%span.label.label-danger
%i.fa.fa-fw.fa-close
%p.data-heading Blogger
%p
- if current_user.blogger?
%span.label.label-success
%i.fa.fa-fw.fa-check
- else
%span.label.label-danger
%i.fa.fa-fw.fa-close
%p.data-heading Translator
%p
- if current_user.translator?
%span.label.label-success
%i.fa.fa-fw.fa-check
- else
%span.label.label-danger
%i.fa.fa-fw.fa-close
.row
.col-md-6.col-sm-6.col-xs-12
%h3 IP

View File

@ -40,7 +40,7 @@
= f.text_field :motivation_header, label: t('views.settings.profile.motivation'), placeholder: t('views.settings.profile.placeholder.motivation')
= f.text_field :website, label: t('views.settings.profile.website'), placeholder: 'http://example.com'
= f.text_field :website, label: t('views.settings.profile.website'), placeholder: 'https://example.com'
= f.text_field :location, label: t('views.settings.profile.location'), placeholder: t('views.settings.profile.placeholder.location')

View File

@ -5,7 +5,7 @@
·
= link_to t('views.general.about'), about_path
·
= link_to "GitHub", 'https://github.com/retrospring/retrospring'
= link_to "GitHub", 'https://github.com/Retrospring/retrospring'
·
= link_to t('views.general.terms'), terms_path
·

View File

@ -4,7 +4,7 @@
.media.question-media
- unless question.author_is_anonymous
%a.pull-left{href: unless hidden then show_user_profile_path(question.user.screen_name) end}
%img.img-rounded.answerbox--img{src: gravatar_url(question.user)}
%img.img-rounded.answerbox--img{src: question.user.profile_picture.url(:medium)}
.media-body.question-body
- if user_signed_in?
.pull-right

View File

@ -29,7 +29,7 @@
.col-md-4
%h3= t 'views.about.opensource.title'
%p= t('views.about.opensource.warning', app_title: APP_CONFIG['site_name'])
%p= raw t('views.about.opensource.desc', app_title: APP_CONFIG['site_name'], github: link_to(t('views.about.opensource.github'), "https://github.com/Retrospring/retrospring"), bugtracker: link_to(t('views.about.opensource.bugtracker'), "https://github.com/Retrospring/bugs"))
%p= raw t('views.about.opensource.desc', app_title: APP_CONFIG['site_name'], github: link_to(t('views.about.opensource.github'), "https://github.com/Retrospring/retrospring"), bugtracker: link_to(t('views.about.opensource.bugtracker'), "https://github.com/Retrospring/retrospring/issues"))
.col-md-4
%a{href: "https://github.com/Retrospring/retrospring"}
.icon-showcase
@ -37,14 +37,6 @@
%h4.heading-about.text-center= t 'views.about.repository.title'
%p.text-center
%em= t 'views.about.repository.desc'
.col-md-4
%h3= t 'views.about.contributors.title'
%p= t('views.about.contributors.desc', app_title: APP_CONFIG['site_name'])
%p= raw t('views.about.contributors.howto', fork: link_to(t('views.about.contributors.fork'), "https://github.com/retrospring/retrospring"))
%ul.about--moderator
- User.where(contributor: true).each do |sup|
%a{href: show_user_profile_path(sup.screen_name), title: sup.screen_name, data: { toggle: :tooltip, placement: :top }}
%img.img-rounded.answerbox--img-small{src: sup.profile_picture.url(:medium)}
.card
.card-body
.row
@ -57,9 +49,9 @@
%h2.entry-text#answered-count= Answer.count
%h4.entry-subtext= t('views.general.answer').pluralize(Answer.count)
.col-md-3.col-sm-6.col-xs-6.statistics
%h2.entry-text#asked-count= Comment.count
%h2.entry-text#comment-count= Comment.count
%h4.entry-subtext= t('views.general.comment').pluralize(Comment.count)
%h2.entry-text#answered-count= Smile.count + CommentSmile.count
%h2.entry-text#smile-count= Smile.count + CommentSmile.count
%h4.entry-subtext= t('views.general.smile').pluralize(Smile.count)
.col-md-3.col-sm-12.col-xs-12.users
.entry-text#follower-count= User.count

View File

@ -88,7 +88,7 @@
We don't like them ourselves, really.
= APP_CONFIG['site_name']
just runs with community funding over
%a{href: "http://patreon.com/retrospring"} Patreon
%a{href: "https://patreon.com/retrospring"} Patreon
which is way better than displaying annoying stuff.
.col-md-4.col-sm-4.col-xs-12
%h3
@ -103,7 +103,7 @@
.card-body
%p
Any questions? Ask us on
%a{href: "https://twitter.com/retrospringnet"} Twitter
%a{href: "https://twitter.com/retrospring"} Twitter
or visit one of the administrator profiles you can find on the
%a{href: about_path} About page!

View File

@ -438,19 +438,11 @@ en:
name: "Group name"
members: "members"
privilege:
blogger: "The user gets that privilege if they blogged something (nice) about Retrospring."
contributor: "This user has contributed to justask (the software behind Retrospring)."
supporter: "This user monetarily supports the site."
moderator: "Someone trustworthy enough to help managing reports."
admin: "This user is part of the core team."
translator: "This user helped translating Retrospring into their language."
user:
follows_you: "Follows you"
title:
admin: "Admin"
moderator: "Moderator"
supporter: "Supporter"
contributor: "Contributor"
blogger: "Blogger"
banned: "Banned"
translator: "Translator"

View File

@ -0,0 +1,10 @@
class RemoveUnusedProfileFlags < ActiveRecord::Migration[5.2]
def change
remove_column :users, :admin
remove_column :users, :blogger
remove_column :users, :contributor
remove_column :users, :moderator
remove_column :users, :supporter
remove_column :users, :translator
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_04_19_185535) do
ActiveRecord::Schema.define(version: 2020_04_25_194536) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -241,12 +241,10 @@ ActiveRecord::Schema.define(version: 2020_04_19_185535) do
t.integer "commented_count", default: 0, null: false
t.string "display_name"
t.integer "smiled_count", default: 0, null: false
t.boolean "admin", default: false, null: false
t.string "motivation_header", default: "", null: false
t.string "website", default: "", null: false
t.string "location", default: "", null: false
t.text "bio", default: "", null: false
t.boolean "moderator", default: false, null: false
t.string "profile_picture_file_name"
t.string "profile_picture_content_type"
t.integer "profile_picture_file_size"
@ -256,14 +254,11 @@ ActiveRecord::Schema.define(version: 2020_04_19_185535) do
t.integer "crop_y"
t.integer "crop_w"
t.integer "crop_h"
t.boolean "supporter", default: false
t.boolean "privacy_allow_anonymous_questions", default: true
t.boolean "privacy_allow_public_timeline", default: true
t.boolean "privacy_allow_stranger_answers", default: true
t.boolean "privacy_show_in_search", default: true
t.boolean "permanently_banned", default: false
t.boolean "blogger", default: false
t.boolean "contributor", default: false
t.string "ban_reason"
t.datetime "banned_until"
t.integer "comment_smiled_count", default: 0, null: false
@ -277,7 +272,6 @@ ActiveRecord::Schema.define(version: 2020_04_19_185535) do
t.integer "crop_h_w"
t.integer "crop_h_h"
t.string "locale", default: "en"
t.boolean "translator", default: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"

View File

@ -34,15 +34,14 @@ class Exporter
private
def collect_user_info
%i(answered_count asked_count ban_reason banned_until bio blogger comment_smiled_count commented_count
confirmation_sent_at confirmed_at contributor created_at crop_h crop_h_h crop_h_w crop_h_x crop_h_y
%i(answered_count asked_count ban_reason banned_until bio comment_smiled_count commented_count
confirmation_sent_at confirmed_at created_at crop_h crop_h_h crop_h_w crop_h_x crop_h_y
crop_w crop_x crop_y current_sign_in_at current_sign_in_ip display_name email follower_count friend_count
id last_sign_in_at last_sign_in_ip locale location motivation_header permanently_banned
privacy_allow_anonymous_questions privacy_allow_public_timeline privacy_allow_stranger_answers
privacy_show_in_search profile_header_content_type profile_header_file_name profile_header_file_size
profile_header_updated_at profile_picture_content_type profile_picture_file_name profile_picture_file_size
profile_picture_updated_at screen_name show_foreign_themes sign_in_count smiled_count supporter translator
updated_at website).each do |f|
profile_picture_updated_at screen_name show_foreign_themes sign_in_count smiled_count updated_at website).each do |f|
@obj[f] = @user.send f
end
@ -229,9 +228,8 @@ class Exporter
def user_stub(user)
uobj = {}
%i(answered_count asked_count bio blogger comment_smiled_count commented_count contributor created_at
display_name follower_count friend_count id location motivation_header permanently_banned screen_name
smiled_count supporter translator website).each do |f|
%i(answered_count asked_count bio comment_smiled_count commented_count created_at display_name follower_count
friend_count id location motivation_header permanently_banned screen_name smiled_count website).each do |f|
uobj[f] = user.send f
end

View File

@ -48,7 +48,7 @@
</head>
<body>
<canvas id="canvas"></canvas>
<h1>404. It dun goofed. <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="http://github.com/Retrospring/bugs">Is it a bug?</a> · <a href="http://retrospring.net">Back to Retrospring</a></span>
<h1>404. It dun goofed. <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="https://github.com/Retrospring/retrospring/issues">Is it a bug?</a> · <a href="https://retrospring.net">Back to Retrospring</a></span>
<script type="text/javascript">
function paintCanvas(){ctx.fillStyle="#5E35B1",ctx.fillRect(0,0,W,H)}function Paddle(a){this.h=5,this.w=150,this.x=W/2-this.w/2,this.y="top"==a?0:H-this.h}function createParticles(a,b,c){this.x=a||0,this.y=b||0,this.radius=1.2,this.vx=-1.5+3*Math.random(),this.vy=c*Math.random()*1.5}function draw(){paintCanvas();for(var a=0;a<paddles.length;a++)p=paddles[a],ctx.fillStyle="white",ctx.fillRect(p.x,p.y,p.w,p.h);ball.draw(),update()}function increaseSpd(){points%4==0&&Math.abs(ball.vx)<15&&(ball.vx+=ball.vx<0?-1:1,ball.vy+=ball.vy<0?-2:2)}function trackPosition(a){mouse.x=a.pageX,mouse.y=a.pageY}function update(){if(updateScore(),mouse.x&&mouse.y)for(var a=1;a<paddles.length;a++)p=paddles[a],p.x=mouse.x-p.w/2;if(ball.x+=ball.vx,ball.y+=ball.vy,p1=paddles[1],p2=paddles[2],collides(ball,p1)?collideAction(ball,p1):collides(ball,p2)?collideAction(ball,p2):(ball.y+ball.r>H?(ball.y=H-ball.r,gameOver()):ball.y<0&&(ball.y=ball.r,gameOver()),ball.x+ball.r>W?(ball.vx=-ball.vx,ball.x=W-ball.r):ball.x-ball.r<0&&(ball.vx=-ball.vx,ball.x=ball.r)),1==flag)for(var b=0;b<particlesCount;b++)particles.push(new createParticles(particlePos.x,particlePos.y,multiplier));emitParticles(),flag=0}function collides(a,b){return a.x+ball.r>=b.x&&a.x-ball.r<=b.x+b.w?a.y>=b.y-b.h&&b.y>0?(paddleHit=1,!0):a.y<=b.h&&0==b.y?(paddleHit=2,!0):!1:void 0}function collideAction(a,b){a.vy=-a.vy,1==paddleHit?(a.y=b.y-b.h,particlePos.y=a.y+a.r,multiplier=-1):2==paddleHit&&(a.y=b.h+a.r,particlePos.y=a.y-a.r,multiplier=1),points++,increaseSpd(),collision&&(points>0&&collision.pause(),collision.currentTime=0,collision.play()),particlePos.x=a.x,flag=1}function emitParticles(){for(var a=0;a<particles.length;a++)par=particles[a],ctx.beginPath(),ctx.fillStyle="white",par.radius>0&&ctx.arc(par.x,par.y,par.radius,0,2*Math.PI,!1),ctx.fill(),par.x+=par.vx,par.y+=par.vy,par.radius=Math.max(par.radius-.05,0)}function updateScore(){ctx.fillStlye="white",ctx.font="16px Arial, sans-serif",ctx.textAlign="left",ctx.textBaseline="top",ctx.fillText("Score: "+points,20,20)}function gameOver(){ctx.fillStlye="white",ctx.font="20px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillText("Game Over - You scored "+points+" points!",W/2,H/2+25),cancelRequestAnimFrame(init),over=1,restartBtn.draw()}function animloop(){init=requestAnimFrame(animloop),draw()}function startScreen(){draw(),startBtn.draw()}function btnClick(a){{var b=a.pageX;a.pageY}b>=startBtn.x&&b<=startBtn.x+startBtn.w&&(animloop(),startBtn={}),1==over&&b>=restartBtn.x&&b<=restartBtn.x+restartBtn.w&&(ball.x=20,ball.y=20,points=0,ball.vx=4,ball.vy=8,animloop(),over=0)}window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){return window.setTimeout(a,1e3/60)}}(),window.cancelRequestAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout}();var canvas=document.getElementById("canvas"),ctx=canvas.getContext("2d"),W=window.innerWidth,H=window.innerHeight/1.09,particles=[],ball={},paddles=[2],mouse={},points=0,fps=60,particlesCount=20,flag=0,particlePos={},multipler=1,startBtn={},restartBtn={},over=0,init,paddleHit;canvas.addEventListener("mousemove",trackPosition,!0),canvas.addEventListener("mousedown",btnClick,!0),collision=document.getElementById("collide"),canvas.width=W,canvas.height=H,paddles.push(new Paddle("bottom")),paddles.push(new Paddle("top")),ball={x:50,y:50,r:5,c:"white",vx:4,vy:8,draw:function(){ctx.beginPath(),ctx.fillStyle=this.c,ctx.arc(this.x,this.y,this.r,0,2*Math.PI,!1),ctx.fill()}},startBtn={w:100,h:50,x:W/2-50,y:H/2-25,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Start",W/2,H/2)}},restartBtn={w:100,h:50,x:W/2-50,y:H/2-50,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Restart",W/2,H/2-25)}},startScreen();
</script>

View File

@ -48,7 +48,7 @@
</head>
<body>
<canvas id="canvas"></canvas>
<h1>422. The change you wanted was rejected. <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="http://github.com/Retrospring/bugs">Is it a bug?</a> · <a href="http://retrospring.net">Back to Retrospring</a></span>
<h1>422. The change you wanted was rejected. <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="https://github.com/Retrospring/retrospring/issues">Is it a bug?</a> · <a href="https://retrospring.net">Back to Retrospring</a></span>
<script type="text/javascript">
function paintCanvas(){ctx.fillStyle="#5E35B1",ctx.fillRect(0,0,W,H)}function Paddle(a){this.h=5,this.w=150,this.x=W/2-this.w/2,this.y="top"==a?0:H-this.h}function createParticles(a,b,c){this.x=a||0,this.y=b||0,this.radius=1.2,this.vx=-1.5+3*Math.random(),this.vy=c*Math.random()*1.5}function draw(){paintCanvas();for(var a=0;a<paddles.length;a++)p=paddles[a],ctx.fillStyle="white",ctx.fillRect(p.x,p.y,p.w,p.h);ball.draw(),update()}function increaseSpd(){points%4==0&&Math.abs(ball.vx)<15&&(ball.vx+=ball.vx<0?-1:1,ball.vy+=ball.vy<0?-2:2)}function trackPosition(a){mouse.x=a.pageX,mouse.y=a.pageY}function update(){if(updateScore(),mouse.x&&mouse.y)for(var a=1;a<paddles.length;a++)p=paddles[a],p.x=mouse.x-p.w/2;if(ball.x+=ball.vx,ball.y+=ball.vy,p1=paddles[1],p2=paddles[2],collides(ball,p1)?collideAction(ball,p1):collides(ball,p2)?collideAction(ball,p2):(ball.y+ball.r>H?(ball.y=H-ball.r,gameOver()):ball.y<0&&(ball.y=ball.r,gameOver()),ball.x+ball.r>W?(ball.vx=-ball.vx,ball.x=W-ball.r):ball.x-ball.r<0&&(ball.vx=-ball.vx,ball.x=ball.r)),1==flag)for(var b=0;b<particlesCount;b++)particles.push(new createParticles(particlePos.x,particlePos.y,multiplier));emitParticles(),flag=0}function collides(a,b){return a.x+ball.r>=b.x&&a.x-ball.r<=b.x+b.w?a.y>=b.y-b.h&&b.y>0?(paddleHit=1,!0):a.y<=b.h&&0==b.y?(paddleHit=2,!0):!1:void 0}function collideAction(a,b){a.vy=-a.vy,1==paddleHit?(a.y=b.y-b.h,particlePos.y=a.y+a.r,multiplier=-1):2==paddleHit&&(a.y=b.h+a.r,particlePos.y=a.y-a.r,multiplier=1),points++,increaseSpd(),collision&&(points>0&&collision.pause(),collision.currentTime=0,collision.play()),particlePos.x=a.x,flag=1}function emitParticles(){for(var a=0;a<particles.length;a++)par=particles[a],ctx.beginPath(),ctx.fillStyle="white",par.radius>0&&ctx.arc(par.x,par.y,par.radius,0,2*Math.PI,!1),ctx.fill(),par.x+=par.vx,par.y+=par.vy,par.radius=Math.max(par.radius-.05,0)}function updateScore(){ctx.fillStlye="white",ctx.font="16px Arial, sans-serif",ctx.textAlign="left",ctx.textBaseline="top",ctx.fillText("Score: "+points,20,20)}function gameOver(){ctx.fillStlye="white",ctx.font="20px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillText("Game Over - You scored "+points+" points!",W/2,H/2+25),cancelRequestAnimFrame(init),over=1,restartBtn.draw()}function animloop(){init=requestAnimFrame(animloop),draw()}function startScreen(){draw(),startBtn.draw()}function btnClick(a){{var b=a.pageX;a.pageY}b>=startBtn.x&&b<=startBtn.x+startBtn.w&&(animloop(),startBtn={}),1==over&&b>=restartBtn.x&&b<=restartBtn.x+restartBtn.w&&(ball.x=20,ball.y=20,points=0,ball.vx=4,ball.vy=8,animloop(),over=0)}window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){return window.setTimeout(a,1e3/60)}}(),window.cancelRequestAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout}();var canvas=document.getElementById("canvas"),ctx=canvas.getContext("2d"),W=window.innerWidth,H=window.innerHeight/1.09,particles=[],ball={},paddles=[2],mouse={},points=0,fps=60,particlesCount=20,flag=0,particlePos={},multipler=1,startBtn={},restartBtn={},over=0,init,paddleHit;canvas.addEventListener("mousemove",trackPosition,!0),canvas.addEventListener("mousedown",btnClick,!0),collision=document.getElementById("collide"),canvas.width=W,canvas.height=H,paddles.push(new Paddle("bottom")),paddles.push(new Paddle("top")),ball={x:50,y:50,r:5,c:"white",vx:4,vy:8,draw:function(){ctx.beginPath(),ctx.fillStyle=this.c,ctx.arc(this.x,this.y,this.r,0,2*Math.PI,!1),ctx.fill()}},startBtn={w:100,h:50,x:W/2-50,y:H/2-25,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Start",W/2,H/2)}},restartBtn={w:100,h:50,x:W/2-50,y:H/2-50,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Restart",W/2,H/2-25)}},startScreen();
</script>

View File

@ -52,9 +52,9 @@
</head>
<body>
<canvas id="canvas"></canvas>
<h1>500. Something went wrong :( <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="http://github.com/Retrospring/bugs">Is it a bug?</a> · <a href="http://retrospring.net">Back to Retrospring</a></span>
<h1>500. Something went wrong :( <small>But play some Pong while you're at it.</small></h1><span id="links"><a href="https://github.com/Retrospring/retrospring/issues">Is it a bug?</a> · <a href="https://retrospring.net">Back to Retrospring</a></span>
<script type="text/javascript">
function paintCanvas(){ctx.fillStyle="#5E35B1",ctx.fillRect(0,0,W,H)}function Paddle(a){this.h=5,this.w=150,this.x=W/2-this.w/2,this.y="top"==a?0:H-this.h}function createParticles(a,b,c){this.x=a||0,this.y=b||0,this.radius=1.2,this.vx=-1.5+3*Math.random(),this.vy=c*Math.random()*1.5}function draw(){paintCanvas();for(var a=0;a<paddles.length;a++)p=paddles[a],ctx.fillStyle="white",ctx.fillRect(p.x,p.y,p.w,p.h);ball.draw(),update()}function increaseSpd(){points%4==0&&Math.abs(ball.vx)<15&&(ball.vx+=ball.vx<0?-1:1,ball.vy+=ball.vy<0?-2:2)}function trackPosition(a){mouse.x=a.pageX,mouse.y=a.pageY}function update(){if(updateScore(),mouse.x&&mouse.y)for(var a=1;a<paddles.length;a++)p=paddles[a],p.x=mouse.x-p.w/2;if(ball.x+=ball.vx,ball.y+=ball.vy,p1=paddles[1],p2=paddles[2],collides(ball,p1)?collideAction(ball,p1):collides(ball,p2)?collideAction(ball,p2):(ball.y+ball.r>H?(ball.y=H-ball.r,gameOver()):ball.y<0&&(ball.y=ball.r,gameOver()),ball.x+ball.r>W?(ball.vx=-ball.vx,ball.x=W-ball.r):ball.x-ball.r<0&&(ball.vx=-ball.vx,ball.x=ball.r)),1==flag)for(var b=0;b<particlesCount;b++)particles.push(new createParticles(particlePos.x,particlePos.y,multiplier));emitParticles(),flag=0}function collides(a,b){return a.x+ball.r>=b.x&&a.x-ball.r<=b.x+b.w?a.y>=b.y-b.h&&b.y>0?(paddleHit=1,!0):a.y<=b.h&&0==b.y?(paddleHit=2,!0):!1:void 0}function collideAction(a,b){a.vy=-a.vy,1==paddleHit?(a.y=b.y-b.h,particlePos.y=a.y+a.r,multiplier=-1):2==paddleHit&&(a.y=b.h+a.r,particlePos.y=a.y-a.r,multiplier=1),points++,increaseSpd(),collision&&(points>0&&collision.pause(),collision.currentTime=0,collision.play()),particlePos.x=a.x,flag=1}function emitParticles(){for(var a=0;a<particles.length;a++)par=particles[a],ctx.beginPath(),ctx.fillStyle="white",par.radius>0&&ctx.arc(par.x,par.y,par.radius,0,2*Math.PI,!1),ctx.fill(),par.x+=par.vx,par.y+=par.vy,par.radius=Math.max(par.radius-.05,0)}function updateScore(){ctx.fillStlye="white",ctx.font="16px Arial, sans-serif",ctx.textAlign="left",ctx.textBaseline="top",ctx.fillText("Score: "+points,20,20)}function gameOver(){ctx.fillStlye="white",ctx.font="20px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillText("Game Over - You scored "+points+" points!",W/2,H/2+25),cancelRequestAnimFrame(init),over=1,restartBtn.draw()}function animloop(){init=requestAnimFrame(animloop),draw()}function startScreen(){draw(),startBtn.draw()}function btnClick(a){{var b=a.pageX;a.pageY}b>=startBtn.x&&b<=startBtn.x+startBtn.w&&(animloop(),startBtn={}),1==over&&b>=restartBtn.x&&b<=restartBtn.x+restartBtn.w&&(ball.x=20,ball.y=20,points=0,ball.vx=4,ball.vy=8,animloop(),over=0)}window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){return window.setTimeout(a,1e3/60)}}(),window.cancelRequestAnimFrame=function(){return window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout}();var canvas=document.getElementById("canvas"),ctx=canvas.getContext("2d"),W=window.innerWidth,H=window.innerHeight/1.09,particles=[],ball={},paddles=[2],mouse={},points=0,fps=60,particlesCount=20,flag=0,particlePos={},multipler=1,startBtn={},restartBtn={},over=0,init,paddleHit;canvas.addEventListener("mousemove",trackPosition,!0),canvas.addEventListener("mousedown",btnClick,!0),collision=document.getElementById("collide"),canvas.width=W,canvas.height=H,paddles.push(new Paddle("bottom")),paddles.push(new Paddle("top")),ball={x:50,y:50,r:5,c:"white",vx:4,vy:8,draw:function(){ctx.beginPath(),ctx.fillStyle=this.c,ctx.arc(this.x,this.y,this.r,0,2*Math.PI,!1),ctx.fill()}},startBtn={w:100,h:50,x:W/2-50,y:H/2-25,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Start",W/2,H/2)}},restartBtn={w:100,h:50,x:W/2-50,y:H/2-50,draw:function(){ctx.strokeStyle="white",ctx.lineWidth="2",ctx.strokeRect(this.x,this.y,this.w,this.h),ctx.font="18px Arial, sans-serif",ctx.textAlign="center",ctx.textBaseline="middle",ctx.fillStlye="white",ctx.fillText("Restart",W/2,H/2-25)}},startScreen();
</script>
</body>
</html>
</html>

View File

@ -79,7 +79,7 @@
<p>Sorry about that. Please try refreshing and contact us if the problem persists.</p>
</div>
<div class="links">
<a href="https://github.com/retrospring/bugs/issues">Bug tracker</a> &middot;
<a href="https://github.com/Retrospring/retrospring/issues">Bug tracker</a> &middot;
<a href="https://twitter.com/retrospring">Twitter</a>
</div>
</body>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 566 KiB

After

Width:  |  Height:  |  Size: 546 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 145 B

View File

@ -1,9 +1,9 @@
/* TEAM */
Georg G. (nilsding [at] nilsding [dot] org)
Site: http://nilsding.org
Location: Austria
Site: https://nilsding.org
Location: Linz, Austria
Andreas N. (pixeldesu [at] outlook [dot] com)
Site: http://pixelde.su
Site: https://pixelde.su
Location: Germany

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -7,7 +7,7 @@ No, Retrospring will always remain free. You can however, if you want to support
## 3. Do you do anything with our data?
No. We don't sell it to any third parties. Your data doesn't leave our servers, because we need it if any user does something illegal we have to provide the data or at least check it up and validate the cases ourselves. But anything other than that is not going to happen, such as us selling your precious data to big bad evil companies.
## 4. Can I contribute to the random question pool?
You are more than welcome too, over on [Github](https://github.com/retrospring/questiongenerator)!
You are more than welcome too, over on [Github](https://github.com/Retrospring/questiongenerator)!
## 5. What are **motivation headers**?
The motivation header is the short text shown in the top of the questionbox on your profile, motivating another user to ask you a question. You can write anything you want, but the best way using it would be including something like "Ask me anything!" or if you want questions about a special thing you did "Ask me anything about my project X!" so people know what they should ask you.
## 6. Will Retrospring be available in different languages?
@ -30,4 +30,4 @@ Quotes? Here you go!:
Don't like the formatting? You can escape it with prepending a `\` before the `*`, `_` or `>`
## 8. Is this open-source software?
Yep! Retrospring runs [justask](https://github.com/nilsding/justask) an open source (AGPLv3) question and answer platform. If you want to contribute or report bugs you find in Retrospring, please look at our [Github repository!](https://github.com/retrospring/retrospring)!
Yep! Retrospring runs [justask](https://github.com/nilsding/justask) an open source (AGPLv3) question and answer platform. If you want to contribute or report bugs you find in Retrospring, please look at our [Github repository!](https://github.com/Retrospring/retrospring)!