the beginning of a new blog
This commit is contained in:
parent
0254610fcf
commit
2ac3320d34
9
Gemfile
9
Gemfile
|
@ -1,10 +1,9 @@
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'puma'
|
gem 'sequel'
|
||||||
|
gem 'do_postgres'
|
||||||
gem 'zero', :git => 'https://github.com/libzero/zero.git'
|
gem 'zero', :git => 'https://github.com/libzero/zero.git'
|
||||||
gem 'slim'
|
gem 'slim'
|
||||||
gem 'sass'
|
gem 'sass'
|
||||||
gem 'compass'
|
gem 'puma'
|
||||||
gem 'kramdown'
|
gem 'rubysl', :platforms => :rbx
|
||||||
gem 'sequel'
|
|
||||||
gem 'do_postgres'
|
|
||||||
|
|
244
Gemfile.lock
244
Gemfile.lock
|
@ -1,6 +1,6 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/libzero/zero.git
|
remote: https://github.com/libzero/zero.git
|
||||||
revision: d92dea46954b388ec13bc53c6e4f9894f25863c4
|
revision: 3e0a3f20a79330f1f152a5f3667b29cb904d959c
|
||||||
specs:
|
specs:
|
||||||
zero (0.2.0)
|
zero (0.2.0)
|
||||||
tilt
|
tilt
|
||||||
|
@ -8,37 +8,245 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
addressable (2.2.6)
|
addressable (2.3.5)
|
||||||
chunky_png (1.2.8)
|
data_objects (0.10.13)
|
||||||
compass (0.12.2)
|
|
||||||
chunky_png (~> 1.2)
|
|
||||||
fssm (>= 0.2.7)
|
|
||||||
sass (~> 3.1)
|
|
||||||
data_objects (0.10.6)
|
|
||||||
addressable (~> 2.1)
|
addressable (~> 2.1)
|
||||||
do_postgres (0.10.6)
|
do_postgres (0.10.13)
|
||||||
data_objects (= 0.10.6)
|
data_objects (= 0.10.13)
|
||||||
fssm (0.2.10)
|
ffi2-generators (0.1.1)
|
||||||
kramdown (1.1.0)
|
minitest (4.7.5)
|
||||||
puma (2.4.0)
|
psych (2.0.1)
|
||||||
|
puma (2.6.0)
|
||||||
rack (>= 1.1, < 2.0)
|
rack (>= 1.1, < 2.0)
|
||||||
|
racc (1.4.10)
|
||||||
rack (1.5.2)
|
rack (1.5.2)
|
||||||
sass (3.2.10)
|
rubysl (2.0.10)
|
||||||
sequel (4.1.1)
|
psych (~> 2.0)
|
||||||
|
racc (~> 1.4)
|
||||||
|
rubysl-abbrev (~> 2.0)
|
||||||
|
rubysl-base64 (~> 2.0)
|
||||||
|
rubysl-benchmark (~> 2.0)
|
||||||
|
rubysl-bigdecimal (~> 2.0)
|
||||||
|
rubysl-cgi (~> 2.0)
|
||||||
|
rubysl-cgi-session (~> 2.0)
|
||||||
|
rubysl-cmath (~> 2.0)
|
||||||
|
rubysl-complex (~> 2.0)
|
||||||
|
rubysl-continuation (~> 2.0)
|
||||||
|
rubysl-coverage (~> 2.0)
|
||||||
|
rubysl-csv (~> 2.0)
|
||||||
|
rubysl-curses (~> 2.0)
|
||||||
|
rubysl-date (~> 2.0)
|
||||||
|
rubysl-delegate (~> 2.0)
|
||||||
|
rubysl-digest (~> 2.0)
|
||||||
|
rubysl-drb (~> 2.0)
|
||||||
|
rubysl-e2mmap (~> 2.0)
|
||||||
|
rubysl-english (~> 2.0)
|
||||||
|
rubysl-enumerator (~> 2.0)
|
||||||
|
rubysl-erb (~> 2.0)
|
||||||
|
rubysl-etc (~> 2.0)
|
||||||
|
rubysl-expect (~> 2.0)
|
||||||
|
rubysl-fcntl (~> 2.0)
|
||||||
|
rubysl-fiber (~> 2.0)
|
||||||
|
rubysl-fileutils (~> 2.0)
|
||||||
|
rubysl-find (~> 2.0)
|
||||||
|
rubysl-forwardable (~> 2.0)
|
||||||
|
rubysl-getoptlong (~> 2.0)
|
||||||
|
rubysl-gserver (~> 2.0)
|
||||||
|
rubysl-io-console (~> 2.0)
|
||||||
|
rubysl-io-nonblock (~> 2.0)
|
||||||
|
rubysl-io-wait (~> 2.0)
|
||||||
|
rubysl-ipaddr (~> 2.0)
|
||||||
|
rubysl-irb (~> 2.0)
|
||||||
|
rubysl-json (~> 2.0)
|
||||||
|
rubysl-logger (~> 2.0)
|
||||||
|
rubysl-mathn (~> 2.0)
|
||||||
|
rubysl-matrix (~> 2.0)
|
||||||
|
rubysl-mkmf (~> 2.0)
|
||||||
|
rubysl-monitor (~> 2.0)
|
||||||
|
rubysl-mutex_m (~> 2.0)
|
||||||
|
rubysl-net-ftp (~> 2.0)
|
||||||
|
rubysl-net-http (~> 2.0)
|
||||||
|
rubysl-net-imap (~> 2.0)
|
||||||
|
rubysl-net-pop (~> 2.0)
|
||||||
|
rubysl-net-protocol (~> 2.0)
|
||||||
|
rubysl-net-smtp (~> 2.0)
|
||||||
|
rubysl-net-telnet (~> 2.0)
|
||||||
|
rubysl-nkf (~> 2.0)
|
||||||
|
rubysl-observer (~> 2.0)
|
||||||
|
rubysl-open-uri (~> 2.0)
|
||||||
|
rubysl-open3 (~> 2.0)
|
||||||
|
rubysl-openssl (~> 2.0)
|
||||||
|
rubysl-optparse (~> 2.0)
|
||||||
|
rubysl-ostruct (~> 2.0)
|
||||||
|
rubysl-pathname (~> 2.0)
|
||||||
|
rubysl-prettyprint (~> 2.0)
|
||||||
|
rubysl-prime (~> 2.0)
|
||||||
|
rubysl-profile (~> 2.0)
|
||||||
|
rubysl-profiler (~> 2.0)
|
||||||
|
rubysl-pstore (~> 2.0)
|
||||||
|
rubysl-pty (~> 2.0)
|
||||||
|
rubysl-rational (~> 2.0)
|
||||||
|
rubysl-readline (~> 2.0)
|
||||||
|
rubysl-resolv (~> 2.0)
|
||||||
|
rubysl-rexml (~> 2.0)
|
||||||
|
rubysl-rinda (~> 2.0)
|
||||||
|
rubysl-ripper (~> 2.0)
|
||||||
|
rubysl-rss (~> 2.0)
|
||||||
|
rubysl-scanf (~> 2.0)
|
||||||
|
rubysl-securerandom (~> 2.0)
|
||||||
|
rubysl-set (~> 2.0)
|
||||||
|
rubysl-shellwords (~> 2.0)
|
||||||
|
rubysl-singleton (~> 2.0)
|
||||||
|
rubysl-socket (~> 2.0)
|
||||||
|
rubysl-stringio (~> 2.0)
|
||||||
|
rubysl-strscan (~> 2.0)
|
||||||
|
rubysl-syslog (~> 2.0)
|
||||||
|
rubysl-tempfile (~> 2.0)
|
||||||
|
rubysl-test-unit (~> 2.0)
|
||||||
|
rubysl-thread (~> 2.0)
|
||||||
|
rubysl-thwait (~> 2.0)
|
||||||
|
rubysl-time (~> 2.0)
|
||||||
|
rubysl-timeout (~> 2.0)
|
||||||
|
rubysl-tmpdir (~> 2.0)
|
||||||
|
rubysl-tracer (~> 2.0)
|
||||||
|
rubysl-tsort (~> 2.0)
|
||||||
|
rubysl-un (~> 2.0)
|
||||||
|
rubysl-uri (~> 2.0)
|
||||||
|
rubysl-weakref (~> 2.0)
|
||||||
|
rubysl-webrick (~> 2.0)
|
||||||
|
rubysl-xmlrpc (~> 2.0)
|
||||||
|
rubysl-yaml (~> 2.0)
|
||||||
|
rubysl-zlib (~> 2.0)
|
||||||
|
rubysl-abbrev (2.0.2)
|
||||||
|
rubysl-base64 (2.0.0)
|
||||||
|
rubysl-benchmark (2.0.0)
|
||||||
|
rubysl-bigdecimal (2.0.2)
|
||||||
|
rubysl-cgi (2.0.0)
|
||||||
|
rubysl-cgi-session (2.0.0)
|
||||||
|
rubysl-cmath (2.0.0)
|
||||||
|
rubysl-complex (2.0.0)
|
||||||
|
rubysl-continuation (2.0.0)
|
||||||
|
rubysl-coverage (2.0.3)
|
||||||
|
rubysl-csv (2.0.1)
|
||||||
|
rubysl-english (~> 2.0)
|
||||||
|
rubysl-curses (2.0.0)
|
||||||
|
rubysl-date (2.0.5)
|
||||||
|
rubysl-delegate (2.0.0)
|
||||||
|
rubysl-digest (2.0.2)
|
||||||
|
rubysl-drb (2.0.1)
|
||||||
|
rubysl-e2mmap (2.0.0)
|
||||||
|
rubysl-english (2.0.0)
|
||||||
|
rubysl-enumerator (2.0.0)
|
||||||
|
rubysl-erb (2.0.0)
|
||||||
|
rubysl-etc (2.0.3)
|
||||||
|
ffi2-generators (~> 0.1)
|
||||||
|
rubysl-expect (2.0.0)
|
||||||
|
rubysl-fcntl (2.0.4)
|
||||||
|
ffi2-generators (~> 0.1)
|
||||||
|
rubysl-fiber (2.0.0)
|
||||||
|
rubysl-fileutils (2.0.2)
|
||||||
|
rubysl-find (2.0.0)
|
||||||
|
rubysl-forwardable (2.0.0)
|
||||||
|
rubysl-getoptlong (2.0.0)
|
||||||
|
rubysl-gserver (2.0.0)
|
||||||
|
rubysl-socket (~> 2.0)
|
||||||
|
rubysl-thread (~> 2.0)
|
||||||
|
rubysl-io-console (2.0.0)
|
||||||
|
rubysl-io-nonblock (2.0.0)
|
||||||
|
rubysl-io-wait (2.0.0)
|
||||||
|
rubysl-ipaddr (2.0.0)
|
||||||
|
rubysl-irb (2.0.3)
|
||||||
|
rubysl-e2mmap (~> 2.0)
|
||||||
|
rubysl-mathn (~> 2.0)
|
||||||
|
rubysl-readline (~> 2.0)
|
||||||
|
rubysl-thread (~> 2.0)
|
||||||
|
rubysl-tracer (~> 2.0)
|
||||||
|
rubysl-json (2.0.2)
|
||||||
|
rubysl-logger (2.0.0)
|
||||||
|
rubysl-mathn (2.0.0)
|
||||||
|
rubysl-matrix (2.1.0)
|
||||||
|
rubysl-e2mmap (~> 2.0)
|
||||||
|
rubysl-mkmf (2.0.1)
|
||||||
|
rubysl-fileutils (~> 2.0)
|
||||||
|
rubysl-shellwords (~> 2.0)
|
||||||
|
rubysl-monitor (2.0.0)
|
||||||
|
rubysl-mutex_m (2.0.0)
|
||||||
|
rubysl-net-ftp (2.0.1)
|
||||||
|
rubysl-net-http (2.0.4)
|
||||||
|
rubysl-cgi (~> 2.0)
|
||||||
|
rubysl-erb (~> 2.0)
|
||||||
|
rubysl-singleton (~> 2.0)
|
||||||
|
rubysl-net-imap (2.0.1)
|
||||||
|
rubysl-net-pop (2.0.1)
|
||||||
|
rubysl-net-protocol (2.0.1)
|
||||||
|
rubysl-net-smtp (2.0.1)
|
||||||
|
rubysl-net-telnet (2.0.0)
|
||||||
|
rubysl-nkf (2.0.1)
|
||||||
|
rubysl-observer (2.0.0)
|
||||||
|
rubysl-open-uri (2.0.0)
|
||||||
|
rubysl-open3 (2.0.0)
|
||||||
|
rubysl-openssl (2.0.0)
|
||||||
|
rubysl-optparse (2.0.1)
|
||||||
|
rubysl-shellwords (~> 2.0)
|
||||||
|
rubysl-ostruct (2.0.4)
|
||||||
|
rubysl-pathname (2.0.0)
|
||||||
|
rubysl-prettyprint (2.0.1)
|
||||||
|
rubysl-prime (2.0.0)
|
||||||
|
rubysl-profile (2.0.0)
|
||||||
|
rubysl-profiler (2.0.1)
|
||||||
|
rubysl-pstore (2.0.0)
|
||||||
|
rubysl-pty (2.0.2)
|
||||||
|
rubysl-rational (2.0.1)
|
||||||
|
rubysl-readline (2.0.2)
|
||||||
|
rubysl-resolv (2.0.0)
|
||||||
|
rubysl-rexml (2.0.1)
|
||||||
|
rubysl-rinda (2.0.0)
|
||||||
|
rubysl-ripper (2.0.0)
|
||||||
|
rubysl-rss (2.0.0)
|
||||||
|
rubysl-scanf (2.0.0)
|
||||||
|
rubysl-securerandom (2.0.0)
|
||||||
|
rubysl-set (2.0.1)
|
||||||
|
rubysl-shellwords (2.0.0)
|
||||||
|
rubysl-singleton (2.0.0)
|
||||||
|
rubysl-socket (2.0.1)
|
||||||
|
rubysl-stringio (2.0.0)
|
||||||
|
rubysl-strscan (2.0.0)
|
||||||
|
rubysl-syslog (2.0.1)
|
||||||
|
ffi2-generators (~> 0.1)
|
||||||
|
rubysl-tempfile (2.0.0)
|
||||||
|
rubysl-test-unit (2.0.1)
|
||||||
|
minitest (~> 4.7)
|
||||||
|
rubysl-thread (2.0.2)
|
||||||
|
rubysl-thwait (2.0.0)
|
||||||
|
rubysl-time (2.0.3)
|
||||||
|
rubysl-timeout (2.0.0)
|
||||||
|
rubysl-tmpdir (2.0.0)
|
||||||
|
rubysl-tracer (2.0.0)
|
||||||
|
rubysl-tsort (2.0.1)
|
||||||
|
rubysl-un (2.0.0)
|
||||||
|
rubysl-fileutils (~> 2.0)
|
||||||
|
rubysl-optparse (~> 2.0)
|
||||||
|
rubysl-uri (2.0.0)
|
||||||
|
rubysl-weakref (2.0.0)
|
||||||
|
rubysl-webrick (2.0.0)
|
||||||
|
rubysl-xmlrpc (2.0.0)
|
||||||
|
rubysl-yaml (2.0.0)
|
||||||
|
rubysl-zlib (2.0.0)
|
||||||
|
sass (3.2.12)
|
||||||
|
sequel (4.3.0)
|
||||||
slim (2.0.1)
|
slim (2.0.1)
|
||||||
temple (~> 0.6.6)
|
temple (~> 0.6.6)
|
||||||
tilt (>= 1.3.3, < 2.1)
|
tilt (>= 1.3.3, < 2.1)
|
||||||
temple (0.6.6)
|
temple (0.6.7)
|
||||||
tilt (1.4.1)
|
tilt (1.4.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
compass
|
|
||||||
do_postgres
|
do_postgres
|
||||||
kramdown
|
|
||||||
puma
|
puma
|
||||||
|
rubysl
|
||||||
sass
|
sass
|
||||||
sequel
|
sequel
|
||||||
slim
|
slim
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env rackup
|
||||||
|
require File.expand_path('../lib/boot.rb', __FILE__)
|
||||||
|
|
||||||
|
Renderer.find_templates('templates')
|
||||||
|
run Application.new(Router, {:renderer => Renderer})
|
|
@ -0,0 +1,10 @@
|
||||||
|
module Routes
|
||||||
|
class MethodNotAllowed
|
||||||
|
def self.call(session)
|
||||||
|
session.response.status = 405
|
||||||
|
session.response.content_type = 'text/html'
|
||||||
|
session.response.body = 'Method not supported by this resource!'
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
module Routes
|
||||||
|
class RouteNotFound
|
||||||
|
def self.call(session)
|
||||||
|
session.response.status = 404
|
||||||
|
session.response.content_type = 'text/html'
|
||||||
|
session.response.body = 'This page does not exist!'
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
module Routes
|
||||||
|
class Welcome < Controller
|
||||||
|
def self.get(session)
|
||||||
|
session.options[:render] = 'posts/index'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
class Application
|
||||||
|
def initialize(start, defaults = {})
|
||||||
|
@start = start
|
||||||
|
@defaults = defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
session = Session.new(
|
||||||
|
::Zero::Request.new(env),
|
||||||
|
::Zero::Response.new,
|
||||||
|
@defaults.clone
|
||||||
|
)
|
||||||
|
worker = @start
|
||||||
|
while not worker.nil?
|
||||||
|
worker = worker.call(session)
|
||||||
|
end
|
||||||
|
session.response.to_a
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
$LOAD_PATH << File.expand_path('..', __FILE__)
|
||||||
|
|
||||||
|
require 'libraries'
|
||||||
|
require 'session'
|
||||||
|
require 'application'
|
||||||
|
require 'controller'
|
||||||
|
require 'renderer'
|
||||||
|
require 'router'
|
||||||
|
|
||||||
|
Dir[File.expand_path('../../controller', __FILE__) + '/**'].each do |controller|
|
||||||
|
require controller
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
class Controller
|
||||||
|
def self.call(session)
|
||||||
|
return call_method(session) if respond_to? session.request.method
|
||||||
|
Routes::MethodNotAllowed.call(session)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.call_method(session)
|
||||||
|
send(session.request.method, session)
|
||||||
|
session.options[:renderer]
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'bundler/setup'
|
||||||
|
require 'sequel'
|
||||||
|
require 'zero'
|
||||||
|
require 'sass'
|
||||||
|
require 'slim'
|
|
@ -0,0 +1,61 @@
|
||||||
|
class Renderer
|
||||||
|
COMPONENT_MATCHER = %r{/?(?<template>[^\.]+)\.(?<type>[^\.]+)\.(?<engine>.+)}
|
||||||
|
DEFAULT_TYPE = :default_type
|
||||||
|
|
||||||
|
def self.call(session)
|
||||||
|
unless session.options.has_key? :render
|
||||||
|
session.response.body = ':render not set!'
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
to_render = session.options[:render]
|
||||||
|
unless templates.has_key? to_render
|
||||||
|
session.response.body = 'Template does not exist!'
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
template = templates[to_render]
|
||||||
|
type = get_preferred_type(session, template)
|
||||||
|
type = template.first[0] if type == DEFAULT_TYPE
|
||||||
|
puts template.inspect
|
||||||
|
session.response.body = Tilt.new(template[type]).render(session.options)
|
||||||
|
puts session.response.body
|
||||||
|
session.response.content_type = type
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# initialize the template cache
|
||||||
|
def self.find_templates(path)
|
||||||
|
@@templates = {}
|
||||||
|
Dir[path + '/**/*'].each do |file|
|
||||||
|
next unless File.file?(file)
|
||||||
|
parts = file.gsub(path, '').match(COMPONENT_MATCHER)
|
||||||
|
@@templates[parts[:template]] ||= {}
|
||||||
|
@@templates[parts[:template]].merge!({parts[:type].to_sym => file})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.templates
|
||||||
|
find_templates 'templates' unless @@templates
|
||||||
|
@@templates
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_types(types)
|
||||||
|
@@types = types
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.types
|
||||||
|
@@types ||= {
|
||||||
|
'*/*' => DEFAULT_TYPE,
|
||||||
|
'text/css' => :css,
|
||||||
|
'text/html' => :html,
|
||||||
|
'application/atom+xml' => :atom
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_preferred_type(session, template)
|
||||||
|
session.request.accept.types.each do |type|
|
||||||
|
next unless types.has_key? type
|
||||||
|
return types[type] if template.has_key? types[type]
|
||||||
|
return DEFAULT_TYPE if types[type] == DEFAULT_TYPE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
class Router
|
||||||
|
ROUTE_REGEX = %r{\A(/(?<controller>[a-zA-Z]+)(/(?<id>[^/]+)?)?)?\Z}
|
||||||
|
# get the controller handling the request
|
||||||
|
def self.call(session)
|
||||||
|
variables = session.request.path.match(ROUTE_REGEX)
|
||||||
|
return default_route unless variables && variables[:controller]
|
||||||
|
session.options[:id] = variables[:id]
|
||||||
|
find variables[:controller]
|
||||||
|
end
|
||||||
|
|
||||||
|
# the namespace of all routes
|
||||||
|
def self.namespace
|
||||||
|
@@namespace ||= ::Routes
|
||||||
|
end
|
||||||
|
|
||||||
|
# set the namespace to a module or class which holds all controllers
|
||||||
|
def self.set_namespace(namespace)
|
||||||
|
@@namespace = namespace
|
||||||
|
end
|
||||||
|
|
||||||
|
# the default route to take when no path is given
|
||||||
|
def self.default_route
|
||||||
|
@@default ||= namespace.const_get(:Welcome)
|
||||||
|
end
|
||||||
|
|
||||||
|
# set the default route to take when no path is given
|
||||||
|
def self.set_default_route(default)
|
||||||
|
@@default = default
|
||||||
|
end
|
||||||
|
|
||||||
|
# check for the controller name and return 404 when not found
|
||||||
|
def self.find(ctrl)
|
||||||
|
ctrl = ctrl.capitalize
|
||||||
|
return namespace.const_get(ctrl) if namespace.const_defined?(ctrl)
|
||||||
|
namespace::RouteNotFound
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1 @@
|
||||||
|
Session = Struct.new(:request, :response, :options)
|
|
@ -0,0 +1,10 @@
|
||||||
|
doctype html
|
||||||
|
html
|
||||||
|
head
|
||||||
|
title zero-knowledge
|
||||||
|
body
|
||||||
|
header
|
||||||
|
h1 zero-knowledge
|
||||||
|
h2 noch ohne blub
|
||||||
|
#content
|
||||||
|
p This is from a slim Template!
|
Loading…
Reference in New Issue