mirror of
https://github.com/neocities/neocities.git
synced 2025-04-24 17:22:35 +02:00
merge parentsites with v2
This commit is contained in:
commit
6995409c50
30 changed files with 820 additions and 551 deletions
520
app.rb
520
app.rb
|
@ -125,6 +125,23 @@ post '/site/:username/set_editor_theme' do
|
|||
'ok'
|
||||
end
|
||||
|
||||
post '/settings/create_child' do
|
||||
require_login
|
||||
site = Site.new
|
||||
|
||||
site.parent_site_id = parent_site.id
|
||||
site.username = params[:username]
|
||||
|
||||
if site.valid?
|
||||
site.save
|
||||
flash[:success] = 'Your new site has been created!'
|
||||
redirect '/settings#sites'
|
||||
else
|
||||
flash[:error] = site.errors.first.last.first
|
||||
redirect '/settings#sites'
|
||||
end
|
||||
end
|
||||
|
||||
post '/site/:username/comment' do |username|
|
||||
require_login
|
||||
|
||||
|
@ -254,68 +271,74 @@ post '/plan/create' do
|
|||
DB.transaction do
|
||||
customer = Stripe::Customer.create(
|
||||
card: params[:stripe_token],
|
||||
description: current_site.username,
|
||||
description: "#{parent_site.username} - #{parent_site.id}",
|
||||
email: current_site.email,
|
||||
plan: params[:selected_plan]
|
||||
)
|
||||
|
||||
current_site.update stripe_customer_id: customer.id, plan_ended: false
|
||||
parent_site.update stripe_customer_id: customer.id, plan_ended: false
|
||||
|
||||
plan_name = customer.subscriptions.first['plan']['name']
|
||||
|
||||
EmailWorker.perform_async({
|
||||
from: 'web@neocities.org',
|
||||
reply_to: 'contact@neocities.org',
|
||||
to: current_site.email,
|
||||
subject: "[Neocities] You've become a supporter!",
|
||||
body: Tilt.new('./views/templates/email_subscription.erb', pretty: true).render(self, plan_name: plan_name)
|
||||
})
|
||||
if current_site.email || parent_site.email
|
||||
EmailWorker.perform_async({
|
||||
from: 'web@neocities.org',
|
||||
reply_to: 'contact@neocities.org',
|
||||
to: current_site.email || parent_site.email,
|
||||
subject: "[Neocities] You've become a supporter!",
|
||||
body: Tilt.new('./views/templates/email_subscription.erb', pretty: true).render(self, plan_name: plan_name)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
redirect '/plan'
|
||||
end
|
||||
|
||||
def get_plan_name(customer_id)
|
||||
subscriptions = Stripe::Customer.retrieve(current_site.stripe_customer_id).subscriptions.all
|
||||
subscriptions = Stripe::Customer.retrieve(parent_site.stripe_customer_id).subscriptions.all
|
||||
@plan_name = subscriptions.first.plan.name
|
||||
end
|
||||
|
||||
def require_active_subscription
|
||||
redirect '/plan' unless parent_site.supporter? && !parent_site.plan_ended
|
||||
end
|
||||
|
||||
get '/plan/manage' do
|
||||
require_login
|
||||
redirect '/plan' unless current_site.supporter? && !current_site.plan_ended
|
||||
require_active_subscription
|
||||
@title = 'Manage Plan'
|
||||
@plan_name = get_plan_name current_site.stripe_customer_id
|
||||
@plan_name = get_plan_name parent_site.stripe_customer_id
|
||||
erb :'plan/manage'
|
||||
end
|
||||
|
||||
get '/plan/end' do
|
||||
require_login
|
||||
redirect '/plan' unless current_site.supporter? && !current_site.plan_ended
|
||||
require_active_subscription
|
||||
@title = 'End Plan'
|
||||
@plan_name = get_plan_name current_site.stripe_customer_id
|
||||
@plan_name = get_plan_name parent_site.stripe_customer_id
|
||||
erb :'plan/end'
|
||||
end
|
||||
|
||||
post '/plan/end' do
|
||||
require_login
|
||||
redirect '/plan' unless current_site.supporter? && !current_site.plan_ended
|
||||
require_active_subscription
|
||||
|
||||
recaptcha_is_valid = ENV['RACK_ENV'] == 'test' || recaptcha_valid?
|
||||
|
||||
if !recaptcha_is_valid
|
||||
@error = 'Recaptcha was filled out incorrectly, please try re-entering.'
|
||||
@plan_name = get_plan_name current_site.stripe_customer_id
|
||||
@plan_name = get_plan_name parent_site.stripe_customer_id
|
||||
halt erb :'plan/end'
|
||||
end
|
||||
|
||||
customer = Stripe::Customer.retrieve current_site.stripe_customer_id
|
||||
customer = Stripe::Customer.retrieve parent_site.stripe_customer_id
|
||||
subscriptions = customer.subscriptions.all
|
||||
|
||||
DB.transaction do
|
||||
subscriptions.each do |subscription|
|
||||
customer.subscriptions.retrieve(subscription.id).delete
|
||||
end
|
||||
current_site.update plan_ended: true
|
||||
parent_site.update plan_ended: true
|
||||
end
|
||||
|
||||
redirect '/plan'
|
||||
|
@ -531,43 +554,59 @@ get '/dashboard' do
|
|||
erb :'dashboard'
|
||||
end
|
||||
|
||||
get '/signin' do
|
||||
dashboard_if_signed_in
|
||||
erb :'signin'
|
||||
get '/settings/?' do
|
||||
require_login
|
||||
@site = parent_site
|
||||
erb :'settings/account'
|
||||
end
|
||||
|
||||
get '/settings' do
|
||||
require_login
|
||||
erb :'settings'
|
||||
def require_ownership_for_settings
|
||||
@site = Site[username: params[:username]]
|
||||
|
||||
not_found if @site.nil?
|
||||
|
||||
unless @site.owned_by? parent_site
|
||||
flash[:error] = 'Cannot edit this site, you do not have permission.'
|
||||
redirect request.referrer
|
||||
end
|
||||
end
|
||||
|
||||
post '/settings/profile' do
|
||||
get '/settings/:username/?' do
|
||||
require_login
|
||||
current_site.update(
|
||||
require_ownership_for_settings
|
||||
erb :'settings/site'
|
||||
end
|
||||
|
||||
post '/settings/:username/profile' do
|
||||
require_login
|
||||
require_ownership_for_settings
|
||||
|
||||
@site.update(
|
||||
profile_comments_enabled: params[:site][:profile_comments_enabled]
|
||||
)
|
||||
flash[:success] = 'Profile settings changed.'
|
||||
redirect '/settings'
|
||||
redirect "/settings/#{@site.username}#profile"
|
||||
end
|
||||
|
||||
post '/settings/ssl' do
|
||||
post '/settings/:username/ssl' do
|
||||
require_login
|
||||
require_ownership_for_settings
|
||||
|
||||
unless params[:key] && params[:cert]
|
||||
flash[:error] = 'SSL key and certificate are required.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
begin
|
||||
key = OpenSSL::PKey::RSA.new params[:key][:tempfile].read, ''
|
||||
rescue => e
|
||||
flash[:error] = 'Could not process SSL key, file may be incorrect, damaged, or passworded (you need to remove the password).'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
if !key.private?
|
||||
flash[:error] = 'SSL Key file does not have private key data.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
certs_string = params[:cert][:tempfile].read
|
||||
|
@ -576,7 +615,7 @@ post '/settings/ssl' do
|
|||
|
||||
if cert_array.empty?
|
||||
flash[:error] = 'Cert file does not contain any certificates.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
cert_valid_for_domain = false
|
||||
|
@ -586,21 +625,21 @@ post '/settings/ssl' do
|
|||
cert = OpenSSL::X509::Certificate.new cert_string
|
||||
rescue => e
|
||||
flash[:error] = 'Could not process SSL certificate, file may be incorrect or damaged.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
if cert.not_after < Time.now
|
||||
flash[:error] = 'SSL Certificate has expired, please create a new one.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
cert_cn = cert.subject.to_a.select {|a| a.first == 'CN'}.flatten[1]
|
||||
cert_valid_for_domain = true if cert_cn && cert_cn.match(current_site.domain)
|
||||
cert_valid_for_domain = true if cert_cn && cert_cn.match(@site.domain)
|
||||
end
|
||||
|
||||
unless cert_valid_for_domain
|
||||
flash[:error] = "Your certificate CN (common name) does not match your domain: #{current_site.domain}"
|
||||
redirect '/custom_domain'
|
||||
flash[:error] = "Your certificate CN (common name) does not match your domain: #{@site.domain}"
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
# Everything else was worse.
|
||||
|
@ -623,7 +662,7 @@ post '/settings/ssl' do
|
|||
access_log off;
|
||||
server {
|
||||
listen 60000 ssl;
|
||||
server_name #{current_site.domain} *.#{current_site.domain};
|
||||
server_name #{@site.domain} *.#{@site.domain};
|
||||
ssl_certificate #{crtfile.path};
|
||||
ssl_certificate_key #{keyfile.path};
|
||||
}
|
||||
|
@ -641,21 +680,209 @@ post '/settings/ssl' do
|
|||
output = line.run path: nginx_testfile.path
|
||||
rescue Cocaine::ExitStatusError => e
|
||||
flash[:error] = "There is something wrong with your certificate, please check with your issuing CA."
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
end
|
||||
|
||||
current_site.update ssl_key: key.to_pem, ssl_cert: cert_array.join
|
||||
@site.update ssl_key: key.to_pem, ssl_cert: cert_array.join
|
||||
|
||||
flash[:success] = 'Updated SSL key/certificate.'
|
||||
redirect '/custom_domain'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
|
||||
post '/settings/:username/change_name' do
|
||||
require_login
|
||||
require_ownership_for_settings
|
||||
|
||||
old_username = @site.username
|
||||
|
||||
if params[:name] == nil || params[:name] == ''
|
||||
flash[:error] = 'Name cannot be blank.'
|
||||
redirect "/settings/#{@site.username}#username"
|
||||
end
|
||||
|
||||
if old_username == params[:name]
|
||||
flash[:error] = 'You already have this name.'
|
||||
redirect "/settings/#{@site.username}#username"
|
||||
end
|
||||
|
||||
old_host = @site.host
|
||||
old_file_paths = @site.file_list.collect {|f| f[:path]}
|
||||
|
||||
@site.username = params[:name]
|
||||
|
||||
if @site.valid?
|
||||
DB.transaction {
|
||||
@site.save_changes
|
||||
@site.move_files_from old_username
|
||||
}
|
||||
|
||||
old_file_paths.each do |file_path|
|
||||
@site.purge_cache file_path
|
||||
end
|
||||
|
||||
flash[:success] = "Site/user name has been changed. You will need to use this name to login, <b>don't forget it</b>."
|
||||
redirect "/settings/#{@site.username}#username"
|
||||
else
|
||||
flash[:error] = @site.errors.first.last.first
|
||||
redirect "/settings/#{old_username}#username"
|
||||
end
|
||||
end
|
||||
|
||||
post '/settings/:username/change_nsfw' do
|
||||
require_login
|
||||
require_ownership_for_settings
|
||||
|
||||
@site.update is_nsfw: params[:is_nsfw]
|
||||
flash[:success] = @site.is_nsfw ? 'Marked 18+' : 'Unmarked 18+'
|
||||
redirect "/settings/#{@site.username}#nsfw"
|
||||
end
|
||||
|
||||
post '/settings/:username/custom_domain' do
|
||||
require_login
|
||||
require_ownership_for_settings
|
||||
|
||||
@site.domain = params[:domain]
|
||||
|
||||
if @site.valid?
|
||||
@site.save_changes
|
||||
flash[:success] = 'The domain has been successfully updated.'
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
else
|
||||
flash[:error] = @site.errors.first.last.first
|
||||
redirect "/settings/#{@site.username}#custom_domain"
|
||||
end
|
||||
end
|
||||
|
||||
post '/settings/change_password' do
|
||||
require_login
|
||||
|
||||
if !Site.valid_login?(parent_site.username, params[:current_password])
|
||||
flash[:error] = 'Your provided password does not match the current one.'
|
||||
redirect "/settings#password"
|
||||
end
|
||||
|
||||
parent_site.password = params[:new_password]
|
||||
parent_site.valid?
|
||||
|
||||
if params[:new_password] != params[:new_password_confirm]
|
||||
parent_site.errors.add :password, 'New passwords do not match.'
|
||||
end
|
||||
|
||||
if parent_site.errors.empty?
|
||||
parent_site.save_changes
|
||||
flash[:success] = 'Successfully changed password.'
|
||||
redirect "/settings#password"
|
||||
else
|
||||
flash[:error] = current_site.errors.first.last.first
|
||||
redirect '/settings#password'
|
||||
end
|
||||
end
|
||||
|
||||
post '/settings/change_email' do
|
||||
require_login
|
||||
|
||||
if params[:email] == parent_site.email
|
||||
flash[:error] = 'You are already using this email address for this account.'
|
||||
redirect '/settings#email'
|
||||
end
|
||||
|
||||
parent_site.email = params[:email]
|
||||
parent_site.email_confirmation_token = SecureRandom.hex 3
|
||||
parent_site.email_confirmed = false
|
||||
|
||||
if parent_site.valid?
|
||||
parent_site.save_changes
|
||||
send_confirmation_email
|
||||
flash[:success] = 'Successfully changed email. We have sent a confirmation email, please use it to confirm your email address.'
|
||||
redirect '/settings#email'
|
||||
end
|
||||
|
||||
flash[:error] = parent_site.errors.first.last.first
|
||||
redirect '/settings#email'
|
||||
end
|
||||
|
||||
get '/password_reset' do
|
||||
erb :'password_reset'
|
||||
end
|
||||
|
||||
post '/send_password_reset' do
|
||||
sites = Site.filter(email: params[:email]).all
|
||||
|
||||
if sites.length > 0
|
||||
token = SecureRandom.uuid.gsub('-', '')
|
||||
sites.each do |site|
|
||||
site.update password_reset_token: token
|
||||
end
|
||||
|
||||
body = <<-EOT
|
||||
Hello! This is the Neocities cat, and I have received a password reset request for your e-mail address. Purrrr.
|
||||
|
||||
Go to this URL to reset your password: http://neocities.org/password_reset_confirm?token=#{token}
|
||||
|
||||
After clicking on this link, your password for all the sites registered to this email address will be changed to this token.
|
||||
|
||||
Token: #{token}
|
||||
|
||||
If you didn't request this reset, you can ignore it. Or hide under a bed. Or take a nap. Your call.
|
||||
|
||||
Meow,
|
||||
the Neocities Cat
|
||||
EOT
|
||||
|
||||
body.strip!
|
||||
|
||||
EmailWorker.perform_async({
|
||||
from: 'web@neocities.org',
|
||||
to: params[:email],
|
||||
subject: '[Neocities] Password Reset',
|
||||
body: body
|
||||
})
|
||||
end
|
||||
|
||||
flash[:success] = 'If your email was valid (and used by a site), the Neocities Cat will send an e-mail to your account with password reset instructions.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
get '/password_reset_confirm' do
|
||||
if params[:token].nil? || params[:token].empty?
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
reset_site = Site[password_reset_token: params[:token]]
|
||||
|
||||
if reset_site.nil?
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
sites = Site.filter(email: reset_site.email).all
|
||||
|
||||
if sites.length > 0
|
||||
sites.each do |site|
|
||||
site.password = reset_site.password_reset_token
|
||||
site.save_changes
|
||||
end
|
||||
|
||||
flash[:success] = 'Your password for all sites with your email address has been changed to the token sent in your e-mail. Please login and change your password as soon as possible.'
|
||||
else
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
end
|
||||
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
get '/signin/?' do
|
||||
dashboard_if_signed_in
|
||||
erb :'signin'
|
||||
end
|
||||
|
||||
post '/signin' do
|
||||
dashboard_if_signed_in
|
||||
|
||||
if Site.valid_login? params[:username], params[:password]
|
||||
site = Site[username: params[:username]]
|
||||
site = Site.get_with_identifier params[:username]
|
||||
|
||||
if site.is_banned
|
||||
flash[:error] = 'Invalid login.'
|
||||
|
@ -678,6 +905,21 @@ get '/signout' do
|
|||
redirect '/'
|
||||
end
|
||||
|
||||
get '/signin/:username' do
|
||||
require_login
|
||||
@site = Site[username: params[:username]]
|
||||
|
||||
not_found if @site.nil?
|
||||
|
||||
if @site.owned_by? current_site
|
||||
session[:id] = @site.id
|
||||
redirect request.referrer
|
||||
end
|
||||
|
||||
flash[:error] = 'You do not have permission to switch to this site.'
|
||||
redirect request.referrer
|
||||
end
|
||||
|
||||
get '/about' do
|
||||
erb :'about'
|
||||
end
|
||||
|
@ -687,96 +929,6 @@ get '/site_files/new_page' do
|
|||
erb :'site_files/new_page'
|
||||
end
|
||||
|
||||
post '/change_password' do
|
||||
require_login
|
||||
|
||||
if !Site.valid_login?(current_site.username, params[:current_password])
|
||||
current_site.errors.add :password, 'Your provided password does not match the current one.'
|
||||
halt erb(:'settings')
|
||||
end
|
||||
|
||||
current_site.password = params[:new_password]
|
||||
current_site.valid?
|
||||
|
||||
if params[:new_password] != params[:new_password_confirm]
|
||||
current_site.errors.add :password, 'New passwords do not match.'
|
||||
end
|
||||
|
||||
if current_site.errors.empty?
|
||||
current_site.save_changes
|
||||
flash[:success] = 'Successfully changed password.'
|
||||
redirect '/settings'
|
||||
else
|
||||
halt erb(:'settings')
|
||||
end
|
||||
end
|
||||
|
||||
post '/change_email' do
|
||||
require_login
|
||||
|
||||
if params[:email] == current_site.email
|
||||
current_site.errors.add :email, 'You are already using this email address for this account.'
|
||||
halt erb(:settings)
|
||||
end
|
||||
|
||||
current_site.email = params[:email]
|
||||
current_site.email_confirmation_token = SecureRandom.hex 3
|
||||
current_site.email_confirmed = false
|
||||
|
||||
if current_site.valid?
|
||||
current_site.save_changes
|
||||
send_confirmation_email
|
||||
flash[:success] = 'Successfully changed email. We have sent a confirmation email, please use it to confirm your email address.'
|
||||
redirect '/settings'
|
||||
end
|
||||
|
||||
current_site.reload
|
||||
erb :settings
|
||||
end
|
||||
|
||||
post '/change_name' do
|
||||
require_login
|
||||
old_username = current_site.username
|
||||
|
||||
if params[:name] == nil || params[:name] == ''
|
||||
flash[:error] = 'Name cannot be blank.'
|
||||
redirect '/settings'
|
||||
end
|
||||
|
||||
if old_username == params[:name]
|
||||
flash[:error] = 'You already have this name.'
|
||||
redirect '/settings'
|
||||
end
|
||||
|
||||
old_host = current_site.host
|
||||
old_file_paths = current_site.file_list.collect {|f| f[:path]}
|
||||
|
||||
current_site.username = params[:name]
|
||||
|
||||
if current_site.valid?
|
||||
DB.transaction {
|
||||
current_site.save_changes
|
||||
current_site.move_files_from old_username
|
||||
}
|
||||
|
||||
old_file_paths.each do |file_path|
|
||||
current_site.purge_cache file_path
|
||||
end
|
||||
|
||||
flash[:success] = "Site/user name has been changed. You will need to use this name to login, <b>don't forget it</b>."
|
||||
redirect '/settings'
|
||||
else
|
||||
halt erb(:'settings')
|
||||
end
|
||||
end
|
||||
|
||||
post '/change_nsfw' do
|
||||
require_login
|
||||
current_site.update is_nsfw: params[:is_nsfw]
|
||||
flash[:success] = current_site.is_nsfw ? 'Marked 18+' : 'Unmarked 18+'
|
||||
redirect '/settings'
|
||||
end
|
||||
|
||||
post '/site_files/create_page' do
|
||||
require_login
|
||||
@errors = []
|
||||
|
@ -1008,95 +1160,6 @@ post '/admin/mark_nsfw' do
|
|||
redirect '/admin'
|
||||
end
|
||||
|
||||
get '/password_reset' do
|
||||
erb :'password_reset'
|
||||
end
|
||||
|
||||
post '/send_password_reset' do
|
||||
sites = Site.filter(email: params[:email]).all
|
||||
|
||||
if sites.length > 0
|
||||
token = SecureRandom.uuid.gsub('-', '')
|
||||
sites.each do |site|
|
||||
site.update password_reset_token: token
|
||||
end
|
||||
|
||||
body = <<-EOT
|
||||
Hello! This is the Neocities cat, and I have received a password reset request for your e-mail address. Purrrr.
|
||||
|
||||
Go to this URL to reset your password: http://neocities.org/password_reset_confirm?token=#{token}
|
||||
|
||||
After clicking on this link, your password for all the sites registered to this email address will be changed to this token.
|
||||
|
||||
Token: #{token}
|
||||
|
||||
If you didn't request this reset, you can ignore it. Or hide under a bed. Or take a nap. Your call.
|
||||
|
||||
Meow,
|
||||
the Neocities Cat
|
||||
EOT
|
||||
|
||||
body.strip!
|
||||
|
||||
EmailWorker.perform_async({
|
||||
from: 'web@neocities.org',
|
||||
to: params[:email],
|
||||
subject: '[Neocities] Password Reset',
|
||||
body: body
|
||||
})
|
||||
end
|
||||
|
||||
flash[:success] = 'If your email was valid (and used by a site), the Neocities Cat will send an e-mail to your account with password reset instructions.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
get '/password_reset_confirm' do
|
||||
if params[:token].nil? || params[:token].empty?
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
reset_site = Site[password_reset_token: params[:token]]
|
||||
|
||||
if reset_site.nil?
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
sites = Site.filter(email: reset_site.email).all
|
||||
|
||||
if sites.length > 0
|
||||
sites.each do |site|
|
||||
site.password = reset_site.password_reset_token
|
||||
site.save_changes
|
||||
end
|
||||
|
||||
flash[:success] = 'Your password for all sites with your email address has been changed to the token sent in your e-mail. Please login and change your password as soon as possible.'
|
||||
else
|
||||
flash[:error] = 'Could not find a site with this token.'
|
||||
end
|
||||
|
||||
redirect '/'
|
||||
end
|
||||
|
||||
get '/custom_domain' do
|
||||
require_login
|
||||
erb :custom_domain
|
||||
end
|
||||
|
||||
post '/custom_domain' do
|
||||
require_login
|
||||
current_site.domain = params[:domain]
|
||||
|
||||
if current_site.valid?
|
||||
current_site.save_changes
|
||||
flash[:success] = 'The domain has been successfully updated.'
|
||||
redirect '/custom_domain'
|
||||
else
|
||||
erb :custom_domain
|
||||
end
|
||||
end
|
||||
|
||||
get '/contact' do
|
||||
erb :'contact'
|
||||
end
|
||||
|
@ -1361,14 +1424,6 @@ post '/site/:username/block' do |username|
|
|||
end
|
||||
end
|
||||
|
||||
post '/site/delete' do
|
||||
require_login
|
||||
if current_site.username != params[:username]
|
||||
current_site.errors.add :username, 'Could not delete site, site name did not match.'
|
||||
halt erb(:settings)
|
||||
end
|
||||
end
|
||||
|
||||
def require_admin
|
||||
redirect '/' unless signed_in? && current_site.is_admin
|
||||
end
|
||||
|
@ -1401,7 +1456,12 @@ end
|
|||
|
||||
def current_site
|
||||
return nil if session[:id].nil?
|
||||
@site ||= Site[id: session[:id]]
|
||||
@_site ||= Site[id: session[:id]]
|
||||
end
|
||||
|
||||
def parent_site
|
||||
return nil if current_site.nil?
|
||||
current_site.parent? ? current_site : current_site.parent
|
||||
end
|
||||
|
||||
def require_unbanned_ip
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
class Numeric
|
||||
ONE_MEGABYTE = 1048576
|
||||
|
||||
def roundup(nearest=10)
|
||||
self % nearest == 0 ? self : self + nearest - (self % nearest)
|
||||
end
|
||||
end
|
||||
|
||||
def to_mb
|
||||
self/ONE_MEGABYTE.to_f
|
||||
end
|
||||
|
||||
def to_space_pretty
|
||||
space = (self.to_f / ONE_MEGABYTE).round(2)
|
||||
space = space.to_i if space.denominator == 1
|
||||
"#{space} MB"
|
||||
end
|
||||
end
|
9
migrations/046_add_site_parent_id.rb
Normal file
9
migrations/046_add_site_parent_id.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
Sequel.migration do
|
||||
up {
|
||||
DB.add_column :sites, :parent_site_id, :integer, index: true
|
||||
}
|
||||
|
||||
down {
|
||||
DB.drop_column :sites, :parent_site_id
|
||||
}
|
||||
end
|
132
models/site.rb
132
models/site.rb
|
@ -34,11 +34,8 @@ class Site < Sequel::Model
|
|||
geojson csv tsv mf ico pdf asc key pgp xml mid midi
|
||||
}
|
||||
|
||||
ONE_MEGABYTE_IN_BYTES = 1048576
|
||||
FREE_MAXIMUM_IN_MEGABYTES = 20
|
||||
SUPPORTER_MAXIMUM_IN_MEGABYTES = 1024
|
||||
FREE_MAXIMUM_IN_BYTES = FREE_MAXIMUM_IN_MEGABYTES * ONE_MEGABYTE_IN_BYTES
|
||||
SUPPORTER_MAXIMUM_IN_BYTES = SUPPORTER_MAXIMUM_IN_MEGABYTES * ONE_MEGABYTE_IN_BYTES
|
||||
FREE_MAXIMUM = 20 * Numeric::ONE_MEGABYTE
|
||||
SUPPORTER_MAXIMUM = 1000 * Numeric::ONE_MEGABYTE
|
||||
|
||||
MINIMUM_PASSWORD_LENGTH = 5
|
||||
BAD_USERNAME_REGEX = /[^\w-]/i
|
||||
|
@ -95,6 +92,7 @@ class Site < Sequel::Model
|
|||
|
||||
SUGGESTIONS_LIMIT = 32
|
||||
SUGGESTIONS_VIEWS_MIN = 500
|
||||
CHILD_SITES_MAX = 100
|
||||
|
||||
PLAN_FEATURES[:catbus] = PLAN_FEATURES[:fatcat].merge(
|
||||
name: 'Cat Bus',
|
||||
|
@ -147,9 +145,42 @@ class Site < Sequel::Model
|
|||
|
||||
one_to_many :site_changes
|
||||
|
||||
many_to_one :parent, :key => :parent_site_id, :class => self
|
||||
one_to_many :children, :key => :parent_site_id, :class => self
|
||||
|
||||
def account_sites_dataset
|
||||
Site.where(Sequel.|({id: owner.id}, {parent_site_id: owner.id})).order(:parent_site_id.desc, :username)
|
||||
end
|
||||
|
||||
def account_sites
|
||||
account_sites_dataset.all
|
||||
end
|
||||
|
||||
def other_sites_dataset
|
||||
account_sites_dataset.exclude(id: self.id)
|
||||
end
|
||||
|
||||
def other_sites
|
||||
account_sites_dataset.exclude(id: self.id).all
|
||||
end
|
||||
|
||||
def account_sites_events_dataset
|
||||
ids = account_sites_dataset.select(:id).all.collect {|s| s.id}
|
||||
Event.where(id: ids)
|
||||
end
|
||||
|
||||
def owner
|
||||
parent? ? self : parent
|
||||
end
|
||||
|
||||
def owned_by?(site)
|
||||
!account_sites_dataset.select(:id).where(id: site.id).first.nil?
|
||||
end
|
||||
|
||||
class << self
|
||||
def valid_login?(username, plaintext)
|
||||
site = self[username: username]
|
||||
def valid_login?(username_or_email, plaintext)
|
||||
site = get_with_identifier username_or_email
|
||||
|
||||
return false if site.nil?
|
||||
site.valid_password? plaintext
|
||||
end
|
||||
|
@ -161,6 +192,16 @@ class Site < Sequel::Model
|
|||
def bcrypt_cost=(cost)
|
||||
@bcrypt_cost = cost
|
||||
end
|
||||
|
||||
def get_with_identifier(username_or_email)
|
||||
if username_or_email =~ /@/
|
||||
site = self.where(email: username_or_email).where(parent_site_id: nil).first
|
||||
else
|
||||
site = self[username: username_or_email]
|
||||
end
|
||||
return nil if site.nil? || site.is_banned || site.owner.is_banned
|
||||
site
|
||||
end
|
||||
end
|
||||
|
||||
def self.banned_ip?(ip)
|
||||
|
@ -204,7 +245,14 @@ class Site < Sequel::Model
|
|||
end
|
||||
|
||||
def valid_password?(plaintext)
|
||||
BCrypt::Password.new(values[:password]) == plaintext
|
||||
valid = BCrypt::Password.new(owner.values[:password]) == plaintext
|
||||
|
||||
if !valid?
|
||||
return false if values[:password].nil?
|
||||
valid = BCrypt::Password.new(values[:password]) == plaintext
|
||||
end
|
||||
|
||||
valid
|
||||
end
|
||||
|
||||
def password=(plaintext)
|
||||
|
@ -297,6 +345,12 @@ class Site < Sequel::Model
|
|||
end
|
||||
end
|
||||
|
||||
def ban_all_sites_on_account!
|
||||
DB.transaction {
|
||||
account_sites.all {|site| site.ban! }
|
||||
}
|
||||
end
|
||||
|
||||
=begin
|
||||
def follows_dataset
|
||||
super.where(Sequel.~(site_id: blocking_site_ids))
|
||||
|
@ -315,15 +369,21 @@ class Site < Sequel::Model
|
|||
=end
|
||||
|
||||
def commenting_allowed?
|
||||
return true if commenting_allowed
|
||||
return true if owner.commenting_allowed
|
||||
|
||||
if events_dataset.exclude(site_change_id: nil).count >= COMMENTING_ALLOWED_UPDATED_COUNT &&
|
||||
created_at < Time.now - 604800
|
||||
if owner.supporter?
|
||||
set commenting_allowed: true
|
||||
save_changes validate: false
|
||||
return true
|
||||
end
|
||||
|
||||
if account_sites_events_dataset.exclude(site_change_id: nil).count >= COMMENTING_ALLOWED_UPDATED_COUNT &&
|
||||
created_at < Time.now - 604800
|
||||
owner.set commenting_allowed: true
|
||||
owner.save_changes validate: false
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
|
@ -554,6 +614,10 @@ class Site < Sequel::Model
|
|||
super
|
||||
end
|
||||
|
||||
def parent?
|
||||
parent_site_id.nil?
|
||||
end
|
||||
|
||||
# def after_destroy
|
||||
# FileUtils.rm_rf files_path
|
||||
# super
|
||||
|
@ -576,7 +640,7 @@ class Site < Sequel::Model
|
|||
end
|
||||
|
||||
# Check that email has been provided
|
||||
if values[:email].empty?
|
||||
if parent? && values[:email].empty?
|
||||
errors.add :email, 'An email address is required.'
|
||||
end
|
||||
|
||||
|
@ -586,12 +650,12 @@ class Site < Sequel::Model
|
|||
email_check.exclude!(id: self.id) unless new?
|
||||
email_check = email_check.first
|
||||
|
||||
if email_check && email_check.id != self.id
|
||||
if parent? && email_check && email_check.id != self.id
|
||||
errors.add :email, 'This email address already exists on Neocities, please use your existing account instead of creating a new one.'
|
||||
end
|
||||
end
|
||||
|
||||
unless values[:email] =~ EMAIL_SANITY_REGEX
|
||||
if parent? && (values[:email] =~ EMAIL_SANITY_REGEX).nil?
|
||||
errors.add :email, 'A valid email address is required.'
|
||||
end
|
||||
|
||||
|
@ -604,7 +668,7 @@ class Site < Sequel::Model
|
|||
end
|
||||
end
|
||||
|
||||
if values[:password].nil? || (@password_length && @password_length < MINIMUM_PASSWORD_LENGTH)
|
||||
if parent? && (values[:password].nil? || (@password_length && @password_length < MINIMUM_PASSWORD_LENGTH))
|
||||
errors.add :password, "Password must be at least #{MINIMUM_PASSWORD_LENGTH} characters."
|
||||
end
|
||||
|
||||
|
@ -622,6 +686,10 @@ class Site < Sequel::Model
|
|||
if !site.nil? && site.id != self.id
|
||||
errors.add :domain, "Domain provided is already being used by another site, please choose another."
|
||||
end
|
||||
|
||||
if new? && !parent? && account_sites_dataset.count >= CHILD_SITES_MAX
|
||||
errors.add :child_site_id, "Cannot add child site, exceeds #{CHILD_SITES_MAX} limit."
|
||||
end
|
||||
end
|
||||
|
||||
if @new_tags_string
|
||||
|
@ -715,49 +783,43 @@ class Site < Sequel::Model
|
|||
list.select {|f| f[:is_directory] == false}.sort_by{|f| f[:name].downcase}
|
||||
end
|
||||
|
||||
def file_size_too_large?(size_in_bytes)
|
||||
return true if size_in_bytes + used_space_in_bytes > maximum_space_in_bytes
|
||||
def file_size_too_large?(size)
|
||||
return true if size + used_space > maximum_space
|
||||
false
|
||||
end
|
||||
|
||||
def used_space_in_bytes
|
||||
def used_space
|
||||
space = Dir.glob(File.join(files_path, '*')).collect {|p| File.size(p)}.inject {|sum,x| sum += x}
|
||||
space.nil? ? 0 : space
|
||||
end
|
||||
|
||||
def used_space_in_megabytes
|
||||
(used_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
||||
def total_used_space
|
||||
total = 0
|
||||
account_sites.each {|s| total += s.used_space}
|
||||
total
|
||||
end
|
||||
|
||||
def available_space_in_bytes
|
||||
remaining = maximum_space_in_bytes - used_space_in_bytes
|
||||
def remaining_space
|
||||
remaining = maximum_space - total_used_space
|
||||
remaining < 0 ? 0 : remaining
|
||||
end
|
||||
|
||||
def available_space_in_megabytes
|
||||
(available_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
||||
end
|
||||
|
||||
def maximum_space_in_bytes
|
||||
supporter? ? self.class::SUPPORTER_MAXIMUM_IN_BYTES : self.class::FREE_MAXIMUM_IN_BYTES
|
||||
end
|
||||
|
||||
def maximum_space_in_megabytes
|
||||
supporter? ? self.class::SUPPORTER_MAXIMUM_IN_MEGABYTES : self.class::FREE_MAXIMUM_IN_MEGABYTES
|
||||
def maximum_space
|
||||
(parent? ? self : parent).supporter? ? SUPPORTER_MAXIMUM : FREE_MAXIMUM
|
||||
end
|
||||
|
||||
def space_percentage_used
|
||||
((used_space_in_bytes.to_f / maximum_space_in_bytes) * 100).round(1)
|
||||
((total_used_space.to_f / maximum_space) * 100).round(1)
|
||||
end
|
||||
|
||||
# This returns true even if they end their support plan.
|
||||
def supporter?
|
||||
!values[:stripe_customer_id].nil?
|
||||
!owner.values[:stripe_customer_id].nil?
|
||||
end
|
||||
|
||||
# This will return false if they have ended their plan.
|
||||
def ended_supporter?
|
||||
values[:plan_ended]
|
||||
owner.values[:plan_ended]
|
||||
end
|
||||
|
||||
def plan_name
|
||||
|
|
|
@ -1051,4 +1051,8 @@ a.tag:hover {
|
|||
.interior .header-Outro.with-columns .col.filter {
|
||||
padding-top: 0px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.dropdown-submenu .dropdown-menu {
|
||||
width: 1px;
|
||||
}
|
86
tests/acceptance/settings/account_tests.rb
Normal file
86
tests/acceptance/settings/account_tests.rb
Normal file
|
@ -0,0 +1,86 @@
|
|||
require_relative '../environment.rb'
|
||||
|
||||
describe 'site/settings' do
|
||||
describe 'email' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
EmailWorker.jobs.clear
|
||||
@email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
@site = Fabricate :site, email: @email
|
||||
page.set_rack_session id: @site.id
|
||||
visit '/settings'
|
||||
end
|
||||
|
||||
it 'should change email' do
|
||||
@new_email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
fill_in 'email', with: @new_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /successfully changed email/i
|
||||
@site.reload
|
||||
@site.email.must_equal @new_email
|
||||
EmailWorker.jobs.length.must_equal 1
|
||||
args = EmailWorker.jobs.first['args'].first
|
||||
args['to'].must_equal @new_email
|
||||
args['subject'].must_match /confirm your email address/i
|
||||
args['body'].must_match /hello #{@site.username}/i
|
||||
args['body'].must_match /#{@site.email_confirmation_token}/
|
||||
end
|
||||
|
||||
it 'should fail for invalid email address' do
|
||||
@new_email = SecureRandom.uuid.gsub '-', ''
|
||||
fill_in 'email', with: @new_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /a valid email address is required/i
|
||||
@site.reload
|
||||
@site.email.wont_equal @new_email
|
||||
EmailWorker.jobs.empty?.must_equal true
|
||||
end
|
||||
|
||||
it 'should fail for existing email' do
|
||||
@existing_email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
@existing_site = Fabricate :site, email: @existing_email
|
||||
|
||||
fill_in 'email', with: @existing_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /this email address already exists on neocities/i
|
||||
@site.reload
|
||||
@site.email.wont_equal @new_email
|
||||
EmailWorker.jobs.empty?.must_equal true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'change password' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
@site = Fabricate :site, password: 'derpie'
|
||||
page.set_rack_session id: @site.id
|
||||
visit '/settings'
|
||||
end
|
||||
|
||||
it 'should change correctly' do
|
||||
fill_in 'current_password', with: 'derpie'
|
||||
fill_in 'new_password', with: 'derpie2'
|
||||
fill_in 'new_password_confirm', with: 'derpie2'
|
||||
click_button 'Change Password'
|
||||
|
||||
page.must_have_content /successfully changed password/i
|
||||
@site.reload
|
||||
@site.valid_password?('derpie').must_equal false
|
||||
@site.valid_password?('derpie2').must_equal true
|
||||
end
|
||||
|
||||
it 'should not change for invalid current password' do
|
||||
fill_in 'current_password', with: 'dademurphy'
|
||||
fill_in 'new_password', with: 'derpie2'
|
||||
fill_in 'new_password_confirm', with: 'derpie2'
|
||||
click_button 'Change Password'
|
||||
|
||||
page.must_have_content /provided password does not match the current one/i
|
||||
@site.reload
|
||||
@site.valid_password?('derpie').must_equal true
|
||||
@site.valid_password?('derpie2').must_equal false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
require_relative './environment.rb'
|
||||
require_relative '../environment.rb'
|
||||
|
||||
def generate_ssl_certs(opts={})
|
||||
# https://github.com/kyledrake/ruby-openssl-cheat-sheet/blob/master/certificate_authority.rb
|
||||
|
@ -80,6 +80,29 @@ def generate_ssl_certs(opts={})
|
|||
end
|
||||
|
||||
describe 'site/settings' do
|
||||
describe 'permissions' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
@parent_site = Fabricate :site
|
||||
@child_site = Fabricate :site, parent_site_id: @parent_site.id
|
||||
@other_site = Fabricate :site
|
||||
end
|
||||
|
||||
it 'fails without permissions' do
|
||||
page.set_rack_session id: @other_site.id
|
||||
|
||||
visit "/settings/#{@parent_site.username}"
|
||||
page.current_path.must_equal '/' # This could be better
|
||||
end
|
||||
|
||||
it 'allows child site editing from parent' do
|
||||
page.set_rack_session id: @parent_site.id
|
||||
visit "/settings/#{@child_site.username}"
|
||||
page.current_path.must_equal "/settings/#{@child_site.username}"
|
||||
end
|
||||
end
|
||||
|
||||
describe 'ssl' do
|
||||
include Capybara::DSL
|
||||
|
||||
|
@ -92,13 +115,13 @@ describe 'site/settings' do
|
|||
it 'fails without domain set' do
|
||||
@site = Fabricate :site
|
||||
page.set_rack_session id: @site.id
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
page.must_have_content /Cannot upload SSL certificate until domain is added/i
|
||||
end
|
||||
|
||||
it 'fails with expired key' do
|
||||
@ssl = generate_ssl_certs domain: @domain, expired: true
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
attach_file 'key', @ssl[:key_path]
|
||||
attach_file 'cert', @ssl[:combined_cert_path]
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
|
@ -107,14 +130,14 @@ describe 'site/settings' do
|
|||
|
||||
it 'works with valid key and unified cert' do
|
||||
@ssl = generate_ssl_certs domain: @domain
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
key = File.read @ssl[:key_path]
|
||||
combined_cert = File.read @ssl[:combined_cert_path]
|
||||
page.must_have_content /status: inactive/i
|
||||
attach_file 'key', @ssl[:key_path]
|
||||
attach_file 'cert', @ssl[:combined_cert_path]
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /Updated SSL/
|
||||
page.must_have_content /status: installed/i
|
||||
@site.reload
|
||||
|
@ -123,9 +146,9 @@ describe 'site/settings' do
|
|||
end
|
||||
|
||||
it 'fails with no uploads' do
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /ssl key.+certificate.+required/i
|
||||
@site.reload
|
||||
@site.ssl_key.must_equal nil
|
||||
|
@ -134,42 +157,42 @@ describe 'site/settings' do
|
|||
|
||||
it 'fails gracefully with encrypted key' do
|
||||
@ssl = generate_ssl_certs domain: @domain
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
attach_file 'key', './tests/files/ssl/derpie.com-encrypted.key'
|
||||
attach_file 'cert', @ssl[:cert_path]
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /could not process ssl key/i
|
||||
end
|
||||
|
||||
it 'fails with junk key' do
|
||||
@ssl = generate_ssl_certs domain: @domain
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
attach_file 'key', './tests/files/index.html'
|
||||
attach_file 'cert', @ssl[:cert_path]
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /could not process ssl key/i
|
||||
end
|
||||
|
||||
it 'fails with junk cert' do
|
||||
@ssl = generate_ssl_certs domain: @domain
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
attach_file 'key', @ssl[:key_path]
|
||||
attach_file 'cert', './tests/files/index.html'
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /could not process ssl certificate/i
|
||||
end
|
||||
|
||||
if ENV['TRAVIS'] != 'true'
|
||||
it 'fails with bad cert chain' do
|
||||
@ssl = generate_ssl_certs domain: @domain
|
||||
visit '/custom_domain'
|
||||
visit "/settings/#{@site.username}#custom_domain"
|
||||
attach_file 'key', @ssl[:key_path]
|
||||
attach_file 'cert', @ssl[:bad_combined_cert_path]
|
||||
click_button 'Upload SSL Key and Certificate'
|
||||
page.current_path.must_equal '/custom_domain'
|
||||
page.current_path.must_equal "/settings/#{@site.username}"
|
||||
page.must_have_content /there is something wrong with your certificate/i
|
||||
end
|
||||
end
|
||||
|
@ -200,7 +223,7 @@ describe 'site/settings' do
|
|||
click_button 'Create My Website'
|
||||
fill_in_valid
|
||||
click_button 'Create Home Page'
|
||||
visit '/settings'
|
||||
visit "/settings/#{@site[:username]}#username"
|
||||
fill_in 'name', with: ''
|
||||
click_button 'Change Name'
|
||||
fill_in 'name', with: '../hack'
|
||||
|
@ -215,87 +238,4 @@ describe 'site/settings' do
|
|||
Site[username: ''].must_equal nil
|
||||
end
|
||||
end
|
||||
|
||||
describe 'email' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
EmailWorker.jobs.clear
|
||||
@email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
@site = Fabricate :site, email: @email
|
||||
page.set_rack_session id: @site.id
|
||||
visit '/settings'
|
||||
end
|
||||
|
||||
it 'should change email' do
|
||||
@new_email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
fill_in 'email', with: @new_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /successfully changed email/i
|
||||
@site.reload
|
||||
@site.email.must_equal @new_email
|
||||
EmailWorker.jobs.length.must_equal 1
|
||||
args = EmailWorker.jobs.first['args'].first
|
||||
args['to'].must_equal @new_email
|
||||
args['subject'].must_match /confirm your email address/i
|
||||
args['body'].must_match /hello #{@site.username}/i
|
||||
args['body'].must_match /#{@site.email_confirmation_token}/
|
||||
end
|
||||
|
||||
it 'should fail for invalid email address' do
|
||||
@new_email = SecureRandom.uuid.gsub '-', ''
|
||||
fill_in 'email', with: @new_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /a valid email address is required/i
|
||||
@site.reload
|
||||
@site.email.wont_equal @new_email
|
||||
EmailWorker.jobs.empty?.must_equal true
|
||||
end
|
||||
|
||||
it 'should fail for existing email' do
|
||||
@existing_email = "#{SecureRandom.uuid.gsub('-', '')}@example.com"
|
||||
@existing_site = Fabricate :site, email: @existing_email
|
||||
|
||||
fill_in 'email', with: @existing_email
|
||||
click_button 'Change Email'
|
||||
page.must_have_content /this email address already exists on neocities/i
|
||||
@site.reload
|
||||
@site.email.wont_equal @new_email
|
||||
EmailWorker.jobs.empty?.must_equal true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'change password' do
|
||||
include Capybara::DSL
|
||||
|
||||
before do
|
||||
@site = Fabricate :site, password: 'derpie'
|
||||
page.set_rack_session id: @site.id
|
||||
visit '/settings'
|
||||
end
|
||||
|
||||
it 'should change correctly' do
|
||||
fill_in 'current_password', with: 'derpie'
|
||||
fill_in 'new_password', with: 'derpie2'
|
||||
fill_in 'new_password_confirm', with: 'derpie2'
|
||||
click_button 'Change Password'
|
||||
|
||||
page.must_have_content /successfully changed password/i
|
||||
@site.reload
|
||||
@site.valid_password?('derpie').must_equal false
|
||||
@site.valid_password?('derpie2').must_equal true
|
||||
end
|
||||
|
||||
it 'should not change for invalid current password' do
|
||||
fill_in 'current_password', with: 'dademurphy'
|
||||
fill_in 'new_password', with: 'derpie2'
|
||||
fill_in 'new_password_confirm', with: 'derpie2'
|
||||
click_button 'Change Password'
|
||||
|
||||
page.must_have_content /provided password does not match the current one/i
|
||||
@site.reload
|
||||
@site.valid_password?('derpie').must_equal true
|
||||
@site.valid_password?('derpie2').must_equal false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,7 +18,7 @@ describe 'signin' do
|
|||
Capybara.reset_sessions!
|
||||
end
|
||||
|
||||
it 'fails for invalid login' do
|
||||
it 'fails for invalid signin' do
|
||||
visit '/'
|
||||
click_link 'Sign In'
|
||||
page.must_have_content 'Welcome Back'
|
||||
|
@ -27,7 +27,7 @@ describe 'signin' do
|
|||
page.must_have_content 'Invalid login'
|
||||
end
|
||||
|
||||
it 'fails for missing login' do
|
||||
it 'fails for missing signin' do
|
||||
visit '/'
|
||||
click_link 'Sign In'
|
||||
auth = {username: SecureRandom.hex, password: Faker::Internet.password}
|
||||
|
@ -37,7 +37,7 @@ describe 'signin' do
|
|||
page.must_have_content 'Invalid login'
|
||||
end
|
||||
|
||||
it 'logs in with proper credentials' do
|
||||
it 'signs in with proper credentials' do
|
||||
visit '/'
|
||||
click_button 'Create My Website'
|
||||
fill_in_valid_signup
|
||||
|
@ -50,4 +50,18 @@ describe 'signin' do
|
|||
click_button 'Sign In'
|
||||
page.must_have_content 'Your Feed'
|
||||
end
|
||||
|
||||
it 'signs in with email' do
|
||||
visit '/'
|
||||
click_button 'Create My Website'
|
||||
fill_in_valid_signup
|
||||
click_button 'Create Home Page'
|
||||
Capybara.reset_sessions!
|
||||
visit '/'
|
||||
click_link 'Sign In'
|
||||
fill_in 'username', with: @site[:email]
|
||||
fill_in 'password', with: @site[:password]
|
||||
click_button 'Sign In'
|
||||
page.must_have_content 'Your Feed'
|
||||
end
|
||||
end
|
|
@ -36,25 +36,39 @@
|
|||
<li class="dropdown">
|
||||
<a href="#" data-toggle="dropdown" class="dropdown-toggle"><%= current_site.username %> <span class="info"><span class="notification-value">3</span><i class="fa fa-caret-down"></i></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/site/<%= current_site.username %>">Profile</a></li>
|
||||
<li><a href="/?activity=mine">Activity <span class="notification-value">3</span></a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="/dashboard">Edit Site</a></li>
|
||||
<li><a href="//<%= current_site.host %>" target="_blank">View Site</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="/settings">Settings</a></li>
|
||||
<li><a href="/signout">Sign Out</a></li>
|
||||
<li><a href="/site/<%= current_site.username %>">Profile</a></li>
|
||||
<li><a href="/?activity=mine">Activity <span class="notification-value">3</span></a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="/dashboard">Edit Site</a></li>
|
||||
<li><a href="//<%= current_site.host %>" target="_blank">View Site</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a href="/settings">Settings</a></li>
|
||||
|
||||
<% if current_site.other_sites_dataset.count > 0 %>
|
||||
<li class="dropdown-submenu pull-left">
|
||||
<a tabindex="-1" href="#" onclick="return false">Switch Site</a>
|
||||
<ul class="dropdown-menu">
|
||||
<% current_site.other_sites_dataset.select(:username).all.each do |site| %>
|
||||
<li>
|
||||
<a href="/signin/<%= site.username %>"><%= site.username %></a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</li>
|
||||
<% end %>
|
||||
|
||||
<li><a href="/signout">Sign Out</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<% end %>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h1 class="logo int-Logo">
|
||||
<a href="/" title="back to home">
|
||||
<span class="hidden">Neocities.org</span>
|
||||
<img src="/img/cat.png" alt="Neocities.org" />
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
</header>
|
||||
<h1 class="logo int-Logo">
|
||||
<a href="/" title="back to home">
|
||||
<span class="hidden">Neocities.org</span>
|
||||
<img src="/img/cat.png" alt="Neocities.org" />
|
||||
</a>
|
||||
</h1>
|
||||
</header>
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col col-50">
|
||||
<h2>Ban User</h2>
|
||||
<h2>Ban Site</h2>
|
||||
<form action="/admin/banhammer" method="POST">
|
||||
<%== csrf_token_input_html %>
|
||||
<p>Site Name:</p>
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
<div class="header-Outro">
|
||||
<div class="row content single-Col">
|
||||
<h1>Custom Domain</h1>
|
||||
<h3 class="subtitle">Add your own domain name to your Neocities site!</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content single-Col misc-page">
|
||||
<h3></h3>
|
||||
<article>
|
||||
|
||||
<% if flash.keys.length > 0 %>
|
||||
<div class="alert alert-block alert-success">
|
||||
<% flash.keys.each do |key| %>
|
||||
<%== flash[key] %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<p>
|
||||
Adding a custom domain allows you to have a domain name attached to your web site. So if you had a domain like <strong>catsknitting.com</strong>, you could have it point to your Neocities site!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You will have to purchase a domain name from a registrar like <a href="https://www.namecheap.com/?aff=53678" target="_blank">Namecheap</a>, and then add an A record to point your domain (catsknitting.com) to the following IP address:
|
||||
</p>
|
||||
|
||||
<p><code>198.27.81.179</code></p>
|
||||
|
||||
<p>
|
||||
If you want to add a <strong>www</strong> subdomain, or use a wildcard that will answer to everything (<strong>*</strong>), you will have to make a CNAME pointing to <strong>catsknitting.com</strong> for <strong>www</strong> and/or <strong>*</strong>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After that, you can add the domain to the box below (just the <strong>catsknitting.com</strong>, don't add any subdomains), and your domain should come online within 5 minutes:
|
||||
</p>
|
||||
|
||||
<form method="POST" action="/custom_domain">
|
||||
<%== csrf_token_input_html %>
|
||||
<input name="domain" type="text" placeholder="catsknitting.com" value="<%= current_site.domain %>">
|
||||
<br>
|
||||
<input class="btn-Action" type="submit" value="Update Domain">
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<h2>Add SSL Certificate</h2>
|
||||
<p>
|
||||
This allows you to add an SSL key and certificate for your domain, enabling encryption for your site (https). It can take up to 5-30 minutes for the changes to go live, so please be patient. All files must be in PEM format. If your certificate is not bundled with the root and intermediate certificates, ask your certificate provider for help on how to do that.
|
||||
</p>
|
||||
|
||||
<% if current_site.domain.nil? || current_site.domain.empty? %>
|
||||
<p><strong>Cannot upload SSL certificate until domain is added.</strong></p>
|
||||
<% else %>
|
||||
|
||||
<form method="POST" action="/settings/ssl" enctype="multipart/form-data">
|
||||
<%== csrf_token_input_html %>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Status: <%= current_site.ssl_installed? ? 'Installed' : 'Inactive' %>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
SSL Key (yourdomain.com.key):
|
||||
<input name="key" type="file">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Bundled Certificates (yourdomain.com-bundle.crt):
|
||||
<input name="cert" type="file">
|
||||
</p>
|
||||
|
||||
<input class="btn-Action" type="submit" value="Upload SSL Key and Certificate">
|
||||
|
||||
</form>
|
||||
<% end %>
|
||||
</article>
|
||||
</div>
|
|
@ -34,7 +34,7 @@
|
|||
<% if current_site.updated_at %>
|
||||
<li>Last updated <%= current_site.updated_at.ago.downcase %></li>
|
||||
<% end %>
|
||||
<li>Using <strong><%= current_site.space_percentage_used %>% (<%= current_site.used_space_in_megabytes %>MB) of your <%= current_site.maximum_space_in_megabytes %> MB</strong>.
|
||||
<li>Using <strong><%= current_site.space_percentage_used %>% (<%= current_site.total_used_space.to_space_pretty %>) of your <%= current_site.maximum_space.to_space_pretty %></strong>.
|
||||
<br>
|
||||
<% if !current_site.supporter? %>Need more space? <a href="/plan">Become a Supporter!</a><% end %></li>
|
||||
<li><strong><%= current_site.hits.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse %></strong> hits</li>
|
||||
|
@ -222,7 +222,7 @@
|
|||
|
||||
Dropzone.options.uploads = {
|
||||
paramName: 'files',
|
||||
maxFilesize: <%= current_site.available_space_in_megabytes %>,
|
||||
maxFilesize: <%= current_site.remaining_space.to_mb %>,
|
||||
clickable: false,
|
||||
addRemoveLinks: false,
|
||||
dictDefaultMessage: '',
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
<span class="intro-Icon"></span>
|
||||
<h2 class="delta">Create your own free website</h2>
|
||||
<p class="tiny">
|
||||
You get <%= Site::FREE_MAXIMUM_IN_MEGABYTES %> MB of free web space to make whatever you’d like!
|
||||
You get <%= Site::FREE_MAXIMUM.to_space_pretty %> of free web space to make whatever you’d like!
|
||||
</p>
|
||||
</li>
|
||||
<li class="intro-Social">
|
||||
|
|
|
@ -73,17 +73,17 @@
|
|||
<p><strong>The site you are creating will be free, forever.</strong> We will never charge you for your web site.</p>
|
||||
<p><a href="/donate" target="_blank">Neocities has to pay the bills though</a>, and we like the idea of being able to work on the site full-time someday. So if you would like to help us reach this goal, we have created the <strong>Supporter Plan</strong>!
|
||||
|
||||
<p>Right now, the <strong>Supporter Plan</strong> is the same as the free plan, except that <strong>Supporter Plan members get 200MB</strong> of web space. You will also be listed as a supporter on our contributors page, and on your site profile page.</p>
|
||||
<p>Right now, the <strong>Supporter Plan</strong> is the same as the free plan, except that <strong>Supporter Plan members get <%= Site::SUPPORTER_MAXIMUM.to_space_pretty %></strong> of web space. You will also be listed as a supporter on our contributors page, and on your site profile page.</p>
|
||||
<p>The base plan is $12 ($1/month) billed once per year, which is the cost of <a href="/img/yafagrillmenu.jpg" target="_blank">a delicious Yafa Combo with a lousy tip</a>. If you ever decide to cancel, you get to keep the extra space. Thanks for helping us run this site!</p>
|
||||
|
||||
<div>
|
||||
<input type="radio" name="plan" value="free" <%= params[:plan].nil? || params[:plan] == 'free' ? 'checked' : '' %>>
|
||||
<span><strong>Free Plan (<%= Site::FREE_MAXIMUM_IN_MEGABYTES %>MB)</strong></span>
|
||||
<span><strong>Free Plan (<%= Site::FREE_MAXIMUM.to_space_pretty %>)</strong></span>
|
||||
</div>
|
||||
<a name="plan_error_link"></a>
|
||||
<div>
|
||||
<input id="supporter" type="radio" name="plan" value="supporter" <%= params[:plan] == 'supporter' ? 'checked' : '' %>>
|
||||
<span><strong>Supporter Plan (<%= Site::SUPPORTER_MAXIMUM_IN_MEGABYTES %>MB)</strong></span>
|
||||
<span><strong>Supporter Plan (<%= Site::SUPPORTER_MAXIMUM.to_space_pretty %>)</strong></span>
|
||||
</div>
|
||||
|
||||
<div id="plan_container" style="margin-top:20px; display: none">
|
||||
|
|
|
@ -122,7 +122,8 @@
|
|||
<h2 class="section-header">Introducing the New Neocities</h2>
|
||||
<p class="intro-text">Now’s a great time to join our community of over
|
||||
<a href="/browse"><%= @sites_count.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse %> websites</a>!
|
||||
You get <%= Site::FREE_MAXIMUM_IN_MEGABYTES %> MB of free web space to make whatever you’d like. </p>
|
||||
You get <%= Site::FREE_MAXIMUM.to_space_pretty %> of free web space to make whatever you’d like.
|
||||
</p>
|
||||
<ul class="intro-List">
|
||||
<li class="intro-Social">
|
||||
<span class="intro-Icon"></span>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="content single-Col misc-page">
|
||||
<h3>Ending the Supporter Plan</h3>
|
||||
<p>
|
||||
You currently have the <strong><%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB)</strong> - <%= @plan_name %>.
|
||||
You currently have the <strong><%= parent_site.plan_name %> (<%= parent_site.maximum_space.to_space_pretty %>)</strong> - <%= @plan_name %>.
|
||||
</p>
|
||||
<p>
|
||||
If you need to end the plan, you can do that here. We'll be sorry to see you go. If there's a reason you're ending that we can help with, please <a href="/contact">contact us</a> and we'll see if we can help you with your issue. Regardless, we'll let you keep your site and the extra space. We hope you'll decide to become a supporter again in the future!
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<div class="content single-Col misc-page">
|
||||
<h3></h3>
|
||||
|
||||
<% if current_site && current_site.supporter? && !current_site.plan_ended %>
|
||||
<% if parent_site && parent_site.supporter? && !parent_site.plan_ended %>
|
||||
<p>
|
||||
You currently have the <strong><%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB)</strong>.
|
||||
You currently have the <strong><%= parent_site.plan_name %> (<%= parent_site.maximum_space.to_space_pretty %>)</strong>.
|
||||
</p>
|
||||
<p>
|
||||
Your support means a lot to us. On behalf of Penelope the cat and everyone at Neocities, thank you. If there's anything we can do to make your experience even better, please don't hesitate to <a href="/contact">contact us</a>.
|
||||
|
@ -27,8 +27,8 @@
|
|||
|
||||
<a href="/plan/manage">Manage plan</a>
|
||||
<% else %>
|
||||
<% if current_site %>
|
||||
<p>You currently have the <strong>Free Plan (<%= current_site.maximum_space_in_megabytes %>MB)</strong>. Need more space? Become a <strong>Neocities Supporter!</strong></p>
|
||||
<% if parent_site %>
|
||||
<p>You currently have the <strong>Free Plan (<%= parent_site.maximum_space.to_space_pretty %>)</strong>. Need more space? Become a <strong>Neocities Supporter!</strong></p>
|
||||
|
||||
<h3>Why upgrade?</h3>
|
||||
<% else %>
|
||||
|
@ -38,7 +38,7 @@
|
|||
<% end %>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>You get more space!</strong> Right now supporter plans get up to <strong><%= Site::SUPPORTER_MAXIMUM_IN_MEGABYTES %>MB</strong> (more coming soon)</strong>.
|
||||
<strong>You get more space!</strong> Right now supporter plans get up to <strong><%= Site::SUPPORTER_MAXIMUM.to_space_pretty %></strong>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>It helps your site.</strong> Funding helps us make your site faster globally, and provide more features.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<div class="content single-Col misc-page">
|
||||
<h3>Change your Supporter Plan</h3>
|
||||
<p>
|
||||
You currently have the <strong><%= current_site.plan_name %> (<%= current_site.maximum_space_in_megabytes %>MB)</strong> - <%= @plan_name %>.
|
||||
You currently have the <strong><%= parent_site.plan_name %> (<%= parent_site.maximum_space.to_space_pretty %>)</strong> - <%= @plan_name %>.
|
||||
</p>
|
||||
|
||||
<a href="/plan/end">End plan</a>
|
||||
|
|
89
views/settings/account.erb
Normal file
89
views/settings/account.erb
Normal file
|
@ -0,0 +1,89 @@
|
|||
<div class="header-Outro">
|
||||
<div class="row content single-Col">
|
||||
<h1>Account Settings</h1>
|
||||
<h3 class="subtitle">Manage the account for your sites</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content single-Col misc-page txt-Center">
|
||||
<article>
|
||||
<section>
|
||||
<div class="txt-Center">
|
||||
<% if flash[:success] %>
|
||||
<div class="alert alert-block alert-success" style="margin-top: 20px">
|
||||
<%== flash[:success] %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if flash[:error] %>
|
||||
<div class="alert alert-block alert-error" style="margin-top: 20px">
|
||||
<%== flash[:error] %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="tabbable" style="margin-top: 20px"> <!-- Only required for left/right tabs -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#plan" data-toggle="tab">Plan</a></li>
|
||||
<li><a href="#sites" data-toggle="tab">Sites</a></li>
|
||||
<li><a href="#password" data-toggle="tab">Password</a></li>
|
||||
<li><a href="#email" data-toggle="tab">Email</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="plan">
|
||||
<%== erb :'settings/account/plan' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="sites">
|
||||
<%== erb :'settings/account/sites' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="password">
|
||||
<%== erb :'settings/account/password' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="email">
|
||||
<%== erb :'settings/account/email' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<h2>Delete Site</h2>
|
||||
<p class="tiny">
|
||||
If you want to delete your account, you can do that here. We're sorry to see you go, but we understand if Neocities isn't right for you. If there's any specific reason you're leaving, it would be great if you <a href="/contact">let us know</a> so we can try to make your experience better in the future.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<a href="#deleteSite" data-toggle="modal" class="btn">Delete Site</a>
|
||||
</div>
|
||||
-->
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="modal hide fade" id="deleteSite" tabindex="-1" role="dialog" aria-labelledby="deleteSiteLabel" aria-hidden="true">
|
||||
<form method="POST" action="/site/delete">
|
||||
<%== csrf_token_input_html %>
|
||||
<div class="modal-header">
|
||||
<button class="close" type="button" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h3 id="deleteSiteLabel">Permanently Delete Site</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<strong style="color: red">WARNING: This will permanently delete your web site and Neocities account. There is no undo!</strong>
|
||||
|
||||
<p>Delete Site Name: <strong><%= current_site.username %></strong></p>
|
||||
<p>Confirm your site name by typing it here:</p>
|
||||
<input class="input-Area" name="username" type="text">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Cancel</button>
|
||||
<button type="submit" class="btn btn-Action">Permanently Delete Site</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
if (location.hash !== '') $('a[href="' + location.hash + '"]').tab('show');
|
||||
return $('a[data-toggle="tab"]').on('shown', function(e) {
|
||||
return location.hash = $(e.target).attr('href').substr(1);
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -1,8 +1,8 @@
|
|||
<h2>Change Email</h2>
|
||||
<form method="POST" action="/change_email">
|
||||
<form method="POST" action="/settings/change_email">
|
||||
<%== csrf_token_input_html %>
|
||||
|
||||
<p>Current Email: <strong><%= current_site.email %></strong></p>
|
||||
<p>Current Email: <strong><%= parent_site.email %></strong></p>
|
||||
<p>New Email:</p>
|
||||
<input class="input-Area" name="email" type="text">
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<h2>Change Password</h2>
|
||||
<form method="POST" action="/change_password">
|
||||
<form method="POST" action="/settings/change_password">
|
||||
<%== csrf_token_input_html %>
|
||||
|
||||
<p>Current Password:</p>
|
|
@ -1,11 +1,11 @@
|
|||
<h2>Neocities Plan</h2>
|
||||
<% if current_site.supporter? && !current_site.plan_ended %>
|
||||
<p class="tiny">You currently have the <strong>Supporter Plan (<%= current_site.maximum_space_in_megabytes %>MB)</strong>. Thank you! We love you.
|
||||
<p class="tiny">You currently have the <strong>Supporter Plan (<%= current_site.maximum_space.to_space_pretty %>)</strong>. Thank you! We love you.
|
||||
</p>
|
||||
<a class="btn-Action" href="/plan">Manage Plan</a>
|
||||
<% else %>
|
||||
<p class="tiny">
|
||||
You currently have the <strong>Free Plan (<%= current_site.maximum_space_in_megabytes %>MB)</strong>.<br>Want to get more space and help Neocities? Become a supporter!
|
||||
You currently have the <strong>Free Plan (<%= current_site.maximum_space.to_space_pretty %>)</strong>.<br>Want to get more space and help Neocities? Become a supporter!
|
||||
</p>
|
||||
<a class="btn-Action" href="/plan">Supporter Info</a>
|
||||
<% end %>
|
32
views/settings/account/sites.erb
Normal file
32
views/settings/account/sites.erb
Normal file
|
@ -0,0 +1,32 @@
|
|||
<h2>Your Sites</h2>
|
||||
|
||||
<table class="table">
|
||||
<% current_site.account_sites_dataset.each do |site| %>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="//<%= site.host %>" target="_blank"><%= site.username %></a>
|
||||
<% if site.parent? %>
|
||||
<strong>(parent account)</strong>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/settings/<%= site.username %>">Settings</a>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
<h3>Create New Site</h3>
|
||||
|
||||
<p>You can now create new sites that are linked to this account! Sites will share the free space you have available. You have <strong><%= Site::CHILD_SITES_MAX - current_site.account_sites_dataset.count %></strong> new sites remaining.</p>
|
||||
|
||||
<form action="/settings/create_child" method="POST">
|
||||
<%== csrf_token_input_html %>
|
||||
|
||||
<p>Site Name:</p>
|
||||
<input name="username" type="text">
|
||||
|
||||
<div>
|
||||
<input class="btn-Action" type="submit" value="Create New Site">
|
||||
</div>
|
||||
</form>
|
|
@ -1,4 +0,0 @@
|
|||
<h2>Custom Domain</h2>
|
||||
<p>
|
||||
You can configure a custom domain for your Neocities site! <strong><a href="/custom_domain">Click Here</a></strong> for more information.
|
||||
</p>
|
|
@ -1,7 +1,7 @@
|
|||
<div class="header-Outro">
|
||||
<div class="row content single-Col">
|
||||
<h1>Settings</h1>
|
||||
<h3 class="subtitle">Manage your account</h3>
|
||||
<h1>Site Settings for <%= @site.username %></h1>
|
||||
<h3 class="subtitle"><strong><a href="/settings">Click here</a> to go back to the account menu.</a></strong></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -9,76 +9,37 @@
|
|||
<article>
|
||||
<section>
|
||||
<div class="txt-Center">
|
||||
<% if !current_site.errors.empty? %>
|
||||
<div class="alert alert-block alert-error" style="margin-top: 20px">
|
||||
<% current_site.errors.each do |error| %>
|
||||
<p><%== error.last.first %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if flash[:success] %>
|
||||
<div class="alert alert-block alert-success" style="margin-top: 20px">
|
||||
<%== flash[:success] %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<!--
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="#">Plan</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Profile</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="">Custom Domain</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="">Password</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="">Email</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="">Username</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="">18+</a>
|
||||
</li>
|
||||
</ul>
|
||||
-->
|
||||
|
||||
<% if flash[:error] %>
|
||||
<div class="alert alert-block alert-error" style="margin-top: 20px">
|
||||
<%== flash[:error] %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="tabbable" style="margin-top: 20px"> <!-- Only required for left/right tabs -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#plan" data-toggle="tab">Plan</a></li>
|
||||
<li><a href="#profile" data-toggle="tab">Profile</a></li>
|
||||
<li><a href="#customdomain" data-toggle="tab">Custom Domain</a></li>
|
||||
<li><a href="#password" data-toggle="tab">Password</a></li>
|
||||
<li><a href="#email" data-toggle="tab">Email</a></li>
|
||||
<li class="active"><a href="#profile" data-toggle="tab">Profile</a></li>
|
||||
<li><a href="#custom_domain" data-toggle="tab">Custom Domain</a></li>
|
||||
<li><a href="#username" data-toggle="tab">Username</a></li>
|
||||
<li><a href="#nsfw" data-toggle="tab">18+</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="plan">
|
||||
<%== erb :'settings/plan' %>
|
||||
<div class="tab-pane active" id="profile">
|
||||
<%== erb :'settings/site/profile' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="profile">
|
||||
<%== erb :'settings/profile' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="customdomain">
|
||||
<%== erb :'settings/custom_domain' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="password">
|
||||
<%== erb :'settings/password' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="email">
|
||||
<%== erb :'settings/email' %>
|
||||
<div class="tab-pane" id="custom_domain">
|
||||
<%== erb :'settings/site/custom_domain' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="username">
|
||||
<%== erb :'settings/username' %>
|
||||
<%== erb :'settings/site/username' %>
|
||||
</div>
|
||||
<div class="tab-pane" id="nsfw">
|
||||
<%== erb :'settings/nsfw' %>
|
||||
<%== erb :'settings/site/nsfw' %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -116,4 +77,13 @@
|
|||
<button type="submit" class="btn btn-Action">Permanently Delete Site</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
if (location.hash !== '') $('a[href="' + location.hash + '"]').tab('show');
|
||||
return $('a[data-toggle="tab"]').on('shown', function(e) {
|
||||
return location.hash = $(e.target).attr('href').substr(1);
|
||||
});
|
||||
});
|
||||
</script>
|
60
views/settings/site/custom_domain.erb
Normal file
60
views/settings/site/custom_domain.erb
Normal file
|
@ -0,0 +1,60 @@
|
|||
<h2>Custom Domain</h2>
|
||||
<h3 class="subtitle">Add your own domain name to your Neocities site!</h3>
|
||||
|
||||
<p>
|
||||
Adding a custom domain allows you to have a domain name attached to your web site. So if you had a domain like <strong>catsknitting.com</strong>, you could have it point to your Neocities site!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You will have to purchase a domain name from a registrar like <a href="https://www.namecheap.com/?aff=53678" target="_blank">Namecheap</a>, and then add an A record to point your domain (catsknitting.com) to the following IP address:
|
||||
</p>
|
||||
|
||||
<p><code>198.27.81.179</code></p>
|
||||
|
||||
<p>
|
||||
If you want to add a <strong>www</strong> subdomain, or use a wildcard that will answer to everything (<strong>*</strong>), you will have to make a CNAME pointing to <strong>catsknitting.com</strong> for <strong>www</strong> and/or <strong>*</strong>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
After that, you can add the domain to the box below (just the <strong>catsknitting.com</strong>, don't add any subdomains), and your domain should come online within 5 minutes:
|
||||
</p>
|
||||
|
||||
<form method="POST" action="/settings/<%= @site.username %>/custom_domain">
|
||||
<%== csrf_token_input_html %>
|
||||
<input name="domain" type="text" placeholder="catsknitting.com" value="<%= @site.domain %>">
|
||||
<br>
|
||||
<input class="btn-Action" type="submit" value="Update Domain">
|
||||
</form>
|
||||
|
||||
<h2>Add SSL Certificate</h2>
|
||||
<p>
|
||||
This allows you to add an SSL key and certificate for your domain, enabling encryption for your site (https). It can take up to 5-30 minutes for the changes to go live, so please be patient. All files must be in PEM format. If your certificate is not bundled with the root and intermediate certificates, ask your certificate provider for help on how to do that.
|
||||
</p>
|
||||
|
||||
<% if @site.domain.nil? || @site.domain.empty? %>
|
||||
<p><strong>Cannot upload SSL certificate until domain is added.</strong></p>
|
||||
<% else %>
|
||||
|
||||
<form method="POST" action="/settings/<%= @site.username %>/ssl" enctype="multipart/form-data">
|
||||
<%== csrf_token_input_html %>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
Status: <%= @site.ssl_installed? ? 'Installed' : 'Inactive' %>
|
||||
</strong>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
SSL Key (yourdomain.com.key):
|
||||
<input name="key" type="file">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Bundled Certificates (yourdomain.com-bundle.crt):
|
||||
<input name="cert" type="file">
|
||||
</p>
|
||||
|
||||
<input class="btn-Action" type="submit" value="Upload SSL Key and Certificate">
|
||||
|
||||
</form>
|
||||
<% end %>
|
|
@ -4,14 +4,14 @@
|
|||
If your site contains objectionable (18+) content, check this box. Your site will not be removed, but it will be listed on a special browse page. We don't have an official policy on what defines 18+ content yet, but basically it's just pornography and lewd/sick/gross images. Thanks for your patience and understanding as we try to find a way to balance out the needs of everyone.
|
||||
</p>
|
||||
|
||||
<form method="POST" action="/change_nsfw">
|
||||
<form method="POST" action="/settings/<%= @site.username %>/change_nsfw">
|
||||
<%== csrf_token_input_html %>
|
||||
<input name="is_nsfw" type="hidden" value="false">
|
||||
<p>
|
||||
<strong>
|
||||
My page has 18+ content:
|
||||
<input name="is_nsfw" type="checkbox" value="true" style="margin-top: 0px"
|
||||
<% if current_site.is_nsfw %>checked<% end %>
|
||||
<% if @site.is_nsfw %>checked<% end %>
|
||||
>
|
||||
</strong>
|
||||
</p>
|
|
@ -1,11 +1,11 @@
|
|||
<h2>Site Profile</h2>
|
||||
<div>
|
||||
<form method="POST" action="/settings/profile">
|
||||
<form method="POST" action="/settings/<%= @site.username %>/profile">
|
||||
<%== csrf_token_input_html %>
|
||||
<p>
|
||||
<input name="site[profile_comments_enabled]" type="hidden" value="true">
|
||||
<input name="site[profile_comments_enabled]" type="checkbox" value="false"
|
||||
<% if current_site.profile_comments_enabled == false %>checked<% end %>
|
||||
<% if @site.profile_comments_enabled == false %>checked<% end %>
|
||||
> Turn off profile comments
|
||||
</p>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<h2>Change Site (User) Name</h2>
|
||||
<form method="POST" action="/change_name">
|
||||
<form method="POST" action="/settings/<%= @site.username %>/change_name">
|
||||
<%== csrf_token_input_html %>
|
||||
<p class="tiny">
|
||||
It cannot contain spaces, and can only use the following characters: a-z A-Z 0-9 _ -
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
<p>
|
||||
Current Name:
|
||||
<span style="color: green"><strong><%= current_site.username %></strong></span>
|
||||
<span style="color: green"><strong><%= @site.username %></strong></span>
|
||||
</p>
|
||||
|
||||
<p>
|
|
@ -1,6 +1,6 @@
|
|||
Hi there!
|
||||
|
||||
We just wanted to email you to confirm your supporter upgrade! You are on the <%= plan_name %> support tier. You now have <%= Site::SUPPORTER_MAXIMUM_IN_MEGABYTES %> MB of space (and we're likely to upgrade this even more soon). We're looking forward to seeing the awesome things you will do with it.
|
||||
We just wanted to email you to confirm your supporter upgrade! You are on the <%= plan_name %> support tier. You now have <%= Site::SUPPORTER_MAXIMUM.to_space_pretty %> of space (and we're likely to upgrade this even more soon). We're looking forward to seeing the awesome things you will do with it.
|
||||
|
||||
Thank you very, very much for supporting Neocities. It means a lot to us. It also helps us keep the free tier for others, so your support helps everyone. We'll try our hardest to keep improving the site and stick to our core values (NO MARKETING OR ADVERTISING, EVER).
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue