mirror of
https://github.com/neocities/neocities.git
synced 2025-04-24 17:22:35 +02:00
Merge branch 'v2' of github.com:neocities/neocities into v2
This commit is contained in:
commit
213faa91d7
9 changed files with 220 additions and 236 deletions
2
Gemfile
2
Gemfile
|
@ -11,7 +11,6 @@ gem 'puma', require: nil
|
||||||
gem 'rubyzip', require: 'zip'
|
gem 'rubyzip', require: 'zip'
|
||||||
gem 'rack-recaptcha', require: 'rack/recaptcha'
|
gem 'rack-recaptcha', require: 'rack/recaptcha'
|
||||||
gem 'rmagick', require: nil
|
gem 'rmagick', require: nil
|
||||||
gem 'selenium-webdriver', require: nil
|
|
||||||
gem 'sidekiq'
|
gem 'sidekiq'
|
||||||
gem 'ago'
|
gem 'ago'
|
||||||
gem 'mail'
|
gem 'mail'
|
||||||
|
@ -19,6 +18,7 @@ gem 'google-api-client', require: 'google/api_client'
|
||||||
gem 'tilt'
|
gem 'tilt'
|
||||||
gem 'erubis'
|
gem 'erubis'
|
||||||
gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby'
|
gem 'stripe', :git => 'https://github.com/stripe/stripe-ruby'
|
||||||
|
gem 'screencap'
|
||||||
|
|
||||||
platform :mri do
|
platform :mri do
|
||||||
gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic
|
gem 'magic' # sudo apt-get install file, For OSX: brew install libmagic
|
||||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -30,8 +30,6 @@ GEM
|
||||||
minitest (>= 2)
|
minitest (>= 2)
|
||||||
celluloid (0.15.2)
|
celluloid (0.15.2)
|
||||||
timers (~> 1.1.0)
|
timers (~> 1.1.0)
|
||||||
childprocess (0.5.2)
|
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
|
||||||
cliver (0.3.2)
|
cliver (0.3.2)
|
||||||
coderay (1.1.0)
|
coderay (1.1.0)
|
||||||
columnize (0.3.6)
|
columnize (0.3.6)
|
||||||
|
@ -136,11 +134,8 @@ GEM
|
||||||
rmagick (2.13.2)
|
rmagick (2.13.2)
|
||||||
rubyzip (1.1.2)
|
rubyzip (1.1.2)
|
||||||
safe_yaml (1.0.1)
|
safe_yaml (1.0.1)
|
||||||
selenium-webdriver (2.40.0)
|
screencap (0.1.1)
|
||||||
childprocess (>= 0.5.0)
|
phantomjs
|
||||||
multi_json (~> 1.0)
|
|
||||||
rubyzip (~> 1.0)
|
|
||||||
websocket (~> 1.0.4)
|
|
||||||
sequel (4.8.0)
|
sequel (4.8.0)
|
||||||
sequel_pg (1.6.9)
|
sequel_pg (1.6.9)
|
||||||
pg (>= 0.8.0)
|
pg (>= 0.8.0)
|
||||||
|
@ -189,7 +184,6 @@ GEM
|
||||||
webmock (1.17.4)
|
webmock (1.17.4)
|
||||||
addressable (>= 2.2.7)
|
addressable (>= 2.2.7)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
websocket (1.0.7)
|
|
||||||
websocket-driver (0.3.2)
|
websocket-driver (0.3.2)
|
||||||
xpath (2.0.0)
|
xpath (2.0.0)
|
||||||
nokogiri (~> 1.3)
|
nokogiri (~> 1.3)
|
||||||
|
@ -230,7 +224,7 @@ DEPENDENCIES
|
||||||
ruby-debug
|
ruby-debug
|
||||||
rubyzip
|
rubyzip
|
||||||
sass
|
sass
|
||||||
selenium-webdriver
|
screencap
|
||||||
sequel
|
sequel
|
||||||
sequel_pg
|
sequel_pg
|
||||||
shotgun
|
shotgun
|
||||||
|
|
60
app.rb
60
app.rb
|
@ -122,13 +122,13 @@ post '/plan/end' do
|
||||||
redirect '/plan' unless current_site.supporter? && !current_site.plan_ended
|
redirect '/plan' unless current_site.supporter? && !current_site.plan_ended
|
||||||
|
|
||||||
recaptcha_is_valid = ENV['RACK_ENV'] == 'test' || recaptcha_valid?
|
recaptcha_is_valid = ENV['RACK_ENV'] == 'test' || recaptcha_valid?
|
||||||
|
|
||||||
if !recaptcha_is_valid
|
if !recaptcha_is_valid
|
||||||
@error = 'Recaptcha was filled out incorrectly, please try re-entering.'
|
@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 current_site.stripe_customer_id
|
||||||
halt erb :'plan/end'
|
halt erb :'plan/end'
|
||||||
end
|
end
|
||||||
|
|
||||||
customer = Stripe::Customer.retrieve current_site.stripe_customer_id
|
customer = Stripe::Customer.retrieve current_site.stripe_customer_id
|
||||||
subscriptions = customer.subscriptions.all
|
subscriptions = customer.subscriptions.all
|
||||||
|
|
||||||
|
@ -389,38 +389,55 @@ get '/site_files/upload' do
|
||||||
slim :'site_files/upload'
|
slim :'site_files/upload'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def file_upload_response(error=nil)
|
||||||
|
http_error_code = 406
|
||||||
|
|
||||||
|
if params[:from_button]
|
||||||
|
if error
|
||||||
|
@error = error
|
||||||
|
halt 200, erb(:'dashboard')
|
||||||
|
else
|
||||||
|
redirect '/dashboard'
|
||||||
|
end
|
||||||
|
else
|
||||||
|
halt http_error_code, error if error
|
||||||
|
halt 200, 'File(s) successfully uploaded.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
post '/site_files/upload' do
|
post '/site_files/upload' do
|
||||||
require_login
|
require_login
|
||||||
@errors = []
|
@errors = []
|
||||||
http_error_code = 406
|
http_error_code = 406
|
||||||
|
|
||||||
if params[:newfile] == '' || params[:newfile].nil?
|
params[:files].each do |file|
|
||||||
@errors << 'You must select a file to upload.'
|
if current_site.file_size_too_large? file[:tempfile].size
|
||||||
halt http_error_code, 'Did not receive file upload.'
|
file_upload_response "#{file[:filename]} is too large, upload cancelled."
|
||||||
|
end
|
||||||
|
|
||||||
|
if !Site.valid_file_type? file
|
||||||
|
file_upload_response "#{file[:filename]}: file type is not allowed on Neocities, upload cancelled."
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if current_site.file_size_too_large? params[:newfile][:tempfile].size
|
uploaded_size = params[:files].collect {|f| f[:tempfile].size}.inject{|sum,x| sum + x }
|
||||||
@errors << 'File size must be smaller than available space.'
|
|
||||||
halt http_error_code, 'File size must be smaller than available space.'
|
if current_site.file_size_too_large? uploaded_size
|
||||||
|
file_upload_response "File(s) do not fit in your available space, upload cancelled."
|
||||||
end
|
end
|
||||||
|
|
||||||
unless Site.valid_file_type? params[:newfile]
|
params[:files].each do |file|
|
||||||
@errors << 'File must me one of the following: HTML, Text, Image (JPG PNG GIF JPEG SVG), JS, CSS, Markdown.'
|
current_site.store_file Site.sanitize_filename(file[:filename]), file[:tempfile]
|
||||||
halt http_error_code, 'File type is not supported.' # slim(:'site_files/new')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
sanitized_filename = params[:newfile][:filename].gsub(/[^a-zA-Z0-9_\-.]/, '')
|
|
||||||
|
|
||||||
current_site.store_file sanitized_filename, params[:newfile][:tempfile]
|
|
||||||
current_site.increment_changed_count
|
current_site.increment_changed_count
|
||||||
|
|
||||||
flash[:success] = "Successfully uploaded file #{sanitized_filename}."
|
file_upload_response
|
||||||
redirect '/dashboard'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
post '/site_files/delete' do
|
post '/site_files/delete' do
|
||||||
require_login
|
require_login
|
||||||
sanitized_filename = params[:filename].gsub(/[^a-zA-Z0-9_\-.]/, '')
|
sanitized_filename = Site.sanitize_filename params[:filename]
|
||||||
|
|
||||||
current_site.delete_file(sanitized_filename)
|
current_site.delete_file(sanitized_filename)
|
||||||
|
|
||||||
|
@ -468,17 +485,10 @@ post '/site_files/save/:filename' do |filename|
|
||||||
halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.'
|
halt 'File is too large to fit in your space, it has NOT been saved. Please make a local copy and then try to reduce the size.'
|
||||||
end
|
end
|
||||||
|
|
||||||
sanitized_filename = filename.gsub(/[^a-zA-Z0-9_\-.]/, '')
|
sanitized_filename = Site.sanitize_filename filename
|
||||||
|
|
||||||
current_site.store_file sanitized_filename, tempfile
|
current_site.store_file sanitized_filename, tempfile
|
||||||
|
|
||||||
if sanitized_filename =~ /index\.html/
|
|
||||||
ScreenshotWorker.perform_async current_site.username
|
|
||||||
current_site.update site_changed: true
|
|
||||||
end
|
|
||||||
|
|
||||||
current_site.update changed_count: 1+current_site.changed_count, updated_at: Time.now
|
|
||||||
|
|
||||||
'ok'
|
'ok'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Site < Sequel::Model
|
||||||
application/xml
|
application/xml
|
||||||
audio/midi
|
audio/midi
|
||||||
}
|
}
|
||||||
VALID_EXTENSIONS = %w{
|
VALID_EXTENSIONS = %w{
|
||||||
html htm txt text css js jpg jpeg png gif svg md markdown eot ttf woff json
|
html htm txt text css js jpg jpeg png gif svg md markdown eot ttf woff json
|
||||||
geojson csv tsv mf ico pdf asc key pgp xml mid midi
|
geojson csv tsv mf ico pdf asc key pgp xml mid midi
|
||||||
}
|
}
|
||||||
|
@ -40,28 +40,30 @@ class Site < Sequel::Model
|
||||||
VALID_HOSTNAME = /^[a-z0-9][a-z0-9-]+?[a-z0-9]$/i # http://tools.ietf.org/html/rfc1123
|
VALID_HOSTNAME = /^[a-z0-9][a-z0-9-]+?[a-z0-9]$/i # http://tools.ietf.org/html/rfc1123
|
||||||
|
|
||||||
# FIXME smarter DIR_ROOT discovery
|
# FIXME smarter DIR_ROOT discovery
|
||||||
DIR_ROOT = './'
|
DIR_ROOT = './'
|
||||||
TEMPLATE_ROOT = File.join DIR_ROOT, 'views', 'templates'
|
TEMPLATE_ROOT = File.join DIR_ROOT, 'views', 'templates'
|
||||||
PUBLIC_ROOT = File.join DIR_ROOT, 'public'
|
PUBLIC_ROOT = File.join DIR_ROOT, 'public'
|
||||||
SITE_FILES_ROOT = File.join PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'sites_test' : 'sites')
|
SITE_FILES_ROOT = File.join PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'sites_test' : 'sites')
|
||||||
|
SCREENSHOTS_ROOT = File.join(PUBLIC_ROOT, (ENV['RACK_ENV'] == 'test' ? 'site_screenshots_test' : 'site_screenshots'))
|
||||||
|
SCREENSHOTS_URL_ROOT = '/site_screenshots'
|
||||||
|
|
||||||
many_to_one :server
|
many_to_one :server
|
||||||
|
|
||||||
many_to_many :tags
|
many_to_many :tags
|
||||||
|
|
||||||
one_to_many :follows
|
one_to_many :follows
|
||||||
one_to_many :followings, key: :actioning_site_id, class: :Follow
|
one_to_many :followings, key: :actioning_site_id, class: :Follow
|
||||||
|
|
||||||
one_to_many :tips
|
one_to_many :tips
|
||||||
one_to_many :tippings, key: :actioning_site_id, class: :Tip
|
one_to_many :tippings, key: :actioning_site_id, class: :Tip
|
||||||
|
|
||||||
one_to_many :blocks
|
one_to_many :blocks
|
||||||
one_to_many :blockings, key: :actioning_site_id, class: :Block
|
one_to_many :blockings, key: :actioning_site_id, class: :Block
|
||||||
|
|
||||||
one_to_many :stats
|
one_to_many :stats
|
||||||
|
|
||||||
one_to_many :events
|
one_to_many :events
|
||||||
|
|
||||||
one_to_many :changes
|
one_to_many :changes
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
@ -128,6 +130,7 @@ class Site < Sequel::Model
|
||||||
|
|
||||||
%w{index not_found}.each do |name|
|
%w{index not_found}.each do |name|
|
||||||
File.write file_path("#{name}.html"), render_template("#{name}.erb")
|
File.write file_path("#{name}.html"), render_template("#{name}.erb")
|
||||||
|
ScreenshotWorker.perform_async values[:username], "#{name}.html"
|
||||||
end
|
end
|
||||||
|
|
||||||
FileUtils.cp template_file_path('cat.png'), file_path('cat.png')
|
FileUtils.cp template_file_path('cat.png'), file_path('cat.png')
|
||||||
|
@ -162,7 +165,7 @@ class Site < Sequel::Model
|
||||||
def self.valid_file_type?(uploaded_file)
|
def self.valid_file_type?(uploaded_file)
|
||||||
mime_type = Magic.guess_file_mime_type uploaded_file[:tempfile].path
|
mime_type = Magic.guess_file_mime_type uploaded_file[:tempfile].path
|
||||||
|
|
||||||
return true if (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) &&
|
return true if (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) &&
|
||||||
Site::VALID_EXTENSIONS.include?(File.extname(uploaded_file[:filename]).sub(/^./, '').downcase)
|
Site::VALID_EXTENSIONS.include?(File.extname(uploaded_file[:filename]).sub(/^./, '').downcase)
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -171,13 +174,13 @@ class Site < Sequel::Model
|
||||||
FileUtils.mv uploaded.path, file_path(filename)
|
FileUtils.mv uploaded.path, file_path(filename)
|
||||||
File.chmod(0640, file_path(filename))
|
File.chmod(0640, file_path(filename))
|
||||||
|
|
||||||
if filename =~ /index\.html/
|
ScreenshotWorker.perform_async values[:username], filename
|
||||||
ScreenshotWorker.perform_async values[:username]
|
|
||||||
self.site_changed = true
|
self.site_changed = true
|
||||||
save(validate: false)
|
self.changed_count += 1
|
||||||
end
|
save(validate: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment_changed_count
|
def increment_changed_count
|
||||||
self.changed_count += 1
|
self.changed_count += 1
|
||||||
self.updated_at = Time.now
|
self.updated_at = Time.now
|
||||||
|
@ -250,14 +253,14 @@ class Site < Sequel::Model
|
||||||
if new? && values[:username].length > 2 && !values[:username].match(VALID_HOSTNAME)
|
if new? && values[:username].length > 2 && !values[:username].match(VALID_HOSTNAME)
|
||||||
errors.add :username, 'A valid user/site name is required.'
|
errors.add :username, 'A valid user/site name is required.'
|
||||||
end
|
end
|
||||||
|
|
||||||
if new? && values[:username].length > 32
|
if new? && values[:username].length > 32
|
||||||
errors.add :username, 'User/site name cannot exceed 32 characters.'
|
errors.add :username, 'User/site name cannot exceed 32 characters.'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for existing user
|
# Check for existing user
|
||||||
user = self.class.select(:id, :username).filter(username: values[:username]).first
|
user = self.class.select(:id, :username).filter(username: values[:username]).first
|
||||||
|
|
||||||
if user
|
if user
|
||||||
if user.id != values[:id]
|
if user.id != values[:id]
|
||||||
errors.add :username, 'This username is already taken. Try using another one.'
|
errors.add :username, 'This username is already taken. Try using another one.'
|
||||||
|
@ -267,7 +270,7 @@ class Site < Sequel::Model
|
||||||
if values[:password].nil? || (@password_length && @password_length < MINIMUM_PASSWORD_LENGTH)
|
if values[:password].nil? || (@password_length && @password_length < MINIMUM_PASSWORD_LENGTH)
|
||||||
errors.add :password, "Password must be at least #{MINIMUM_PASSWORD_LENGTH} characters."
|
errors.add :password, "Password must be at least #{MINIMUM_PASSWORD_LENGTH} characters."
|
||||||
end
|
end
|
||||||
|
|
||||||
if !values[:domain].nil? && !values[:domain].empty?
|
if !values[:domain].nil? && !values[:domain].empty?
|
||||||
if !(values[:domain] =~ /^[a-zA-Z0-9.-]+\.[a-zA-Z0-9]+$/i) || values[:domain].length > 90
|
if !(values[:domain] =~ /^[a-zA-Z0-9.-]+\.[a-zA-Z0-9]+$/i) || values[:domain].length > 90
|
||||||
errors.add :domain, "Domain provided is not valid. Must take the form of domain.com"
|
errors.add :domain, "Domain provided is not valid. Must take the form of domain.com"
|
||||||
|
@ -291,7 +294,7 @@ class Site < Sequel::Model
|
||||||
def files_path(name=nil)
|
def files_path(name=nil)
|
||||||
File.join SITE_FILES_ROOT, (name || username)
|
File.join SITE_FILES_ROOT, (name || username)
|
||||||
end
|
end
|
||||||
|
|
||||||
def file_path(filename)
|
def file_path(filename)
|
||||||
File.join files_path, filename
|
File.join files_path, filename
|
||||||
end
|
end
|
||||||
|
@ -309,7 +312,7 @@ class Site < Sequel::Model
|
||||||
space = Dir.glob(File.join(files_path, '*')).collect {|p| File.size(p)}.inject {|sum,x| sum += x}
|
space = Dir.glob(File.join(files_path, '*')).collect {|p| File.size(p)}.inject {|sum,x| sum += x}
|
||||||
space.nil? ? 0 : space
|
space.nil? ? 0 : space
|
||||||
end
|
end
|
||||||
|
|
||||||
def used_space_in_megabytes
|
def used_space_in_megabytes
|
||||||
(used_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
(used_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
||||||
end
|
end
|
||||||
|
@ -318,7 +321,7 @@ class Site < Sequel::Model
|
||||||
remaining = maximum_space_in_bytes - used_space_in_bytes
|
remaining = maximum_space_in_bytes - used_space_in_bytes
|
||||||
remaining < 0 ? 0 : remaining
|
remaining < 0 ? 0 : remaining
|
||||||
end
|
end
|
||||||
|
|
||||||
def available_space_in_megabytes
|
def available_space_in_megabytes
|
||||||
(available_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
(available_space_in_bytes.to_f / self.class::ONE_MEGABYTE_IN_BYTES).round(2)
|
||||||
end
|
end
|
||||||
|
@ -339,18 +342,22 @@ class Site < Sequel::Model
|
||||||
def supporter?
|
def supporter?
|
||||||
!values[:stripe_customer_id].nil?
|
!values[:stripe_customer_id].nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
# This will return false if they have ended their support plan.
|
# This will return false if they have ended their support plan.
|
||||||
def ended_supporter?
|
def ended_supporter?
|
||||||
values[:ended_plan]
|
values[:ended_plan]
|
||||||
end
|
end
|
||||||
|
|
||||||
def plan_name
|
def plan_name
|
||||||
return 'Free Plan' if !supporter? || (supporter? && ended_supporter?)
|
return 'Free Plan' if !supporter? || (supporter? && ended_supporter?)
|
||||||
'Supporter Plan'
|
'Supporter Plan'
|
||||||
end
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
values[:title] || values[:username]
|
values[:title] || values[:username]
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
def screenshot_url(filename, resolution)
|
||||||
|
"#{SCREENSHOTS_URL_ROOT}/#{values[:username]}/#{filename}.#{resolution}.jpg"
|
||||||
|
end
|
||||||
|
end
|
2
public/js/dropzone.min.js
vendored
2
public/js/dropzone.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -49,7 +49,7 @@
|
||||||
<% @sites.each do |site| %>
|
<% @sites.each do |site| %>
|
||||||
<li>
|
<li>
|
||||||
<a href="http://<%= site.username %>.neocities.org" class="neo-Screen-Shot" target="_blank" title="Website of <%= site.username %>">
|
<a href="http://<%= site.username %>.neocities.org" class="neo-Screen-Shot" target="_blank" title="Website of <%= site.username %>">
|
||||||
<span class="img-Holder" style="background:url(/site_screenshots/<%= site.username %>.jpg) no-repeat;">
|
<span class="img-Holder" style="background:url(<%= site.screenshot_url('index.html', '270x162') %>) no-repeat;">
|
||||||
<img src="/img/placeholder.png" alt="<%= site.username %>" />
|
<img src="/img/placeholder.png" alt="<%= site.username %>" />
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,14 +1,28 @@
|
||||||
<script type="text/javascript">
|
<style>
|
||||||
function confirmFileDelete(name) {
|
.dz-default {
|
||||||
$('#deleteFileName').html(name);
|
display: none;
|
||||||
$('#deleteConfirmModal').modal();
|
|
||||||
}
|
}
|
||||||
|
.dz-preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dz-processing {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dz-error {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dz-image-preview {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<!--
|
||||||
|
<form action="/site_files/upload" class="dropzone" id="uploads">
|
||||||
|
<input name="csrf_token" type="hidden" value="#{csrf_token}">
|
||||||
|
</form>
|
||||||
|
|
||||||
function fileDelete() {
|
|
||||||
$('#deleteFilenameInput').val($('#deleteFileName').html());
|
<div id="upload_status" style="font-size: 16pt"></div>
|
||||||
$('#deleteFilenameForm').submit();
|
-->
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="header-Outro">
|
<div class="header-Outro">
|
||||||
<div class="row content wide" style="padding-top: 10px">
|
<div class="row content wide" style="padding-top: 10px">
|
||||||
|
@ -16,20 +30,20 @@
|
||||||
<div class="col col-50 signup-Area" style="width: 289px;">
|
<div class="col col-50 signup-Area" style="width: 289px;">
|
||||||
<div class="signup-Form">
|
<div class="signup-Form">
|
||||||
<fieldset class="content">
|
<fieldset class="content">
|
||||||
<img class="screenshot" src="/site_screenshots/<%= current_site.username %>.jpg">
|
<img class="screenshot" src="<%= current_site.screenshot_url('index.html', '270x162') %>">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col col-50">
|
<div class="col col-50">
|
||||||
<h2 class="eps">My Website</h2>
|
<h2 class="eps">My Website</h2>
|
||||||
<p style="font-size:19px; margin-top: -11px; margin-bottom: 8px;"><a href="http://<%= current_site.username %>.neocities.org" target="_blank">http://<%= current_site.username %>.neocities.org</a></p>
|
<p style="font-size:19px; margin-top: -11px; margin-bottom: 8px;"><a href="http://<%= current_site.username %>.neocities.org" target="_blank">http://<%= current_site.username %>.neocities.org</a></p>
|
||||||
<ul>
|
<ul>
|
||||||
<% if current_site.updated_at %>
|
<% if current_site.updated_at %>
|
||||||
<li>Last updated <%= current_site.updated_at %></li>
|
<li>Last updated <%= current_site.updated_at.ago %></li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<li>Using <strong><%= current_site.space_percentage_used %>% of your <%= current_site.maximum_space_in_megabytes %> MB</strong>. <% if !current_site.supporter? %>Need more space? <a href="/plan">Become a Supporter!</a><% end %></li>
|
<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>. <% 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> visitors</li>
|
<li><strong><%= current_site.hits.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse %></strong> hits</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -37,9 +51,9 @@
|
||||||
</div> <!-- end .header-Outro -->
|
</div> <!-- end .header-Outro -->
|
||||||
|
|
||||||
<main class="content-Base">
|
<main class="content-Base">
|
||||||
|
|
||||||
<div class="content wide">
|
<div class="content wide">
|
||||||
|
|
||||||
<div class="welcome">
|
<div class="welcome">
|
||||||
<div class="close-button"></div>
|
<div class="close-button"></div>
|
||||||
<h4>Hello! Welcome to your new site.</h4>
|
<h4>Hello! Welcome to your new site.</h4>
|
||||||
|
@ -52,53 +66,55 @@
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<a href="/site_files/new_page" class="btn-Action new-Page"><span>New Page</span></a>
|
<a href="/site_files/new_page" class="btn-Action new-Page"><span>New Page</span></a>
|
||||||
<!--<a href="" class="btn-Action new-Folder"><span>New Folder</span></a>-->
|
<!--<a href="" class="btn-Action new-Folder"><span>New Folder</span></a>-->
|
||||||
<a href="/site_files/upload" class="btn-Action upload"><span>Upload</span></a>
|
<a href="#" class="btn-Action upload" onclick="clickUploadFiles(); return false"><span>Upload</span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="upload-Boundary with-instruction"> <!--should remove instruction after at least one new file have been uploaded-->
|
<form action="/site_files/upload" class="dropzone" id="uploads">
|
||||||
|
<div class="dz-message" style="display: none"></div>
|
||||||
<% current_site.file_list.each do |file| %>
|
<input name="csrf_token" type="hidden" value="<%= csrf_token %>">
|
||||||
<div class="file filehover">
|
<div class="upload-Boundary with-instruction"> <!--should remove instruction after at least one new file have been uploaded-->
|
||||||
<% if file.ext.match(/html|htm/) %>
|
<% current_site.file_list.each do |file| %>
|
||||||
<div class="html-thumbnail html fileimagehover">
|
<div class="file filehover">
|
||||||
<img src="https://neocities.org//site_screenshots/bigpig.jpg">
|
<% if file.ext.match(/html|htm/) %>
|
||||||
<div class="overlay"></div>
|
<div class="html-thumbnail html fileimagehover">
|
||||||
</div>
|
<img src="<%= current_site.screenshot_url(file.filename, '105x63') %>">
|
||||||
<% elsif file.ext.match(/jpg|png|bmp|gif/) %>
|
<div class="overlay"></div>
|
||||||
<div class="html-thumbnail image fileimagehover">
|
</div>
|
||||||
<img src="https://neocities.org/assets/img/cat-larger.png" style="">
|
<% elsif file.ext.match(/jpg|png|bmp|gif/) %>
|
||||||
<div class="overlay"></div>
|
<div class="html-thumbnail image fileimagehover">
|
||||||
</div>
|
<img src="https://neocities.org/assets/img/cat-larger.png" style="">
|
||||||
<% else %>
|
<div class="overlay"></div>
|
||||||
<div class="html-thumbnail misc fileimagehover">
|
</div>
|
||||||
<div class="misc-icon"><%= file.ext %></div>
|
|
||||||
<div class="overlay"></div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<a class="title">
|
|
||||||
<% if file.filename.length > 15 %>
|
|
||||||
<%= file.filename.slice(0..15) %>…
|
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= file.filename %>
|
<div class="html-thumbnail misc fileimagehover">
|
||||||
|
<div class="misc-icon"><%= file.ext %></div>
|
||||||
|
<div class="overlay"></div>
|
||||||
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</a>
|
|
||||||
<div class="overlay">
|
<a class="title">
|
||||||
<% if file.ext.match(/html|htm|txt|js|css|md/) %>
|
<% if file.filename.length > 15 %>
|
||||||
<a href="/site_files/text_editor/<%= file.filename %>"><i class="icon-edit" style="margin-right: 10px" title="Edit"> Edit</i></a>
|
<%= file.filename.slice(0..15) %>…
|
||||||
<% end %>
|
<% else %>
|
||||||
<% if file.filename != 'index.html' %>
|
<%= file.filename %>
|
||||||
<a href="#" onclick="confirmFileDelete('<%= file.filename %>')"><i class="icon-trash" style="margin-right: 10px" title="Delete"> Delete</i></a>
|
<% end %>
|
||||||
<% end %>
|
</a>
|
||||||
<a class="link-overlay" href="http://<%= current_site.username %>.neocities.org/<%= file.filename %>" title="View <%= file.filename %>" target="_blank"></a>
|
<div class="overlay">
|
||||||
|
<% if file.ext.match(/html|htm|txt|js|css|md/) %>
|
||||||
|
<a href="/site_files/text_editor/<%= file.filename %>"><i class="icon-edit" style="margin-right: 10px" title="Edit"> Edit</i></a>
|
||||||
|
<% end %>
|
||||||
|
<% if file.filename != 'index.html' %>
|
||||||
|
<a href="#" onclick="confirmFileDelete('<%= file.filename %>')"><i class="icon-trash" style="margin-right: 10px" title="Delete"> Delete</i></a>
|
||||||
|
<% end %>
|
||||||
|
<a class="link-overlay" href="http://<%= current_site.username %>.neocities.org/<%= file.filename %>" title="View <%= file.filename %>" target="_blank"></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% end %>
|
||||||
<% end %>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form method="POST" action="/site_files/delete" id="deleteFilenameForm">
|
<form method="POST" action="/site_files/delete" id="deleteFilenameForm">
|
||||||
<input name="csrf_token" type="hidden" value="<%= csrf_token %>">
|
<input name="csrf_token" type="hidden" value="<%= csrf_token %>">
|
||||||
<input type="hidden" id="deleteFilenameInput" name="filename">
|
<input type="hidden" id="deleteFilenameInput" name="filename">
|
||||||
|
@ -123,4 +139,50 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<form id="uploadFilesButtonForm" method="POST" action="/site_files/upload" enctype=multipart/form-data style="display: none">
|
||||||
|
<input name="csrf_token" type="hidden" value="<%= csrf_token %>">
|
||||||
|
<input name="from_button" type="hidden" value="true">
|
||||||
|
<input id="uploadFiles" type="file" name="files[]" multiple onchange="$('#uploadFilesButtonForm').submit()">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script src="/js/dropzone.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function confirmFileDelete(name) {
|
||||||
|
$('#deleteFileName').html(name);
|
||||||
|
$('#deleteConfirmModal').modal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileDelete() {
|
||||||
|
$('#deleteFilenameInput').val($('#deleteFileName').html());
|
||||||
|
$('#deleteFilenameForm').submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickUploadFiles() {
|
||||||
|
$("input[id='uploadFiles']").click()
|
||||||
|
}
|
||||||
|
|
||||||
|
Dropzone.options.uploads = {
|
||||||
|
paramName: 'files',
|
||||||
|
maxFilesize: <%= current_site.available_space_in_megabytes %>,
|
||||||
|
clickable: false,
|
||||||
|
addRemoveLinks: false,
|
||||||
|
dictDefaultMessage: '',
|
||||||
|
uploadMultiple: true,
|
||||||
|
init: function() {
|
||||||
|
this.on("successmultiple", function(file) {
|
||||||
|
location.reload()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.on("error", function(file, errorMessage) {
|
||||||
|
alert('Failed: '+errorMessage)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.on("uploadprogress", function(file, progress) {
|
||||||
|
// $('#upload_progress_bar').css('width', progress+'%');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
css:
|
|
||||||
#files a {
|
|
||||||
color: #D00000;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
javascript:
|
|
||||||
function confirmFileDelete(name) {
|
|
||||||
$('#deleteFileName').html(name);
|
|
||||||
$('#deleteConfirmModal').modal();
|
|
||||||
}
|
|
||||||
|
|
||||||
function fileDelete() {
|
|
||||||
$('#deleteFilenameInput').val($('#deleteFileName').html());
|
|
||||||
$('#deleteFilenameForm').submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-Base
|
|
||||||
.row.content
|
|
||||||
.col.col-60
|
|
||||||
|
|
||||||
h1 Your Website
|
|
||||||
p These are the files on your site. <strong>index.html</strong> is the main page, it is the first page loaded when users visit your site. <strong>not_found.html</strong> is the page users see when they try to access a file on the site that's not there (it is also commonly referred to as the 404 page).
|
|
||||||
- current_site.file_list.each do |file|
|
|
||||||
hr style="margin: 12px 0px"
|
|
||||||
.row style="margin-bottom: 0px" id="files"
|
|
||||||
.col style="padding-left: 20px;"
|
|
||||||
a href="http://#{current_site.username}.neocities.org/#{file.filename}" title="View #{file.filename}" target="_blank"
|
|
||||||
- if file.ext.match(/jpg|png|bmp|gif/)
|
|
||||||
i class="icon-picture icon": span style="margin-left: 5px" #{file.filename}
|
|
||||||
- else
|
|
||||||
i class="icon-file-alt icon": span style="margin-left: 10px;" #{file.filename}
|
|
||||||
span class="float-Right"
|
|
||||||
- if file.ext.match(/html|htm|txt|js|css|md/)
|
|
||||||
a href="/site_files/text_editor/#{file.filename}" : i class="icon-edit" style="margin-right: 10px" title="Edit"
|
|
||||||
- else
|
|
||||||
span style="margin-left: 25px"
|
|
||||||
|
|
||||||
a href="/site_files/download/#{file.filename}": i class="icon-download" style="margin-right: 10px" title="Download"
|
|
||||||
- if file.filename != 'index.html'
|
|
||||||
a href="#" onclick="confirmFileDelete('#{file.filename}')": i class="icon-trash" style="margin-right: 10px" title="Delete"
|
|
||||||
- else
|
|
||||||
span style="margin-left: 22px"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.col.col-40
|
|
||||||
|
|
||||||
div.txt-Center.dash-SS
|
|
||||||
a href="http://#{current_site.username}.neocities.org" style="display:block" target="_blank": img src="/site_screenshots/#{current_site.username}.jpg" alt="screen shot"
|
|
||||||
br
|
|
||||||
h3 class="base" style="line-height:1": a href="http://#{current_site.username}.neocities.org" target="_blank" http://#{current_site.username}.neocities.org
|
|
||||||
p Your page has been accessed <b>#{current_site.hits.to_s.reverse.gsub(/...(?=.)/,'\&,').reverse}</b> times.
|
|
||||||
p: small
|
|
||||||
|
|
|
||||||
(access statistics updated hourly)
|
|
||||||
|
|
||||||
.progress.progress-info.progress-striped
|
|
||||||
.bar style="width: #{(current_site.total_space / Site::MAX_SPACE.to_f) * 100}%"
|
|
||||||
|
|
||||||
h4.base.txt-Center
|
|
||||||
p Using #{((current_site.total_space.to_f / Site::MAX_SPACE) * 100).round(1)}% (#{current_site.total_space_in_megabytes}MB) of your #{(Site::MAX_SPACE.to_f / 2**20).to_i}MB of free space.
|
|
||||||
|
|
||||||
div.txt-Center
|
|
||||||
a href="/site_files/upload" class="btn-Action" style="margin-bottom:10px" Upload New Files
|
|
||||||
br
|
|
||||||
a href="/site_files/new_page" class="btn-Action" style="margin-bottom:10px" Create New HTML Page
|
|
||||||
br
|
|
||||||
a href="/site_files/#{current_site.username}.zip" class="btn-Action" Download Entire Site
|
|
||||||
|
|
||||||
hr
|
|
||||||
|
|
||||||
div
|
|
||||||
<i class="icon-question-sign icon-3x"></i> <span style="font-size: 20pt">Need to learn web design?</span>
|
|
||||||
br
|
|
||||||
i class="icon-globe"
|
|
||||||
a href="/tutorials" target="_blank" Web Design, HTML, CSS, JavaScript Tutorials
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
form method="POST" action="/site_files/delete" id="deleteFilenameForm"
|
|
||||||
input name="csrf_token" type="hidden" value="#{csrf_token}"
|
|
||||||
input type="hidden" id="deleteFilenameInput" name="filename"
|
|
||||||
|
|
||||||
.modal.hide.fade id="deleteConfirmModal" tabindex="-1" role="dialog" aria-labelledby="deleteConfirmModalLabel" aria-hidden="true"
|
|
||||||
.modal-header
|
|
||||||
button.close type="button" data-dismiss="modal" aria-hidden="true" x
|
|
||||||
h3 id="deleteConfirmModalLabel" Confirm delete of file
|
|
||||||
.modal-body
|
|
||||||
p You are about to delete the file <span id="deleteFileName"></span>. Are you sure?
|
|
||||||
.modal-footer
|
|
||||||
button.btn data-dismiss="modal" aria-hidden="true" Cancel
|
|
||||||
button.btn.btn-danger onclick="fileDelete()" Delete
|
|
|
@ -1,34 +1,40 @@
|
||||||
require 'selenium-webdriver'
|
|
||||||
require 'RMagick'
|
require 'RMagick'
|
||||||
|
|
||||||
class ScreenshotWorker
|
class ScreenshotWorker
|
||||||
|
REQUIRED_RESOLUTIONS = ['235x141', '105x63', '270x162']
|
||||||
|
SCREENSHOTS_PATH = File.join DIR_ROOT, 'public', 'site_screenshots'
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
sidekiq_options queue: :screenshots, retry: 3, backtrace: true
|
sidekiq_options queue: :screenshots, retry: 3, backtrace: true
|
||||||
|
|
||||||
def perform(username)
|
def perform(username, filename)
|
||||||
screenshot = Tempfile.new 'neocities_screenshot'
|
screenshot = Tempfile.new 'neocities_screenshot'
|
||||||
screenshot.close
|
screenshot.close
|
||||||
|
screenshot_output_path = screenshot.path+'.png'
|
||||||
|
|
||||||
caps = Selenium::WebDriver::Remote::Capabilities.htmlunit javascript_enabled: true, takesScreenshot: true
|
f = Screencap::Fetcher.new("http://#{username}.neocities.org/#{filename}")
|
||||||
|
f.fetch(
|
||||||
|
output: screenshot_output_path,
|
||||||
|
width: 1280,
|
||||||
|
height: 720
|
||||||
|
)
|
||||||
|
|
||||||
driver = Selenium::WebDriver.for :remote, url: $config['phantomjs_url'][rand($config['phantomjs_url'].length)], desired_capabilities: caps
|
|
||||||
driver.manage.window.resize_to 1280, 720
|
|
||||||
|
|
||||||
wait = Selenium::WebDriver::Wait.new(timeout: 10) # seconds
|
|
||||||
wait.until {
|
|
||||||
driver.navigate.to "http://#{username}.neocities.org"
|
|
||||||
driver.save_screenshot screenshot.path
|
|
||||||
}
|
|
||||||
|
|
||||||
driver.quit
|
|
||||||
|
|
||||||
img_list = Magick::ImageList.new
|
img_list = Magick::ImageList.new
|
||||||
img_list.read screenshot.path
|
img_list.from_blob File.read(screenshot_output_path)
|
||||||
|
|
||||||
screenshot.unlink
|
screenshot.unlink
|
||||||
|
File.unlink screenshot_output_path
|
||||||
|
|
||||||
img_list.new_image(img_list.first.columns, img_list.first.rows) { self.background_color = "white" }
|
img_list.new_image(img_list.first.columns, img_list.first.rows) { self.background_color = "white" }
|
||||||
img = img_list.reverse.flatten_images
|
img = img_list.reverse.flatten_images
|
||||||
img.crop!(0, 0, 1280, 720)
|
|
||||||
img.resize! 208, 125
|
user_screenshots_path = File.join SCREENSHOTS_PATH, username
|
||||||
img.write File.join(DIR_ROOT, 'public', 'site_screenshots', "#{username}.jpg")
|
FileUtils.mkdir_p user_screenshots_path
|
||||||
|
|
||||||
|
REQUIRED_RESOLUTIONS.each do |res|
|
||||||
|
img.scale(*res.split('x').collect {|r| r.to_i}).write(File.join(user_screenshots_path, "#{filename}.#{res}.jpg")) {
|
||||||
|
self.quality = 90
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
Loading…
Add table
Reference in a new issue