229 lines
5.9 KiB
Ruby
229 lines
5.9 KiB
Ruby
|
|
def dashboard_if_signed_in
|
||
|
|
redirect '/dashboard' if signed_in?
|
||
|
|
end
|
||
|
|
|
||
|
|
def csrf_safe?
|
||
|
|
csrf_token == params[:csrf_token] || csrf_token == request.env['HTTP_X_CSRF_TOKEN']
|
||
|
|
end
|
||
|
|
|
||
|
|
def csrf_token
|
||
|
|
session[:_csrf_token] ||= SecureRandom.base64(32)
|
||
|
|
end
|
||
|
|
|
||
|
|
def is_education?
|
||
|
|
current_site && current_site.is_education
|
||
|
|
end
|
||
|
|
|
||
|
|
def require_login
|
||
|
|
redirect '/' unless signed_in? && current_site
|
||
|
|
end
|
||
|
|
|
||
|
|
def signed_in?
|
||
|
|
return false if current_site.nil?
|
||
|
|
true
|
||
|
|
end
|
||
|
|
|
||
|
|
def signout
|
||
|
|
@_site = nil
|
||
|
|
@_parent_site = nil
|
||
|
|
session[:id] = nil
|
||
|
|
session.clear
|
||
|
|
response.delete_cookie 'neocities', path: '/'
|
||
|
|
request.env['rack.session.options'][:drop] = true
|
||
|
|
end
|
||
|
|
|
||
|
|
def current_site
|
||
|
|
return nil if session[:id].nil?
|
||
|
|
@_site ||= Site[id: session[:id]]
|
||
|
|
@_parent_site ||= @_site.parent
|
||
|
|
|
||
|
|
if @_site.is_banned || @_site.is_deleted || (@_parent_site && (@_parent_site.is_banned || @_parent_site.is_deleted))
|
||
|
|
signout
|
||
|
|
end
|
||
|
|
|
||
|
|
@_site
|
||
|
|
end
|
||
|
|
|
||
|
|
def parent_site
|
||
|
|
@_parent_site || current_site
|
||
|
|
end
|
||
|
|
|
||
|
|
def meta_robots(newtag=nil)
|
||
|
|
if newtag
|
||
|
|
@_meta_robots = newtag
|
||
|
|
end
|
||
|
|
|
||
|
|
@_meta_robots
|
||
|
|
end
|
||
|
|
|
||
|
|
def meta_description(new_description=nil)
|
||
|
|
if new_description
|
||
|
|
@_meta_description = new_description
|
||
|
|
end
|
||
|
|
|
||
|
|
description = @_meta_description || @description
|
||
|
|
return nil if description.nil? || description.strip.empty?
|
||
|
|
|
||
|
|
description
|
||
|
|
end
|
||
|
|
|
||
|
|
def title
|
||
|
|
out = "Neocities"
|
||
|
|
return out if request.path == '/'
|
||
|
|
|
||
|
|
full_title = if @title
|
||
|
|
"#{out} - #{@title}"
|
||
|
|
else
|
||
|
|
path_parts = request.path.split('/').reject(&:empty?)
|
||
|
|
formatted_parts = path_parts.map do |part|
|
||
|
|
part.split(/[_\-]/).map(&:capitalize).join(' ')
|
||
|
|
end
|
||
|
|
"#{out} - #{formatted_parts.join(' - ')}"
|
||
|
|
end
|
||
|
|
|
||
|
|
if full_title.length >= 70
|
||
|
|
full_title[0..65] + "..."
|
||
|
|
else
|
||
|
|
full_title
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def encoding_fix(file)
|
||
|
|
begin
|
||
|
|
Rack::Utils.escape_html file
|
||
|
|
rescue ArgumentError => e
|
||
|
|
if e.message =~ /invalid byte sequence in UTF-8/ ||
|
||
|
|
e.message =~ /incompatible character encodings/
|
||
|
|
return Rack::Utils.escape_html(file.force_encoding('BINARY'))
|
||
|
|
end
|
||
|
|
fail
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def send_confirmation_email(site=current_site)
|
||
|
|
# Child sites don't need email confirmation - they use parent site's email
|
||
|
|
return if !site.parent?
|
||
|
|
|
||
|
|
if site.email_confirmation_count > Site::MAXIMUM_EMAIL_CONFIRMATIONS
|
||
|
|
flash[:error] = 'You sent too many email confirmation requests, cannot continue.'
|
||
|
|
redirect request.referrer
|
||
|
|
end
|
||
|
|
|
||
|
|
DB['UPDATE sites set email_confirmation_count=email_confirmation_count+1 WHERE id=?', site.id].first
|
||
|
|
|
||
|
|
EmailWorker.perform_async({
|
||
|
|
from: 'web@neocities.org',
|
||
|
|
reply_to: 'noreply@neocities.org',
|
||
|
|
to: site.email,
|
||
|
|
subject: "[Neocities] Confirm your email address",
|
||
|
|
body: Tilt.new('./views/templates/email/confirm.erb', pretty: true).render(self, site: site)
|
||
|
|
})
|
||
|
|
end
|
||
|
|
|
||
|
|
def dont_browser_cache
|
||
|
|
headers['Cache-Control'] = 'private, no-store, max-age=0, no-cache, must-revalidate, post-check=0, pre-check=0'
|
||
|
|
headers['Pragma'] = 'no-cache'
|
||
|
|
headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
|
||
|
|
@dont_browser_cache = true
|
||
|
|
end
|
||
|
|
|
||
|
|
def sanitize_comment(text)
|
||
|
|
Rinku.auto_link Sanitize.fragment(text), :all, 'target="_blank" rel="nofollow"'
|
||
|
|
end
|
||
|
|
|
||
|
|
def flash_message_keys
|
||
|
|
flash.keys.select {|k| [:success, :error, :errors].include?(k) && !flash[k].to_s.empty?}
|
||
|
|
end
|
||
|
|
|
||
|
|
def flash_message_entries
|
||
|
|
entries = []
|
||
|
|
|
||
|
|
Array(@error).each do |message|
|
||
|
|
entries << {type: 'error', message: message} unless message.to_s.empty?
|
||
|
|
end
|
||
|
|
|
||
|
|
flash_message_keys.each do |key|
|
||
|
|
type = key == :success ? 'success' : 'error'
|
||
|
|
|
||
|
|
Array(flash[key]).each do |message|
|
||
|
|
entries << {type: type, message: message} unless message.to_s.empty?
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
entries
|
||
|
|
end
|
||
|
|
|
||
|
|
def normalize_comment_message(message)
|
||
|
|
message.to_s.gsub(/\r\n?/, "\n").gsub(/\n{3,}/, "\n\n").strip
|
||
|
|
end
|
||
|
|
|
||
|
|
def comment_message_error(message)
|
||
|
|
return 'Comment cannot be empty.' if message.empty?
|
||
|
|
return "Comments must be #{Site::MAX_COMMENT_SIZE} characters or fewer." if message.length > Site::MAX_COMMENT_SIZE
|
||
|
|
|
||
|
|
lines = message.split("\n")
|
||
|
|
return "Comments must be #{Site::MAX_COMMENT_LINES} lines or fewer." if lines.length > Site::MAX_COMMENT_LINES
|
||
|
|
|
||
|
|
return nil if lines.length < Site::MULTILINE_COMMENT_AVERAGE_LINE_THRESHOLD
|
||
|
|
|
||
|
|
nonblank_lines = lines.map(&:strip).reject(&:empty?)
|
||
|
|
return 'Comment cannot be empty.' if nonblank_lines.empty?
|
||
|
|
|
||
|
|
average_line_length = nonblank_lines.sum(&:length).to_f / nonblank_lines.length
|
||
|
|
return nil if average_line_length >= Site::MIN_MULTILINE_COMMENT_AVERAGE_LINE_LENGTH
|
||
|
|
|
||
|
|
'Multiline comments need a little more text on each line.'
|
||
|
|
end
|
||
|
|
|
||
|
|
def valid_comment_message?(message)
|
||
|
|
comment_message_error(message).nil?
|
||
|
|
end
|
||
|
|
|
||
|
|
def comment_unavailable_message(site=nil)
|
||
|
|
return 'Comments are disabled for this site.' if site && site.profile_comments_enabled == false
|
||
|
|
|
||
|
|
if current_site && !current_site.commenting_allowed?
|
||
|
|
if current_site.commenting_too_much?
|
||
|
|
return "To prevent spam, comments are limited to #{Site::MAX_COMMENTS_PER_DAY} per day. Please try again tomorrow."
|
||
|
|
end
|
||
|
|
|
||
|
|
return "To prevent spam, you cannot comment until you have updated your site #{Site::COMMENTING_ALLOWED_UPDATED_COUNT} times on separate days, and your account is one week old."
|
||
|
|
end
|
||
|
|
|
||
|
|
'You cannot comment on this right now.'
|
||
|
|
end
|
||
|
|
|
||
|
|
def flash_display(opts={})
|
||
|
|
erb :'_flash', layout: false, locals: {opts: opts}
|
||
|
|
end
|
||
|
|
|
||
|
|
def hcaptcha_valid?
|
||
|
|
return true if ENV['RACK_ENV'] == 'test' || ENV['CI']
|
||
|
|
return false unless params[:'h-captcha-response']
|
||
|
|
|
||
|
|
resp = HTTP.get('https://hcaptcha.com/siteverify', params: {
|
||
|
|
secret: $config['hcaptcha_secret_key'],
|
||
|
|
response: params[:'h-captcha-response']
|
||
|
|
})
|
||
|
|
|
||
|
|
resp = JSON.parse resp
|
||
|
|
|
||
|
|
if resp['success'] == true
|
||
|
|
true
|
||
|
|
else
|
||
|
|
false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
JS_ESCAPE_MAP = {"\\" => "\\\\", "</" => '<\/', "\r\n" => '\n', "\n" => '\n', "\r" => '\n', '"' => '\\"', "'" => "\\'", "`" => "\\`", "$" => "\\$"}
|
||
|
|
|
||
|
|
def escape_javascript(javascript)
|
||
|
|
javascript = javascript.to_s
|
||
|
|
if javascript.empty?
|
||
|
|
result = ""
|
||
|
|
else
|
||
|
|
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
|
||
|
|
end
|
||
|
|
result
|
||
|
|
end
|