Init
This commit is contained in:
commit
72067ba038
19 changed files with 7758 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
tmp
|
||||||
|
output
|
16
Gemfile
Normal file
16
Gemfile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
source :rubygems
|
||||||
|
|
||||||
|
gem 'nanoc'
|
||||||
|
gem 'nanoc-cachebuster'
|
||||||
|
gem 'redcarpet'
|
||||||
|
gem 'slim'
|
||||||
|
gem 'rainpress'
|
||||||
|
gem 'nokogiri'
|
||||||
|
gem 'adsf'
|
||||||
|
gem 'builder'
|
||||||
|
gem 'mime-types'
|
||||||
|
gem 'rack'
|
||||||
|
gem 'unicorn'
|
||||||
|
gem 'rack-rewrite'
|
||||||
|
gem 'pygments.rb', :git => 'git://github.com/akzhan/pygments.rb.git', :branch => 'rubypython-0.6'
|
||||||
|
gem 'clogger'
|
64
Gemfile.lock
Normal file
64
Gemfile.lock
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
GIT
|
||||||
|
remote: git://github.com/akzhan/pygments.rb.git
|
||||||
|
revision: dfa0d936da40f25e249a7d20f895e6d4cab72b50
|
||||||
|
branch: rubypython-0.6
|
||||||
|
specs:
|
||||||
|
pygments.rb (0.2.12)
|
||||||
|
rubypython (~> 0.6.1)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: http://rubygems.org/
|
||||||
|
specs:
|
||||||
|
adsf (1.1.1)
|
||||||
|
rack (>= 1.0.0)
|
||||||
|
blankslate (2.1.2.4)
|
||||||
|
builder (3.0.0)
|
||||||
|
clogger (1.1.0)
|
||||||
|
rack (> 0.9)
|
||||||
|
colored (1.2)
|
||||||
|
cri (2.3.0)
|
||||||
|
colored (>= 1.2)
|
||||||
|
ffi (1.0.11)
|
||||||
|
kgio (2.7.4)
|
||||||
|
mime-types (1.19)
|
||||||
|
nanoc (3.4.0)
|
||||||
|
cri (~> 2.2)
|
||||||
|
nanoc-cachebuster (0.3.1)
|
||||||
|
nanoc (>= 3.3.0)
|
||||||
|
nokogiri (1.5.5)
|
||||||
|
rack (1.4.1)
|
||||||
|
rack-rewrite (1.2.1)
|
||||||
|
raindrops (0.10.0)
|
||||||
|
rainpress (1.0)
|
||||||
|
redcarpet (2.1.1)
|
||||||
|
rubypython (0.6.2)
|
||||||
|
blankslate (>= 2.1.2.3)
|
||||||
|
ffi (~> 1.0.7)
|
||||||
|
slim (1.2.2)
|
||||||
|
temple (~> 0.4.0)
|
||||||
|
tilt (~> 1.3.3)
|
||||||
|
temple (0.4.0)
|
||||||
|
tilt (1.3.3)
|
||||||
|
unicorn (4.3.1)
|
||||||
|
kgio (~> 2.6)
|
||||||
|
rack
|
||||||
|
raindrops (~> 0.7)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
adsf
|
||||||
|
builder
|
||||||
|
clogger
|
||||||
|
mime-types
|
||||||
|
nanoc
|
||||||
|
nanoc-cachebuster
|
||||||
|
nokogiri
|
||||||
|
pygments.rb!
|
||||||
|
rack
|
||||||
|
rack-rewrite
|
||||||
|
rainpress
|
||||||
|
redcarpet
|
||||||
|
slim
|
||||||
|
unicorn
|
76
Rules
Normal file
76
Rules
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
preprocess do
|
||||||
|
create_robots_txt
|
||||||
|
create_webmaster_tools_authentications
|
||||||
|
create_sitemap
|
||||||
|
end
|
||||||
|
|
||||||
|
compile '/assets/css/*' do
|
||||||
|
filter :cache_buster
|
||||||
|
filter :rainpress
|
||||||
|
end
|
||||||
|
|
||||||
|
route %r{^/(assets/.*|sitemap|robots|atom)/$} do
|
||||||
|
next nil if item.identifier.split('/')[-1][0,1] == '_' # partial
|
||||||
|
|
||||||
|
fp = cachebust?(item) ? fingerprint(item[:filename]) : ''
|
||||||
|
item.identifier.chop + fp + '.' + item[:extension]
|
||||||
|
end
|
||||||
|
|
||||||
|
compile '/sitemap/' do
|
||||||
|
filter :erb
|
||||||
|
end
|
||||||
|
|
||||||
|
compile '/sitemap/', rep: 'gzip' do
|
||||||
|
filter :erb
|
||||||
|
filter :shellcmd, cmd: 'gzip'
|
||||||
|
end
|
||||||
|
|
||||||
|
route '/sitemap/', rep: 'gzip' do
|
||||||
|
'/sitemap.xml.gz'
|
||||||
|
end
|
||||||
|
|
||||||
|
compile '/posts/feed/' do
|
||||||
|
filter :erb
|
||||||
|
end
|
||||||
|
|
||||||
|
route '/posts/feed/' do
|
||||||
|
'/posts.xml'
|
||||||
|
end
|
||||||
|
|
||||||
|
compile %r{^/(google|robots|assets|favicon)} do
|
||||||
|
end
|
||||||
|
|
||||||
|
compile '*' do
|
||||||
|
case item[:extension]
|
||||||
|
when 'slim'
|
||||||
|
filter :slim
|
||||||
|
when 'md'
|
||||||
|
filter :redcarpet, renderer: MarkdownHTML, options: { fenced_code_blocks: true }
|
||||||
|
end
|
||||||
|
|
||||||
|
case item.identifier
|
||||||
|
when %r{^/posts/}
|
||||||
|
layout 'post'
|
||||||
|
else
|
||||||
|
layout 'default'
|
||||||
|
end
|
||||||
|
|
||||||
|
filter :cache_buster
|
||||||
|
end
|
||||||
|
|
||||||
|
route '/' do
|
||||||
|
'/index.html'
|
||||||
|
end
|
||||||
|
|
||||||
|
route '*' do
|
||||||
|
if item.binary?
|
||||||
|
# Write item with identifier /foo/ to /foo.ext
|
||||||
|
item.identifier.chop + '.' + item[:extension]
|
||||||
|
else
|
||||||
|
item.identifier.chop + '.html'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
layout '*', :slim
|
78
config.ru
Normal file
78
config.ru
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
require 'bundler/setup'
|
||||||
|
require 'cgi'
|
||||||
|
require 'rack/static'
|
||||||
|
require 'rack/deflater'
|
||||||
|
require 'rack/rewrite'
|
||||||
|
require 'clogger'
|
||||||
|
|
||||||
|
ONE_YEAR = 31_556_926
|
||||||
|
|
||||||
|
class AssetExpires
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
status, headers, body = @app.call(env)
|
||||||
|
headers.merge!('Cache-Control' => "max-age=#{ONE_YEAR}, public", 'Expires' => CGI.rfc1123_date(Time.now + ONE_YEAR)) if asset?(env)
|
||||||
|
[status, headers, body]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def asset?(env)
|
||||||
|
%w(js css jpg jpeg png gif).include?(env['PATH_INFO'].split('.').last) && env['PATH_INFO'] =~ /-cb/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ContentTypeCharset
|
||||||
|
def initialize(app)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
status, headers, body = @app.call(env)
|
||||||
|
headers['Content-Type'] += ';charset=utf-8' unless headers['Content-Type'] =~ /charset/ || headers['Content-Type'].nil?
|
||||||
|
[status, headers, body]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# modified from https://github.com/meskyanichi/nanoc-heroku/blob/master/config.ru
|
||||||
|
class TryStatic < Rack::Static
|
||||||
|
def initialize(app, options)
|
||||||
|
super
|
||||||
|
@try = ([''] + Array(options.delete(:try)) + [''])
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
|
||||||
|
@next = 0
|
||||||
|
while @next < @try.size && 404 == (resp = super(try_next(env)))[0]
|
||||||
|
@next += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
resp
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def try_next(env)
|
||||||
|
env.merge('PATH_INFO' => env['PATH_INFO'] + @try[@next])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
use Clogger, format: :NginxCombined, logger: STDOUT, reentrant: true if ENV['RACK_ENV'] == 'production'
|
||||||
|
use Rack::Rewrite do
|
||||||
|
r301 %r{.*}, 'http://tent.io$&', if: Proc.new { |env|
|
||||||
|
env['SERVER_NAME'] != 'tent.io' && !env['QUERY_STRING'].include?('skip_redirect')
|
||||||
|
} if ENV['RACK_ENV'] == 'production'
|
||||||
|
|
||||||
|
r301 %r{(.+)(/|\.html)$}, '$1'
|
||||||
|
end
|
||||||
|
|
||||||
|
use Rack::Deflater
|
||||||
|
use AssetExpires
|
||||||
|
use ContentTypeCharset
|
||||||
|
use TryStatic, root: 'output', urls: %w[/], try: %w(.html index.html /index.html)
|
||||||
|
|
||||||
|
run lambda { |env| [404, {}, ''] }
|
116
config.yaml
Normal file
116
config.yaml
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# A list of file extensions that nanoc will consider to be textual rather than
|
||||||
|
# binary. If an item with an extension not in this list is found, the file
|
||||||
|
# will be considered as binary.
|
||||||
|
text_extensions: [ 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'sass', 'scss', 'txt', 'xhtml', 'xml' ]
|
||||||
|
|
||||||
|
# The path to the directory where all generated files will be written to. This
|
||||||
|
# can be an absolute path starting with a slash, but it can also be path
|
||||||
|
# relative to the site directory.
|
||||||
|
output_dir: output
|
||||||
|
|
||||||
|
# A list of index filenames, i.e. names of files that will be served by a web
|
||||||
|
# server when a directory is requested. Usually, index files are named
|
||||||
|
# “index.html”, but depending on the web server, this may be something else,
|
||||||
|
# such as “default.htm”. This list is used by nanoc to generate pretty URLs.
|
||||||
|
index_filenames: [ 'index.html' ]
|
||||||
|
|
||||||
|
# Whether or not to generate a diff of the compiled content when compiling a
|
||||||
|
# site. The diff will contain the differences between the compiled content
|
||||||
|
# before and after the last site compilation.
|
||||||
|
enable_output_diff: false
|
||||||
|
|
||||||
|
prune:
|
||||||
|
# Whether to automatically remove files not managed by nanoc from the output
|
||||||
|
# directory. For safety reasons, this is turned off by default.
|
||||||
|
auto_prune: false
|
||||||
|
|
||||||
|
# Which files and directories you want to exclude from pruning. If you version
|
||||||
|
# your output directory, you should probably exclude VCS directories such as
|
||||||
|
# .git, .svn etc.
|
||||||
|
exclude: [ '.git', '.hg', '.svn', 'CVS' ]
|
||||||
|
|
||||||
|
# The data sources where nanoc loads its data from. This is an array of
|
||||||
|
# hashes; each array element represents a single data source. By default,
|
||||||
|
# there is only a single data source that reads data from the “content/” and
|
||||||
|
# “layout/” directories in the site directory.
|
||||||
|
data_sources:
|
||||||
|
-
|
||||||
|
# The type is the identifier of the data source. By default, this will be
|
||||||
|
# `filesystem_unified`.
|
||||||
|
type: filesystem_unified
|
||||||
|
|
||||||
|
# The path where items should be mounted (comparable to mount points in
|
||||||
|
# Unix-like systems). This is “/” by default, meaning that items will have
|
||||||
|
# “/” prefixed to their identifiers. If the items root were “/en/”
|
||||||
|
# instead, an item at content/about.html would have an identifier of
|
||||||
|
# “/en/about/” instead of just “/about/”.
|
||||||
|
items_root: /
|
||||||
|
|
||||||
|
# The path where layouts should be mounted. The layouts root behaves the
|
||||||
|
# same as the items root, but applies to layouts rather than items.
|
||||||
|
layouts_root: /
|
||||||
|
|
||||||
|
# Whether to allow periods in identifiers. When turned off, everything
|
||||||
|
# past the first period is considered to be the extension, and when
|
||||||
|
# turned on, only the characters past the last period are considered to
|
||||||
|
# be the extension. For example, a file named “content/about.html.erb”
|
||||||
|
# will have the identifier “/about/” when turned off, but when turned on
|
||||||
|
# it will become “/about.html/” instead.
|
||||||
|
allow_periods_in_identifiers: false
|
||||||
|
|
||||||
|
# Configuration for the “watch” command, which watches a site for changes and
|
||||||
|
# recompiles if necessary.
|
||||||
|
watcher:
|
||||||
|
# A list of directories to watch for changes. When editing this, make sure
|
||||||
|
# that the “output/” and “tmp/” directories are _not_ included in this list,
|
||||||
|
# because recompiling the site will cause these directories to change, which
|
||||||
|
# will cause the site to be recompiled, which will cause these directories
|
||||||
|
# to change, which will cause the site to be recompiled again, and so on.
|
||||||
|
dirs_to_watch: [ 'content', 'layouts', 'lib' ]
|
||||||
|
|
||||||
|
# A list of single files to watch for changes. As mentioned above, don’t put
|
||||||
|
# any files from the “output/” or “tmp/” directories in here.
|
||||||
|
files_to_watch: [ 'config.yaml', 'Rules' ]
|
||||||
|
|
||||||
|
# When to send notifications (using Growl or notify-send).
|
||||||
|
notify_on_compilation_success: true
|
||||||
|
notify_on_compilation_failure: true
|
||||||
|
|
||||||
|
google_analytics_id: UA-34258323-1
|
||||||
|
base_url: http://tent.io
|
||||||
|
|
||||||
|
# Configure the robots.txt file for this site.
|
||||||
|
# Setting 'default' to true-ish will use sensible defaults. If you
|
||||||
|
# wish to customize it, you can list paths to allow and to disallow.
|
||||||
|
# Finally, you could manually set the path to the sitemap file.
|
||||||
|
#
|
||||||
|
# You can customize the robots file fairly well like this, but you
|
||||||
|
# can always manually create a content file with the exact contents
|
||||||
|
# you need.
|
||||||
|
robots:
|
||||||
|
default: true # point at sitemap
|
||||||
|
# disallow:
|
||||||
|
# - '/tag'
|
||||||
|
# - '/newsletter'
|
||||||
|
# allow:
|
||||||
|
# - '/tag/foo'
|
||||||
|
# sitemap: '/site-map.txt'
|
||||||
|
|
||||||
|
# Set up authentication files for various webmaster tools (or something
|
||||||
|
# similar). This simply creates a plain text file when generating the site.
|
||||||
|
#
|
||||||
|
# identifier: identifier of the output file, e.g. '/google12345/'
|
||||||
|
# content: content of the file, e.g. 'aoa8202ns001'
|
||||||
|
# extension: extension of the output file, e.g. 'html' or 'xml'
|
||||||
|
webmaster_tools:
|
||||||
|
-
|
||||||
|
identifier:
|
||||||
|
extension:
|
||||||
|
content:
|
||||||
|
|
||||||
|
# Should Nanoc generate some standard asset files for you, or skip them
|
||||||
|
# completely?
|
||||||
|
#
|
||||||
|
# This currently controls the generation of robots.txt, sitemap.xml,
|
||||||
|
# sitemap.xml.gz and webmaster tools authentication files.
|
||||||
|
output_generated_assets: true
|
1040
content/assets/css/bootstrap-responsive.css
vendored
Normal file
1040
content/assets/css/bootstrap-responsive.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
5624
content/assets/css/bootstrap.css
vendored
Normal file
5624
content/assets/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
61
content/assets/css/style.css
Normal file
61
content/assets/css/style.css
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
.hll { background-color: #ffffcc }
|
||||||
|
.c { color: #408080; font-style: italic } /* Comment */
|
||||||
|
.err { border: 1px solid #FF0000 } /* Error */
|
||||||
|
.k { color: #008000; font-weight: bold } /* Keyword */
|
||||||
|
.o { color: #666666 } /* Operator */
|
||||||
|
.cm { color: #408080; font-style: italic } /* Comment.Multiline */
|
||||||
|
.cp { color: #BC7A00 } /* Comment.Preproc */
|
||||||
|
.c1 { color: #408080; font-style: italic } /* Comment.Single */
|
||||||
|
.cs { color: #408080; font-style: italic } /* Comment.Special */
|
||||||
|
.gd { color: #A00000 } /* Generic.Deleted */
|
||||||
|
.ge { font-style: italic } /* Generic.Emph */
|
||||||
|
.gr { color: #FF0000 } /* Generic.Error */
|
||||||
|
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||||
|
.gi { color: #00A000 } /* Generic.Inserted */
|
||||||
|
.go { color: #808080 } /* Generic.Output */
|
||||||
|
.gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||||
|
.gs { font-weight: bold } /* Generic.Strong */
|
||||||
|
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||||
|
.gt { color: #0040D0 } /* Generic.Traceback */
|
||||||
|
.kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||||
|
.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||||
|
.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||||
|
.kp { color: #008000 } /* Keyword.Pseudo */
|
||||||
|
.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||||
|
.kt { color: #B00040 } /* Keyword.Type */
|
||||||
|
.m { color: #666666 } /* Literal.Number */
|
||||||
|
.s { color: #BA2121 } /* Literal.String */
|
||||||
|
.na { color: #7D9029 } /* Name.Attribute */
|
||||||
|
.nb { color: #008000 } /* Name.Builtin */
|
||||||
|
.nc { color: #0000FF; font-weight: bold } /* Name.Class */
|
||||||
|
.no { color: #880000 } /* Name.Constant */
|
||||||
|
.nd { color: #AA22FF } /* Name.Decorator */
|
||||||
|
.ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||||||
|
.ne { color: #D2413A; font-weight: bold } /* Name.Exception */
|
||||||
|
.nf { color: #0000FF } /* Name.Function */
|
||||||
|
.nl { color: #A0A000 } /* Name.Label */
|
||||||
|
.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
|
||||||
|
.nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||||
|
.nv { color: #19177C } /* Name.Variable */
|
||||||
|
.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
|
||||||
|
.w { color: #bbbbbb } /* Text.Whitespace */
|
||||||
|
.mf { color: #666666 } /* Literal.Number.Float */
|
||||||
|
.mh { color: #666666 } /* Literal.Number.Hex */
|
||||||
|
.mi { color: #666666 } /* Literal.Number.Integer */
|
||||||
|
.mo { color: #666666 } /* Literal.Number.Oct */
|
||||||
|
.sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||||
|
.sc { color: #BA2121 } /* Literal.String.Char */
|
||||||
|
.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||||
|
.s2 { color: #BA2121 } /* Literal.String.Double */
|
||||||
|
.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
|
||||||
|
.sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||||
|
.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
|
||||||
|
.sx { color: #008000 } /* Literal.String.Other */
|
||||||
|
.sr { color: #BB6688 } /* Literal.String.Regex */
|
||||||
|
.s1 { color: #BA2121 } /* Literal.String.Single */
|
||||||
|
.ss { color: #19177C } /* Literal.String.Symbol */
|
||||||
|
.bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||||
|
.vc { color: #19177C } /* Name.Variable.Class */
|
||||||
|
.vg { color: #19177C } /* Name.Variable.Global */
|
||||||
|
.vi { color: #19177C } /* Name.Variable.Instance */
|
||||||
|
.il { color: #666666 } /* Literal.Number.Integer.Long */
|
422
content/docs/app-auth.md
Normal file
422
content/docs/app-auth.md
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
---
|
||||||
|
title: App Authentication
|
||||||
|
---
|
||||||
|
|
||||||
|
# App Authentication
|
||||||
|
|
||||||
|
Tent uses [OAuth 2](http://tools.ietf.org/html/draft-ietf-oauth-v2-31) for app
|
||||||
|
authentication. Because of the distributed nature of Tent, it is necessary for
|
||||||
|
apps to register with the Tent entity before doing the authentication flow.
|
||||||
|
|
||||||
|
After the authentication flow, requests are authenticated using the credentials
|
||||||
|
with [MAC Access
|
||||||
|
Authentication](http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01).
|
||||||
|
|
||||||
|
|
||||||
|
## App Registration
|
||||||
|
|
||||||
|
Before authenticating a user, the application must be registered with the
|
||||||
|
specified Tent entity. The first step is to perform discovery on the provided
|
||||||
|
entity url.
|
||||||
|
|
||||||
|
```text
|
||||||
|
HEAD /
|
||||||
|
Host: titanous.com
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
200 OK
|
||||||
|
Tent-Server: https://tent.titanous.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The next step is to register the application with the server. Registration
|
||||||
|
specifies some details about the app, as well as the exact `redirect_uris` that
|
||||||
|
will be used during the OAuth flow. The response `id`, `secret`, and
|
||||||
|
`mac_algorithm` are used to authenticate future requests to the Tent server.
|
||||||
|
The `id` is used as the `client_id` in all OAuth requests.
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /apps
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "FooApp",
|
||||||
|
"description": "Does amazing foos with your data",
|
||||||
|
"url": "http://foooo.com",
|
||||||
|
"icon": "http://foooo.com/icon.png",
|
||||||
|
"redirect_uris": ["http://fooapp.com/tent/callback"],
|
||||||
|
"scopes": {
|
||||||
|
"write_profile": "Uses an app profile section to describe foos",
|
||||||
|
"read_followings": "Calculates foos based on your followings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
201 Created
|
||||||
|
Content-Type: application/json
|
||||||
|
Location: https://tent.titanous.com/apps/6737b
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "6737b",
|
||||||
|
"secret": "3d2adf9a68bf64f4eaff70a7c7700a8",
|
||||||
|
"mac_algorithm": "hmac-sha-256"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Parameters
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Required</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>name</code></td>
|
||||||
|
<td>Required</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>The human name of the app to show the user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>description</code></td>
|
||||||
|
<td>Required</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>A short description of the application to show the user</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>url</code></td>
|
||||||
|
<td>Required</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>The main url of the app</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>icon</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>The url to an icon for the app</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>redirect_uris</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>Array</td>
|
||||||
|
<td>
|
||||||
|
A list of <strong>exact</strong> (including parameters) urls that will
|
||||||
|
be used as OAuth <code>redirect_uri</code>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>scopes</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>Object</td>
|
||||||
|
<td>
|
||||||
|
A list of scope key to description value mappings of all scopes that
|
||||||
|
the app might use. The descriptions should describe why the specific
|
||||||
|
scope is necessary for the app to function.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Response Parameters
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>id</code></td>
|
||||||
|
<td>
|
||||||
|
The identifier of the app. This is used as the MAC key identifier for
|
||||||
|
requests to/from the Tent server, as well as the <code>client_id</code>
|
||||||
|
in the OAuth flow.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>secret</code></td>
|
||||||
|
<td>
|
||||||
|
The secret used as the MAC key when modifying the registration and
|
||||||
|
receiving notifications.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>mac_algorithm</code></td>
|
||||||
|
<td>The MAC algorithm to be used.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## App Registration Modification
|
||||||
|
|
||||||
|
The request must be authenticated with a MAC generated using the secret from the
|
||||||
|
initial registration.
|
||||||
|
|
||||||
|
### PATCH /apps/:id
|
||||||
|
|
||||||
|
```text
|
||||||
|
PATCH /apps/aff70a7
|
||||||
|
Host: tent.titanous.com
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
Authorization: MAC id="aff70a7",
|
||||||
|
ts="1336363200",
|
||||||
|
nonce="dj83hs9s",
|
||||||
|
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "FooApp",
|
||||||
|
"description": "Does amazing foos with your data",
|
||||||
|
"url": "http://foooo.com",
|
||||||
|
"icon": "http://foooo.com/icon.png",
|
||||||
|
"redirect_uris": ["http://fooapp.com/tent/callback"],
|
||||||
|
"scopes": {
|
||||||
|
"write_profile": "Uses an app profile section to describe foos",
|
||||||
|
"read_followings": "Calculates foos based on your followings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Authentication Flow
|
||||||
|
|
||||||
|
### Auth Request
|
||||||
|
|
||||||
|
The app requests the user's Tent identifier, and performs discovery on it to
|
||||||
|
find the Tent API root. The app then builds an auth request and redirects the
|
||||||
|
user-agent to it:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/auth?client_id=6737b
|
||||||
|
&redirect_uri=http://fooapp.com/tent/callback
|
||||||
|
&scope=read_posts,read_profile
|
||||||
|
&state=87351cc2f6737bfc8ba
|
||||||
|
&tent_profile_info_types=https://tent.io/types/info/music
|
||||||
|
&tent_post_types=https://tent.io/types/posts/status#0.2.0,https://tent.io/types/posts/photo#0.2.0
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Required</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>client_id</code></td>
|
||||||
|
<td>Required</td>
|
||||||
|
<td>
|
||||||
|
The <code>id</code> obtained by registering with the Tent server
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>redirect_uri</code></td>
|
||||||
|
<td>Required</td>
|
||||||
|
<td>
|
||||||
|
The URI to redirect to after authentication is complete. It must
|
||||||
|
<strong>exactly</strong> match a URI (including parameters)
|
||||||
|
provided during app registration in <code>redirect_uris</code>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>state</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>
|
||||||
|
This parameter will be added to the <code>redirect_uri</code> and should
|
||||||
|
always be set to a random string that is stored in the session, and then
|
||||||
|
verified to prevent cross-site request forgery attacks.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>scope</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>
|
||||||
|
A comma-separated list of scopes that the app is requesting access to.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tent_profile_info_types</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>
|
||||||
|
A comma-separated list of <code>profile_info_type_url#version</code>
|
||||||
|
profile info type specifiers that the app is requesting access to. Set
|
||||||
|
to <code>all</code> to request full access to the profile.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>tent_post_types</code></td>
|
||||||
|
<td>Optional</td>
|
||||||
|
<td>
|
||||||
|
A comma-separated list of <code>post_type_url#version</code>
|
||||||
|
type/version specifiers that the app is requesting access to.
|
||||||
|
Set to <code>all</code> to request access to all posts.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
#### Scopes
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Scope</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>read_profile</code></td>
|
||||||
|
<td>Read profile sections listed in the <code>profile_info</code> parameter</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>write_profile</code></td>
|
||||||
|
<td>Read and write profile sections listed in the <code>profile_info</code> parameter</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>read_followers</code></td>
|
||||||
|
<td>Read follower list</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>write_followers</code></td>
|
||||||
|
<td>Read follower list and block followers</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>read_followings</code></td>
|
||||||
|
<td>Read followings list</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>write_followings</code></td>
|
||||||
|
<td>Read followings list and follow new entities</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>read_posts</code></td>
|
||||||
|
<td>Read posts with types listed in the <code>post_types</code> parameter</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>write_posts</code></td>
|
||||||
|
<td>Read and publish posts with types listed in the <code>post_types</code> parameter</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Redirect
|
||||||
|
|
||||||
|
After the user has authorized the application, the Tent server will redirect the
|
||||||
|
User-Agent back to the specified `redirect_uri` with a `code` that can be used
|
||||||
|
to retrieve authentication details from the server.
|
||||||
|
|
||||||
|
The `state` parameter should be matched against the `state` parameter sent in
|
||||||
|
the initial request to prevent request forgery.
|
||||||
|
|
||||||
|
```text
|
||||||
|
302 Found
|
||||||
|
Location: http://fooapp.com/tent/callback?code=27ec1c54980f1af74&state=87351cc2f6737bfc8ba
|
||||||
|
```
|
||||||
|
|
||||||
|
### Access Token
|
||||||
|
|
||||||
|
The `code` must be traded for permanent authentication details that can be used
|
||||||
|
to access the Tent server on behalf of the user.
|
||||||
|
|
||||||
|
#### POST /apps/:id/access\_token
|
||||||
|
|
||||||
|
The request must be signed with a MAC using the secret obtained during app
|
||||||
|
registration. Currently only the `mac` `token_type` is supported.
|
||||||
|
|
||||||
|
```text
|
||||||
|
POST /apps/6737b/access_token
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
Authorization: MAC id="6737b",
|
||||||
|
ts="1336363200",
|
||||||
|
nonce="dj83hs9s",
|
||||||
|
mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "27ec1c54980f1af74",
|
||||||
|
"token_type": "mac"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The server will respond with the authentication details necessary to interact
|
||||||
|
with the Tent server on behalf of the user.
|
||||||
|
|
||||||
|
```text
|
||||||
|
200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"access_token": "7b7df67598602",
|
||||||
|
"mac_key": "18bcf2bae86948731",
|
||||||
|
"mac_algorithm": "hmac-sha-256"
|
||||||
|
"token_type": "mac",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Response Parameters
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><code>access_token</code></td>
|
||||||
|
<td>Used as the MAC key identifier.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>mac_key</code></td>
|
||||||
|
<td>Used as the MAC key for requests.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>mac_algorithm</code></td>
|
||||||
|
<td>The MAC algorithm to be used.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>token_type</code></td>
|
||||||
|
<td>Specifies the token type. Currently always <code>mac</code></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Request Authentication
|
||||||
|
|
||||||
|
Tent uses [HTTP MAC Access
|
||||||
|
Authentication](http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01) to
|
||||||
|
authenticate app requests. Requests to modify the app's registration details
|
||||||
|
must be authorized using the provided `id` and `secret` as the
|
||||||
|
MAC key identifier and MAC key respectively.
|
||||||
|
|
||||||
|
Requests to the Tent server on behalf of an authenticated user must be
|
||||||
|
authorized using the credentials retrieved at the end of the auth flow.
|
||||||
|
The `access_token` and `mac_key` are the MAC key identifier and MAC key
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
Currently all Authorization headers in this documentation are fake, but they
|
||||||
|
will be real examples when we have the example generator written.
|
14
content/index.html
Normal file
14
content/index.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
title: Home
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1>A Brand New nanoc Site</h1>
|
||||||
|
|
||||||
|
<p>You’ve just created a new nanoc site. The page you are looking at right now is the home page for your site. To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>Change this page’s content</strong> by editing the “index.html” file in the “content” directory. This is the actual page content, and therefore doesn’t include the header, sidebar or style information (those are part of the layout).</p></li>
|
||||||
|
<li><p><strong>Change the layout</strong>, which is the “default.html” file in the “layouts” directory, and create something unique (and hopefully less bland).</p></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>If you need any help with customizing your nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!</p>
|
6
layouts/default.slim
Normal file
6
layouts/default.slim
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
doctype html
|
||||||
|
html lang='en'
|
||||||
|
head == render 'head'
|
||||||
|
body
|
||||||
|
#container
|
||||||
|
== yield
|
17
layouts/head.slim
Normal file
17
layouts/head.slim
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
meta name="viewport" content='width=device-width, initial-scale=1.0'
|
||||||
|
title = "#{@item[:title] + ' · ' if @item[:title]}Tent"
|
||||||
|
link href='/assets/css/bootstrap.css' rel='stylesheet' type='text/css' media='screen'
|
||||||
|
link href='/assets/css/bootstrap-responsive.css' rel='stylesheet' type='text/css' media='screen'
|
||||||
|
link href='/assets/css/style.css' rel='stylesheet' type='text/css' media='screen'
|
||||||
|
link rel='alternate' type='application/atom+xml' title='Atom feed' href="#{@site.config[:base_url]}/posts.xml"
|
||||||
|
javascript:
|
||||||
|
var _gaq = _gaq || [];
|
||||||
|
_gaq.push(['_setAccount', '#{@site.config[:google_analytics_id]}']);
|
||||||
|
_gaq.push(['_setSiteSpeedSampleRate', 50]);
|
||||||
|
_gaq.push(['_trackPageview']);
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||||
|
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||||
|
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||||
|
})();
|
8
lib/blogging.rb
Normal file
8
lib/blogging.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module Nanoc3::Helpers::Blogging
|
||||||
|
# Patch to add analytics param to feed urls
|
||||||
|
alias url_for_without_analytics url_for
|
||||||
|
def url_for(item)
|
||||||
|
url = url_for_without_analytics(item)
|
||||||
|
url.sub(/(\.html|\/)$/, '') + '?utm_source=feed'
|
||||||
|
end
|
||||||
|
end
|
12
lib/default.rb
Normal file
12
lib/default.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
require 'nanoc/cachebuster'
|
||||||
|
require 'slim'
|
||||||
|
require 'redcarpet'
|
||||||
|
|
||||||
|
include Nanoc3::Helpers::Rendering
|
||||||
|
include Nanoc3::Helpers::XMLSitemap
|
||||||
|
include Nanoc3::Helpers::Blogging
|
||||||
|
include Nanoc3::Helpers::Filtering
|
||||||
|
include Nanoc3::Helpers::LinkTo
|
||||||
|
include Nanoc3::Helpers::CacheBusting
|
||||||
|
|
||||||
|
Slim::Engine.set_default_options pretty: true
|
9
lib/markdown_html.rb
Normal file
9
lib/markdown_html.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
require 'pygments.rb'
|
||||||
|
|
||||||
|
RubyPython.start python_exe: 'python2.6' if ENV['RACK_ENV'] == 'production'
|
||||||
|
|
||||||
|
class MarkdownHTML < Redcarpet::Render::HTML
|
||||||
|
def block_code(code, language)
|
||||||
|
Pygments.highlight(code, lexer: language)
|
||||||
|
end
|
||||||
|
end
|
86
lib/meta.rb
Normal file
86
lib/meta.rb
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# Source: https://github.com/avdgaag/nanoc-template/blob/master/lib/preprocessors.rb
|
||||||
|
|
||||||
|
|
||||||
|
# Generate a sitemap.xml file using Nanoc's own xml_sitemap helper method by
|
||||||
|
# dynamically adding a new item.
|
||||||
|
#
|
||||||
|
# Make items that should not appear in the sitemap hidden. This by default
|
||||||
|
# works on all image files and typical assets, as well as error pages and
|
||||||
|
# htaccess. The is_hidden attribute is only explicitly set if it is absent,
|
||||||
|
# allowing per-file overriding.
|
||||||
|
#
|
||||||
|
# @todo extract hidden file types into configuration file?
|
||||||
|
def create_sitemap
|
||||||
|
return unless @site.config[:output_generated_assets]
|
||||||
|
|
||||||
|
@items.each do |item|
|
||||||
|
if %w{png gif jpg jpeg coffee scss sass less css xml js txt ico}.include?(item[:extension]) ||
|
||||||
|
item.identifier =~ /404|500|htaccess/
|
||||||
|
item[:is_hidden] = true unless item.attributes.has_key?(:is_hidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@items << Nanoc3::Item.new(
|
||||||
|
"<%= xml_sitemap %>",
|
||||||
|
{ :extension => 'xml', :is_hidden => true },
|
||||||
|
'/sitemap/'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Use special settings from the site configuration to generate the files
|
||||||
|
# necessary for various webmaster tools authentications, such as the services
|
||||||
|
# from Google, Yahoo and Bing.
|
||||||
|
#
|
||||||
|
# This loops through all the items in the `webmaster_tools` setting, using
|
||||||
|
# its properties to generate a new item.
|
||||||
|
#
|
||||||
|
# See config.yaml for more documentation on the input format.
|
||||||
|
def create_webmaster_tools_authentications
|
||||||
|
return unless @site.config[:output_generated_assets]
|
||||||
|
|
||||||
|
@site.config[:webmaster_tools].each do |file|
|
||||||
|
next if file[:identifier].nil?
|
||||||
|
content = file.delete(:content)
|
||||||
|
identifier = file.delete(:identifier)
|
||||||
|
file.merge!(:is_hidden => true)
|
||||||
|
@items << Nanoc3::Item.new(
|
||||||
|
content,
|
||||||
|
file,
|
||||||
|
identifier
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a robots.txt file in the root of the site by dynamically creating
|
||||||
|
# a new item.
|
||||||
|
#
|
||||||
|
# This will either output a default robots.txt file, that disallows all
|
||||||
|
# assets except images, and points to the sitemap file.
|
||||||
|
#
|
||||||
|
# You can override the contents of the output of this method using the site
|
||||||
|
# configuration, specifying Allow and Disallow directives. See the config.yaml
|
||||||
|
# file for more information on the expected input format.
|
||||||
|
def create_robots_txt
|
||||||
|
return unless @site.config[:output_generated_assets]
|
||||||
|
|
||||||
|
if @site.config[:robots]
|
||||||
|
content = if @site.config[:robots][:default]
|
||||||
|
<<-EOS
|
||||||
|
User-agent: *
|
||||||
|
Allow: *
|
||||||
|
Sitemap: #{@site.config[:base_url]}/sitemap.xml
|
||||||
|
EOS
|
||||||
|
else
|
||||||
|
[
|
||||||
|
'User-Agent: *',
|
||||||
|
@site.config[:robots][:disallow].map { |l| "Disallow: #{l}" },
|
||||||
|
(@site.config[:robots][:allow] || []).map { |l| "Allow: #{l}" },
|
||||||
|
"Sitemap: #{@site.config[:robots][:sitemap]}"
|
||||||
|
].flatten.compact.join("\n")
|
||||||
|
end
|
||||||
|
@items << Nanoc3::Item.new(
|
||||||
|
content,
|
||||||
|
{ :extension => 'txt', :is_hidden => true },
|
||||||
|
'/robots/'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
31
lib/shellcmd.rb
Normal file
31
lib/shellcmd.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
require 'open3'
|
||||||
|
|
||||||
|
# This nanoc filter is a general purpose filter that simply pipes
|
||||||
|
# the contents of an item into a given shell command, and sets
|
||||||
|
# the items output to the output of it.
|
||||||
|
#
|
||||||
|
# It is NOT safe to use on large inputs, which will cause I/O
|
||||||
|
# deadlocks. Any safer implementation is encouraged.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# compile '/static/js/*/' do
|
||||||
|
# # minify JS :)
|
||||||
|
# filter :shellcmd, "java -jar js-compiler.jar"
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# Written by Vincent Driessen (http://twitter.com/nvie) and
|
||||||
|
# released to the public domain.
|
||||||
|
#
|
||||||
|
# http://nvie.com
|
||||||
|
class ShellCmdFilter < Nanoc3::Filter
|
||||||
|
identifier :shellcmd
|
||||||
|
|
||||||
|
def run(content, params)
|
||||||
|
Open3.popen3(params[:cmd]) do |stdin, stdout, stderr|
|
||||||
|
stdin.write(content)
|
||||||
|
stdin.close
|
||||||
|
stdout.read
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
76
lib/xml_sitemap.rb
Normal file
76
lib/xml_sitemap.rb
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Source: https://github.com/ddfreyne/nanoc/blob/master/lib/nanoc/helpers/xml_sitemap.rb
|
||||||
|
# hacked to remove trailing slash
|
||||||
|
module Nanoc::Helpers
|
||||||
|
|
||||||
|
# Contains functionality for building XML sitemaps that will be crawled by
|
||||||
|
# search engines. See the [Sitemaps protocol site](http://www.sitemaps.org)
|
||||||
|
# for details.
|
||||||
|
module XMLSitemap
|
||||||
|
|
||||||
|
# Builds an XML sitemap and returns it.
|
||||||
|
#
|
||||||
|
# The following attributes can optionally be set on items to change the
|
||||||
|
# behaviour of the sitemap:
|
||||||
|
#
|
||||||
|
# * `changefreq` — The estimated change frequency as defined by the
|
||||||
|
# Sitemaps protocol
|
||||||
|
#
|
||||||
|
# * `priority` — The item's priority, ranging from 0.0 to 1.0, as defined
|
||||||
|
# by the Sitemaps protocol
|
||||||
|
#
|
||||||
|
# The sitemap will also include dates on which the items were updated.
|
||||||
|
# These are generated automatically; the way this happens depends on the
|
||||||
|
# used data source (the filesystem data source checks the file mtimes, for
|
||||||
|
# instance).
|
||||||
|
#
|
||||||
|
# The site configuration will need to have the following attributes:
|
||||||
|
#
|
||||||
|
# * `base_url` — The URL to the site, without trailing slash. For example,
|
||||||
|
# if the site is at "http://example.com/", the `base_url` would be
|
||||||
|
# "http://example.com".
|
||||||
|
#
|
||||||
|
# @example Excluding binary items from the sitemap
|
||||||
|
#
|
||||||
|
# <%= xml_sitemap :items => @items.reject{ |i| i[:is_hidden] || i.binary? } %>
|
||||||
|
#
|
||||||
|
# @option params [Array] :items A list of items to include in the sitemap
|
||||||
|
#
|
||||||
|
# @return [String] The XML sitemap
|
||||||
|
def xml_sitemap(params={})
|
||||||
|
require 'builder'
|
||||||
|
|
||||||
|
# Extract parameters
|
||||||
|
items = params[:items] || @items.reject { |i| i[:is_hidden] }
|
||||||
|
|
||||||
|
# Create builder
|
||||||
|
buffer = ''
|
||||||
|
xml = Builder::XmlMarkup.new(:target => buffer, :indent => 2)
|
||||||
|
|
||||||
|
# Check for required attributes
|
||||||
|
if @site.config[:base_url].nil?
|
||||||
|
raise RuntimeError.new("The Nanoc::Helpers::XMLSitemap helper requires the site configuration to specify the base URL for the site.")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Build sitemap
|
||||||
|
xml.instruct!
|
||||||
|
xml.urlset(:xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9') do
|
||||||
|
# Add item
|
||||||
|
items.each do |item|
|
||||||
|
item.reps.reject { |r| r.raw_path.nil? }.each do |rep|
|
||||||
|
xml.url do
|
||||||
|
xml.loc @site.config[:base_url] + rep.path.sub(/(\.html|\/)$/, '')
|
||||||
|
xml.lastmod item.mtime.to_iso8601_date unless item.mtime.nil?
|
||||||
|
xml.changefreq item[:changefreq] unless item[:changefreq].nil?
|
||||||
|
xml.priority item[:priority] unless item[:priority].nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return sitemap
|
||||||
|
buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Add table
Add a link
Reference in a new issue