Skip to content

Instantly share code, notes, and snippets.

@pachacamac
Created December 1, 2012 09:44
Show Gist options
  • Save pachacamac/4181330 to your computer and use it in GitHub Desktop.
Save pachacamac/4181330 to your computer and use it in GitHub Desktop.
NikiWiki

tags: home main index start author: admin protected: admin

Welcome to NikiWiki

(The N stands for nexe or nano)

Here is a list of all pages we have right now

-=index=-

And a few custom commands for your pleasure

The current time is: -=time=-

-=uptime=-

tags: markdown cheatsheet help author: admin protected: admin

Markdown Cheatsheet

{:.no_toc}

  • toc {:toc}

General

#### This is an h4 tag
###### This is an h6 tag

This is an h4 tag

This is an h6 tag
*This text will be italic*
_This will also be italic_
**This text will be bold**
__This will also be bold__
*You __can__ combine them*

This text will be italic This will also be italic This text will be bold This will also be bold You can combine them


Links

[Reddit](http://reddit.com)

Reddit


Images

![octo goodness](https://a248.e.akamai.net/assets.github.com/images/modules/dashboard/octofication.png)

octo goodness


Lists

* Item 1
* Item 2
  1. Item 2a
  2. Item 2b
  3. Item 2c
  • Item 1
  • Item 2
    1. Item 2a
    2. Item 2b
    3. Item 2c

Codeblocks

start with 4 spaces

    void main(){
        return -1;
    }

void main(){
    return -1;
}

Blockquotes

> We're living the future so
> the present is our past.

We're living the future so the present is our past.


Custom styles

[Reddit](http://reddit.com){: #myid .btn .btn-primary}

Reddit{: #myid .btn .btn-primary}


Footnotes

* footnotes [^foot]

[^foot]: I really was missing those.
  • footnotes 1

Tables

Col1 | Very very long head | Very very long head |
-----|:-------------------:|-------------------:|
cell1 | center-align    | right-align        |
cell2 | one more        | one more        |
{: .table .table-bordered .table-hover}
Col1 Very very long head Very very long head
cell1 center-align right-align
cell2 one more one more
{: .table .table-bordered .table-hover}

Markdown headers

The first lines before a double linebreak are treated as header lines.

Here you can use these special headers among other normal markdown headers:

private: foo         # page is only accessible by the user foo
private: foo bar     # page is only accessible by the users foo and bar
protected: fritz     # page is only editable by the user fritz
protected: hans foo  # page is only editable by the users hans and foo

tags: blah stuff crap  # feel free to use this to enhance searchability

Embedding stuff (special)

This is a specialty of this Wiki and has nothing to do with markdown.

Use -=func=- or -=func arg=- where func should be defined in the replacers method.

The following youtube video is embedded using embed as func and http://www.youtube.com/embed/SDnYMbYB-nU as arg

-=embed http://www.youtube.com/embed/SDnYMbYB-nU=-

Footnotes

  1. I really was missing those.

#!/usr/bin/env ruby
DATADIR = (i=ARGV.index('--datadir')) ? ARGV.slice!(i,i+1)[1] : 'data' # hack to cope with Sinatras ARGV greediness
require 'sinatra'
require 'haml'
require 'maruku'
require 'yaml/store'
STDERR.reopen(File.new('access.log','a')).sync = true
def clean(name, fallback=nil, re=/\w+/) # clean a name with a regexp
name.tr(' ','_').match(re)[0] rescue fallback
end
def pages(name=nil, version=nil) # find matching files/versions
Dir.glob("#{DATADIR}/#{'.'if version}#{clean(name,'*')}.#{clean(version,'*')}.*")
end
def markdown_parts(s) # read markdown headers and body and convert headers to a hash
lines = s.lines.to_a
[{},''].tap{|a| a[0][$1.to_sym]=$2 while (l=lines.shift) =~ /^(\w[\w\s\-]+): +(.*?)$/; a[1] = ([l]+lines).join}
end
def protect!(levels, headers=@headers, user=@user) # keep out the unauthorized
levels.each{|l| throw(:halt, [401, "Not authorized - #{l}\n"]) if headers[l] && !headers[l].split.map(&:strip).include?(user)}
end
def replacers(s) # special replacers to add more dynamic to the wiki
s.gsub(/-=index=-/){ @files = pages('*').map{|e| File.basename(e).split('.')[0]}.sort; haml(:list, layout: false) }
.gsub(/-=versions (.*?)=-/){ @files = pages($1,'*').map{|e| File.basename(e).split('.')[1,2]}; haml(:list, layout: false) }
.gsub(/-=partial (.*?)=-/){ h,c=markdown_parts(File.read(pages($1)[0]));protect!([:private],h); Maruku.new(c).to_html }
.gsub(/-=embed (.*?)=-/){ %(<iframe src="#{URI.parse($1).to_s}" frameborder="0">&nbsp;</iframe>) }
.gsub(/-=diff=-/){ %x{diff -Bu #{pages(@page,@version).first} #{pages(@page).first}} }
.gsub(/-=time=-/, Time.now.to_s) # you can simply add custom stuff like this
.gsub(/-=uptime=-/, %x{uptime}.strip) # remember to keep it safe ;)
end
configure{ set :sessions => true }
before{ @user = session[:user]; content_type 'text/html', :charset => 'utf-8' } # before every request
get '/' do redirect '/page/home' end
get '/page/?:page?/?' do
@page, @version = clean(params[:page], params.has_key?('edit') ? nil : 'home'), clean(params[:version])
@raw_content = (@page ? (File.read(pages(@page,@version).first) rescue '') : '').gsub(/</, '&lt;') #xss protection?!
@headers, @content = markdown_parts(replacers(@raw_content)) # replacer magic and header retrieval
protect!([:private]) # if the page has a private field in the header, honor it
haml (@content=='' || params.has_key?('edit')) ? :edit : :show
end
post '/page/?:page?/?' do # create or update a page .. yea not RESTful
throw(:halt, [401, "Not logged in\n"]) unless @user # only users can play here
FileUtils.mkdir_p(DATADIR) unless File.exist?(DATADIR) # create data dir
@page, time = clean(params[:page]), Time.now.strftime('%y%m%d%H%I%S') # pagename and version
file = pages(@page).first || nil # edit or create?
throw(:halt, [405, "Pagename invalid. (only a-zA-Z0-9_)\n"]) unless @page
protect!([:protected, :private], markdown_parts(File.read(file))[0]) if file # check rights
@headers, @content = markdown_parts(params[:content])
FileUtils.copy(file, "#{DATADIR}/.#{@page}.#{time}.md") if file # backup current to a version
@headers = @headers.merge({author: @user}).map{|h| h.join(': ')}.join("\r\n") # overwrite author header
File.write(file || "#{DATADIR}/#{@page}.#{time}.md", "#{@headers}\r\n\r\n#{@content}")
redirect "/page/#{@page}"
end
post '/?' do # user stuff and search
if params[:q] # the search happens here. could be a GET but who cares?
@files = (pages('*') + pages('*','*')).map do |f| # searching in latest and versions
bnc = File.basename(f).split('.') # "basename components"
name, version = bnc[0]=='' ? bnc[1,2] : [bnc[0], nil]
hits = File.read(f).scan(/#{params[:q]}/i).size # magic search operation
[name, version, "#{hits} hits"] if hits > 0
end.compact.sort{|a,b| b.last.to_i<=>a.last.to_i} # throw out non hits and sort by hits
@page = 'Search Results'; return haml :list
end
db = YAML::Store.new('users.yml') # User stuff happens here! login/logout/register
(@user = session[:user] = nil; return redirect(back)) if params[:logout] # that's the logout
throw(:halt, [401, "Username invalid. (only a-zA-Z0-9_)\n"]) unless (clean_user = clean(params[:user])) # clean names only
db.transaction{db[clean_user] = Digest::SHA2.base64digest(params[:pass]) unless db[clean_user]} if params[:register] # register
db.transaction{db[clean_user] == Digest::SHA2.base64digest(params[:pass]).to_s} ?
(session[:user] = clean_user; redirect(back)) : # successfully logged in
throw(:halt, [401, "Login invalid\n"]) # login failed
end
__END__
@@ layout
!!! 5
%html
%head
%title= @page ? "niki - #{@page}" : 'niki'
%meta{'http-equiv' => 'Content-Type', content: 'text/html', charset: 'utf-8'}
%meta{name: 'keywords', content: (@headers[:tags].split(/\W/).uniq.join(', ') rescue '')}
%link{rel: 'shortcut icon', href: 'about:blank'}
%link{rel: 'stylesheet', type: 'text/css', href: '//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css'}
:css
body { padding: 0; }
.container-narrow { margin: 40px auto; max-width: 820px; }
textarea{ width: 99%; }
iframe{ width: 99%; height:360px; }
%body
.container-narrow
%form.form-inline.pull-right{action: '/', method: 'post'}
%input.input-small{type: 'text', name: 'q', placeholder: 'search', required:''}
%button.btn{type:'submit'} go
%ul.nav.nav-pills.pull-right
%li
%a{href: '/page/home'}
%i.icon-home
home
%li
%a{href: "/page?edit"}
%i.icon-plus
new
%li
%a{href: "/page/#{@page}?edit#{"&version=#{@version}" if @version}"}
%i.icon-pencil
edit
%h3.muted= @page || 'New Page'
%hr
= yield
%hr
%footer.muted
&copy; The Open Source Community
%form.form-inline.pull-right{action: '/', method: 'post'}
- if @user
%button.btn.btn-link{type:'submit', name: 'logout', value: '1'}&= "logout #{@user}"
- else
%input.input-small{type: 'text', name: 'user', placeholder: 'username', required:''}
%input.input-small{type: 'password', name: 'pass', placeholder: 'password', required:''}
%button.btn{type:'submit'} login
%button.btn{type:'submit', name: 'register', value: '1'} register
@@ list
%ul.nav.nav-list
- @files.each do |name, version, extra|
%li
%a{href: "/page/#{name}#{"?version=#{version}" if version}"}= [name, version, extra].compact.join(' &ndash; ')
@@ show
%div{:ondblclick => "location.href='/page/#{@page}?edit'"}
:markdown
#{@content}
@@ edit
%form{action: "/page/#{@page}", method: 'post'}
- unless @page
%input{type: 'text', name: 'page', required: '', placeholder: 'page name', autofocus: '', required:''}
%textarea{name: 'content', rows: '20', placeholder: 'page content in markdown', required:''}= @raw_content
%a.btn{href: "#{@page ? "/page/#{@page}" : '/page'}"} back
%input.btn.btn-primary{type: 'submit', value: 'save'}
- if @page
- if @version
%h4 Diff with latest version
%pre= replacers("-=diff=-")
%h4 Older versions of this page:
= replacers("-=versions #{@page}=-")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment