├── jinja2 ├── testsuite │ ├── res │ │ ├── __init__.py │ │ └── templates │ │ │ ├── test.html │ │ │ ├── foo │ │ │ └── test.html │ │ │ ├── broken.html │ │ │ └── syntaxerror.html │ ├── doctests.py │ ├── debug.py │ ├── utils.py │ ├── tests.py │ └── __init__.py ├── defaults.py ├── optimizer.py ├── __init__.py ├── visitor.py └── tests.py ├── markdown ├── extensions │ ├── __init__.py │ ├── extra.py │ ├── html_tidy.py │ ├── meta.py │ ├── abbr.py │ ├── tables.py │ ├── def_list.py │ ├── fenced_code.py │ ├── imagelinks.py │ └── rss.py ├── etree_loader.py ├── postprocessors.py ├── blockparser.py └── commandline.py ├── .gitignore ├── blog ├── static │ ├── css │ │ ├── handheld.css │ │ ├── fonts │ │ │ ├── slkscr-webfont.eot │ │ │ ├── slkscr-webfont.ttf │ │ │ ├── slkscr-webfont.woff │ │ │ ├── slkscrb-webfont.eot │ │ │ ├── slkscrb-webfont.ttf │ │ │ ├── slkscrb-webfont.woff │ │ │ ├── Chunkfive-webfont.eot │ │ │ ├── Chunkfive-webfont.ttf │ │ │ ├── Chunkfive-webfont.woff │ │ │ ├── DroidSansMono-webfont.eot │ │ │ ├── DroidSansMono-webfont.ttf │ │ │ ├── DroidSansMono-webfont.woff │ │ │ ├── fontin_sans_b_45b-webfont.eot │ │ │ ├── fontin_sans_b_45b-webfont.ttf │ │ │ ├── fontin_sans_b_45b-webfont.woff │ │ │ ├── fontin_sans_i_45b-webfont.eot │ │ │ ├── fontin_sans_i_45b-webfont.ttf │ │ │ ├── fontin_sans_i_45b-webfont.woff │ │ │ ├── fontin_sans_r_45b-webfont.eot │ │ │ ├── fontin_sans_r_45b-webfont.ttf │ │ │ └── fontin_sans_r_45b-webfont.woff │ │ ├── syntax.css │ │ └── boilerplate.css │ ├── favicon.ico │ ├── img │ │ ├── img_1.png │ │ ├── img_10.png │ │ ├── img_11.png │ │ ├── img_12.png │ │ ├── img_2.png │ │ ├── img_3.png │ │ ├── img_4.png │ │ ├── img_5.png │ │ ├── img_6.png │ │ ├── img_7.png │ │ ├── img_8.png │ │ └── img_9.png │ ├── js │ │ ├── plugins.js │ │ └── script.js │ └── pages │ │ └── about.md ├── templates │ ├── about.html │ ├── home.html │ ├── login.html │ ├── admin.html │ ├── edit_entry.html │ ├── add_entry.html │ ├── layout.html │ └── list_entries.html ├── __init__.py ├── schema │ ├── schema.sql │ └── fixture-sqlite.sql └── models.py ├── werkzeug ├── debug │ ├── shared │ │ ├── less.png │ │ ├── more.png │ │ ├── source.png │ │ ├── console.png │ │ ├── codetable.tmpl │ │ ├── vartable.tmpl │ │ └── body.tmpl │ ├── templates │ │ ├── source.html │ │ ├── traceback_plaintext.html │ │ ├── help_command.html │ │ ├── frame.html │ │ ├── dump_object.html │ │ ├── traceback_summary.html │ │ ├── console.html │ │ └── traceback_full.html │ ├── utils.py │ └── render.py ├── contrib │ ├── __init__.py │ ├── limiter.py │ └── testtools.py ├── posixemulation.py └── security.py ├── app.yaml ├── config └── __init__.py ├── main.py ├── index.yaml ├── flask ├── globals.py ├── __init__.py ├── session.py ├── logging.py ├── testing.py ├── signals.py ├── ctx.py ├── wrappers.py └── templating.py ├── LICENSE └── pygments ├── styles ├── vs.py ├── fruity.py ├── bw.py ├── borland.py ├── trac.py ├── native.py ├── vim.py ├── __init__.py ├── autumn.py ├── perldoc.py ├── manni.py ├── emacs.py ├── pastie.py ├── friendly.py ├── default.py ├── murphy.py └── colorful.py ├── formatters ├── __init__.py └── bbcode.py ├── console.py ├── plugin.py ├── filter.py ├── formatter.py ├── __init__.py ├── lexers └── special.py └── scanner.py /jinja2/testsuite/res/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /markdown/extensions/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/test.html: -------------------------------------------------------------------------------- 1 | BAR 2 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/foo/test.html: -------------------------------------------------------------------------------- 1 | FOO 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.db 4 | *.*~ 5 | config.cfg 6 | -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/broken.html: -------------------------------------------------------------------------------- 1 | Before 2 | {{ fail() }} 3 | After 4 | -------------------------------------------------------------------------------- /blog/static/css/handheld.css: -------------------------------------------------------------------------------- 1 | *{float:none;font-size:80%;background:#fff;color:#000;} 2 | -------------------------------------------------------------------------------- /blog/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/favicon.ico -------------------------------------------------------------------------------- /jinja2/testsuite/res/templates/syntaxerror.html: -------------------------------------------------------------------------------- 1 | Foo 2 | {% for item in broken %} 3 | ... 4 | {% endif %} 5 | -------------------------------------------------------------------------------- /blog/static/img/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_1.png -------------------------------------------------------------------------------- /blog/static/img/img_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_10.png -------------------------------------------------------------------------------- /blog/static/img/img_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_11.png -------------------------------------------------------------------------------- /blog/static/img/img_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_12.png -------------------------------------------------------------------------------- /blog/static/img/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_2.png -------------------------------------------------------------------------------- /blog/static/img/img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_3.png -------------------------------------------------------------------------------- /blog/static/img/img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_4.png -------------------------------------------------------------------------------- /blog/static/img/img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_5.png -------------------------------------------------------------------------------- /blog/static/img/img_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_6.png -------------------------------------------------------------------------------- /blog/static/img/img_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_7.png -------------------------------------------------------------------------------- /blog/static/img/img_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_8.png -------------------------------------------------------------------------------- /blog/static/img/img_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/img/img_9.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/werkzeug/debug/shared/less.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/werkzeug/debug/shared/more.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/werkzeug/debug/shared/source.png -------------------------------------------------------------------------------- /werkzeug/debug/shared/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/werkzeug/debug/shared/console.png -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscr-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscr-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscr-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscr-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscrb-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscrb-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/slkscrb-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/slkscrb-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/Chunkfive-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/Chunkfive-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/Chunkfive-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/Chunkfive-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/DroidSansMono-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/DroidSansMono-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/DroidSansMono-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/DroidSansMono-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_b_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_b_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_b_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_b_45b-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_i_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_i_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_i_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_i_45b-webfont.woff -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_r_45b-webfont.eot -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_r_45b-webfont.ttf -------------------------------------------------------------------------------- /blog/static/css/fonts/fontin_sans_r_45b-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proudlygeek/proudlygeek-blog/HEAD/blog/static/css/fonts/fontin_sans_r_45b-webfont.woff -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: proudlygeek 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | 7 | handlers: 8 | - url: /.* 9 | script: main.py 10 | 11 | - url: /static 12 | static_dir: static 13 | expiration: "7d" 14 | -------------------------------------------------------------------------------- /werkzeug/debug/shared/codetable.tmpl: -------------------------------------------------------------------------------- 1 |
| $line.lineno | 5 |$line.code | 6 |
| ${line.lineno} | 5 |$escape(line.code) | 6 |
$text6 | <% else %> 7 |
Type help(object) for help about object.
9 | <% endif %> 10 |$escape(frame.function_name)${escape(frame.current_line.strip())}
6 | | $escape(key) | 10 |$value | 11 |
|---|
| no data given | |
|---|---|
| $escape(value) | |
| Name | Value |
| $escape(key) | $escape(item) |
| $escape(item) |
Error: {{ error }}{% endif %} 8 |
$escape(traceback.exception)12 | <% else %> 13 | <% if include_title %> 14 |
$escape(traceback.exception)22 | <% endif %> 23 |
| id | 10 |slug | 11 |author | 12 |title | 13 |tag(s) | 14 | 15 | {% for entry in entries %} 16 |
|---|---|---|---|---|
| {{entry.id}} | 18 |{{entry.slug}} | 19 |{{entry.author}} 20 | | {{entry.title}} | 21 |22 | {% for tag in entry.tags %} 23 | {{tag}} 24 | {% else %} 25 | - 26 | {% endfor %} 27 | | 28 |
8 | Error: 9 |
$escape(traceback.exception)
20 |%s
" % 56 | (markdown.preprocessors.HTML_PLACEHOLDER % i), 57 | html + "\n") 58 | text = text.replace(markdown.preprocessors.HTML_PLACEHOLDER % i, 59 | html) 60 | return text 61 | 62 | def escape(self, html): 63 | """ Basic html escaping """ 64 | html = html.replace('&', '&') 65 | html = html.replace('<', '<') 66 | html = html.replace('>', '>') 67 | return html.replace('"', '"') 68 | 69 | 70 | class AndSubstitutePostprocessor(Postprocessor): 71 | """ Restore valid entities """ 72 | def __init__(self): 73 | pass 74 | 75 | def run(self, text): 76 | text = text.replace(markdown.AMP_SUBSTITUTE, "&") 77 | return text 78 | -------------------------------------------------------------------------------- /pygments/styles/friendly.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.friendly 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A modern style based on the VIM pyte theme. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class FriendlyStyle(Style): 18 | """ 19 | A modern style based on the VIM pyte theme. 20 | """ 21 | 22 | background_color = "#f0f0f0" 23 | default_style = "" 24 | 25 | styles = { 26 | Whitespace: "#bbbbbb", 27 | Comment: "italic #60a0b0", 28 | Comment.Preproc: "noitalic #007020", 29 | Comment.Special: "noitalic bg:#fff0f0", 30 | 31 | Keyword: "bold #007020", 32 | Keyword.Pseudo: "nobold", 33 | Keyword.Type: "nobold #902000", 34 | 35 | Operator: "#666666", 36 | Operator.Word: "bold #007020", 37 | 38 | Name.Builtin: "#007020", 39 | Name.Function: "#06287e", 40 | Name.Class: "bold #0e84b5", 41 | Name.Namespace: "bold #0e84b5", 42 | Name.Exception: "#007020", 43 | Name.Variable: "#bb60d5", 44 | Name.Constant: "#60add5", 45 | Name.Label: "bold #002070", 46 | Name.Entity: "bold #d55537", 47 | Name.Attribute: "#4070a0", 48 | Name.Tag: "bold #062873", 49 | Name.Decorator: "bold #555555", 50 | 51 | String: "#4070a0", 52 | String.Doc: "italic", 53 | String.Interpol: "italic #70a0d0", 54 | String.Escape: "bold #4070a0", 55 | String.Regex: "#235388", 56 | String.Symbol: "#517918", 57 | String.Other: "#c65d09", 58 | Number: "#40a070", 59 | 60 | Generic.Heading: "bold #000080", 61 | Generic.Subheading: "bold #800080", 62 | Generic.Deleted: "#A00000", 63 | Generic.Inserted: "#00A000", 64 | Generic.Error: "#FF0000", 65 | Generic.Emph: "italic", 66 | Generic.Strong: "bold", 67 | Generic.Prompt: "bold #c65d09", 68 | Generic.Output: "#888", 69 | Generic.Traceback: "#04D", 70 | 71 | Error: "border:#FF0000" 72 | } 73 | -------------------------------------------------------------------------------- /pygments/styles/default.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pygments.styles.default 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | The default highlighting style. 7 | 8 | :copyright: Copyright 2006-2010 by the Pygments team, see AUTHORS. 9 | :license: BSD, see LICENSE for details. 10 | """ 11 | 12 | from pygments.style import Style 13 | from pygments.token import Keyword, Name, Comment, String, Error, \ 14 | Number, Operator, Generic, Whitespace 15 | 16 | 17 | class DefaultStyle(Style): 18 | """ 19 | The default style (inspired by Emacs 22). 20 | """ 21 | 22 | background_color = "#f8f8f8" 23 | default_style = "" 24 | 25 | styles = { 26 | Whitespace: "#bbbbbb", 27 | Comment: "italic #408080", 28 | Comment.Preproc: "noitalic #BC7A00", 29 | 30 | #Keyword: "bold #AA22FF", 31 | Keyword: "bold #008000", 32 | Keyword.Pseudo: "nobold", 33 | Keyword.Type: "nobold #B00040", 34 | 35 | Operator: "#666666", 36 | Operator.Word: "bold #AA22FF", 37 | 38 | Name.Builtin: "#008000", 39 | Name.Function: "#0000FF", 40 | Name.Class: "bold #0000FF", 41 | Name.Namespace: "bold #0000FF", 42 | Name.Exception: "bold #D2413A", 43 | Name.Variable: "#19177C", 44 | Name.Constant: "#880000", 45 | Name.Label: "#A0A000", 46 | Name.Entity: "bold #999999", 47 | Name.Attribute: "#7D9029", 48 | Name.Tag: "bold #008000", 49 | Name.Decorator: "#AA22FF", 50 | 51 | String: "#BA2121", 52 | String.Doc: "italic", 53 | String.Interpol: "bold #BB6688", 54 | String.Escape: "bold #BB6622", 55 | String.Regex: "#BB6688", 56 | #String.Symbol: "#B8860B", 57 | String.Symbol: "#19177C", 58 | String.Other: "#008000", 59 | Number: "#666666", 60 | 61 | Generic.Heading: "bold #000080", 62 | Generic.Subheading: "bold #800080", 63 | Generic.Deleted: "#A00000", 64 | Generic.Inserted: "#00A000", 65 | Generic.Error: "#FF0000", 66 | Generic.Emph: "italic", 67 | Generic.Strong: "bold", 68 | Generic.Prompt: "bold #000080", 69 | Generic.Output: "#888", 70 | Generic.Traceback: "#04D", 71 | 72 | Error: "border:#FF0000" 73 | } 74 | -------------------------------------------------------------------------------- /blog/static/css/boilerplate.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,abbr,address,cite,code,del,dfn,em,img,ins,kbd,q,samp,small,strong,sub,sup,var,b,i,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,figure,footer,header,hgroup,menu,nav,section,menu,time,mark,audio,video{border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}nav ul{list-style:none;}blockquote,q{quotes:none;}blockquote:before,blockquote:after,q:before,q:after{content:none;}a{font-size:100%;vertical-align:baseline;background:transparent;margin:0;padding:0;}ins{background-color:#ff9;color:#000;text-decoration:none;}mark{background-color:#ff9;color:#000;font-style:italic;font-weight:700;}del{text-decoration:line-through;}abbr[title],dfn[title]{border-bottom:1px dotted #000;cursor:help;}table{border-collapse:collapse;border-spacing:0;font-size:inherit;font:100%;}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0;}input,select{vertical-align:middle;}body{font-size:small;font:x-small;line-height:1.22;}select,input,textarea{font:99% sans-serif;}pre,code,kbd,samp{font-family:monospace, sans-serif;}body,select,input,textarea{color:#444;}h1,h2,h3,h4,h5,h6{font-weight:700;text-rendering:optimizeLegibility;}html{-webkit-font-smoothing:antialiased;overflow-y:scroll;}a:hover,a:active{outline:none;}a,a:active,a:visited{color:#607890;}a:hover{color:#036;}ul{margin-left:30px;}ol{margin-left:30px;list-style-type:decimal;}small{font-size:85%;}strong,th{font-weight:700;}td,td img{vertical-align:top;}sub{vertical-align:sub;font-size:smaller;}sup{vertical-align:super;font-size:smaller;}pre{white-space:pre-line;word-wrap:break-word;padding:15px;}input[type=checkbox]{vertical-align:baseline;}label,input[type=button],input[type=submit],button{cursor:pointer;}a:link{-webkit-tap-highlight-color:#FF5E99;}button{width:auto;overflow:visible;}.ie7 img{-ms-interpolation-mode:bicubic;}.ir{display:block;text-indent:-999em;overflow:hidden;background-repeat:no-repeat;}.hidden{display:none;visibility:hidden;}.visuallyhidden{position:absolute!important;clip:rect(1px,1px,1px,1px);}.invisible{visibility:hidden;}.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}* html .clearfix{height:1%;}article,aside,figure,footer,header,hgroup,nav,section,.clearfix{display:block;}input[type=radio],.ie6 input{vertical-align:text-bottom;}::-moz-selection,::selection{background:#FF5E99;color:#fff;text-shadow:none;}@media print{*{background:transparent!important;color:#444!important;text-shadow:none;}a,a:visited{color:#444!important;text-decoration:underline;}a:after{content:" (" attr(href) ")";}abbr:after{content:" (" attr(title) ")";}.ir a:after{content:"";}pre,blockquote{border:1px solid #999;page-break-inside:avoid;}img{page-break-inside:avoid;}@page{margin:.5cm;}p,h2,h3{orphans:3;widows:3;}h2,h3{page-break-after:avoid;}}@media screen and max-device-width 480px{html{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;}} 2 | -------------------------------------------------------------------------------- /markdown/extensions/meta.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/python 2 | 3 | """ 4 | Meta Data Extension for Python-Markdown 5 | ======================================= 6 | 7 | This extension adds Meta Data handling to markdown. 8 | 9 | Basic Usage: 10 | 11 | >>> import markdown 12 | >>> text = '''Title: A Test Doc. 13 | ... Author: Waylan Limberg 14 | ... John Doe 15 | ... Blank_Data: 16 | ... 17 | ... The body. This is paragraph one. 18 | ... ''' 19 | >>> md = markdown.Markdown(['meta']) 20 | >>> md.convert(text) 21 | u'The body. This is paragraph one.
' 22 | >>> md.Meta 23 | {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']} 24 | 25 | Make sure text without Meta Data still works (markdown < 1.6b returns a). 26 | 27 | >>> text = ' Some Code - not extra lines of meta data.' 28 | >>> md = markdown.Markdown(['meta']) 29 | >>> md.convert(text) 30 | u'
Some Code - not extra lines of meta data.\\n'
31 | >>> md.Meta
32 | {}
33 |
34 | Copyright 2007-2008 [Waylan Limberg](http://achinghead.com).
35 |
36 | Project website: Some text with an ABBR and a REF. Ignore REFERENCE and ref.
' 18 | 19 | Copyright 2007-2008 20 | * [Waylan Limberg](http://achinghead.com/) 21 | * [Seemant Kulleen](http://www.kulleen.org/) 22 | 23 | 24 | ''' 25 | 26 | import markdown, re 27 | from markdown import etree 28 | 29 | # Global Vars 30 | ABBR_REF_RE = re.compile(r'[*]\[(?P[^\]]*)\][ ]?:\s*(?P$escape(exception_value)
14 | 15 |16 | $escape(last_frame['filename']) in 17 | $escape(last_frame['function']), 18 | line $last_frame['lineno'] 19 |
20 | 21 |A problem occurred in your Python WSGI application. 24 | Here is the sequence of function calls leading up to the error, in the order 25 | they occurred. Activate a code line to toggle context lines.
26 | 27 | <% for num, frame in enumerate(frames) %> 28 | 43 | <% endfor %> 44 |Here is the plain Python traceback for copy and paste:
48 |$escape(plaintb)49 |
50 | Create a new Paste with 51 | this traceback in the lodgeit pastebin. 52 |
53 |The following list contains all important request variables. 58 | Select a header to expand the list.
59 | <% for num, (key, info) in enumerate(req_vars) %> 60 |A paragraph before a fenced code block:
\\nFenced code block\\n'
20 |
21 | Works with safe_mode also (we check this because we are using the HtmlStash):
22 |
23 | >>> markdown.markdown(text, extensions=['fenced_code'], safe_mode='replace')
24 | u'A paragraph before a fenced code block:
\\nFenced code block\\n'
25 |
26 | Include tilde's in a code block and wrap with blank lines:
27 |
28 | >>> text = '''
29 | ... ~~~~~~~~
30 | ...
31 | ... ~~~~
32 | ...
33 | ... ~~~~~~~~'''
34 | >>> markdown.markdown(text, extensions=['fenced_code'])
35 | u'\\n~~~~\\n\\n'
36 |
37 | Multiple blocks and language tags:
38 |
39 | >>> text = '''
40 | ... ~~~~{.python}
41 | ... block one
42 | ... ~~~~
43 | ...
44 | ... ~~~~.html
45 | ... block two
46 | ... ~~~~''' 47 | >>> markdown.markdown(text, extensions=['fenced_code']) 48 | u'block one\\n\\n\\n<p>block two</p>\\n'
49 |
50 | Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
51 |
52 | Project website: .*?)(?P=fence)[ ]*$',
68 | re.MULTILINE|re.DOTALL
69 | )
70 | CODE_WRAP = '%s
'
71 | LANG_TAG = ' class="%s"'
72 |
73 |
74 | class FencedCodeExtension(markdown.Extension):
75 |
76 | def extendMarkdown(self, md, md_globals):
77 | """ Add FencedBlockPreprocessor to the Markdown instance. """
78 |
79 | md.preprocessors.add('fenced_code_block',
80 | FencedBlockPreprocessor(md),
81 | "_begin")
82 |
83 |
84 | class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor):
85 |
86 | def run(self, lines):
87 | """ Match and store Fenced Code Blocks in the HtmlStash. """
88 | text = "\n".join(lines)
89 | while 1:
90 | m = FENCED_BLOCK_RE.search(text)
91 | if m:
92 | lang = ''
93 | if m.group('lang'):
94 | lang = LANG_TAG % m.group('lang')
95 | code = CODE_WRAP % (lang, self._escape(m.group('code')))
96 | placeholder = self.markdown.htmlStash.store(code, safe=True)
97 | text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
98 | else:
99 | break
100 | return text.split("\n")
101 |
102 | def _escape(self, txt):
103 | """ basic html escaping """
104 | txt = txt.replace('&', '&')
105 | txt = txt.replace('<', '<')
106 | txt = txt.replace('>', '>')
107 | txt = txt.replace('"', '"')
108 | return txt
109 |
110 |
111 | def makeExtension(configs=None):
112 | return FencedCodeExtension()
113 |
114 |
115 | if __name__ == "__main__":
116 | import doctest
117 | doctest.testmod()
118 |
--------------------------------------------------------------------------------
/werkzeug/posixemulation.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | r"""
3 | werkzeug.posixemulation
4 | ~~~~~~~~~~~~~~~~~~~~~~~
5 |
6 | Provides a POSIX emulation for some features that are relevant to
7 | web applications. The main purpose is to simplify support for
8 | systems such as Windows NT that are not 100% POSIX compatible.
9 |
10 | Currently this only implements a :func:`rename` function that
11 | follows POSIX semantics. Eg: if the target file already exists it
12 | will be replaced without asking.
13 |
14 | This module was introduced in 0.6.1 and is not a public interface.
15 | It might become one in later versions of Werkzeug.
16 |
17 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
18 | :license: BSD, see LICENSE for more details.
19 | """
20 | import os
21 | import errno
22 | import time
23 | import random
24 |
25 |
26 | can_rename_open_file = False
27 | if os.name == 'nt': # pragma: no cover
28 | _rename = lambda src, dst: False
29 | _rename_atomic = lambda src, dst: False
30 |
31 | try:
32 | import ctypes
33 |
34 | _MOVEFILE_REPLACE_EXISTING = 0x1
35 | _MOVEFILE_WRITE_THROUGH = 0x8
36 | _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
37 |
38 | def _rename(src, dst):
39 | if not isinstance(src, unicode):
40 | src = unicode(src, sys.getfilesystemencoding())
41 | if not isinstance(dst, unicode):
42 | dst = unicode(dst, sys.getfilesystemencoding())
43 | if _rename_atomic(src, dst):
44 | return True
45 | retry = 0
46 | rv = False
47 | while not rv and retry < 100:
48 | rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
49 | _MOVEFILE_WRITE_THROUGH)
50 | if not rv:
51 | time.sleep(0.001)
52 | retry += 1
53 | return rv
54 |
55 | # new in Vista and Windows Server 2008
56 | _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
57 | _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
58 | _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
59 | _CloseHandle = ctypes.windll.kernel32.CloseHandle
60 | can_rename_open_file = True
61 |
62 | def _rename_atomic(src, dst):
63 | ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
64 | if ta == -1:
65 | return False
66 | try:
67 | retry = 0
68 | rv = False
69 | while not rv and retry < 100:
70 | rv = _MoveFileTransacted(src, dst, None, None,
71 | _MOVEFILE_REPLACE_EXISTING |
72 | _MOVEFILE_WRITE_THROUGH, ta)
73 | if rv:
74 | rv = _CommitTransaction(ta)
75 | break
76 | else:
77 | time.sleep(0.001)
78 | retry += 1
79 | return rv
80 | finally:
81 | _CloseHandle(ta)
82 | except Exception:
83 | pass
84 |
85 | def rename(src, dst):
86 | # Try atomic or pseudo-atomic rename
87 | if _rename(src, dst):
88 | return
89 | # Fall back to "move away and replace"
90 | try:
91 | os.rename(src, dst)
92 | except OSError, e:
93 | if e.errno != errno.EEXIST:
94 | raise
95 | old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
96 | os.rename(dst, old)
97 | os.rename(src, dst)
98 | try:
99 | os.unlink(old)
100 | except Exception:
101 | pass
102 | else:
103 | rename = os.rename
104 | can_rename_open_file = True
105 |
--------------------------------------------------------------------------------
/jinja2/tests.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | jinja2.tests
4 | ~~~~~~~~~~~~
5 |
6 | Jinja test functions. Used with the "is" operator.
7 |
8 | :copyright: (c) 2010 by the Jinja Team.
9 | :license: BSD, see LICENSE for more details.
10 | """
11 | import re
12 | from jinja2.runtime import Undefined
13 |
14 | # nose, nothing here to test
15 | __test__ = False
16 |
17 |
18 | number_re = re.compile(r'^-?\d+(\.\d+)?$')
19 | regex_type = type(number_re)
20 |
21 |
22 | try:
23 | test_callable = callable
24 | except NameError:
25 | def test_callable(x):
26 | return hasattr(x, '__call__')
27 |
28 |
29 | def test_odd(value):
30 | """Return true if the variable is odd."""
31 | return value % 2 == 1
32 |
33 |
34 | def test_even(value):
35 | """Return true if the variable is even."""
36 | return value % 2 == 0
37 |
38 |
39 | def test_divisibleby(value, num):
40 | """Check if a variable is divisible by a number."""
41 | return value % num == 0
42 |
43 |
44 | def test_defined(value):
45 | """Return true if the variable is defined:
46 |
47 | .. sourcecode:: jinja
48 |
49 | {% if variable is defined %}
50 | value of variable: {{ variable }}
51 | {% else %}
52 | variable is not defined
53 | {% endif %}
54 |
55 | See the :func:`default` filter for a simple way to set undefined
56 | variables.
57 | """
58 | return not isinstance(value, Undefined)
59 |
60 |
61 | def test_undefined(value):
62 | """Like :func:`defined` but the other way round."""
63 | return isinstance(value, Undefined)
64 |
65 |
66 | def test_none(value):
67 | """Return true if the variable is none."""
68 | return value is None
69 |
70 |
71 | def test_lower(value):
72 | """Return true if the variable is lowercased."""
73 | return unicode(value).islower()
74 |
75 |
76 | def test_upper(value):
77 | """Return true if the variable is uppercased."""
78 | return unicode(value).isupper()
79 |
80 |
81 | def test_string(value):
82 | """Return true if the object is a string."""
83 | return isinstance(value, basestring)
84 |
85 |
86 | def test_number(value):
87 | """Return true if the variable is a number."""
88 | return isinstance(value, (int, long, float, complex))
89 |
90 |
91 | def test_sequence(value):
92 | """Return true if the variable is a sequence. Sequences are variables
93 | that are iterable.
94 | """
95 | try:
96 | len(value)
97 | value.__getitem__
98 | except:
99 | return False
100 | return True
101 |
102 |
103 | def test_sameas(value, other):
104 | """Check if an object points to the same memory address than another
105 | object:
106 |
107 | .. sourcecode:: jinja
108 |
109 | {% if foo.attribute is sameas false %}
110 | the foo attribute really is the `False` singleton
111 | {% endif %}
112 | """
113 | return value is other
114 |
115 |
116 | def test_iterable(value):
117 | """Check if it's possible to iterate over an object."""
118 | try:
119 | iter(value)
120 | except TypeError:
121 | return False
122 | return True
123 |
124 |
125 | def test_escaped(value):
126 | """Check if the value is escaped."""
127 | return hasattr(value, '__html__')
128 |
129 |
130 | TESTS = {
131 | 'odd': test_odd,
132 | 'even': test_even,
133 | 'divisibleby': test_divisibleby,
134 | 'defined': test_defined,
135 | 'undefined': test_undefined,
136 | 'none': test_none,
137 | 'lower': test_lower,
138 | 'upper': test_upper,
139 | 'string': test_string,
140 | 'number': test_number,
141 | 'sequence': test_sequence,
142 | 'iterable': test_iterable,
143 | 'callable': test_callable,
144 | 'sameas': test_sameas,
145 | 'escaped': test_escaped
146 | }
147 |
--------------------------------------------------------------------------------
/werkzeug/security.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | werkzeug.security
4 | ~~~~~~~~~~~~~~~~~
5 |
6 | Security related helpers such as secure password hashing tools.
7 |
8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
9 | :license: BSD, see LICENSE for more details.
10 | """
11 | import hmac
12 | import string
13 | from random import SystemRandom
14 |
15 | # because the API of hmac changed with the introduction of the
16 | # new hashlib module, we have to support both. This sets up a
17 | # mapping to the digest factory functions and the digest modules
18 | # (or factory functions with changed API)
19 | try:
20 | from hashlib import sha1, md5
21 | _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5}
22 | _sha1_mod = sha1
23 | _md5_mod = md5
24 | except ImportError:
25 | import sha as _sha1_mod, md5 as _md5_mod
26 | _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod}
27 | _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new}
28 |
29 |
30 | SALT_CHARS = string.letters + string.digits
31 |
32 |
33 | _sys_rng = SystemRandom()
34 |
35 |
36 | def gen_salt(length):
37 | """Generate a random string of SALT_CHARS with specified ``length``."""
38 | if length <= 0:
39 | raise ValueError('requested salt of length <= 0')
40 | return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length))
41 |
42 |
43 | def _hash_internal(method, salt, password):
44 | """Internal password hash helper. Supports plaintext without salt,
45 | unsalted and salted passwords. In case salted passwords are used
46 | hmac is used.
47 | """
48 | if method == 'plain':
49 | return password
50 | if salt:
51 | if method not in _hash_mods:
52 | return None
53 | if isinstance(salt, unicode):
54 | salt = salt.encode('utf-8')
55 | h = hmac.new(salt, None, _hash_mods[method])
56 | else:
57 | if method not in _hash_funcs:
58 | return None
59 | h = _hash_funcs[method]()
60 | if isinstance(password, unicode):
61 | password = password.encode('utf-8')
62 | h.update(password)
63 | return h.hexdigest()
64 |
65 |
66 | def generate_password_hash(password, method='sha1', salt_length=8):
67 | """Hash a password with the given method and salt with with a string of
68 | the given length. The format of the string returned includes the method
69 | that was used so that :func:`check_password_hash` can check the hash.
70 |
71 | The format for the hashed string looks like this::
72 |
73 | method$salt$hash
74 |
75 | This method can **not** generate unsalted passwords but it is possible
76 | to set the method to plain to enforce plaintext passwords. If a salt
77 | is used, hmac is used internally to salt the password.
78 |
79 | :param password: the password to hash
80 | :param method: the hash method to use (``'md5'`` or ``'sha1'``)
81 | :param salt_length: the lengt of the salt in letters
82 | """
83 | salt = method != 'plain' and gen_salt(salt_length) or ''
84 | h = _hash_internal(method, salt, password)
85 | if h is None:
86 | raise TypeError('invalid method %r' % method)
87 | return '%s$%s$%s' % (method, salt, h)
88 |
89 |
90 | def check_password_hash(pwhash, password):
91 | """check a password against a given salted and hashed password value.
92 | In order to support unsalted legacy passwords this method supports
93 | plain text passwords, md5 and sha1 hashes (both salted and unsalted).
94 |
95 | Returns `True` if the password matched, `False` otherwise.
96 |
97 | :param pwhash: a hashed string like returned by
98 | :func:`generate_password_hash`
99 | :param password: the plaintext password to compare against the hash
100 | """
101 | if pwhash.count('$') < 2:
102 | return False
103 | method, salt, hashval = pwhash.split('$', 2)
104 | return _hash_internal(method, salt, password) == hashval
105 |
--------------------------------------------------------------------------------
/markdown/extensions/imagelinks.py:
--------------------------------------------------------------------------------
1 | """
2 | ========================= IMAGE LINKS =================================
3 |
4 |
5 | Turns paragraphs like
6 |
7 | <~~~~~~~~~~~~~~~~~~~~~~~~
8 | dir/subdir
9 | dir/subdir
10 | dir/subdir
11 | ~~~~~~~~~~~~~~
12 | dir/subdir
13 | dir/subdir
14 | dir/subdir
15 | ~~~~~~~~~~~~~~~~~~~>
16 |
17 | Into mini-photo galleries.
18 |
19 | """
20 |
21 | import re, markdown
22 | import url_manager
23 |
24 |
25 | IMAGE_LINK = """
"""
26 | SLIDESHOW_LINK = """[slideshow]"""
27 | ALBUM_LINK = """ [%s]"""
28 |
29 |
30 | class ImageLinksExtension(markdown.Extension):
31 |
32 | def extendMarkdown(self, md, md_globals):
33 |
34 | md.preprocessors.add("imagelink", ImageLinkPreprocessor(md), "_begin")
35 |
36 |
37 | class ImageLinkPreprocessor(markdown.preprocessors.Preprocessor):
38 |
39 | def run(self, lines):
40 |
41 | url = url_manager.BlogEntryUrl(url_manager.BlogUrl("all"),
42 | "2006/08/29/the_rest_of_our")
43 |
44 |
45 | all_images = []
46 | blocks = []
47 | in_image_block = False
48 |
49 | new_lines = []
50 |
51 | for line in lines:
52 |
53 | if line.startswith("<~~~~~~~"):
54 | albums = []
55 | rows = []
56 | in_image_block = True
57 |
58 | if not in_image_block:
59 |
60 | new_lines.append(line)
61 |
62 | else:
63 |
64 | line = line.strip()
65 |
66 | if line.endswith("~~~~~~>") or not line:
67 | in_image_block = False
68 | new_block = "
\n"
69 |
70 | album_url_hash = {}
71 |
72 | for row in rows:
73 | for photo_url, title in row:
74 | new_block += " "
75 | new_block += IMAGE_LINK % (photo_url,
76 | photo_url.get_thumbnail(),
77 | title)
78 |
79 | album_url_hash[str(photo_url.get_album())] = 1
80 |
81 | new_block += "
"
82 |
83 | new_block += ""
84 | new_block += SLIDESHOW_LINK % url.get_slideshow()
85 |
86 | album_urls = album_url_hash.keys()
87 | album_urls.sort()
88 |
89 | if len(album_urls) == 1:
90 | new_block += ALBUM_LINK % (album_urls[0], "complete album")
91 | else :
92 | for i in range(len(album_urls)) :
93 | new_block += ALBUM_LINK % (album_urls[i],
94 | "album %d" % (i + 1) )
95 |
96 | new_lines.append(new_block + "
")
97 |
98 | elif line[1:6] == "~~~~~" :
99 | rows.append([]) # start a new row
100 | else :
101 | parts = line.split()
102 | line = parts[0]
103 | title = " ".join(parts[1:])
104 |
105 | album, photo = line.split("/")
106 | photo_url = url.get_photo(album, photo,
107 | len(all_images)+1)
108 | all_images.append(photo_url)
109 | rows[-1].append((photo_url, title))
110 |
111 | if not album in albums :
112 | albums.append(album)
113 |
114 | return new_lines
115 |
116 |
117 | def makeExtension(configs):
118 | return ImageLinksExtension(configs)
119 |
120 |
--------------------------------------------------------------------------------
/markdown/extensions/rss.py:
--------------------------------------------------------------------------------
1 | import markdown
2 | from markdown import etree
3 |
4 | DEFAULT_URL = "http://www.freewisdom.org/projects/python-markdown/"
5 | DEFAULT_CREATOR = "Yuri Takhteyev"
6 | DEFAULT_TITLE = "Markdown in Python"
7 | GENERATOR = "http://www.freewisdom.org/projects/python-markdown/markdown2rss"
8 |
9 | month_map = { "Jan" : "01",
10 | "Feb" : "02",
11 | "March" : "03",
12 | "April" : "04",
13 | "May" : "05",
14 | "June" : "06",
15 | "July" : "07",
16 | "August" : "08",
17 | "September" : "09",
18 | "October" : "10",
19 | "November" : "11",
20 | "December" : "12" }
21 |
22 | def get_time(heading):
23 |
24 | heading = heading.split("-")[0]
25 | heading = heading.strip().replace(",", " ").replace(".", " ")
26 |
27 | month, date, year = heading.split()
28 | month = month_map[month]
29 |
30 | return rdftime(" ".join((month, date, year, "12:00:00 AM")))
31 |
32 | def rdftime(time):
33 |
34 | time = time.replace(":", " ")
35 | time = time.replace("/", " ")
36 | time = time.split()
37 | return "%s-%s-%sT%s:%s:%s-08:00" % (time[0], time[1], time[2],
38 | time[3], time[4], time[5])
39 |
40 |
41 | def get_date(text):
42 | return "date"
43 |
44 | class RssExtension (markdown.Extension):
45 |
46 | def extendMarkdown(self, md, md_globals):
47 |
48 | self.config = { 'URL' : [DEFAULT_URL, "Main URL"],
49 | 'CREATOR' : [DEFAULT_CREATOR, "Feed creator's name"],
50 | 'TITLE' : [DEFAULT_TITLE, "Feed title"] }
51 |
52 | md.xml_mode = True
53 |
54 | # Insert a tree-processor that would actually add the title tag
55 | treeprocessor = RssTreeProcessor(md)
56 | treeprocessor.ext = self
57 | md.treeprocessors['rss'] = treeprocessor
58 | md.stripTopLevelTags = 0
59 | md.docType = '\n'
60 |
61 | class RssTreeProcessor(markdown.treeprocessors.Treeprocessor):
62 |
63 | def run (self, root):
64 |
65 | rss = etree.Element("rss")
66 | rss.set("version", "2.0")
67 |
68 | channel = etree.SubElement(rss, "channel")
69 |
70 | for tag, text in (("title", self.ext.getConfig("TITLE")),
71 | ("link", self.ext.getConfig("URL")),
72 | ("description", None)):
73 |
74 | element = etree.SubElement(channel, tag)
75 | element.text = text
76 |
77 | for child in root:
78 |
79 | if child.tag in ["h1", "h2", "h3", "h4", "h5"]:
80 |
81 | heading = child.text.strip()
82 | item = etree.SubElement(channel, "item")
83 | link = etree.SubElement(item, "link")
84 | link.text = self.ext.getConfig("URL")
85 | title = etree.SubElement(item, "title")
86 | title.text = heading
87 |
88 | guid = ''.join([x for x in heading if x.isalnum()])
89 | guidElem = etree.SubElement(item, "guid")
90 | guidElem.text = guid
91 | guidElem.set("isPermaLink", "false")
92 |
93 | elif child.tag in ["p"]:
94 | try:
95 | description = etree.SubElement(item, "description")
96 | except UnboundLocalError:
97 | # Item not defined - moving on
98 | pass
99 | else:
100 | if len(child):
101 | content = "\n".join([etree.tostring(node)
102 | for node in child])
103 | else:
104 | content = child.text
105 | pholder = self.markdown.htmlStash.store(
106 | "" % content)
107 | description.text = pholder
108 |
109 | return rss
110 |
111 |
112 | def makeExtension(configs):
113 |
114 | return RssExtension(configs)
115 |
--------------------------------------------------------------------------------
/blog/templates/list_entries.html:
--------------------------------------------------------------------------------
1 | {% extends "layout.html" %}
2 | {% block title %}
3 | {{title}}
4 | {% endblock %}
5 |
6 | {% block body %}
7 |
8 | {% for entry in entries %}
9 |
10 | {% set year = entry.creation_date.year %}
11 | {% set month = entry.creation_date.month %}
12 | {% set day = entry.creation_date.day %}
13 |
14 | {{ entry.title }}
15 |
16 |
17 |
18 |
39 | {{ entry.content|safe }}
40 |
41 | {% if entries|count == 1 %}
42 |
43 |
44 |
56 |
57 | blog comments powered by Disqus
58 | {% endif %}
59 |
60 |
61 | {% else %}
62 | Unbelievable. No entries here so far
63 |
64 | {% endfor %}
65 | {% if pages is defined and entries|count > 1%}
66 |
67 | {% if actual_page == 1 %}
68 | ‹‹ previous
69 | {% else %}
70 | ‹‹ previous
71 | {% endif %}
72 | {% for page in pages %}
73 | {% if page == actual_page or page == '...' %}
74 | {{page}}
75 | {% else %}
76 | {{page}}
77 | {% endif %}
78 | {% endfor %}
79 | {% if actual_page == pages|count %}
80 | next ››
81 | {% else %}
82 | next ››
83 |
84 | {% endif %}
85 | {% endif %}
86 |
87 |
88 |
99 | {% endblock %}
100 |
--------------------------------------------------------------------------------
{{ entry.human_date }}
21 |30 | {% for tag in entry.tags %} 31 |-
32 | {{ tag }}
33 |
34 | {% endfor %}
35 |
36 | 37 |