├── utils ├── __init__.py ├── external │ ├── __init__.py │ └── simplejson │ │ └── scanner.py ├── django_libs │ ├── __init__.py │ ├── gravatar.py │ └── description.py ├── codehighlighter.py ├── authorized.py ├── sanitizer.py └── template.py ├── handlers ├── __init__.py ├── bloog │ ├── __init__.py │ ├── cache_stats.py │ ├── contact.py │ └── timings.py ├── shell │ └── __init__.py └── restful.py ├── dev ├── docs │ └── BloogTalk.key │ │ ├── .typeAttributes.dict │ │ ├── Contents │ │ └── PkgInfo │ │ ├── index.apxl.gz │ │ ├── thumbs │ │ ├── st0.tiff │ │ ├── st1.tiff │ │ ├── st2.tiff │ │ ├── st3.tiff │ │ └── st4.tiff │ │ ├── RESTfulBook-1.jpg │ │ ├── droppedImage.tiff │ │ ├── ROA Diagram (Just Bloog).png │ │ └── ROA Diagram transparent.png ├── tests │ ├── curl │ │ ├── put_edit_file │ │ ├── edit_user_blog │ │ └── add_user_comment │ ├── restclient-2.1-jar-with-dependencies.jar │ ├── restclient_requests │ │ ├── PUT_blog_entry_user.rcq │ │ └── PUT_blog_entry_admin.rcq │ └── test.py └── scripts │ └── clear_datastore.py ├── static ├── robots.txt ├── favicon.ico ├── spinner.gif ├── screenshot.png ├── ajax-loader.gif ├── images │ ├── meetup.png │ ├── meetup-bw.png │ ├── architecture1.png │ ├── architecture2.png │ └── appengine-noborder-120x30.gif ├── default │ ├── images │ │ ├── js.gif │ │ ├── li.gif │ │ ├── py.gif │ │ ├── rb.gif │ │ ├── css.gif │ │ ├── dot.gif │ │ ├── html.gif │ │ ├── php.gif │ │ ├── avatar.png │ │ ├── li_dark.gif │ │ ├── arrow_next.gif │ │ ├── arrow_prev.gif │ │ ├── btn_search.gif │ │ ├── btn_submit.gif │ │ ├── chat_grey.gif │ │ ├── guide │ │ │ ├── is.gif │ │ │ └── grid_focus_531.gif │ │ ├── lgrey_diag.gif │ │ ├── arrow_nextno.gif │ │ └── arrow_prevno.gif │ ├── js │ │ ├── ojay │ │ │ ├── pkg │ │ │ │ ├── mouse-min.js │ │ │ │ ├── history-min.js │ │ │ │ ├── keyboard-min.js │ │ │ │ ├── http-min.js │ │ │ │ ├── paginator-min.js │ │ │ │ ├── overlay-min.js │ │ │ │ └── mouse.js │ │ │ └── js-class-min.js │ │ ├── bloog_base.js │ │ └── bloog_comments.js │ └── editor.css ├── chili │ ├── python.js │ ├── java.js │ ├── csharp.js │ ├── delphi.js │ ├── js.js │ ├── html.js │ ├── cplusplus.js │ ├── lotusscript.js │ ├── mysql.js │ ├── php.js │ └── css.js └── shell.js ├── views ├── default │ ├── bloog │ │ ├── blog │ │ │ ├── blog_entry.html │ │ │ ├── root.html │ │ │ ├── articles.html │ │ │ ├── sitemap.xml │ │ │ ├── month.html │ │ │ ├── year.html │ │ │ ├── tag.html │ │ │ ├── comment.html │ │ │ ├── search.html │ │ │ ├── atom.xml │ │ │ ├── form_comment.html │ │ │ └── article.html │ │ ├── contact │ │ │ ├── contact.post.html │ │ │ └── contact.get.html │ │ ├── pager.html │ │ ├── article_excerpt.html │ │ ├── form_editor.html │ │ ├── timings │ │ │ └── timing.admin.html │ │ ├── cache_stats │ │ │ └── cache_stats.admin.html │ │ ├── bloog_intro.html │ │ └── base.html │ ├── notfound.html │ └── unauthorized.html └── shell │ └── shell.html ├── .gitignore ├── .gitmodules ├── legacy_aliases.py ├── app.yaml ├── index.yaml ├── main.py ├── config.py └── models └── blog.py /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handlers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handlers/bloog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /handlers/shell/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/external/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/django_libs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/.typeAttributes.dict: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | ???????? -------------------------------------------------------------------------------- /static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /tag/* 3 | -------------------------------------------------------------------------------- /views/default/bloog/blog/blog_entry.html: -------------------------------------------------------------------------------- 1 | {% extends "article.html" %} -------------------------------------------------------------------------------- /views/default/bloog/blog/root.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | .project 4 | .pydevproject 5 | *~ 6 | *.swp 7 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/favicon.ico -------------------------------------------------------------------------------- /static/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/spinner.gif -------------------------------------------------------------------------------- /static/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/screenshot.png -------------------------------------------------------------------------------- /static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/ajax-loader.gif -------------------------------------------------------------------------------- /static/images/meetup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/images/meetup.png -------------------------------------------------------------------------------- /static/default/images/js.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/js.gif -------------------------------------------------------------------------------- /static/default/images/li.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/li.gif -------------------------------------------------------------------------------- /static/default/images/py.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/py.gif -------------------------------------------------------------------------------- /static/default/images/rb.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/rb.gif -------------------------------------------------------------------------------- /static/images/meetup-bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/images/meetup-bw.png -------------------------------------------------------------------------------- /static/default/images/css.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/css.gif -------------------------------------------------------------------------------- /static/default/images/dot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/dot.gif -------------------------------------------------------------------------------- /static/default/images/html.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/html.gif -------------------------------------------------------------------------------- /static/default/images/php.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/php.gif -------------------------------------------------------------------------------- /static/images/architecture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/images/architecture1.png -------------------------------------------------------------------------------- /static/images/architecture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/images/architecture2.png -------------------------------------------------------------------------------- /dev/tests/curl/put_edit_file: -------------------------------------------------------------------------------- 1 | name=Bill&email=f@bill.com&title=My%20Edited%20Title&body=This%20is%20a%20PUT%20body. 2 | -------------------------------------------------------------------------------- /static/default/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/avatar.png -------------------------------------------------------------------------------- /static/default/images/li_dark.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/li_dark.gif -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/index.apxl.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/index.apxl.gz -------------------------------------------------------------------------------- /static/default/images/arrow_next.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/arrow_next.gif -------------------------------------------------------------------------------- /static/default/images/arrow_prev.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/arrow_prev.gif -------------------------------------------------------------------------------- /static/default/images/btn_search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/btn_search.gif -------------------------------------------------------------------------------- /static/default/images/btn_submit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/btn_submit.gif -------------------------------------------------------------------------------- /static/default/images/chat_grey.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/chat_grey.gif -------------------------------------------------------------------------------- /static/default/images/guide/is.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/guide/is.gif -------------------------------------------------------------------------------- /static/default/images/lgrey_diag.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/lgrey_diag.gif -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/thumbs/st0.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/thumbs/st0.tiff -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/thumbs/st1.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/thumbs/st1.tiff -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/thumbs/st2.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/thumbs/st2.tiff -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/thumbs/st3.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/thumbs/st3.tiff -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/thumbs/st4.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/thumbs/st4.tiff -------------------------------------------------------------------------------- /static/default/images/arrow_nextno.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/arrow_nextno.gif -------------------------------------------------------------------------------- /static/default/images/arrow_prevno.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/arrow_prevno.gif -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/RESTfulBook-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/RESTfulBook-1.jpg -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/droppedImage.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/droppedImage.tiff -------------------------------------------------------------------------------- /dev/tests/curl/edit_user_blog: -------------------------------------------------------------------------------- 1 | curl -T put_edit_file -b dev_appserver_login=foo@user.com http://localhost:8080/2008/8/New-Blog 2 | -------------------------------------------------------------------------------- /static/images/appengine-noborder-120x30.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/images/appengine-noborder-120x30.gif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "utils/external/firepython"] 2 | path = utils/external/firepython 3 | url = git://github.com/darwin/firepython.git 4 | -------------------------------------------------------------------------------- /static/default/images/guide/grid_focus_531.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/static/default/images/guide/grid_focus_531.gif -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/ROA Diagram (Just Bloog).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/ROA Diagram (Just Bloog).png -------------------------------------------------------------------------------- /dev/docs/BloogTalk.key/ROA Diagram transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/docs/BloogTalk.key/ROA Diagram transparent.png -------------------------------------------------------------------------------- /dev/tests/restclient-2.1-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcherry/bloog/master/dev/tests/restclient-2.1-jar-with-dependencies.jar -------------------------------------------------------------------------------- /dev/tests/curl/add_user_comment: -------------------------------------------------------------------------------- 1 | curl -d name=Bill&email=f@bill.com&title=My%20Title&body=This%20is%20body. -b dev_appserver_login=foo@user.com http://localhost:8080/2008/8/New-Blog 2 | -------------------------------------------------------------------------------- /views/default/bloog/contact/contact.post.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block first_column %} 3 |
4 | 5 |
6 |

Contact Me

7 |

Thank you for your message.

8 |
9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /views/default/bloog/pager.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | {% if prev_offset %} 4 | Prev, 5 | {% endif %} 6 | {% if next_offset %} 7 | Next 8 | {% endif %} 9 |

10 |
11 | -------------------------------------------------------------------------------- /legacy_aliases.py: -------------------------------------------------------------------------------- 1 | # Site-specific legacy url mapping. 2 | # 3 | # The data is imported and used by the Bloog blog handler. 4 | # This file can be (1) set manually or (2) created automatically 5 | # by utilities/drupal_uploader. 6 | # Place below a "redirects" dictionary with aliases as the key 7 | # (without host uri stem) and the permalink as the value. 8 | 9 | redirects = { 10 | } 11 | -------------------------------------------------------------------------------- /dev/tests/restclient_requests/PUT_blog_entry_user.rcq: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://localhost:8080/2008/8/New-Blog?_method=PUT 5 | POST 6 | 7 |
8 | 9 | name=Bill&email=f@bill.com&title=My%20Edited%20Title&body=This%20is%20a%20PUT%20body. 10 | 11 | 12 | -------------------------------------------------------------------------------- /dev/tests/restclient_requests/PUT_blog_entry_admin.rcq: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://localhost:8080/2008/8/New-Blog?_method=PUT 5 | POST 6 | 7 |
8 | 9 | name=Bill&email=f@bill.com&title=My%20Edited%20Title&body=This%20is%20a%20PUT%20body. 10 | 11 | 12 | -------------------------------------------------------------------------------- /views/default/bloog/article_excerpt.html: -------------------------------------------------------------------------------- 1 |
2 | 8 |

{{ article.title }}

9 |
10 |

{{ article.html|truncatewords_html:68 }}

11 |
12 |
13 | -------------------------------------------------------------------------------- /views/default/bloog/blog/articles.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
5 |
6 |

List of Articles

7 | {% for article in articles %} 8 |

9 | {{ article.title }} 10 |

11 | {% endfor %} 12 |
13 | {% if not articles %} 14 | {% include 'bloog_intro.html' %} 15 | {% endif %} 16 | {% include 'pager.html' %} 17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /views/default/bloog/blog/sitemap.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | {{root_url}}/ 8 | 1.00 9 | daily 10 | 11 | {% for article in articles %} 12 | 13 | {{article.full_permalink}} 14 | 0.80 15 | {{article.rfc3339_published}} 16 | monthly 17 | 18 | {% endfor %} 19 | 20 | -------------------------------------------------------------------------------- /views/default/bloog/blog/month.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
5 | {% if articles %} 6 |
7 |

Articles from the month {{ month }}/{{ year }}

8 |
9 | {% for article in articles %} 10 | {% include '../article_excerpt.html' %} 11 | {% endfor %} 12 | {% else %} 13 |
14 | 15 |

No articles for this year

16 |
17 |

Todo -- Show list of articles which are available by year...

18 |
19 |
20 | {% endif %} 21 |
22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /views/default/bloog/blog/year.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
5 | {% if articles %} 6 |
7 |

Articles from the year {{ year }}

8 |
9 | {% for article in articles %} 10 | {% include 'article_excerpt.html' %} 11 | {% endfor %} 12 | {% include 'pager.html' %} 13 | {% else %} 14 |
15 | 16 |

No articles for this year

17 |
18 |

Todo -- Show list of articles which are available by year...

19 |
20 |
21 | {% endif %} 22 |
23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /views/default/notfound.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
5 |
6 | 7 |

Nobody Here But Us Chickens!

8 |
9 |

Sorry, the page you tried to access isn't available.

10 |

I realize this message is little consolation, but you could try:

11 |
    12 |
  • the search box on the right
  • 13 |
  • the ARCHIVES on the menu bar just above
  • 14 |
  • one of the Categories links to the right
  • 15 |
16 |
17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: adequatelygood 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | handlers: 7 | - url: /static 8 | static_dir: static 9 | 10 | - url: /images 11 | static_dir: static/images 12 | 13 | - url: /favicon\.ico 14 | static_files: static/favicon.ico 15 | upload: static/favicon.ico 16 | 17 | - url: /robots\.txt 18 | static_files: static/robots.txt 19 | upload: static/robots.txt 20 | 21 | - url: /admin/shell.* 22 | script: handlers/shell/shell.py 23 | login: admin 24 | 25 | - url: .* 26 | script: main.py 27 | 28 | skip_files: | 29 | ^(.*/)?( 30 | (app\.yaml)| 31 | (app\.yml)| 32 | (index\.yaml)| 33 | (index\.yml)| 34 | (#.*#)| 35 | (.*~)| 36 | (.*\.py[co])| 37 | (.*/RCS/.*)| 38 | (\..*)| 39 | (dev/.*)| 40 | (tests/.*)| 41 | (docs/.*)| 42 | (.*\.markdown)| 43 | (license\.txt)| 44 | (setup.py) 45 | )$ 46 | -------------------------------------------------------------------------------- /views/default/bloog/blog/tag.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
5 | {% if articles %} 6 |
7 |

Articles tagged with '{{ tag }}'

8 |
9 | {% for article in articles %} 10 | {% include '../article_excerpt.html' %} 11 | {% endfor %} 12 | {% include 'pager.html' %} 13 | {% else %} 14 |
15 | 16 |

No articles tagged with '{{ tag }}'

17 |
18 |

It's possible that an article with the tag was removed.

19 |

Please try another tag or use the full-text search.

20 |
21 |
22 | {% endif %} 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /views/default/bloog/blog/comment.html: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 |

    4 | {% if use_gravatars and comment.email %} 5 | 6 | {% endif %} 7 | {{ comment.title|default:"Re: Article"}} by 8 | {{ comment.name|default:"Anonymous" }} 9 | ({{ comment.published.date }}) 10 |

    11 |
    12 | {{ comment.body }} 13 |
    14 | {% if allow_comments %} 15 | 18 | {% endif %} 19 |
    20 |
  • 21 | -------------------------------------------------------------------------------- /static/chili/python.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Python recipe for Chili syntax highlighter for jQuery 3 | * 4 | * Copyright Ben Godfrey , 2009. 5 | */ 6 | { 7 | _name: 'python' 8 | , _case: true 9 | , _main: { 10 | sl_comment: { 11 | _match: /#.*/ 12 | , _style: 'color: green;' 13 | } 14 | , string: { 15 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 16 | , _style: 'color: teal;' 17 | } 18 | , num: { 19 | _match: /\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b/ 20 | , _style: 'color: red;' 21 | } 22 | , statement: { 23 | _match: /\b(and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield)\b/ 24 | , _style: 'color: navy; font-weight: bold;' 25 | } 26 | , property: { 27 | _match: /\b(None|True|False)\b/ 28 | , _style: 'color: Purple; font-weight: bold;' 29 | } 30 | } 31 | } 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /views/default/bloog/blog/search.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
    5 | {% if articles %} 6 |
    7 |

    Articles found under '{{ search_term }}'

    8 |
    9 | {% for article in articles %} 10 | {% include '../article_excerpt.html' %} 11 | {% endfor %} 12 | {% include 'pager.html' %} 13 | {% else %} 14 |
    15 | 16 |

    No articles found for '{{ search_term }}'

    17 |
    18 |

    {{ search_error_message }}

    19 |

    Please try your search again.

    20 |

    If you feel that you should be staring at something a little more concrete, 21 | try one of the tags or browse the archives.

    22 |
    23 |
    24 | {% endif %} 25 |
    26 | {% endblock %} 27 | -------------------------------------------------------------------------------- /index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | - kind: Article 4 | properties: 5 | - name: __searchable_text_index 6 | - name: published 7 | direction: desc 8 | 9 | - kind: Article 10 | properties: 11 | - name: article_type 12 | - name: published 13 | direction: desc 14 | 15 | - kind: Article 16 | properties: 17 | - name: article_type 18 | - name: title 19 | 20 | - kind: Article 21 | properties: 22 | - name: display_type 23 | - name: published 24 | direction: desc 25 | 26 | - kind: Article 27 | properties: 28 | - name: tags 29 | - name: published 30 | direction: desc 31 | 32 | - kind: Comment 33 | properties: 34 | - name: article 35 | - name: thread 36 | 37 | # AUTOGENERATED 38 | 39 | # This index.yaml is automatically updated whenever the dev_appserver 40 | # detects that a new type of query is run. If you want to manage the 41 | # index.yaml file manually, remove the above marker line (the line 42 | # saying "# AUTOGENERATED"). If you want to manage some indexes 43 | # manually, move them above the marker line. The index.yaml file is 44 | # automatically uploaded to the admin console when you next deploy 45 | # your application using appcfg.py. 46 | -------------------------------------------------------------------------------- /views/default/unauthorized.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block first_column %} 4 |
    5 |
    6 | 7 |

    Permission Denied

    8 |
    9 |

    Sorry. You are trying to access a web page without proper authorization.

    10 | {% if user %} 11 |

    You are currently logged in as {{ user.email }}, and this account doesn't have permission to access the information.

    12 |

    If you believe this is a mistake, try logging out and then 13 | logging in again.

    14 | {% else %} 15 |

    You should try to log in with your Google ID if you feel you should have access.

    16 | {% endif %} 17 |

    After logging in with an authorized account, revisit the desired web page.

    18 |
    19 |
    20 |
    21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /views/default/bloog/blog/atom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ blog.title }} 5 | {{ blog.description }} 6 | {{ blog_updated_timestamp }} 7 | tag:example.org,2003:3 8 | 9 | 10 | Copyright (c) 2008, {{ blog.author }} 11 | 12 | 13 | Bloog for AppEngine 14 | 15 | 16 | {% for article in articles %} 17 | 18 | {{ article.title }} 19 | 20 | 21 | {{ article.full_permalink }} 22 | 23 | {{ article.rfc3339_updated }} 24 | {{ article.rfc3339_published}} 25 | 26 | 27 | {{ blog.author }} 28 | {{ blog.root_url }} 29 | 30 | 31 | 32 |
    33 | {{ article.to_atom_xml }} 34 |
    35 |
    36 |
    37 | {% endfor %} 38 | 39 |
    -------------------------------------------------------------------------------- /utils/django_libs/gravatar.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 Matteo Crippa 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | Gravatar Support for Bloog 25 | """ 26 | __author__ = 'Matteo Crippa' 27 | 28 | import md5 29 | 30 | from google.appengine.ext import webapp 31 | 32 | register = webapp.template.create_template_register() 33 | 34 | def gravatar(email): 35 | return md5.new(email).hexdigest() 36 | 37 | register.filter(gravatar) 38 | -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/mouse-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[3-9k-zA-D]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('6.p=q r.Singleton({u:r.Observable,initialize:5(){4.9={m:8,l:8}},A:5(b,a){3 c=YAHOO.util.Event.getXY(a);4.9={m:c[0],l:c[1]};4.notifyObservers(4.9)},k:5(g,d,e,f){7(!/^(?:x|y)$/.z(g))throw q TypeError(\'Movement is not recognised\');3 i=(d instanceof 6.C);3 h=i?d:8;3 d=i?8:6(d);3 j=false;4.addObserver(5(b){3 a=h||d.getRegion();3 c=4.w(a);7(g==\'x\'&&!j&&c)e.o(f||8,4.9);7(g==\'y\'&&j&&!c)e.o(f||8,4.9);j=c},4)},w:5(b){b=6.C.convert(b);7(!b)n undefined;3 a=4.9;n a.m>=b.m&&a.m<=b.right&&a.l>=b.l&&a.l<=b.bottom}});6(document).k(\'mousemove\',6.p.method(\'A\'));6.B.u({k:6.B.prototype.k.wrap(5(){3 c=Array.from(arguments),g=c.shift();3 d=c[0],e=c[1],f=c[2];7(!/^D(v|t)$/.z(d))n g(d,e,f);3 i=d.match(/^D(v|t)$/)[1].replace(/e?$/,\'ing\');3 h=q r.MethodChain();7(e&&s e!=\'5\')f=e;4.forEach(5(a){6.p.k(i,a,5(b){7(s e==\'5\')e.o(f||8,a,b);h.fire(f||a)})});n h})});',[],41,'|||var|this|function|Ojay|if|null|position|||||||||||on|top|left|return|call|Mouse|new|JS|typeof|leave|include|enter|isInside|entering|leaving|test|updatePosition|DomCollection|Region|mouse'.split('|'),0,{})) -------------------------------------------------------------------------------- /utils/django_libs/description.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 Matteo Crippa 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | Description Support for Bloog 25 | """ 26 | __author__ = 'Matteo Crippa' 27 | 28 | import re 29 | 30 | from google.appengine.ext import webapp 31 | 32 | register = webapp.template.create_template_register() 33 | 34 | def description(value): 35 | return re.sub(r'<[^>]*?>', '', value).replace("\n","")[0:150] 36 | 37 | register.filter(description) 38 | -------------------------------------------------------------------------------- /static/chili/java.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "java" 14 | , _case: true 15 | , _main: { 16 | mlcom : { 17 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 18 | , _style: "color: #4040c2;" 19 | } 20 | , com : { 21 | _match: /\/\/.*/ 22 | , _style: "color: green;" 23 | } 24 | , string : { 25 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 26 | , _style: "color: teal;" 27 | } 28 | , number : { 29 | _match: /(?:\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b)|(?:0x[a-f0-9]+)\b/ 30 | , _style: "color: red;" 31 | } 32 | , meta : { 33 | _match: /(?!\@interface\b)\@[\$\w]+\b/ 34 | , _style: "color: red;" 35 | } 36 | , keyword: { 37 | _match: /\b(?:while|volatile|void|try|true|transient|throws|throw|this|synchronized|switch|super|strictfp|static|short|return|public|protected|private|package|null|new|native|long|interface|int|instanceof|import|implements|if|goto|for|float|finally|final|false|extends|enum|else|double|do|default|continue|const|class|char|catch|case|byte|break|boolean|assert|abstract)\b/ 38 | , _style: "color: navy; font-weight: bold;" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /static/chili/csharp.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "cs" 14 | , _case: true 15 | , _main: { 16 | mlcom : { 17 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 18 | , _style: "color: #4040c2;" 19 | } 20 | , com : { 21 | _match: /\/\/.*/ 22 | , _style: "color: green;" 23 | } 24 | , string : { 25 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 26 | , _style: "color: teal;" 27 | } 28 | , preproc: { 29 | _match: /^\s*#.*/ 30 | , _style: "color: red;" 31 | } 32 | , number : { 33 | _match: /\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b/ 34 | , _style: "color: red;" 35 | } 36 | , keyword: { 37 | _match: /\b(?:while|volatile|void|virtual|using|ushort|unsafe|unchecked|ulong|uint|typeof|try|true|throw|this|switch|struct|string|static|stackalloc|sizeof|short|sealed|sbyte|return|ref|readonly|public|protected|private|params|override|out|operator|object|null|new|namespace|long|lock|is|internal|interface|int|in|implicit|if|goto|foreach|for|float|fixed|finally|false|extern|explicit|event|enum|else|double|do|delegate|default|decimal|continue|const|class|checked|char|catch|case|byte|break|bool|base|as|abstract)\b/ 38 | , _style: "color: navy; font-weight: bold;" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /views/default/bloog/form_editor.html: -------------------------------------------------------------------------------- 1 | {% if user_is_admin %} 2 | 3 | {% block head %} 4 | 5 | {% endblock %} 6 | 7 |
    8 |
    Post Editor
    9 |
    10 |
    11 |
    12 |

    13 |
    14 |

    15 |

    16 |
    17 |
    18 | Separate tags with commas 19 |

    20 | 24 | 25 |
    26 |
    27 |
    28 | 29 | 30 | 31 | 32 | 33 | 34 | {% endif %} 35 | 36 | -------------------------------------------------------------------------------- /handlers/bloog/cache_stats.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | cache_stats.py 25 | 26 | Created by William Katz on 2008-05-04. 27 | Copyright (c) 2008 Publishare LLC. Distributed under MIT License. 28 | """ 29 | __author__ = "William T. Katz" 30 | 31 | import time 32 | import urlparse 33 | import os 34 | 35 | from google.appengine.api import memcache 36 | 37 | from handlers import restful 38 | from utils import authorized 39 | import view 40 | 41 | class CacheStatsHandler(restful.Controller): 42 | @authorized.role("admin") 43 | def get(self): 44 | cache_stats = memcache.get_stats() 45 | view.ViewPage(cache_time=0).render(self, {"stats": cache_stats}) 46 | 47 | @authorized.role("admin") 48 | def delete(self): 49 | memcache.flush_all() 50 | -------------------------------------------------------------------------------- /views/default/bloog/contact/contact.get.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block first_column %} 3 |
    4 | 5 |
    6 |

    Contact Me

    7 | {% ifequal blog.email "you@foo.com" %} 8 |
    9 |

    We're sorry. The administrator of this blog hasn't set his or her e-mail yet.

    10 |

    This poses a conundrum. How are you to let the blog author know this blog is 11 | improperly setup when this blog contact form is the only way you can reach the author?

    12 |

    Most vexing. I haven't got a clue. You might want to try Facebook or a general Google search 13 | to see if the author has a public contact address.

    14 |
    15 | {% else %} 16 |
    17 |

    18 |

    19 |

    20 |

    21 |

    22 |

    23 |

    24 |

    25 | 26 | 27 |
    28 | {% endifequal %} 29 |
    30 |
    31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /views/default/bloog/timings/timing.admin.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block first_column %} 3 |
    4 | 5 |
    6 | 9 |

    Timing Data

    10 |
    11 |

    12 | The following data is in the global cache of the currently selected server: 13 |

    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for urlstat in stats|dictsortreversed:"avg_speed" %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {% endfor %} 41 |
    urltime/callmin callmax calltotal timecalls (uncached)
    All URLs combined{{ avg_speed|floatformat:4 }}{{ total_time|floatformat:3 }}{{ total_calls }} ({{ total_full_renders }})
    {{ urlstat.url }}{{ urlstat.avg_speed|floatformat:4 }}{{ urlstat.min_time|floatformat:4 }}{{ urlstat.max_time|floatformat:4 }}{{ urlstat.duration|floatformat:3 }}{{ urlstat.runs }} ({{ urlstat.full_renders }})
    42 |
    43 |
    44 |
    45 | {% endblock %} 46 | 47 | {% block third_column %} 48 | {% endblock %} 49 | 50 | {% block bottom_body %} 51 | {% endblock %} -------------------------------------------------------------------------------- /static/chili/delphi.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "pas" 14 | , _case: true 15 | , _main: { 16 | mlcom: { 17 | _match: /(?:\(\*[\w\W]*?\*\))|(?:{(?!\$)[\w\W]*?})/ 18 | , _style: "color: #4040c2;" 19 | } 20 | , com: { 21 | _match: /\/\/.*/ 22 | , _style: "color: green;" 23 | } 24 | , string: { 25 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')/ 26 | , _style: "color: teal;" 27 | } 28 | , number: { 29 | _match: /(?:\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b)|(?:\$[a-zA-Z0-9]+\b)/ 30 | , _style: "color: red;" 31 | } 32 | , direct: { 33 | _match: /\{\$[a-zA-Z]+ .+\}/ 34 | , _style: "color: red;" 35 | } 36 | , keyword: { 37 | _match: /\b(?:abs|addr|and|ansichar|ansistring|array|as|asm|begin|boolean|byte|cardinal|case|char|class|comp|const|constructor|currency|destructor|div|do|double|downto|else|end|except|exports|extended|false|file|finalization|finally|for|function|goto|if|implementation|in|inherited|initialization|int64|integer|interface|is|label|library|longint|longword|mod|nil|not|object|of|on|or|packed|pansichar|pansistring|pchar|pcurrency|pdatetime|pextended|pint64|pointer|private|procedure|program|property|protected|pshortstring|pstring|public|published|pvariant|pwidechar|pwidestring|raise|real|real48|record|repeat|set|shl|shortint|shortstring|shr|single|smallint|string|then|threadvar|to|true|try|type|unit|until|uses|val|var|varirnt|while|widechar|widestring|with|word|write|writeln|xor)\b/ 38 | , _style: "color: navy; font-weight: bold;" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/history-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[2-9p-rt-zA-L]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('8.I=(3(h){2 i={\'?\':\'--Qq--\',\'=\':\'--Ee--\',\'&\':\'--Aa--\',\'_\':\'--Uu--\',\'/\':\'--Ss--\'},l=3(a){2 c=encodeURIComponent(k(a));6(2 b 7 i)c=c.p(b,i[b]);4 c},k=3(a){a=decodeURIComponent(q(a));6(2 c 7 i)a=a.p(i[c],c);4 a},n=3(a){5(typeof a!=\'string\')4 a;5((/^\\-?\\d+(?:\\.\\d+)?$/.test(a)))a=Number(a);2 c={\'w\':w,\'x\':x,\'y\':y,\'z\':z};6(2 b 7 c){5(a==b)a=c[b]}4 a},m=3(a){5(!a)4\'\';2 c=[];6(2 b 7 a)c.push(l(b)+\'_\'+l(a[b]));4 c.join(\'/\')},j=3(a){a=q(a).p(/^\\s*(.*?)\\s*$/,\'$1\');5(!a)4{};2 c=a.E(\'/\'),b,g={},e;6(2 f=0,d=c.length;f 4 | 5 |
    6 | 9 |

    Caching Stats

    10 |
    11 |

    12 | The following data is from memcache: 13 |

    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
    StatValueExplanation
    Hits{{ stats.hits }}Number of cache get requests resulting in a cache hit.
    Misses{{ stats.misses }}Number of cache get requests resulting in a cache miss.
    Byte Hits{{ stats.byte_hits }}Sum of bytes transferred on get requests. Rolls over to zero on overflow.
    Items{{ stats.items }}Number of key/value pairs in the cache.
    Bytes{{ stats.bytes }}Total size of all items in the cache.
    Oldest Age{{ stats.oldest_item_age }}How long in seconds since the oldest item in the cache was accessed.
    51 |
    52 |
    53 | 54 | {% endblock %} 55 | 56 | {% block third_column %} 57 | {% endblock %} 58 | 59 | {% block bottom_body %} 60 | {% endblock %} -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/keyboard-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'([2-9t-zA-Z]|1\\w)'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(3(m,h,k){5 i=m.KEY;5 r=3(a){a=a.trim();4 a?a.split(/\\s+/):[]},o=3(a,b){4 a-b},p=3(a){4 a&&15(a).14().charCodeAt(0)},l=3(c){7(Z c==\'Y\')c=r(c);4 c.map(3(a){5 b=G;7(b=i[15(a).14()])a=b;7(Z a==\'Y\')a=p(a);4 a}).1a(o)},s=3(c){4 c.reduce(3(a,b){switch(b){D i.CONTROL:a.ctrl=u;C;D i.SHIFT:a.shift=u;C;D i.ALT:a.alt=u;C;default:a.T.L(b)}4 a},{T:[]})},j=3(a){4 a.1a(o).join(\':\')};5 q=R.Keyboard=8 v.N({listen:3(a,b,c,e){5 g=8 n(a,b,c,e);g.x();4 g},isPressed:3(a){4 l(a).every(f.method(\'J\'))}});5 n=8 v.11({12:3(a,b,c,e){a=R(a).node;7(e)c=c.bind(e);2.E=l(b);2.I=8 m(a,s(2.E),c)},x:3(){2.B=u;2.I.x();2.z&&d.P(2);4 2},A:3(){2.B=O;2.I.A();2.z&&d.K(2);4 2},10:3(){2.z=u;2.B&&d.P(2);4 2},allowDefault:3(){2.z=O;2.B&&d.K(2);4 2},9:3(){5 a=j(2.E);2.9=3(){4 a};4 a}});q.RuleSet=8 v.11({12:3(a){2.6={};5 b,c;M(b Q a){c=8 n(S,b,a[b]);2.6[c.9()]=c}},w:3(a,b){a=Function.from(a);M(5 c Q 2.6)a.call(b||G,2.6[c])},x:3(){2.w(\'x\');4 2},A:3(){2.w(\'A\');4 2},get:3(a){4 2.6[j(l(a))]||G},merge:3(b){5 c={},e=3(a){c[a.9()]=a};[2,b].w({w:e});5 g=8 2.klass({});g.6=c;4 g}});5 f=8 v.N({t:[],U:3(a){7(!2.J(a))2.t.L(a)},V:3(b){2.t=2.t.19(3(a){4 a!=b})},J:3(a){4 2.t.indexOf(a)!=-1},9:3(){4 j(2.t)}});5 d=8 v.N({6:[],P:3(a){2.6.L(a)},K:3(b){2.6=2.6.19(3(a){4 a!=b})},F:3(a,b){7(2.W(b))h.10(a)},W:3(a){M(5 b=0,c=2.6.length;b 2 | p span.displaynone { display:none; } 3 | 4 |
    5 |
    Enter Your Comment
    6 |
    7 |
    8 |

    9 |
    12 | 13 |

    14 |

    15 |
    16 | 17 |

    18 |

    19 |
    20 | 21 |

    22 |

    23 |
    24 | 25 |

    26 |

    27 |
    28 | 29 |

    30 | 33 |
    34 |
    35 |
    36 | 37 | {% if not user_is_admin %} 38 | 39 | 40 | 41 | 42 | {% endif %} 43 | 44 | 45 | -------------------------------------------------------------------------------- /static/default/editor.css: -------------------------------------------------------------------------------- 1 | /* CSS for YUI Editor used in Bloog */ 2 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-pythonbtn span.yui-toolbar-icon { 3 | background-image: url(/static/default/images/py.gif); 4 | background-position: 1px 0px; 5 | left: 5px; 6 | } 7 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-pythonbtn-selected span.yui-toolbar-icon { 8 | background-image: url(/static/default/images/py.gif); 9 | background-position: 1px 0px; 10 | left: 5px; 11 | } 12 | 13 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-rubybtn span.yui-toolbar-icon { 14 | background-image: url(/static/default/images/rb.gif); 15 | background-position: 1px 0px; 16 | left: 5px; 17 | } 18 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-rubybtn-selected span.yui-toolbar-icon { 19 | background-image: url(/static/default/images/rb.gif); 20 | background-position: 1px 0px; 21 | left: 5px; 22 | } 23 | 24 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-jsbtn span.yui-toolbar-icon { 25 | background-image: url(/static/default/images/js.gif); 26 | background-position: 1px 0px; 27 | left: 5px; 28 | } 29 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-jsbtn-selected span.yui-toolbar-icon { 30 | background-image: url(/static/default/images/js.gif); 31 | background-position: 1px 0px; 32 | left: 5px; 33 | } 34 | 35 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-phpbtn span.yui-toolbar-icon { 36 | background-image: url(/static/default/images/php.gif); 37 | background-position: 0px 0px; 38 | left: 3px; 39 | width: 22px; 40 | } 41 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-phpbtn-selected span.yui-toolbar-icon { 42 | background-image: url(/static/default/images/php.gif); 43 | background-position: 0px 0px; 44 | left: 3px; 45 | width: 22px; 46 | } 47 | 48 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-htmlbtn span.yui-toolbar-icon { 49 | background-image: url(/static/default/images/html.gif); 50 | background-position: 0px 0px; 51 | left: 2px; 52 | width: 24px; 53 | } 54 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-htmlbtn-selected span.yui-toolbar-icon { 55 | background-image: url(/static/default/images/html.gif); 56 | background-position: 1px 0px; 57 | left: 5px; 58 | } 59 | 60 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-cssbtn span.yui-toolbar-icon { 61 | background-image: url(/static/default/images/css.gif); 62 | background-position: 0px 0px; 63 | left: 3px; 64 | width: 22px; 65 | } 66 | .yui-skin-sam .yui-toolbar-container .yui-toolbar-cssbtn-selected span.yui-toolbar-icon { 67 | background-image: url(/static/default/images/css.gif); 68 | background-position: 0px 0px; 69 | left: 3px; 70 | width: 22px; 71 | } -------------------------------------------------------------------------------- /utils/codehighlighter.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | import string 24 | import re 25 | import logging 26 | 27 | from external.BeautifulSoup import BeautifulSoup 28 | from utils import sanitizer 29 | 30 | language_jsfiles = { 31 | 'python': 'Python', 32 | 'ruby': 'Ruby', 33 | 'js': 'JScript', 34 | 'html': 'Xml', 35 | 'php': 'Php', 36 | 'css': 'Css', 37 | 'cpp': 'Cpp' 38 | } 39 | 40 | def process_html(html): 41 | """Processes HTML for embedded code using SyntaxHighlighter 42 | 43 | Determines languages used by checking class attribute of pre tags 44 | with name="code". 45 | 46 | Args: 47 | html: HTML to be processed for embedded code 48 | 49 | Returns: 50 | The modified html and a list of strings giving the embedded 51 | code languages. 52 | """ 53 | code_tag = re.compile('\s*
    ', 
    54 |                           re.MULTILINE)
    55 |     languages = set([])
    56 |     soup = BeautifulSoup(html)
    57 |     clean_html = ''
    58 |     for section in soup.contents:
    59 |         txt = str(section)
    60 |         matchobj = re.match(code_tag, txt)
    61 |         if matchobj:
    62 |             languages.add(matchobj.group(1))
    63 |             clean_html += re.sub(r'
    ', "\n", txt) 64 | else: 65 | clean_html += txt 66 | 67 | # Map the language class names to the spelling for javascript files 68 | list_language_files = [language_jsfiles[lang] for lang in list(languages)] 69 | return clean_html.decode('utf-8'), list_language_files 70 | 71 | -------------------------------------------------------------------------------- /static/chili/js.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: 'js' 14 | , _case: true 15 | , _main: { 16 | ml_comment: { 17 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 18 | , _style: 'color: gray;' 19 | } 20 | , sl_comment: { 21 | _match: /\/\/.*/ 22 | , _style: 'color: green;' 23 | } 24 | , string: { 25 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 26 | , _style: 'color: teal;' 27 | } 28 | , num: { 29 | _match: /\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b/ 30 | , _style: 'color: red;' 31 | } 32 | , reg_not: { //this prevents "a / b / c" to be interpreted as a reg_exp 33 | _match: /(?:\w+\s*)\/[^\/\\\n]*(?:\\.[^\/\\\n]*)*\/[gim]*(?:\s*\w+)/ 34 | , _replace: function( all ) { 35 | return this.x( all, '//num' ); 36 | } 37 | } 38 | , reg_exp: { 39 | _match: /\/[^\/\\\n]*(?:\\.[^\/\\\n]*)*\/[gim]*/ 40 | , _style: 'color: maroon;' 41 | } 42 | , brace: { 43 | _match: /[\{\}]/ 44 | , _style: 'color: red; font-weight: bold;' 45 | } 46 | , statement: { 47 | _match: /\b(with|while|var|try|throw|switch|return|if|for|finally|else|do|default|continue|const|catch|case|break)\b/ 48 | , _style: 'color: navy; font-weight: bold;' 49 | } 50 | , error: { 51 | _match: /\b(URIError|TypeError|SyntaxError|ReferenceError|RangeError|EvalError|Error)\b/ 52 | , _style: 'color: Coral;' 53 | } 54 | , object: { 55 | _match: /\b(String|RegExp|Object|Number|Math|Function|Date|Boolean|Array)\b/ 56 | , _style: 'color: DeepPink;' 57 | } 58 | , property: { 59 | _match: /\b(undefined|arguments|NaN|Infinity)\b/ 60 | , _style: 'color: Purple; font-weight: bold;' 61 | } 62 | , 'function': { 63 | _match: /\b(parseInt|parseFloat|isNaN|isFinite|eval|encodeURIComponent|encodeURI|decodeURIComponent|decodeURI)\b/ 64 | , _style: 'color: olive;' 65 | } 66 | , operator: { 67 | _match: /\b(void|typeof|this|new|instanceof|in|function|delete)\b/ 68 | , _style: 'color: RoyalBlue; font-weight: bold;' 69 | } 70 | , liveconnect: { 71 | _match: /\b(sun|netscape|java|Packages|JavaPackage|JavaObject|JavaClass|JavaArray|JSObject|JSException)\b/ 72 | , _style: 'text-decoration: overline;' 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /static/chili/html.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: 'html' 14 | , _case: false 15 | , _main: { 16 | doctype: { 17 | _match: // 18 | , _style: "color: #CC6600;" 19 | } 20 | , ie_style: { 21 | _match: /()/ 22 | , _replace: function( all, open, content, close ) { 23 | return "" + this.x( open ) + "" 24 | + this.x( content, '//style' ) 25 | + "" + this.x( close ) + ""; 26 | } 27 | , _style: "color: DarkSlateGray; font-weight: bold;" 28 | } 29 | , comment: { 30 | _match: // 31 | , _style: "color: #4040c2;" 32 | } 33 | , script: { 34 | _match: /(]*>)([\w\W]*?)(<\/script\s*>)/ 35 | , _replace: function( all, open, content, close ) { 36 | return this.x( open, '//tag_start' ) 37 | + this.x( content, 'js' ) 38 | + this.x( close, '//tag_end' ); 39 | } 40 | } 41 | , style: { 42 | _match: /(]*>)([\w\W]*?)(<\/style\s*>)/ 43 | , _replace: function( all, open, content, close ) { 44 | return this.x( open, '//tag_start' ) 45 | + this.x( content, 'css' ) 46 | + this.x( close, '//tag_end' ); 47 | } 48 | } 49 | // matches a starting tag of an element (with attrs) 50 | // like "
    " or "" 51 | , tag_start: { 52 | _match: /(<\w+)((?:[?%]>|[\w\W])*?)(\/>|>)/ 53 | , _replace: function( all, open, content, close ) { 54 | return "" + this.x( open ) + "" 55 | + this.x( content, '/tag_attrs' ) 56 | + "" + this.x( close ) + ""; 57 | } 58 | , _style: "color: navy; font-weight: bold;" 59 | } 60 | // matches an ending tag 61 | // like "
    " 62 | , tag_end: { 63 | _match: /<\/\w+\s*>|\/>/ 64 | , _style: "color: navy;" 65 | } 66 | , entity: { 67 | _match: /&\w+?;/ 68 | , _style: "color: blue;" 69 | } 70 | } 71 | , tag_attrs: { 72 | // matches a name/value pair 73 | attr: { 74 | // before in $1, name in $2, between in $3, value in $4 75 | _match: /(\W*?)([\w-]+)(\s*=\s*)((?:\'[^\']*(?:\\.[^\']*)*\')|(?:\"[^\"]*(?:\\.[^\"]*)*\"))/ 76 | , _replace: "$1$2$3$4" 77 | , _style: { attr_name: "color: green;", attr_value: "color: maroon;" } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /views/default/bloog/blog/article.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% if article.embedded_code %} 4 | {% block head %} 5 | 6 | {% endblock %} 7 | {% endif %} 8 | 9 | {% block first_column %} 10 | {% if two_columns %} 11 |
    12 | {% else %} 13 |
    14 | {% endif %} 15 | {% if article %} 16 |
    17 | 27 |

    {{ article.title }}

    28 |
    29 | {{ article.html }} 30 |
    31 |
    32 |
    33 |

    Category: 34 | {% for tag in article.tags %} 35 | {{ tag }} 36 | {% endfor %} 37 |

    38 |
    39 | {% else %} 40 |
    41 | 42 |

    No Article at Given Address

    43 |
    44 |

    You seem to have found a mis-linked page or search query with no associated results.

    45 |

    Please trying your search again. 46 | If you feel that you should be staring at something a little more concrete, feel free to email the author of this site 47 | or browse the archives.

    48 |
    49 |
    50 | {% endif %} 51 |
    52 | {% if allow_comments %} 53 | Add a comment... 54 | {% else %} 55 | {% if article.num_comments %} 56 |

    Comments are closed

    57 | {% endif %} 58 | {% endif %} 59 | {% if allow_comments or article.num_comments %} 60 |
    61 |

    {{ article.num_comments }} Comments

    62 |
      63 | {% for comment in article.comments %} 64 | {% include "comment.html" %} 65 | {% endfor %} 66 |
    67 |
    68 | {% endif %} 69 |
    70 |
    71 | {% endblock %} 72 | 73 | {% block end_body %} 74 | 75 | {% if allow_comments %} 76 | {% include "form_comment.html" %} 77 | {% endif %} 78 | 79 | {% endblock %} 80 | 81 | -------------------------------------------------------------------------------- /handlers/bloog/contact.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | contact.py 25 | This module provides a simple form for entering a message and the 26 | handlers for receiving the message through a HTTP POST. 27 | """ 28 | __author__ = 'William T. Katz' 29 | 30 | import logging 31 | import string 32 | import time 33 | 34 | from google.appengine.api import users 35 | 36 | from handlers import restful 37 | import view 38 | import config 39 | 40 | RANDOM_TOKEN = '08yzek30krn4l' + config.BLOG['root_url'] 41 | 42 | class ContactHandler(restful.Controller): 43 | def get(self): 44 | user = users.get_current_user() 45 | # Don't use cache since we want to get current time for each post. 46 | view.ViewPage(cache_time=0). \ 47 | render(self, {'email': user.email() if user else 'Required', 48 | 'nickname': user.nickname() if user else '', 49 | 'token': RANDOM_TOKEN, 'curtime': time.time()}) 50 | 51 | def post(self): 52 | from google.appengine.api import mail 53 | 54 | if self.request.get('token') != RANDOM_TOKEN or \ 55 | time.time() - string.atof(self.request.get('curtime')) < 2.0: 56 | logging.info("Aborted contact mailing because form submission " 57 | "was less than 2 seconds.") 58 | self.error(403) 59 | 60 | user = users.get_current_user() 61 | sender = user.email() if user else config.BLOG['email'] 62 | reply_to = self.request.get('email') or \ 63 | (user_email() if user else 'unknown@foo.com') 64 | mail.send_mail( 65 | sender = sender, 66 | reply_to = self.request.get('author') + '<' + reply_to + '>', 67 | to = config.BLOG['email'], 68 | subject = self.request.get('subject') or 'No Subject Given', 69 | body = self.request.get('message') or 'No Message Given' 70 | ) 71 | 72 | view.ViewPage(cache_time=36000).render(self) -------------------------------------------------------------------------------- /views/shell/shell.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Interactive Shell 7 | 8 | 73 | 74 | 75 | 76 | 77 |

    Interactive server-side Python shell 78 | (original source) 79 |

    80 |

    81 | Return to Bloog home 82 |

    83 | 84 | 88 | 89 |
    90 | 91 | 94 | 96 | 97 | 98 | 99 |
    100 | 101 |

    102 | 103 |

    104 | {% if user %} 105 | {{ user.nickname }} 106 | (log out) 107 | {% else %} 108 | log in 109 | {% endif %} 110 | | Shift-Up/Down for history | 111 | 115 | 116 |

    117 | 118 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | 24 | __author__ = 'William T. Katz' 25 | 26 | import config 27 | import os 28 | import sys 29 | 30 | # Force sys.path to have our own directory first, so we can import from it. 31 | sys.path.insert(0, config.APP_ROOT_DIR) 32 | sys.path.insert(1, os.path.join(config.APP_ROOT_DIR, 'utils/external')) 33 | 34 | import logging 35 | import wsgiref.handlers 36 | from firepython.middleware import FirePythonWSGI 37 | from google.appengine.ext import webapp 38 | from google.appengine.api import users 39 | from handlers.bloog import blog, contact, cache_stats, timings 40 | 41 | # Import custom django libraries 42 | webapp.template.register_template_library('utils.django_libs.gravatar') 43 | webapp.template.register_template_library('utils.django_libs.description') 44 | 45 | # Log a message each time this module get loaded. 46 | logging.info('Loading %s, app version = %s', 47 | __name__, os.getenv('CURRENT_VERSION_ID')) 48 | 49 | ROUTES = [ 50 | ('/*$', blog.RootHandler), 51 | ('/403.html', blog.UnauthorizedHandler), 52 | ('/404.html', blog.NotFoundHandler), 53 | ('/([12]\d\d\d)/*$', blog.YearHandler), 54 | ('/([12]\d\d\d)/(\d|[01]\d)/*$', blog.MonthHandler), 55 | ('/([12]\d\d\d)/(\d|[01]\d)/([-\w]+)/*$', blog.BlogEntryHandler), 56 | ('/admin/cache_stats/*$', cache_stats.CacheStatsHandler), 57 | ('/admin/timings/*$', timings.TimingHandler), 58 | ('/search', blog.SearchHandler), 59 | ('/contact/*$', contact.ContactHandler), 60 | ('/tag/(.*)', blog.TagHandler), 61 | (config.BLOG['master_atom_url'] + '/*$', blog.AtomHandler), 62 | ('/articles', blog.ArticlesHandler), 63 | ('/sitemap.xml', blog.SitemapHandler), 64 | ('/(.*)', blog.ArticleHandler)] 65 | 66 | def main(): 67 | path = timings.start_run() 68 | application = webapp.WSGIApplication(ROUTES, debug=config.DEBUG) 69 | if users.is_current_user_admin(): 70 | application = FirePythonWSGI(application) 71 | wsgiref.handlers.CGIHandler().run(application) 72 | timings.stop_run(path) 73 | 74 | if __name__ == "__main__": 75 | main() 76 | -------------------------------------------------------------------------------- /static/chili/cplusplus.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "cpp" 14 | , _case: true 15 | , _main: { 16 | mlcom : { 17 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 18 | , _style: "color: #4040c2;" 19 | } 20 | , com : { 21 | _match: /\/\/.*/ 22 | , _style: "color: green;" 23 | } 24 | , preproc : { 25 | _match: /(?=^|\n)\s*#\w+/ 26 | , _style: "color: red;" 27 | } 28 | , string : { 29 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 30 | , _style: "color: teal;" 31 | } 32 | , number : { 33 | _match: /\b[+-]?(?:\d*\.?\d+|\d+\.?\d*)(?:[eE][+-]?\d+)?\b/ 34 | , _style: "color: red;" 35 | } 36 | , datatype: { 37 | _match: /\b(?:wint_t|wctype_t|wctrans_t|wchar_t|va_list|uintptr_t|tm|time_t|terminate_function|size_t|signed|sig_atomic_t|short|ptrdiff_t|mbstate_t|long|ldiv_t|lconv|jmp_buf|intptr_t|int|fpos_t|float|double|div_t|clock_t|char|bool|_wfinddatai64_t|_wfinddata_t|_utimbuf|_timeb|_stati64|_stat|_purecall_handler|_onexit_t|_off_t|_finddatai64_t|_finddata_t|_exception|_diskfree_t|_dev_t|_complex|__wfinddata64_t|__wchar_t|__timeb64|__time64_t|__stat64|__int8|__int64|__int32|__int16|__finddata64_t|_PNH|_HFILE|_HEAPINFO|_FPIEEE_RECORD|_EXCEPTION_POINTERS|WPARAM|WORD|WCHAR|VOID|USN|USHORT|ULONG_PTR|ULONGLONG|ULONG64|ULONG32|ULONG|UINT_PTR|UINT64|UINT32|UINT|UHALF_PTR|UCHAR|TCHAR|TBYTE|SSIZE_T|SIZE_T|SHORT|SERVICE_STATUS_HANDLE|SC_LOCK|SC_HANDLE|PWSTR|PWORD|PWCHAR|PVOID|PUSHORT|PULONG_PTR|PULONGLONG|PULONG64|PULONG32|PULONG|PUINT_PTR|PUINT64|PUINT32|PUINT|PUHALF_PTR|PUCHAR|PTSTR|PTCHAR|PTBYTE|PSTR|PSSIZE_T|PSIZE_T|PSHORT|POINTER_64|POINTER_32|PLONG_PTR|PLONGLONG|PLONG64|PLONG32|PLONG|PLCID|PINT_PTR|PINT64|PINT32|PINT|PHKEY|PHANDLE|PHALF_PTR|PFLOAT|PDWORD_PTR|PDWORDLONG|PDWORD64|PDWORD32|PCWSTR|PCTSTR|PCSTR|PCHAR|PBYTE|PBOOLEAN|PBOOL|LRESULT|LPWSTR|LPWORD|LPVOID|LPTSTR|LPSTR|LPLONG|LPINT|LPHANDLE|LPDWORD|LPCWSTR|LPCVOID|LPCTSTR|LPCSTR|LPCOLORREF|LPBYTE|LPBOOL|LPARAM|LONG_PTR|LONGLONG|LONG64|LONG32|LONG|LGRPID|LCTYPE|LCID|LANGID|INT_PTR|INT64|INT32|INT|HWND|HWINSTA|HSZ|HRSRC|HRGN|HRESULT|HPEN|HPALETTE|HMONITOR|HMODULE|HMETAFILE|HMENU|HLOCAL|HKL|HKEY|HINSTANCE|HICON|HHOOK|HGLOBAL|HGDIOBJ|HFONT|HFILE|HENHMETAFILE|HDWP|HDROP|HDESK|HDDEDATA|HDC|HCURSOR|HCONVLIST|HCONV|HCOLORSPACE|HBRUSH|HBITMAP|HANDLE|HALF_PTR|HACCEL|FLOAT|FILE|DWORD_PTR|DWORDLONG|DWORD64|DWORD32|DWORD|COLORREF|CHAR|BYTE|BOOLEAN|BOOL|ATOM)\b/ 38 | , _style: "color: blue;" 39 | } 40 | , keyword : { 41 | _match: /\b(?:while|whcar_t|volatile|void|virtual|uuid|using|union|typename|typeid|typedef|try|true|throw|thread|this|template|switch|struct|static_cast|static|sizeof|selectany|return|reinterpret_cast|register|public|protected|private|nothrow|noreturn|noinline|new|namespace|naked|mutable|inline|if|goto|friend|for|false|extern|explicit|enum|else|dynamic_cast|do|dllimport|dllexport|deprecated|delete|default|continue|const_cast|const|class|catch|case|break|__try|__finally|__exception|__declspec)\b/ 42 | , _style: "color: navy; font-weight: bold;" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /utils/authorized.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | authorized.py 25 | 26 | Created by William Katz on 2008-05-04. 27 | Copyright (c) 2008 Publishare LLC. Distributed under MIT License. 28 | """ 29 | __author__ = 'William T. Katz' 30 | 31 | from google.appengine.api import users 32 | 33 | import logging 34 | 35 | def role(role): 36 | """ 37 | A decorator to enforce user roles, currently 'user' (logged in) 38 | and 'admin'. 39 | 40 | To use it, decorate your handler methods like this: 41 | 42 | import authorized 43 | @authorized.role("admin") 44 | def get(self): 45 | user = users.GetCurrentUser(self) 46 | self.response.out.write('Hello, ' + user.nickname()) 47 | 48 | If this decorator is applied to a GET handler, we check if the 49 | user is logged in and redirect her to the create_login_url() if not. 50 | 51 | For HTTP verbs other than GET, we cannot do redirects to the login 52 | url because the return redirects are done as GETs (not the original 53 | HTTP verb for the handler). So if the user is not logged in, we 54 | return an error. 55 | """ 56 | def wrapper(handler_method): 57 | def check_login(self, *args, **kwargs): 58 | user = users.get_current_user() 59 | if not user: 60 | if self.request.method != 'GET': 61 | logging.debug("Not user - aborting") 62 | self.error(403) 63 | else: 64 | logging.debug("User not logged in -- force login") 65 | self.redirect(users.create_login_url(self.request.uri)) 66 | elif role == "user" or (role == "admin" and 67 | users.is_current_user_admin()): 68 | logging.debug("Role is %s so will allow handler", role) 69 | handler_method(self, *args, **kwargs) 70 | else: 71 | if self.request.method == 'GET': 72 | logging.debug("Unknown role (%s) on GET", role) 73 | self.redirect("/403.html") 74 | else: 75 | logging.debug("Unknown role: %s", role) 76 | self.error(403) # User didn't meet role. 77 | # TODO: Give better feedback/status code. 78 | return check_login 79 | return wrapper 80 | 81 | 82 | -------------------------------------------------------------------------------- /views/default/bloog/bloog_intro.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    Welcome to Bloog

    4 |
    5 |

    This is the default root page for Bloog, a simple RESTful blog/homepage on Google AppEngine, and 6 | shows what it looks like out of the box with no posts or pages. You can see 7 | a Bloog with data here or what Bloog likes like 8 | out of the box.

    9 |

    Bloog is open sourced under the MIT License. 10 | Source code can be found on the GitHub Bloog repository. 11 | Git allows you to easily customize the base app while later merging changes from the main repository 12 | (see here).

    13 |

    If you are old school and prefer a tarball of code, 14 | click here.

    15 |

    Bloog includes the following features:

    16 |
      17 |
    • A resource-oriented architecture, as described in the great book 18 | RESTful Web Services
    • 19 |
    • A Drupal converter/uploader that queries a local MySQL database 20 | and uploads the data to a Bloog through REST calls.
    • 21 |
    • A datastore deletion utility that can clear out your entities in your Bloog's datastore.
    • 22 |
    • Arbitrary URL aliases, which can be created by the drupal uploader, that provide redirection from legacy urls. 23 | There's also a programmatic aliasing function that can take a regex like 'node/(.*)' and map it to legacy IDs 24 | stored with the blog entries. (http://foo.com/node/4 is a typical Drupal url.)
    • 25 |
    • A Yahoo UI 26 | AJAX front-end for posting and managing entries in a RESTful way.
    • 27 |
    • Easy code syntax highlighting for a number of programming languages.
    • 28 |
    • Sanitization of incoming HTML via Beautiful Soup parsing.
    • 29 |
    • Some modularization of look-and-feel so you can more easily 30 | modify the theme without yanking too much code apart (I hope).
    • 31 |
    • Dynamic per-article sidebars. (in progress)
    • 32 |
    33 |

    From an AppEngine developer's perspective, you may find the source code a reasonable starting framework 34 | on which you can build your own application. Points of coding interest include:

    35 |
      36 |
    • RESTful design using webapp. Accepts http verbs via overloaded POSTs 37 | using _method or X-HTTP-Method-Override headers. Simple urls provide 38 | a uniform interface with standard verbs.
    • 39 |
    • Convention-over-configuration in organizing controller and view files.
    • 40 |
    • Authorization using python decorators and built-in Google authentication.
    • 41 |
    • Caching using the memcached API and also some timing caching using global variables.
    • 42 |
    • JSON serialization for db.Model.
    • 43 |
    • Sharded counters to decrease write contentions.
    • 44 |
    • Integrated python shell using one of Google's example apps. 45 | This provides a command-line console even when running on Google's App Engine cloud.
    • 46 |
    • Serialization of a python object into a datastore blob.
    • 47 |
    • An example of full-text searching using an extended version of the appengine.ext.search module. 48 | Extensions include ways of limiting indexed properties.
    • 49 |
    50 |

    If your login has administrative permission for this Bloog, you can add articles after 51 | login.

    52 |
    53 |
    54 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | APP_ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) 5 | 6 | # If we're debugging, turn the cache off, etc. 7 | # Set to true if we want to have our webapp print stack traces, etc 8 | DEBUG = os.environ['SERVER_SOFTWARE'].startswith('Dev') 9 | logging.info("Starting application in DEBUG mode: %s", DEBUG) 10 | 11 | # Don't change default_blog or default_page to prevent conflicts when merging # Bloog source code updates. 12 | # Do change blog or page dictionaries at the bottom of this config module. 13 | 14 | BLOG = { 15 | "bloog_version": "0.8", 16 | "html_type": "text/html", 17 | "charset": "utf-8", 18 | "title": "Adequately Good", 19 | "author": "Ben Cherry", 20 | # This must be the email address of a registered administrator for the 21 | # application due to mail api restrictions. 22 | "email": "bcherry@gmail.com", 23 | "description": "Adequately good programming advice.", 24 | "root_url": "http://www.adequatelygood.com", 25 | "master_atom_url": "/feeds/atom.xml", 26 | # By default, visitors can comment on article for this many days. 27 | # This can be overridden by setting article.allow_comments 28 | "days_can_comment": 60, 29 | # You can override this default for each page through a handler's call to 30 | # view.ViewPage(cache_time=...) 31 | "cache_time": 0 if DEBUG else 3600, 32 | 33 | # Use the default YUI-based theme. 34 | # If another string is used besides 'default', calls to static files and 35 | # use of template files in /views will go to directory by that name. 36 | "theme": ["default"], 37 | 38 | # Display gravatars alongside user comments? 39 | "use_gravatars": True, 40 | 41 | # Do you want to be emailed when new comments are posted? 42 | "send_comment_notification": True, 43 | 44 | # If you want to use legacy ID mapping for your former blog platform, 45 | # define it here and insert the necessary mapping code in the 46 | # legacy_id_mapping() function in ArticleHandler (blog.py). 47 | # Currently only "Drupal" is supported. 48 | "legacy_blog_software": None, 49 | #"legacy_blog_software": "Drupal", 50 | #"legacy_blog_software": "Serendipity", 51 | 52 | # If you want imported legacy entries _not_ mapped in the file above to 53 | # redirect to their new permanent URL rather than responding on their 54 | # old URL, set this flag to True. 55 | "legacy_entry_redirect": False, 56 | } 57 | 58 | PAGE = { 59 | "title": BLOG["title"], 60 | "articles_per_page": 5, 61 | "navlinks": [ 62 | { "title": "About", "description": "About this blog", 63 | "url": "/About-Ben"}, 64 | { "title": "Contact", "description": "Send me a note", 65 | "url": "/contact"}, 66 | ], 67 | "featuredMyPages": { 68 | "title": "The Author", 69 | "description": "Ben is a 22 year-old software engineer and geek living in the San Francisco Bay Area. He splits his time between work, hobby programming, video games, and the interwebs.", 70 | "entries": [ 71 | { "title": "Git Hub", 72 | "url": "http://github.com/bcherry", 73 | "description": "My Hobby Code" }, 74 | { "title": "Facebook", 75 | "url": "http://www.facebook.com/bcherry", 76 | "description": "That Social Thing" }, 77 | { "title": "Twitter", 78 | "url": "http://twitter.com/bcherry", 79 | "description": "Rambling Tweets" }, 80 | { "title": "LinkedIn", 81 | "url": "http://www.linkedin.com/in/bcherryprogrammer", 82 | "description": "Serious Professional Stuff" }, 83 | { "title": "Google Profile", 84 | "url": "http://www.google.com/profiles/bcherry", 85 | "description": "Google Owns My Soul" }, 86 | ] 87 | }, 88 | #"featuredOthersPages": { 89 | # "title": "Google App Engine", 90 | # "description": "Developer Resources", 91 | # "entries": [ 92 | # { "title": "Google App Engine", 93 | # "url": "http://code.google.com/appengine/", 94 | # "description": "The mothership" }, 95 | # ] 96 | #}, 97 | } 98 | -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/http-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'([5-9o-rt-vx-zA-VX-Z]|[12]\\w)'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('8.p=A y.Singleton({include:8.Observable,READY_STATE:{UNINITIALIZED:0,LOADING:1,LOADED:2,INTERACTIVE:3,COMPLETE:4},15:\'V E PUT DELETE HEAD\'.11(/\\s+/)});8.p.15.1q(6(d){8.p[d]=6(b,a,c){9 e=A 8.p.22(d,b,a,c);e._3();7 e.1l}});8.p.22=A y.1k({1j:6(b,a,c,e){5.P=b.toUpperCase();o(8.p.15.1R(5.P)==-1)7;5._5=a;5._4=c||{};5.F=e||{};5.1l=A y.1L()},1K:6(){o(5.16)7 5.16;7 5.16=8.q.M(5._5,5._4)},_3:6(){9 f=5.1K();9 i=(5.P==\'E\')?f._1():f.10();9 h=(5.P==\'E\')?f.17():undefined;8.p.G(\'1F\',{H:5});1b.1c.Connect.asyncRequest(5.P,i,{scope:5,1A:6(b){9 a=A 8.p.1g(5,b);9 c=5.F.onSuccess;9 e=5.F[\'on\'+a.N];9 d=5.F.1y;c&&J.I(c)(a);e&&J.I(e)(a);d&&J.I(d)(a);5.1l.fire(a);8.p.G(\'1A\',{H:a});8.p.G(a.N,{H:a});8.p.G(\'1u\',{H:a})},1t:6(b){9 a=A 8.p.1g(5,b);9 c=5.F.onFailure;9 e=5.F[\'on\'+a.N];9 d=5.F.1y;c&&J.I(c)(a);e&&J.I(e)(a);d&&J.I(d)(a);8.p.G(\'1t\',{H:a});8.p.G(a.N,{H:a});8.p.G(\'1u\',{H:a})}},h)}});8.p.1g=A y.1k({1j:6(a,c){\'N statusText O responseXML readyState\'.11(/\\s+/).1q(6(b){5[b]=c[b]},5);5.1F=a;5.transport=c},insertInto:6(b,a){b=b.1w?b:8(b);9 c=(5.O||\'\').stripScripts();o(!a)b.1w(c);else b.Y(c,a);7 5},T:6(){o(5.O)5.O.T();7 5},1B:6(){7(5.O||\'\').1B()},1C:6(){7 5.T()}.traced(\'1C() is deprecated. Use T() instead.\',\'warn\')});(6(){9 g=8.p;9 j={y:/\\.js$/i,1M:/\\.1N$/i};9 k=\'__ojay_cross_domain__\';9 n=6(){8(1e.1f).Y(\'<1S 1T="\'+k+\'" style="display: none;">\',\'1i\')}.runs(1);9 m=6(b){switch(13){21 j.y.1o(b):7\'23\';1p;21 j.1M.1o(b):7\'1N\';1p;default:7\'23\';1p}};y.12(g,{V:g.V.24(6(b,a,c,e){o(8.q.B(a).1m()||!1b.1c.1W)7 b(a,c,e);5.1V(a,c,e)}),E:g.E.24(6(b,a,c,e){o(8.q.B(a).1m())7 b(a,c,e);5.1U(a,c)}),1V:6(b,a,c){9 e=8.q.B(b).z,d=m(e);1b.1c.1W[d](8.q.M(b,a).10(),c||{})},1U:6(b,a){9 c=5._2(b,a,13);n();8(1e.1f).Y(c.18,\'1i\');c.18.1H();c.remove()},_2:6(c,e,d){9 f=8.q.M(c,e),i=f._1(),h=f.x;9 l={action:i,1d:\'E\'};o(d)l.target=k;7 8(8.HTML.form(l,6(b){S(9 a Z h)b.input({type:\'hidden\',1T:a,value:1n(h[a])})})).hide()}});g.V.1v=6(b,a){L.R.href=8.q.M(b,a).10()};g.E.1v=6(b,a){9 c=g._2(b,a,K).18;8(1e.1f).Y(c,\'1i\');c.1H()};y.1L.addMethods(g)})();8.q=A y.1k({12:{1r:6(b){7 1n(b).1s().C(\'&\',\'&\').C(\'&\',\'&\')},B:6(e){o(e instanceof 5)7 e;9 d=A 5;e=5.1r(e).C(/^(\\w+)(:\\/+)/,6(b,a,c){d.r=a;7 c}).C(/^:\\/+([^\\:\\/]+)/,6(b,a){d.t=a;7\'\'}).C(/^:(\\d+)/,6(b,a){d.u=a;7\'\'}).C(/^[^\\?\\#]+/,6(b,a){d.z=b;7\'\'}).C(/#(.*)$/,6(b,a){d.Q=a;7\'\'});o(!d.u)d.u=(d.t==5.v.t)?5.v.u:5.U[d.r];o(d.z.charAt(0)!=\'/\'&&d.t==5.v.t)d.z=5.v.1Y+d.z;o(/^\\?/.1o(e))e.slice(1).11(\'&\').1q(6(1X){9 f=1X.11(\'=\');d.1h(f[0],f[1])});7 d},M:6(b,a){9 c=5.B(b),a=a||{},e;S(9 d Z a){e=(typeof a[d]==\'6\')?a[d]():a[d];c.1h(d,e)}7 c},25:6(b,a){7 5.B(b).1I(a)},U:{1G:\'80\',https:\'443\'}},1j:6(){5.r=5.D.v.r;5.t=5.D.v.t;5.z=\'\';5.X=[];5.x={};5.10=5._6},_6:6(){9 b=5._1(),a=[];9 c=5.17();o(c.length)b+=\'?\'+c;o(5.Q)b+=\'#\'+5.Q;7 b},_1:6(){7 5._7()+(5.t||\'\')+5._8()+(5.z||\'\')},17:6(){7 5.X.sort().19(6(b){7 1J(b)+\'=\'+1J(5.x[b])},5).join(\'&\')},_7:6(){7 5.r?5.r+\'://\':\'\'},_8:6(){o(!5.u||5.u==5.D.U[5.r])7\'\';7\':\'+5.u},1I:6(b){b=5.D.B(b);o(5.t!=b.t||5.r!=b.r||5.u!=b.u||5.z!=b.z||5.Q!=b.Q)7 K;o(!5.1P(b))7 K;7 13},1h:6(b,a){9 c=[b,a].19(decodeURIComponent).19(\'1s\');o(5.X.1R(c[0])==-1)5.X.push(c[0]);5.x[c[0]]=c[1]},1P:6(b){b=5.D.B(b);S(9 a Z 5.x){o(5.x[a]!=b.x[a])7 K}S(a Z b.x){o(5.x[a]!=b.x[a])7 K}7 13},1m:6(){7 5.r==5.D.v.r&&5.t==5.D.v.t&&5.u==5.D.v.u}});8.q.12({v:{r:L.R.r.C(/\\W/g,\'\'),t:L.R.hostname,1Y:L.R.pathname.C(/[^\\/]*$/,\'\')}});8.q.v.u=L.R.u||8.q.U[8.q.v.r||\'1G\'];y.12(1n.prototype,{parseURI:8.q.1d(\'B\').1Z(),equalsURI:8.q.1d(\'25\').1Z()});',[],131,'|||||this|function|return|Ojay|var|||||||||||||||if|HTTP|URI|protocol||domain|port|local||params|JS|path|new|parse|replace|klass|POST|_0|notifyObservers|receiver|from|Function|false|window|build|status|responseText|verb|hash|location|for|evalScripts|DEFAULT_PORTS|GET||keys|insert|in|toString|split|extend|true||VERBS|uri|getQueryString|node|map||YAHOO|util|method|document|body|Response|setParam|top|initialize|Class|chain|isLocal|String|test|break|forEach|sanitize|trim|failure|complete|redirectTo|setContent||onComplete||success|parseJSON|evalScriptTags|||request|http|submit|equals|encodeURIComponent|getURI|MethodChain|CSS|css||paramsEqual||indexOf|iframe|name|send|load|Get|pair|directory|methodize||case|Request|script|wrap|compare'.split('|'),0,{})) -------------------------------------------------------------------------------- /handlers/bloog/timings.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | timings.py 25 | 26 | Created by William Katz on 2008-05-04. 27 | Copyright (c) 2008 Publishare LLC. Distributed under MIT License. 28 | """ 29 | __author__ = "William T. Katz" 30 | 31 | # Global that stores timing runs, all keyed to incoming url path. 32 | # Note that since this is a global, you'll only get stats from the 33 | # currently visited server and it could be reset. The timing 34 | # utility is not meant to be comprehensive but only a hack that 35 | # doesn't interfere with memcached stats. 36 | TIMINGS = {} 37 | 38 | import time 39 | import urlparse 40 | import os 41 | 42 | from handlers import restful 43 | from utils import authorized 44 | import view 45 | 46 | def start_run(): 47 | url = os.environ['PATH_INFO'] 48 | scheme, netloc, path, query, fragment = urlparse.urlsplit(url) 49 | 50 | global TIMINGS 51 | if not path in TIMINGS: 52 | TIMINGS[path] = { 53 | "runs": 0, 54 | "duration": 0.0, 55 | "min_time": None, 56 | "max_time": None, 57 | "mutex_lock": False 58 | } 59 | timing = TIMINGS[path] 60 | if not timing["mutex_lock"]: 61 | timing["mutex_lock"] = True 62 | timing["start_time"] = time.time() 63 | return path 64 | return None 65 | 66 | def stop_run(path): 67 | global TIMINGS 68 | if path and path in TIMINGS: 69 | timing = TIMINGS[path] 70 | elapsed_time = time.time() - timing["start_time"] 71 | timing["duration"] += elapsed_time 72 | timing["runs"] += 1 73 | if (not timing["min_time"]) or timing["min_time"] > elapsed_time: 74 | timing["min_time"] = elapsed_time 75 | if (not timing["max_time"]) or timing["max_time"] < elapsed_time: 76 | timing["max_time"] = elapsed_time 77 | timing["mutex_lock"] = False 78 | 79 | class TimingHandler(restful.Controller): 80 | @authorized.role("admin") 81 | def get(self): 82 | global TIMINGS 83 | stats = [] 84 | total_time = 0.0 85 | avg_speed = 0.0 86 | total_calls = 0 87 | total_full_renders = 0 88 | for key in TIMINGS: 89 | 90 | full_renders = 0 91 | if key in view.NUM_FULL_RENDERS: 92 | full_renders = view.NUM_FULL_RENDERS[key] 93 | total_full_renders += full_renders 94 | url_timing = TIMINGS[key] 95 | if url_timing["runs"] > 0: 96 | url_stats = url_timing.copy() 97 | url_stats.update({'url': key, 98 | 'avg_speed': url_timing["duration"] / 99 | url_timing["runs"], 100 | 'full_renders': full_renders}) 101 | stats.append(url_stats) 102 | total_time += url_timing["duration"] 103 | total_calls += url_timing["runs"] 104 | 105 | 106 | if total_calls > 0: 107 | avg_speed = total_time / total_calls 108 | view.ViewPage(cache_time=0).render(self, {"stats": stats, 109 | "avg_speed": avg_speed, 110 | "total_time": total_time, 111 | "total_calls": total_calls, 112 | "total_full_renders": 113 | total_full_renders}) 114 | 115 | @authorized.role("admin") 116 | def delete(self): 117 | global TIMINGS 118 | TIMINGS = {} -------------------------------------------------------------------------------- /static/chili/lotusscript.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "ls" 14 | , _case: false 15 | , _main: { 16 | mlcom: { 17 | _match: /((?:^|\n)%REM\b.*)([\w\W]*?)(\n%END\s*REM\b.*)/ 18 | , _replace: '$1$2$3' 19 | , _style: "color: #4040c2;" 20 | } 21 | , com: { 22 | _match: /(?:\'.*)|(?:\bREM\b.*)/ 23 | , _style: "color: green;" 24 | } 25 | , mlstr: { 26 | _match: /(?:\{[^}]*\})|(?:\|[^|]*\|)/ 27 | , _style: "color: red;" 28 | } 29 | , str: { 30 | _match: /(?:\"[^\"].*?\")|\"\"/ 31 | , _style: "color: teal;" 32 | } 33 | , keyd: { 34 | _match: /\b(?:Ustring|Uchr|Ucase|Trim|Time|Strtoken|Strrightback|Strright|Strleftback|Strleft|String|Str|Space|Rtrim|Rightc|Rightbp|Rightb|Right|Oct|Midc|Midbp|Midb|Mid|Ltrim|Leftc|Leftbp|Leftb|Left|Lcase|Inputbp|Inputbox|Inputb|Input|Implode|Hex|Format|Error|Environ|Dir|Date|Curdrive|Curdir|Command|Chr|Bin)[$](?:\s)/ 35 | , _style: "color: fuchsia;" 36 | } 37 | , keyw: { 38 | _match: /\b(?:Yield|Year|Xor|Write|With|Width|While|Wend|Weekday|Vartype|Variant|Val|Ustring|Uselsx|Use|Until|Unlock|Unicode|Uni|Uchr|Ucase|Ubound|Typename|Type|True|Trim|Today|To|Timevalue|Timeserial|Timer|Timenumber|Time|Then|Text|Tan|Tab|Sub|Strtoken|Strrightback|Strright|Strleftback|Strleft|String|Strconv|Strcompare|Strcomp|Str|Stop|Step|Static|Sqr|Split|Spc|Space|Sleep|Single|Sin|Shell|Shared|Sgn|Setfileattr|Setattr|Set|Sendkeys|Select|Seek|Second|Rtrim|Rset|Round|Rnd|Rmdir|Rightc|Rightbp|Rightb|Right|Return|Resume|Reset|Replace|Remove|Redim|Read|Randomize|Random|Put|Published|Public|Property|Private|Print|Preserve|Pitch|Pi|Output|Or|Option|Open|On|Oct|Null|Now|Nothing|Not|Nopitch|Nocase|Next|New|Name|Msgbox|Month|Mod|Mkdir|Minute|Midc|Midbp|Midb|Mid|Messagebox|Me|Ltrim|Lsserver|Lsi_info|Lset|Loop|Long|Log|Lof|Lock|Loc|Lmbcs|Listtag|List|Line|Like|Lib|Let|Lenc|Lenbp|Lenb|Len|Leftc|Leftbp|Leftb|Left|Lcase|Lbound|Kill|Join|Isunknown|Isscalar|Isobject|Isnumeric|Isnull|Islist|Isempty|Iselement|Isdate|Isarray|Isa|Is|Integer|Int|Instrc|Instrbp|Instrb|Instr|Inputbp|Inputbox|Inputb|Input|In|Implode|Imp|Imestatus|Imesetmode|If|Hour|Hex|Goto|Gosub|Getthreadinfo|Getfileattr|Getattr|Get|Function|Fulltrim|From|Freefile|Fraction|Format|Forall|For|Fix|Filelen|Filedatetime|Filecopy|Fileattr|False|Explicit|Exp|Exit|Execute|Event|Evaluate|Error|Err|Erl|Erase|Eqv|Eof|Environ|End|Elseif|Else|Double|Doevents|Do|Dir|Dim|Destroylock|Delete|Defvar|Defstr|Defsng|Deflng|Defint|Defdbl|Defcur|Defbyte|Defbool|Declare|Day|Datevalue|Dateserial|Datenumber|Date|Datatype|Cvdate|Cvar|Currency|Curdrive|Curdir|Cstr|Csng|Createlock|Cos|Const|Compare|Command|Codeunlock|Codelockcheck|Codelock|Close|Clng|Class|Cint|Chr|Chdrive|Chdir|Cdbl|Cdat|Ccur|Cbyte|Cbool|Case|Call|Byval|Byte|Boolean|Bind|Binary|Bin|Beep|Base|Atn2|Atn|Asin|Asc|As|Arrayunique|Arrayreplace|Arraygetindex|Arrayappend|Append|Appactivate|Any|And|Alias|Activateapp|Acos|Access|Abs)\b/ 39 | , _style: "color: maroon; font-weight: bold;" 40 | } 41 | , directive: { 42 | _match: /((?:^|\n)(?:%if|%end|%elseif|%else)\b)|(?:(?:^|\n)%include\b.*)/ 43 | , _style: "color: #5f5f5f;" 44 | } 45 | , notes: { 46 | _match: /\b(?:NotesXSLTransformer|NotesXMLProcessor|NotesViewNavigator|NotesViewEntryCollection|NotesViewEntry|NotesViewColumn|NotesView|NotesTimer|NotesStream|NotesSession|NotesSAXParser|NotesSAXException|NotesSAXAttributeList|NotesRichTextTable|NotesRichTextTab|NotesRichTextStyle|NotesRichTextSection|NotesRichTextRange|NotesRichTextParagraphStyle|NotesRichTextNavigator|NotesRichTextItem|NotesRichTextDocLink|NotesReplicationEntry|NotesReplication|NotesRegistration|NotesOutlineEntry|NotesOutline|NotesNoteCollection|NotesNewsLetter|NotesName|NotesMIMEHeader|NotesMIMEEntity|NotesLog|NotesItem|NotesInternational|NotesForm|NotesEmbeddedObject|NotesDocumentCollection|NotesDocument|NotesDbDirectory|NotesDateTime|NotesDateRange|NotesDatabase|NotesDXLImporter|NotesDXLExporter|NotesDOMXMLDeclNode|NotesDOMTextNode|NotesDOMProcessingInstructionNode|NotesDOMParser|NotesDOMNotationNode|NotesDOMNodeList|NotesDOMNode|NotesDOMNamedNodeMap|NotesDOMEntityReferenceNode|NotesDOMEntityNode|NotesDOMElementNode|NotesDOMDocumentTypeNode|NotesDOMDocumentNode|NotesDOMDocumentFragmentNode|NotesDOMCommentNode|NotesDOMCharacterDataNode|NotesDOMCDATASectionNode|NotesDOMAttributeNode|NotesColorObject|NotesAgent|NotesAdministrationProcess|NotesACLEntry|NotesACL)\b/ 47 | , _style: "color: navy;" 48 | } 49 | , notesui: { 50 | _match: /\b(?:NotesUIWorkspace|NotesUIView|NotesUIScheduler|NotesUIDocument|NotesUIDatabase|Navigator|Field|Button)\b/ 51 | , _style: "color: purple;" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /static/default/js/ojay/js-class-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 James Coglan, http://jsclass.jcoglan.com 3 | Licensed under the MIT license, http://www.opensource.org/licenses/mit-license.php 4 | */ 5 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'([3-9k-zA-Z]|1\\w)'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('6={m:4(a,b){9(7 c q b)a[c]=b[c]},S:4(a){7 b=3,c=b.1E=b.1E||{};k((c[a]||{}).fn==b[a])5 c[a].bd;5(c[a]={fn:b[a],bd:b[a].12(b)}).bd},Q:{}};C.H=4(a){k(!a)5[];k(a.1r)5 a.1r();7 b=a.p,c=[];L(b--)c[b]=a[b];5 c};6.m(x.o,{12:4(){7 a=3,b=C.H(8),c=b.U()||W;5 4(){5 a.u(c,b.1k(C.H(8)))}},1o:4(){5/\\bcallSuper\\b/.K(3.1c())},w:4(a){5 r a==\'4\'}});6.l=4(){7 a=C.H(8),b,c=x.w(a[0])?a.U():W,d=6.l.1t(c);L(b=a.U())d.B(b);c&&x.w(c.1w)&&c.1w(d);5 d};6.m(6.l,{1t:4(a){7 b=4(){3.17.u(3,8)};3.14(b);a&&3.1x(a,b);7 c=b.o;c.M=c.constructor=b;b.B(3.10,n);b.X(\'m\',3.10.m,n);5 b},14:4(a,b){a.D=a.D||Object;a.T=a.T||[];k(b===n)5 a;9(7 c q 3.N)3.N.16(c)&&(a[c]=3.N[c]);5 a},1x:4(a,b){3.14(a,n);b.D=a;a.T.z(b);7 c=4(){};c.o=a.o;b.o=y c();b.m(a);5 b},1y:4(a){7 b={},c,d=3.14(4(){});Z:9(7 e q a){9(c q d){k(e==c)1g Z}b[e]=a[e]}5 b},s:4(f,i,g,h){k(!x.w(h))5(f[g]=h);k(!h.1o())5(f[g]=h);7 j=4(){7 b=i[g],c=C.H(8),d=3.15,e;x.w(b)&&(3.15=4(){7 a=8.p;L(a--)c[a]=8[a];5 b.u(3,c)});e=h.u(3,8);d?3.15=d:delete 3.15;5 e};j.valueOf=4(){5 h};j.1c=4(){5 h.1c()};f[g]=j},10:{17:4(){},S:6.S,m:4(a){9(7 b q a)a.16(b)&&6.l.s(3,3.M.o,b,a[b]);5 3},isA:4(a){7 b=3.M;L(b){k(b===a)5 G;b=b.D}5 n}},N:{B:4(a,b){7 c,d,e,f=a.B,i=a.m;k(f){c=[].1k(f);9(d=0,e=c.p;d35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'([2-46-9j-zA-Z]|[12]\\w)'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('9.1g=18 JS.1y({include:[9.Observable,JS.State],1o:{17:\'1M\',1X:\'w\',27:\'item\',28:0.5,29:\'horizontal\',20:\'easeBoth\'},1m:4(a,b){2._m=a;2.8={};b=2.j=b||{};b.1n=b.1n||2.s.28;b.J=b.J||2.s.29;b.13=b.13||2.s.20;2.F(\'t\')},1I:4(){3{w:1}},1G:4(a){6(a.w!==m)2._a(a.w);3 2},P:4(){7 a=2.8,b=2.j;6(a.o)3 a.o;7 d=9(9.1b.y({x:2.s.17}));d.n(2.j.J);7 e=b.M,c=b.T,f;6(b.1s||b.1t){f=2.X();6(b.1s)c=(b.1s*f.C())+\'p\';6(b.1t)e=(b.1t*f.B())+\'p\'}d.E({M:e,T:c,overflow:\'hidden\',1w:\'0 0 0 0\',1r:\'1l\',1Z:\'relative\'});3 a.o=d},21:4(){3 2.j.J},2a:4(){3 2.P()},getSubject:4(){3 2.8.r||m},v:4(){6(!2.8.o)3 m;3 2.8.o.v()},X:4(){7 a=2.8;6(!a.r)3 m;6(a.k)3 a.k;a.k=a.r.children(2.j.selector);a.k.E({24:\'0 0 0 0\'});3 a.k},19:4(){6(2.l)3 2.l;7 a=2.X();6(!a)3 m;6(a.Q===0)3 0;7 b=2.v(),d=a.at(0).v();2._l=d.B();2._k=d.C();2._j=(b.B()/2._l).1p()||1;2._h=(b.C()/2._k).1p()||1;2._c=2._h*2._j;2.l=(a.Q/2._c).1F();6(2.j.grouping!==1u)2._i();3 2.l},_i:4(){7 e=2.v(),c=e.B(),f=e.C(),g=2._c,h=2.8.k.toArray();2.l.1v(4(a){7 b=h.slice(a*g,(a+1)*g);7 d=9(9.1b.y({x:2.s.1X}));d.E({\'float\':\'12\',M:c+\'p\',T:f+\'p\',24:\'0 0 0 0\',1w:\'0 0 0 0\',1r:\'1l\'});b.V(d.1E(\'D\'));2.8.r.D(d.A)},2)},1z:4(){3 2.u||m},1J:4(a){6(!2.l)3 m;7 b=2.8.k.Q;6(a<1||a>b)3 m;3((a-1)/2._c).1p()+1},addControls:4(a){6(2.K(\'t\')||!/^(?:before|1N)$/.test(a))3 m;7 b=18 2.s.1P(2);2.2a().D(b.P().A,a);3 b},1Q:{t:{setup:4(){7 a=2.8.r=9(2._m).at(0);6(!a.A)3 2;7 b=2.P();a.D(b.A,\'1N\');b.D(a.A);a.E({1w:\'0 0 0 0\',1r:\'1l\',1Z:\'absolute\',12:0,right:0});7 d=2.l=2.19(),e=2.v();7 c=(2.j.J==\'11\')?{M:e.B()+\'p\',T:(d*e.C()+1V)+\'p\'}:{M:(d*e.B()+1V)+\'p\',T:e.C()+\'p\'};a.E(c);7 f=2.1I();2.F(\'R\');2.u=f.w;2._a(f.w);3 2}},R:{U:4(a){a=Number(a);6(a==2.u||a<1||a>2.l)3 2;2.1G({w:a});3 2},_a:4(a){2.1k((a-1)/(2.l-1),{Z:1i})},22:4(){3 2.U(2.u+1)},23:4(){3 2.U(2.u-1)},snapToPage:4(a){2.1k((2.u-1)/(2.l-1),{Z:a!==1u,25:1i});3 2},focusItem:4(a){7 b=2.1J(a);6(!b)3 2;7 d=2.8.k.at(a-1);2.z(\'focusitem\',a,d);2.U(b);2.8.k.L(\'1e\');d.n(\'1e\');3 2},1k:4(b,d){7 e=2.j.J,c;7 f=(e==\'11\')?\'C\':\'B\';7 g=2.l,h=2.v()[f]()*(g-1);6(b>=0&&b<=1)b=b*h;6(b<0||b>h)3 2;2.8.k.L(\'1e\');d=d||{};6(d.Z&&YAHOO.util.Anim){2.F(\'2e\');c=(e==\'11\')?{2f:{to:-b}}:{12:{to:-b}};2.8.r.Z(c,2.j.1n,{13:2.j.13})._(4(a){a.F(\'R\')},2)}else{c=(e==\'11\')?{2f:(-b)+\'p\'}:{12:(-b)+\'p\'};2.8.r.E(c)}6(!d.25)2.z(\'scroll\',b/h,h);7 i=(g*(b/h)).1F()||1;6(i!=2.u){2.u=i;2.z(\'2d\',i);6(i==1)2.z(\'2c\');6(i==2.l)2.z(\'2b\')}3 2}},2e:{}}});9.AjaxPaginator=18 JS.1y(9.1g,{1m:4(b,d){2.1c();2.j.S=2.j.S.map(4(a){3{_d:a,_g:1u}})},X:4(){7 d=2.8;6(d.k)3 d.k;6(!d.r)3 m;7 e=2.j.S;6(!e.Q)3 m;e.Q.1v(4(a){7 b=9(9.1b.y({x:2.s.27}));d.r.D(b.A,\'bottom\')},2);7 c=2.1c();c.fitToRegion(2.v());3 c},1h:4(a){3!!(2.j.S[a-1]||{})._g},1Y:4(b,d,e){6(2.1h(b)||2.K(\'t\'))3 2;7 c=2.j.S[b-1],f=2;2.z(\'pagerequest\',c._d);9.HTTP.GET(c._d,{},{onSuccess:4(a){a.insertInto(f.8.k.at(b-1));c._g=1i;f.z(\'pageload\',c._d,a);6(typeof d==\'4\')d.call(e||N)}});3 2},1Q:{R:{_a:4(a){6(2.1h(a))3 2.1c();7 b=2.1E(\'1c\');2.F(\'1T\');2.1Y(a,4(){2.F(\'R\');b()},2)}},1T:{}}});9.1g.1o({1P:18 JS.1y({1o:{17:\'1M-controls\',1S:\'previous\',1R:\'next\',1O:\'pages\'},1m:4(a){2.I=a;2.8={}},P:4(){6(2.I.K(\'t\'))3 N;7 c=2.8,f=2.s,g=2.I;6(c.o)3 c.o;c.o=9(9.1b.y({x:f.17},4(e){c.G=9(e.y({x:f.1S},\'Previous\'));c._f=9(e.y({x:f.1O},4(d){c.Y=[];g.19().1v(4(a){7 b=c.Y[a]=9(d.1C(String(a+1)));b.q(\'1B\').n(\'W\');b.q(\'1A\').L(\'W\')})}));c.H=9(e.y({x:f.1R},\'Next\'))}));c.G.q(\'1x\')._(g).23();c.H.q(\'1x\')._(g).22();c._f.q(\'1x\',9.delegateEvent({1C:4(a,b){g.U(a.A.innerHTML)}}));7 h=[c.G,c.H];h.V(it().q(\'1B\').n(\'W\'));h.V(it().q(\'1A\').L(\'W\'));g.q(\'2d\',4(a,b){2._e(b);h.V(it().L(\'O\'))},2);7 i=g.1z();2._e(i);g.q(\'2c\')._(c.G).n(\'O\');g.q(\'2b\')._(c.H).n(\'O\');6(i==1)c.G.n(\'O\');6(i==g.19())c.H.n(\'O\');c.o.n(g.21());3 c.o},_e:4(a){2.8.Y.V({L:\'26\'});2.8.Y[a-1].n(\'26\')},getPreviousButton:4(){6(2.I.K(\'t\'))3 N;3 2.8.G},getNextButton:4(){6(2.I.K(\'t\'))3 N;3 2.8.H},getPageButtons:4(){6(2.I.K(\'t\'))3 N;3 2.8._f}})});',[],141,'||this|return|function||if|var|_0|Ojay||||||||||_1|_2|_3|undefined|addClass|_4|px|on|_5|klass|CREATED|_6|getRegion|page|className|div|notifyObservers|node|getWidth|getHeight|insert|setStyle|setState|_9|_7|_8|direction|inState|removeClass|width|null|disabled|getHTML|length|READY|urls|height|setPage|forEach|hovered|getItems|_b|animate||vertical|left|easing||||CONTAINER_CLASS|new|getPages||HTML|callSuper||focused||Paginator|pageLoaded|true||setScroll|none|initialize|scrollTime|extend|floor||border|rows|columns|false|times|padding|click|Class|getCurrentPage|mouseout|mouseover|span||method|ceil|changeState||getInitialState|pageForItem|||paginator|after|PAGE_LINKS_CLASS|Controls|states|NEXT_CLASS|PREVIOUS_CLASS|REQUESTING||1000||PAGE_CLASS|loadPage|position|EASING|getDirection|incrementPage|decrementPage|margin|silent|selected|ITEM_CLASS|SCROLL_TIME|DIRECTION|getContainer|lastpage|firstpage|pagechange|SCROLLING|top'.split('|'),0,{})) -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/overlay-min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'([6-9i-rt-zA-Z]|[12]\\w)'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(8(d){i h=8(b){7 8(){i a=6.k.m;a.n({25:\'2d\'});6.u(\'P\',{V:y})[b]().t(\'P\',{V:y});a.n({25:\'\'});7 6}};d.w=x v.14({include:[v.State,d.Observable],1h:{1L:1000,T:20,2f:{o:400,r:300},28:{p:50,l:50},1i:1,23:\'1T-container\',O:0.4,1a:\'easeOutStrong\',1g:x v.Singleton({_7:{},1M:x v.2m([\'t\',\'u\']),_b:{t:8(a){7 a},u:8(a){7 a}},Y:8(a,b){v.2m.ensure(b,6.1M);6._7[a]=b;7 6},1A:8(a){7 6._7[a]||6._b}}),L:8(a){9(a.L)7 X(a.L());9(a.nodeType==d.1F.ELEMENT_NODE||1Q a==\'string\')a=d(a);9(a.1W)7 X(a.1W(\'22\'))||0;7 0}},1c:8(a){6.k={};a=6._6=a||{};d(1m.2c).1k(6.1j().1D,\'l\');6.B(\'R\');6.E(a.o,a.r);6.Q(a.p,a.l);6.1b(a.layer);6.1X(a.A)},1j:8(){i a=6,b=a.k;9(b.m)7 b.m;i c=d(d.1F.1R({1r:6.j.23}));c.n({1O:\'absolute\',overflow:\'2d\'}).t();c.n({padding:\'0 0 0 0\',border:\'P\'});(6._6.1r||\'\').trim().split(/\\s+/).1B(c.1C(\'26\'));7 b.m=c},G:8(){7 6.k.m},Q:8(a,b){9(6.z(\'D\'))7 6;i c=6.j.28;a=6.W(a===I?c.p:a);b=6.W(b===I?c.l:b);6._a={p:a,l:b};9(6.z(\'H\'))6.k.m.n(6._a);7 6},10:8(a){i b=6._a,c=b.p,e=b.l;7 a?{p:c,l:e}:{p:12(c),l:12(e)}},E:8(a,b){9(6.z(\'D\'))7 6;i c=6.j.2f;a=6.W(a===I?c.o:a);b=6.W(b===I?c.r:b);6._8={o:a,r:b};9(6.z(\'H\'))6.k.m.n(6._8);7 6},17:8(a){i b=6._8,c=b.o,e=b.r;7 a?{o:c,r:e}:{o:12(c),r:12(e)}},F:8(){7!6.z(\'R\',\'D\')?6.k.m.F():I},1X:8(a){6.S=(a===I)?6.j.1i:X(a);9(6.S>1)6.S/=13;9(6.z(\'H\'))6.k.m.n({A:6.S});7 6},Z:8(){7 6.S},positionBehind:8(a){7 6.1b(6.j.L(a)-1)},positionInFront:8(a){7 6.1b(6.j.L(a)+1)},1b:8(a){9(6.z(\'D\'))7 6;6._9=(a===I)?6.j.1L:X(a);6.k.m.n({22:6._9});7 6},L:8(){7 6._9},1p:{R:{1o:h(\'1o\'),u:8(a,b){6.B(\'1Z\');a=6.j.1g.1A(a||\'P\');i c=x v.21()._(6).B(\'H\');9((b||{}).V!==y)c._(6).1n(\'u\');c._(6);7 a.u(6,c)},11:8(a){6.k.m.remove();6.B(\'D\');9((a||{}).V!==y)6.1n(\'11\');7 6}},1Z:{},H:{1o:8(){i a=6.F(),b=d.getVisibleRegion(),c=b.p+(b.J()-a.J())/2,e=b.l+(b.N()-a.N())/2;9(c<6.j.T)c=6.j.T;9(e<6.j.T)e=6.j.T;7 6.Q(c,e)},t:8(a,b){6.B(\'29\');a=6.j.1g.1A(a||\'P\');i c=x v.21()._(6).B(\'R\');9((b||{}).V!==y)c._(6).1n(\'t\');c._(6);7 a.t(6,c)},11:8(a,b){7 6.t(a,b)._(6).11(b)},2a:8(a,b,c,e,f){i g=a,f=f||{};9(1Q g==\'object\'){f=b||{};a=g.p;b=g.l;c=g.J();e=g.N()}6.B(\'2e\');7 6.k.m.U({p:{q:a},l:{q:b},o:{q:c},r:{q:e}},f.duration||6.j.O,{1e:f.1e||6.j.1a})._(6).E(c,e)._(6).Q(a,b)._(6).B(\'H\')._(6)}},29:{},2e:{},D:{}},W:8(a){7 2i(a).2j(/^(-?\\d+(?:\\.\\d+)?)$/g,\'$1px\')}});d.w.1g.Y(\'P\',{t:8(a,b){a.G().t();b.2l();7 a},u:8(a,b){a.G().n({A:a.Z()}).n(a.17(y)).n(a.10(y)).u();b.2l();7 a}}).Y(\'fade\',{t:8(a,b){a.G().U({A:{q:0}},d.w.O).t()._(b.1f());7 b},u:8(a,b){a.G().n({A:0}).n(a.17(y)).n(a.10(y)).u().U({A:{q:a.Z()}},d.w.O)._(b.1f());7 b}}).Y(\'zoom\',{t:8(a,b){i c=a.F().scale(0.5),e=c.getCenter();a.G().U({A:{q:0},p:{q:c.p},o:{q:c.J()},l:{q:c.l},r:{q:c.N()}},d.w.O,{1e:d.w.1a}).t()._(b.1f());7 b},u:8(a,b){i c=a.10(),e=a.17();a.G().n({A:0,p:(c.p+e.o/4)+\'px\',l:(c.l+e.r/4)+\'px\',o:(e.o/2)+\'px\',r:(e.r/2)+\'px\'}).u().U({A:{q:a.Z()},p:{q:c.p},o:{q:e.o},l:{q:c.l},r:{q:e.r}},d.w.O,{1e:d.w.1a})._(b.1f());7 b}});d.2k=x v.14(d.w,{1h:{2h:\'1T-2g\'},1c:8(a){6.C();6.15(6._6.2g)},1j:8(){i a=6,b=a.k;9(b.K)7 b.m;i c=6.C().1D,e=x d.HtmlBuilder(c);b.K=d(e.1R({1r:a.j.2h}));7 b.m},15:8(a){9(6.z(\'D\'))7 6;6.k.K.15(a||\'\');7 6},getContentElement:8(){7 6.k.K},1k:8(a,b){9(6.z(\'D\'))7 6;6.k.K.1k(a,b);7 6},1p:{R:{1l:h(\'1l\')},H:{1l:8(){i a=6.k.K.F();7 6.E(a.J(),a.N())}}}});d.2b=x v.14(d.2k,{1c:8(a,b){6.C(b);6.k.m.26(\'tooltip\');6.15(a);6.j.M.24(6)},1h:{1V:8(c,e){i f=19.util.Event.getXY(e);6.M.1B(8(a){i b=a.F();o=b?b.J():6.1S;a.Q(f[0]+6.1s-o/2,f[1]+6.1s)},6)},M:[],1S:13,1s:20}});d(1m).on(\'mousemove\',d.2b.1C(\'1V\'));d.1I=x v.14(d.w,{1h:{1G:\'000000\',1i:0.5,M:[],1E:8(){6.M.1B(\'E\')}},1c:8(a){6.j.M.24(6);6.C();6.1H(6._6.color);9(!19.1z.ua.ie)6.k.m.n({1O:\'fixed\'})},Q:8(){7 6.C(0,0)},E:8(){9(!19.1z.ua.ie)7 6.C(\'13%\',\'13%\');i a=d(1m.2c).F(),b=d.getViewportSize();7 6.C(1w.1J(a.J(),b.o),1w.1J(a.N(),b.r))},1H:8(c){6._c=(1U.1Y==3)?Array.from(1U).map(8(a){i b=1w.round(a%256).toString(16);7(b.1Y==1?\'0\':\'\')+b}).join(\'\'):(c?2i(c).2j(/[^0-9a-f]/ig,\'\'):6.j.1G);6.k.m.n({backgroundColor:\'#\'+6._c});7 6},1p:{R:{u:8(){6.E();7 6.C()}}}});9(19.1z.ua.ie)d(window).on(\'2a\',d.1I.1C(\'1E\'))})(Ojay);',[],148,'||||||this|return|function|if|||||||||var|klass|_0|top|_1|setStyle|width|left|to|height||hide|show|JS|Overlay|new|true|inState|opacity|setState|callSuper|CLOSED|setSize|getRegion|getContainer|VISIBLE|undefined|getWidth|_3|getLayer|_2|getHeight|TRANSITION_TIME|none|setPosition|INVISIBLE|_4|MINIMUM_OFFSET|animate|silent|_5|Number|add|getOpacity|getPosition|close|parseInt|100|Class|setContent||getSize||YAHOO|EASING|setLayer|initialize||easing|toFunction|Transitions|extend|DEFAULT_OPACITY|getHTML|insert|fitToContent|document|notifyObservers|center|states||className|MOUSE_OFFSET||||Math|||env|get|forEach|method|node|resizeAll|HTML|DEFAULT_COLOR|setColor|PageMask|max||BASE_LAYER|INTERFACE||position||typeof|div|DEFAULT_WIDTH|overlay|arguments|update|getStyle|setOpacity|length|SHOWING||MethodChain|zIndex|CONTAINER_CLASS|push|visibility|addClass||DEFAULT_POSITION|HIDING|resize|Tooltip|body|hidden|RESIZING|DEFAULT_SIZE|content|CONTENT_CLASS|String|replace|ContentOverlay|fire|Interface'.split('|'),0,{})) -------------------------------------------------------------------------------- /handlers/restful.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | """ 24 | RESTful Controller 25 | 26 | We want our RESTful controllers to simply throw up their hands if they get 27 | an unhandled HTTP verb. This is better for rich clients and server load 28 | than throwing back lots of useless HTML. 29 | 30 | These inherited methods should be overridden if there's a chance a human 31 | browser is involved. 32 | 33 | TODO: Return more information HTTP status codes that won't autotrip 34 | browser login forms. For example, return status 405 (Method not allowed) 35 | with an Allow header containing the list of valid methods. 36 | """ 37 | __author__ = 'William T. Katz' 38 | 39 | from google.appengine.ext import webapp 40 | 41 | import logging 42 | 43 | # Some useful module methods 44 | def send_successful_response(handler, response): 45 | # Response is probably just a URL. 46 | logging.debug("Sending successful response: %s", response) 47 | handler.response.out.write(response) 48 | 49 | def get_sent_properties(request_func, propname_list): 50 | """ 51 | This maps request strings to values in a hash, optionally run through 52 | a function with previous request values as parameters to the func. 53 | 1) key -> just read in the corresponding request value 54 | 2) tuple (key, func) -> Read the request value for the string key 55 | and pass it through func 56 | 3) tuple (key, func, additional keys...) -> Get the request 57 | values for the additional keys and pass them through func 58 | before setting the key's value with the output. 59 | If a key is not present in the request, then we do not insert a key 60 | with None or empty string. The key is simply absent, therefore allowing 61 | you to use the returned hash to initial a Model instance. 62 | """ 63 | prop_hash = {} 64 | for item in propname_list: 65 | if isinstance(item, basestring): 66 | key = item 67 | value = request_func(item) 68 | elif isinstance(item, tuple): 69 | key = item[0] 70 | prop_func = item[1] 71 | if len(item) <= 2: 72 | value = prop_func(request_func(key)) 73 | else: 74 | try: 75 | addl_keys = map(prop_hash.get, item[2:]) 76 | value = prop_func(*addl_keys) 77 | except: 78 | return None 79 | if value: 80 | prop_hash[key] = value 81 | return prop_hash 82 | 83 | def methods_via_query_allowed(handler_method): 84 | """ 85 | A decorator to automatically re-route overloaded POSTs 86 | that specify the real HTTP method in a _method query string. 87 | 88 | To use it, decorate your post method like this: 89 | 90 | import restful 91 | ... 92 | @restful.methods_via_query_allowed 93 | def post(self): 94 | pass 95 | 96 | The decorator will check for a _method query string or POST argument, 97 | and if present, will redirect to delete(), put(), etc. 98 | """ 99 | def redirect_if_needed(self, *args, **kwargs): 100 | real_verb = self.request.get('_method', None) 101 | if not real_verb and 'X-HTTP-Method-Override' in self.request.environ: 102 | real_verb = self.request.environ['X-HTTP-Method-Override'] 103 | if real_verb: 104 | logging.debug("Redirected from POST. Detected method override = %s", real_verb) 105 | method = real_verb.upper() 106 | if method == 'HEAD': 107 | self.head(*args, **kwargs) 108 | elif method == 'PUT': 109 | self.put(*args, **kwargs) 110 | elif method == 'DELETE': 111 | self.delete(*args, **kwargs) 112 | elif method == 'TRACE': 113 | self.trace(*args, **kwargs) 114 | elif method == 'OPTIONS': 115 | self.head(*args, **kwargs) 116 | # POST and GET included for completeness 117 | elif method == 'POST': 118 | self.post(*args, **kwargs) 119 | elif method == 'GET': 120 | self.get(*args, **kwargs) 121 | else: 122 | self.error(405) 123 | else: 124 | handler_method(self, *args, **kwargs) 125 | return redirect_if_needed 126 | 127 | class Controller(webapp.RequestHandler): 128 | def get(self, *params): 129 | self.redirect("/403.html") 130 | 131 | def head(self, *params): 132 | pass 133 | -------------------------------------------------------------------------------- /static/default/js/ojay/pkg/mouse.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007-2008 the OTHER media Limited 3 | Licensed under the BSD license, http://ojay.othermedia.org/license.html 4 | */ 5 | // @require ojay/core-min 6 | /** 7 | * @overview 8 | *

    The Mouse module, when included in a web page, automatically keeps track 9 | * of the current mouse position by listening to the document's mousemove event. You 10 | * can grab the current mouse position from anywhere in your code by checking the left and 11 | * top properties of Ojay.Mouse.position.

    12 | * 13 | *

    You can also use Mouse to listen for mouseenter and mouseleave 14 | * events, which are like mouseover and mouseout except without the problems 15 | * caused by missed events and nested elements. When Mouse is present on a page, 16 | * you can register event listeners as follows:

    17 | * 18 | *
        Ojay('p').on('mouseenter', function(element, position) {
     19 |  *         // respond to event
     20 |  *     }, scope);
    21 | * 22 | *

    Within your callback function, element refers to the element the mouse entered, 23 | * and position is an object whose left and top properties represent 24 | * the position of the mouse at the time of the event. The optional scope argument sets 25 | * the meaning of this inside your callback.

    26 | * 27 | *

    More generally, you can subscribe to all mouse movement as follows:

    28 | * 29 | *
        Ojay.Mouse.subscribe(function(position) {
     30 |  *         // respond to mouse movement with
     31 |  *         // position.left and position.top
     32 |  *     }, scope);
    33 | * 34 | *

    Your callback is fired every time the mouse moves, and it is given the mouse position in 35 | * the position argument. Again, use scope to set the meaning of this 36 | * inside the callback.

    37 | */ 38 | Ojay.Mouse = new JS.Singleton(/** @scope Ojay.Mouse */{ 39 | include: JS.Observable, 40 | 41 | initialize: function() { 42 | this.position = {left: null, top: null}; 43 | }, 44 | 45 | /** 46 | *

    Callback used to respond to mousemove events. Calls notifyObservers with 47 | * the current mouse position.

    48 | * @param {Event} e 49 | */ 50 | updatePosition: function(doc, e) { 51 | var xy = YAHOO.util.Event.getXY(e); 52 | this.position = {left: xy[0], top: xy[1]}; 53 | this.notifyObservers(this.position); 54 | }, 55 | 56 | /** 57 | *

    Provides support for detecting events when the mouse pointer enters or leaves an 58 | * element, in a way that doesn't cause the problems of mouseover/mouseout. Firstly, the 59 | * mouse pointer is monitored across the whole document, so you've less chance of missing 60 | * a mouseout event due to fast movement. Second, this function will not fire a mouse-out 61 | * event if you mouse over an element nested inside the element you're listening to.

    62 | * 63 | * @param {String} movement Either 'entering' or 'leaving' 64 | * @param {Region|HTMLElement|String} element The element to watch for mouse events 65 | * @param {Function} callback A function to fire 66 | * @param {Object} scope The scope in which to fire the callback (optional) 67 | */ 68 | on: function(movement, element, callback, scope) { 69 | if (!/^(?:entering|leaving)$/.test(movement)) 70 | throw new TypeError('Movement is not recognised'); 71 | 72 | var isRegion = (element instanceof Ojay.Region); 73 | var region = isRegion ? element : null; 74 | var element = isRegion ? null: Ojay(element); 75 | var wasInside = false; 76 | this.addObserver(function(position) { 77 | var currentRegion = region || element.getRegion(); 78 | var isInside = this.isInside(currentRegion); 79 | if (movement == 'entering' && !wasInside && isInside) 80 | callback.call(scope || null, this.position); 81 | if (movement == 'leaving' && wasInside && !isInside) 82 | callback.call(scope || null, this.position); 83 | wasInside = isInside; 84 | }, this); 85 | }, 86 | 87 | /** 88 | *

    Returns true iff the mouse pointer is currently inside the given region or 89 | * element. region can be an HTML element reference or a CSS selector, in which 90 | * case the test region will be the area of the first element that matches the selector. 91 | * Returns undefined if no region can be found.

    92 | * @param {Region|HTMLElement|String} region 93 | * @returns {Boolean} 94 | */ 95 | isInside: function(region) { 96 | region = Ojay.Region.convert(region); 97 | if (!region) return undefined; 98 | var pos = this.position; 99 | return pos.left >= region.left && pos.left <= region.right && 100 | pos.top >= region.top && pos.top <= region.bottom; 101 | } 102 | }); 103 | 104 | Ojay(document).on('mousemove', Ojay.Mouse.method('updatePosition')); 105 | 106 | /** 107 | *

    DomCollection#on is wrapped to provide mouseenter and mouseleave 108 | * event detection support.

    109 | */ 110 | Ojay.DomCollection.include({on: Ojay.DomCollection.prototype.on.wrap(function() { 111 | var args = Array.from(arguments), original = args.shift(); 112 | var eventName = args[0], callback = args[1], scope = args[2]; 113 | 114 | if (!/^mouse(enter|leave)$/.test(eventName)) 115 | return original(eventName, callback, scope); 116 | 117 | var type = eventName.match(/^mouse(enter|leave)$/)[1].replace(/e?$/, 'ing'); 118 | var collector = new JS.MethodChain(); 119 | if (callback && typeof callback != 'function') scope = callback; 120 | 121 | this.forEach(function(element) { 122 | Ojay.Mouse.on(type, element, function(position) { 123 | if (typeof callback == 'function') callback.call(scope || null, element, position); 124 | collector.fire(scope || element); 125 | }); 126 | }); 127 | 128 | return collector; 129 | })}); 130 | -------------------------------------------------------------------------------- /utils/sanitizer.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | import logging 24 | import string 25 | import re 26 | 27 | from external.BeautifulSoup import BeautifulSoup, Comment 28 | 29 | acceptable_tags = ['a', 'abbr', 'acronym', 'address', 'area', 'b', 'big', 30 | 'blockquote', 'br', 'button', 'caption', 'center', 'cite', 'code', 31 | 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 32 | 'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 33 | 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'map', 34 | 'menu', 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp', 35 | 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table', 36 | 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 37 | 'ul', 'var'] 38 | 39 | acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey', 40 | 'action', 'align', 'alt', 'axis', 'border', 'cellpadding', 41 | 'cellspacing', 'char', 'charoff', 'charset', 'checked', 'cite', 'class', 42 | 'clear', 'cols', 'colspan', 'color', 'compact', 'coords', 'datetime', 43 | 'dir', 'disabled', 'enctype', 'for', 'frame', 'headers', 'height', 44 | 'href', 'hreflang', 'hspace', 'id', 'ismap', 'label', 'lang', 45 | 'longdesc', 'maxlength', 'media', 'method', 'multiple', 'name', 46 | 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly', 'rel', 'rev', 47 | 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 48 | 'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title', 49 | 'type', 'usemap', 'valign', 'value', 'vspace', 'width'] 50 | 51 | tags_for_trusted_source = ['object', 'param', 'embed', 'style'] 52 | attributes_for_trusted_source = ['style', 'wmode'] 53 | 54 | danger_elements = ['script', 'applet'] 55 | js_possible_attributes = ['href', 'src'] 56 | 57 | href_matcher = re.compile("^https?://", re.IGNORECASE) 58 | javascript_matcher = re.compile("javascript:", re.IGNORECASE | re.MULTILINE) 59 | 60 | class DangerousHTMLError(Exception): 61 | def __init__(self, value): 62 | self.value = chop_up(value) # Even when logging, could be displayed 63 | def __str__(self): 64 | return ' ~ '.join(self.value) 65 | 66 | def sanitize_html(html='

    No comment

    ', encoding=None, 67 | allow_tags=[], allow_attributes=[], 68 | blacklist_tags=[], blacklist_attributes=[], 69 | trusted_source=False): 70 | """Parses HTML and tries to sanitize it using white list. 71 | 72 | This method is a mishmash of code from Django snippets 73 | (http://www.djangosnippets.org/snippets/169) and the 74 | HTML sanitization of Universal Feed Parser. It explicitly 75 | looks for valid hrefs to prevent scripts lurking in there. 76 | Unfortunately, style, either as a tag or attribute, can 77 | contain malicious script through executable CSS definitions. 78 | So sanitized HTML cannot be colored or highlighted using styles. 79 | 80 | Args: 81 | html: HTML to be sanitized. 82 | allow_tags: limit all tags to just this list 83 | allow_attributes: limit all tags to just this list 84 | allow_styling: should only be TRUE if you trust source 85 | 86 | Returns: 87 | Sanitized version of html 88 | 89 | Raises: 90 | DangerousHTMLError if the supplied HTML has dangerous elements. 91 | """ 92 | if not allow_tags: 93 | allow_tags = acceptable_tags 94 | if not allow_attributes: 95 | allow_attributes = acceptable_attributes 96 | allow_tags = [tag for tag in allow_tags if tag not in blacklist_tags] 97 | allow_attributes = [tag for tag in allow_attributes 98 | if tag not in blacklist_tags] 99 | if trusted_source: 100 | allow_attributes += attributes_for_trusted_source 101 | allow_tags += tags_for_trusted_source 102 | 103 | if isinstance(html, unicode) and not encoding: 104 | logging.debug("Sanitizing unicode input.") 105 | soup = BeautifulSoup(html, 106 | convertEntities=BeautifulSoup.XHTML_ENTITIES) 107 | else: 108 | if not encoding: 109 | encoding = 'latin-1' 110 | logging.debug("Sanitizing string input, assuming %s", encoding) 111 | soup = BeautifulSoup(html.decode(encoding, 'ignore'), 112 | convertEntities=BeautifulSoup.XHTML_ENTITIES) 113 | for comment in soup.findAll( 114 | text = lambda text: isinstance(text, Comment)): 115 | comment.extract() 116 | for tag in soup.findAll(True): 117 | if tag.name not in allow_tags: 118 | tag.hidden = True 119 | if tag.name in danger_elements: 120 | raise DangerousHTMLError(html) 121 | ok_attrs = [] 122 | for attr, val in tag.attrs: 123 | if attr == 'href' and not href_matcher.match(val) and not trusted_source: 124 | continue 125 | if attr in allow_attributes: 126 | if attr in js_possible_attributes: 127 | if javascript_matcher.match(val): 128 | raise DangerousHTMLError(html) 129 | ok_attrs += [(attr, val)] 130 | tag.attrs = ok_attrs 131 | return soup.renderContents().decode('utf-8') 132 | 133 | def chop_up(text, chop_size=5): 134 | "Returns a list of smaller chunks of text" 135 | chars = len(text) 136 | blocks = chars / chop_size 137 | if chars % chop_size: 138 | blocks += 1 139 | return [text[i*chop_size:min(chars,(i+1)*chop_size)] 140 | for i in xrange(0, blocks)] 141 | -------------------------------------------------------------------------------- /dev/tests/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import urllib 3 | from utils import template 4 | from google.appengine.api import apiproxy_stub_map 5 | from google.appengine.api import datastore_file_stub 6 | from google.appengine.api import user_service_stub 7 | from google.appengine.api import urlfetch_stub 8 | from google.appengine.api import mail_stub 9 | from google.appengine.api.memcache import memcache_stub 10 | from google.appengine.ext import webapp 11 | import os 12 | import time 13 | 14 | APP_ID = u'test_app' 15 | AUTH_DOMAIN = 'gmail.com' 16 | LOGGED_IN_USER = 't...@example.com' # set to '' for no logged in user 17 | HOST = 'localhost:8080' 18 | 19 | os.environ['SERVER_SOFTWARE'] = 'TestServer/1.0' 20 | os.environ['APPLICATION_ID'] = 'test_app' 21 | os.environ['TZ'] = 'UTC' 22 | os.environ['USER_IS_ADMIN'] = '1' 23 | time.tzset() 24 | 25 | from handlers.bloog import blog 26 | import models.blog 27 | 28 | class BloogTest(unittest.TestCase): 29 | 30 | def setUp(self): 31 | # Start with a fresh api proxy. 32 | apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap() 33 | 34 | # Use a fresh stub datastore. 35 | stub = datastore_file_stub.DatastoreFileStub(APP_ID, '/dev/null', 36 | '/dev/null') 37 | apiproxy_stub_map.apiproxy.RegisterStub('datastore_v3', stub) 38 | 39 | # Use a fresh stub UserService. 40 | apiproxy_stub_map.apiproxy.RegisterStub( 41 | 'user', user_service_stub.UserServiceStub()) 42 | os.environ['AUTH_DOMAIN'] = AUTH_DOMAIN 43 | os.environ['USER_EMAIL'] = LOGGED_IN_USER 44 | 45 | # Use a fresh urlfetch stub. 46 | apiproxy_stub_map.apiproxy.RegisterStub( 47 | 'urlfetch', urlfetch_stub.URLFetchServiceStub()) 48 | 49 | # Use a fresh mail stub. 50 | apiproxy_stub_map.apiproxy.RegisterStub( 51 | 'mail', mail_stub.MailServiceStub()) 52 | 53 | # Use a fresh memcache stub 54 | apiproxy_stub_map.apiproxy.RegisterStub( 55 | 'memcache', memcache_stub.MemcacheServiceStub()) 56 | 57 | # Create a fake remplate renderer 58 | self.render_calls = [] 59 | def template_render(filename, params, debug, template_dirs): 60 | self.render_calls.append(params) 61 | template.render = template_render 62 | 63 | def createHandler(self, cls, uri, env=None, auth=False): 64 | handler = cls() 65 | environ = { 66 | 'wsgi.url_scheme': 'http', 67 | 'HTTP_HOST': HOST, 68 | 'SCRIPT_NAME': uri, 69 | } 70 | if auth: 71 | environ['HTTP_COOKIE'] = 'dev_appserver_login="root@example.com:True"' 72 | if env: 73 | environ.update(env) 74 | request = webapp.Request(environ) 75 | response = webapp.Response() 76 | handler.initialize(request, response) 77 | return handler, request, response 78 | 79 | def testRoot(self): 80 | root, request, response = self.createHandler(blog.RootHandler, '/') 81 | root.get() 82 | self.failUnlessEqual(len(self.render_calls), 1) 83 | self.failUnlessEqual(self.render_calls[0]['articles'], []) 84 | 85 | def testArticleSubmission(self): 86 | root, request, response = self.createHandler(blog.RootHandler, '/', { 87 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 88 | 'REQUEST_METHOD': 'POST', 89 | }) 90 | postdata = { 91 | 'title': 'Test post', 92 | 'body': 'Post body', 93 | 'format': 'html', 94 | 'published': '', 95 | 'updated': '', 96 | 'tags': 'foo,bar', 97 | } 98 | request.body = urllib.urlencode(postdata) 99 | root.post() 100 | self.failUnlessEqual(response.out.getvalue(), '/Test-post') 101 | 102 | article = models.blog.Article.all().get() 103 | self.failUnlessEqual(article.permalink, 'Test-post') 104 | self.failUnlessEqual(article.title, postdata['title']) 105 | self.failUnlessEqual(article.body, postdata['body']) 106 | self.failUnlessEqual(article.tags, postdata['tags'].split(',')) 107 | self.failUnlessEqual(article.article_type, 'article') 108 | 109 | articles, request, response = self.createHandler(blog.ArticlesHandler, 110 | '/articles') 111 | articles.get() 112 | self.failUnlessEqual(len(self.render_calls), 1) 113 | self.failUnlessEqual(self.render_calls[0]['articles'][0].key(), 114 | article.key()) 115 | 116 | handler, request, response = self.createHandler(blog.ArticleHandler, 117 | '/Test-post') 118 | handler.get('Test-post') 119 | self.failUnlessEqual(len(self.render_calls), 2) 120 | self.failUnlessEqual(self.render_calls[1]['article'].key(), 121 | article.key()) 122 | 123 | def testPostSubmission(self): 124 | url = '2008/1' 125 | root, request, response = self.createHandler(blog.MonthHandler, url, { 126 | 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 127 | 'REQUEST_METHOD': 'POST', 128 | }) 129 | postdata = { 130 | 'title': 'Test blog post', 131 | 'body': 'Another post body', 132 | 'format': 'html', 133 | 'published': '2008-01-01 12:00:00', 134 | 'updated': '', 135 | 'tags': 'foo,baz,bleh', 136 | } 137 | request.body = urllib.urlencode(postdata) 138 | root.post('2008', '01') 139 | self.failUnlessEqual(response.out.getvalue(), '/2008/1/Test-blog-post') 140 | 141 | article = models.blog.Article.all().get() 142 | self.failUnlessEqual(article.permalink, '2008/1/Test-blog-post') 143 | self.failUnlessEqual(article.title, postdata['title']) 144 | self.failUnlessEqual(article.body, postdata['body']) 145 | self.failUnlessEqual(article.tags, postdata['tags'].split(',')) 146 | self.failUnlessEqual(article.article_type, 'blog entry') 147 | 148 | root, request, response = self.createHandler(blog.RootHandler, '/') 149 | root.get() 150 | self.failUnlessEqual(len(self.render_calls), 1) 151 | self.failUnlessEqual(self.render_calls[0]['articles'][0].key(), 152 | article.key()) 153 | 154 | handler, request, response = self.createHandler(blog.BlogEntryHandler, 155 | '/2008/1/Test-blog-post') 156 | handler.get('2008', '1', 'Test-blog-post') 157 | self.failUnlessEqual(len(self.render_calls), 2) 158 | self.failUnlessEqual(self.render_calls[1]['article'].key(), 159 | article.key()) 160 | 161 | 162 | if __name__ == '__main__': 163 | unittest.main() 164 | -------------------------------------------------------------------------------- /static/shell.js: -------------------------------------------------------------------------------- 1 | // Copyright 2007 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /** 16 | * @fileoverview 17 | * Javascript code for the interactive AJAX shell. 18 | * 19 | * Part of http://code.google.com/p/google-app-engine-samples/. 20 | * 21 | * Includes a function (shell.runStatement) that sends the current python 22 | * statement in the shell prompt text box to the server, and a callback 23 | * (shell.done) that displays the results when the XmlHttpRequest returns. 24 | * 25 | * Also includes cross-browser code (shell.getXmlHttpRequest) to get an 26 | * XmlHttpRequest. 27 | */ 28 | 29 | /** 30 | * Shell namespace. 31 | * @type {Object} 32 | */ 33 | var shell = {} 34 | 35 | /** 36 | * The shell history. history is an array of strings, ordered oldest to 37 | * newest. historyCursor is the current history element that the user is on. 38 | * 39 | * The last history element is the statement that the user is currently 40 | * typing. When a statement is run, it's frozen in the history, a new history 41 | * element is added to the end of the array for the new statement, and 42 | * historyCursor is updated to point to the new element. 43 | * 44 | * @type {Array} 45 | */ 46 | shell.history = ['']; 47 | 48 | /** 49 | * See {shell.history} 50 | * @type {number} 51 | */ 52 | shell.historyCursor = 0; 53 | 54 | /** 55 | * A constant for the XmlHttpRequest 'done' state. 56 | * @type Number 57 | */ 58 | shell.DONE_STATE = 4; 59 | 60 | /** 61 | * A cross-browser function to get an XmlHttpRequest object. 62 | * 63 | * @return {XmlHttpRequest?} a new XmlHttpRequest 64 | */ 65 | shell.getXmlHttpRequest = function() { 66 | if (window.XMLHttpRequest) { 67 | return new XMLHttpRequest(); 68 | } else if (window.ActiveXObject) { 69 | try { 70 | return new ActiveXObject('Msxml2.XMLHTTP'); 71 | } catch(e) { 72 | return new ActiveXObject('Microsoft.XMLHTTP'); 73 | } 74 | } 75 | 76 | return null; 77 | }; 78 | 79 | /** 80 | * This is the prompt textarea's onkeypress handler. Depending on the key that 81 | * was pressed, it will run the statement, navigate the history, or update the 82 | * current statement in the history. 83 | * 84 | * @param {Event} event the keypress event 85 | * @return {Boolean} false to tell the browser not to submit the form. 86 | */ 87 | shell.onPromptKeyPress = function(event) { 88 | var statement = document.getElementById('statement'); 89 | 90 | if (this.historyCursor == this.history.length - 1) { 91 | // we're on the current statement. update it in the history before doing 92 | // anything. 93 | this.history[this.historyCursor] = statement.value; 94 | } 95 | 96 | // should we pull something from the history? 97 | if (event.shiftKey && event.keyCode == 38 /* up arrow */) { 98 | if (this.historyCursor > 0) { 99 | statement.value = this.history[--this.historyCursor]; 100 | } 101 | return false; 102 | } else if (event.shiftKey && event.keyCode == 40 /* down arrow */) { 103 | if (this.historyCursor < this.history.length - 1) { 104 | statement.value = this.history[++this.historyCursor]; 105 | } 106 | return false; 107 | } else if (!event.altKey) { 108 | // probably changing the statement. update it in the history. 109 | this.historyCursor = this.history.length - 1; 110 | this.history[this.historyCursor] = statement.value; 111 | } 112 | 113 | // should we submit? 114 | var ctrlEnter = (document.getElementById('submit_key').value == 'ctrl-enter'); 115 | if (event.keyCode == 13 /* enter */ && !event.altKey && !event.shiftKey && 116 | event.ctrlKey == ctrlEnter) { 117 | return this.runStatement(); 118 | } 119 | }; 120 | 121 | /** 122 | * The XmlHttpRequest callback. If the request succeeds, it adds the command 123 | * and its resulting output to the shell history div. 124 | * 125 | * @param {XmlHttpRequest} req the XmlHttpRequest we used to send the current 126 | * statement to the server 127 | */ 128 | shell.done = function(req) { 129 | if (req.readyState == this.DONE_STATE) { 130 | var statement = document.getElementById('statement') 131 | statement.className = 'prompt'; 132 | 133 | // add the command to the shell output 134 | var output = document.getElementById('output'); 135 | 136 | output.value += '\n>>> ' + statement.value; 137 | statement.value = ''; 138 | 139 | // add a new history element 140 | this.history.push(''); 141 | this.historyCursor = this.history.length - 1; 142 | 143 | // add the command's result 144 | var result = req.responseText.replace(/^\s*|\s*$/g, ''); // trim whitespace 145 | if (result != '') 146 | output.value += '\n' + result; 147 | 148 | // scroll to the bottom 149 | output.scrollTop = output.scrollHeight; 150 | if (output.createTextRange) { 151 | var range = output.createTextRange(); 152 | range.collapse(false); 153 | range.select(); 154 | } 155 | } 156 | }; 157 | 158 | /** 159 | * This is the form's onsubmit handler. It sends the python statement to the 160 | * server, and registers shell.done() as the callback to run when it returns. 161 | * 162 | * @return {Boolean} false to tell the browser not to submit the form. 163 | */ 164 | shell.runStatement = function() { 165 | var form = document.getElementById('form'); 166 | 167 | // build a XmlHttpRequest 168 | var req = this.getXmlHttpRequest(); 169 | if (!req) { 170 | document.getElementById('ajax-status').innerHTML = 171 | "Your browser doesn't support AJAX. :("; 172 | return false; 173 | } 174 | 175 | req.onreadystatechange = function() { shell.done(req); }; 176 | 177 | // build the query parameter string 178 | var params = ''; 179 | for (i = 0; i < form.elements.length; i++) { 180 | var elem = form.elements[i]; 181 | if (elem.type != 'submit' && elem.type != 'button' && elem.id != 'caret') { 182 | var value = escape(elem.value).replace(/\+/g, '%2B'); // escape ignores + 183 | params += '&' + elem.name + '=' + value; 184 | } 185 | } 186 | 187 | // send the request and tell the user. 188 | document.getElementById('statement').className = 'prompt processing'; 189 | req.open(form.method, form.action + '?' + params, true); 190 | req.setRequestHeader('Content-type', 191 | 'application/x-www-form-urlencoded;charset=UTF-8'); 192 | req.send(null); 193 | 194 | return false; 195 | }; 196 | -------------------------------------------------------------------------------- /static/default/js/bloog_comments.js: -------------------------------------------------------------------------------- 1 | /** 2 | The MIT License 3 | 4 | Copyright (c) 2008 William T. Katz 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to 8 | deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | **/ 24 | 25 | YAHOO.bloog.initComments = function() { 26 | 27 | var showRTE = function(e) { 28 | YAHOO.util.Dom.removeClass('commentDialog', 'initialHide'); 29 | YAHOO.bloog.commentEditor.setEditorHTML('

    Comment goes here

    '); 30 | YAHOO.bloog.commentDialog.render(); 31 | YAHOO.bloog.commentDialog.show(); 32 | YAHOO.bloog.commentEditor.show(); 33 | } 34 | 35 | var handleSuccess = function(o) { 36 | var response = o.responseText; 37 | // Insert the comment into the appropriate place then hide dialog. 38 | parent_id = YAHOO.bloog.action.split('#')[1]; 39 | if (parent_id == '') { 40 | // Should be inserted at top 41 | Ojay('#commentslist').insert(response, 'top'); 42 | } 43 | else { 44 | Ojay('#' + parent_id).insert(response, 'after'); 45 | } 46 | var num_comments = Number(document.getElementById('num_comments').innerHTML) + 1; 47 | Ojay('#num_comments').setContent(String(num_comments)); 48 | YAHOO.bloog.commentEditor.hide(); 49 | YAHOO.bloog.commentDialog.hide(); 50 | } 51 | var handleFailure = function(o) { 52 | alert("Sorry, could not save comment!"); 53 | } 54 | var handleSubmit = function() { 55 | YAHOO.bloog.commentEditor.saveHTML(); 56 | var html = YAHOO.bloog.commentEditor.get('element').value; 57 | var captcha = YAHOO.util.Dom.get('captcha').value; 58 | var name = YAHOO.util.Dom.get('commentName').value; 59 | var email = YAHOO.util.Dom.get('commentEmail').value; 60 | var homepage = YAHOO.util.Dom.get('commentHomepage').value; 61 | var title = YAHOO.util.Dom.get('commentTitle').value; 62 | // Key needs to be transmitted because fragment doesn't seem to make 63 | // it through webob request object. 64 | var postData = 'key=' + encodeURIComponent(YAHOO.bloog.action) + '&' + 65 | 'captcha=' + encodeURIComponent(captcha) + '&' + 66 | 'name=' + encodeURIComponent(name) + '&' + 67 | 'email=' + encodeURIComponent(email) + '&' + 68 | 'homepage=' + encodeURIComponent(homepage) + '&' + 69 | 'title=' + encodeURIComponent(title) + '&' + 70 | 'body=' + encodeURIComponent(html); 71 | var cObj = YAHOO.util.Connect.asyncRequest( 72 | 'POST', 73 | YAHOO.bloog.action, 74 | { success: handleSuccess, 75 | failure: handleFailure }, 76 | postData); 77 | } 78 | 79 | YAHOO.bloog.commentDialog = new YAHOO.widget.Dialog( 80 | "commentDialog", { 81 | width: "520px", 82 | fixedcenter: true, 83 | visible: false, 84 | modal: true, 85 | constraintoviewpoint: true, 86 | buttons: [ { text: "Submit", handler: handleSubmit, 87 | isDefault:true }, 88 | { text: "Cancel", handler: YAHOO.bloog.handleCancel } ] 89 | }); 90 | YAHOO.bloog.commentDialog.validate = function () { 91 | var data = this.getData(); 92 | if (data.commentName == "") { 93 | alert("Please enter your name"); 94 | return false; 95 | } 96 | return true; 97 | } 98 | var handleDialogSuccess = function() { 99 | alert("We are having success from commentDialog"); 100 | } 101 | YAHOO.bloog.commentDialog.callback = { success: handleDialogSuccess, 102 | failure: YAHOO.bloog.handleFailure }; 103 | 104 | YAHOO.bloog.commentEditor = new YAHOO.widget.SimpleEditor( 105 | 'commentBody', { 106 | height: '150px', 107 | width: '500px', 108 | dompath: false, 109 | animate: true, 110 | toolbar: { 111 | titlebar: '', 112 | buttons: [ 113 | { group: 'fontstyle', label: 'Font Style', 114 | buttons: [ 115 | { type: 'push', label: 'Bold', value: 'bold' }, 116 | { type: 'push', label: 'Italic', value: 'italic' }, 117 | { type: 'push', label: 'Underline', value: 'underline' } 118 | ] 119 | }, 120 | { type: 'separator' }, 121 | { group: 'indentlist', label: 'Lists', 122 | buttons: [ 123 | { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' }, 124 | { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' } 125 | ] 126 | }, 127 | { type: 'separator' }, 128 | { group: 'insertitem', label: 'Insert Item', 129 | buttons: [ 130 | { type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink' }, 131 | { type: 'push', label: 'Insert Image', value: 'insertimage', disabled: true } 132 | ] 133 | } 134 | ] 135 | } 136 | }); 137 | YAHOO.bloog.commentEditor.render(); 138 | YAHOO.bloog.commentDialog.showEvent.subscribe(YAHOO.bloog.commentEditor.show, YAHOO.bloog.commentEditor, true); 139 | YAHOO.bloog.commentDialog.hideEvent.subscribe(YAHOO.bloog.commentEditor.hide, YAHOO.bloog.commentEditor, true); 140 | 141 | // Use event bubbling so we don't have to attach listeners to each reply 142 | Ojay('div#comments_wrapper').on('click', Ojay.delegateEvent({ 143 | 'a.replybtn': function(link, e) { 144 | e.stopDefault(); 145 | YAHOO.bloog.action = link.node.href; 146 | showRTE(); 147 | } 148 | })) 149 | } 150 | 151 | YAHOO.util.Event.onDOMReady(YAHOO.bloog.initComments); 152 | -------------------------------------------------------------------------------- /dev/scripts/clear_datastore.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: utf-8 3 | # 4 | # The MIT License 5 | # 6 | # Copyright (c) 2008 William T. Katz 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to 10 | # deal in the Software without restriction, including without limitation 11 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | # and/or sell copies of the Software, and to permit persons to whom the 13 | # Software is furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | # DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | # --- Significant portions of the code was taken from Google App Engine SDK 28 | # --- which is licensed under Apache 2.0 29 | # 30 | # 31 | # Copyright 2007 Google Inc. 32 | # 33 | # Licensed under the Apache License, Version 2.0 (the "License"); 34 | # you may not use this file except in compliance with the License. 35 | # You may obtain a copy of the License at 36 | # 37 | # http://www.apache.org/licenses/LICENSE-2.0 38 | # 39 | # Unless required by applicable law or agreed to in writing, software 40 | # distributed under the License is distributed on an "AS IS" BASIS, 41 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 42 | # See the License for the specific language governing permissions and 43 | # limitations under the License. 44 | # 45 | 46 | 47 | import sys 48 | import getopt 49 | 50 | import datetime 51 | import getpass 52 | import logging 53 | import mimetypes 54 | import os 55 | import re 56 | import sha 57 | import sys 58 | import time 59 | import httplib 60 | import urllib 61 | import urlparse 62 | import socket 63 | import string 64 | 65 | help_message = ''' 66 | First argument must be an authentication cookie that can be cut & pasted after 67 | logging in with a browser. Cookies can be easily viewed by using the Web 68 | Developer plugin with Firefox. 69 | 70 | For example, for uploading data into the local datastore, you'd do something 71 | like this: 72 | 73 | clear_datastore.py 'dev_appserver_login="root@example.com:True"' 74 | (or you could skip the first argument and use the -r or --root options) 75 | 76 | For uploading data into a Google AppEngine-hosted app, the cookie would begin 77 | with ACSID: 78 | 79 | clear_datastore.py 'ACSID=AJXUWfE-aefkae...' 80 | 81 | Options: 82 | -r, --root sets authorization cookie for local dev admin 83 | -l, --url = the url (web location) of the Bloog app 84 | ''' 85 | 86 | 87 | class Error(Exception): 88 | """Base-class for exceptions in this module.""" 89 | 90 | class UsageError(Error): 91 | def __init__(self, msg): 92 | self.msg = msg 93 | 94 | class HTTPConnectError(Error): 95 | """An error has occured while trying to connect to the Bloog app.""" 96 | 97 | class RequestError(Error): 98 | """An error occured while trying a HTTP request to the Bloog app.""" 99 | 100 | class UnsupportedSchemeError(Error): 101 | """Tried to access url with unsupported scheme (not http or https).""" 102 | 103 | class HttpRESTClient(object): 104 | 105 | @staticmethod 106 | def connect(scheme, netloc): 107 | if scheme == 'http': 108 | return httplib.HTTPConnection(netloc) 109 | if scheme == 'https': 110 | return httplib.HTTPSConnection(netloc) 111 | raise UnsupportedSchemeError() 112 | 113 | def __init__(self, auth_cookie): 114 | self.auth_cookie = auth_cookie 115 | 116 | def delete(self, url): 117 | headers = {'Cookie': self.auth_cookie} 118 | scheme, netloc, path, query, fragment = urlparse.urlsplit(url) 119 | success = False 120 | try: 121 | connection = HttpRESTClient.connect(scheme, netloc) 122 | try: 123 | connection.request('DELETE', path, '', headers) 124 | response = connection.getresponse() 125 | status = response.status 126 | reason = response.reason 127 | content = response.read() 128 | tuple_headers = response.getheaders() 129 | print('Received response code %d: %s\n%s' % \ 130 | (status, reason, content)) 131 | success = (status == httplib.OK) 132 | finally: 133 | connection.close() 134 | except (IOError, httplib.HTTPException, socket.error), e: 135 | print('Encountered exception accessing HTTP server: %s', e) 136 | raise HTTPConnectError(e) 137 | 138 | return success 139 | 140 | 141 | def main(argv): 142 | try: 143 | try: 144 | opts, args = getopt.gnu_getopt(argv, 'hrl:v', ["help", "url="]) 145 | except getopt.error, msg: 146 | raise UsageError(msg) 147 | 148 | app_url = 'http://localhost:8080' 149 | local_admin = '' 150 | for option, value in opts: 151 | print "Looking at option:", str(option), str(value) 152 | if option == "-v": 153 | verbose = True 154 | if option in ("-h", "--help"): 155 | raise UsageError(help_message) 156 | if option in ("-r", "--root"): 157 | local_admin = 'dev_appserver_login="root@example.com:True"' 158 | if option in ("-l", "--url"): 159 | print "Got url:", value 160 | app_url = value 161 | if app_url[:4] != 'http': 162 | app_url = 'http://' + app_url 163 | if app_url[-1] == '/': 164 | app_url = app_url[:-1] 165 | 166 | if len(args) < 2 and not local_admin: 167 | raise UsageError("Please specify the authentication cookie " 168 | "string as first argument.") 169 | else: 170 | auth_cookie = local_admin or args[1] 171 | 172 | #TODO - Use mechanize module to programmatically login 173 | #email = raw_input("E-mail: ") 174 | #passwd = getpass.getpass("Password: ") 175 | 176 | webserver = HttpRESTClient(auth_cookie) 177 | while webserver.delete(app_url + '/Article'): 178 | pass 179 | while webserver.delete(app_url + '/Comment'): 180 | pass 181 | while webserver.delete(app_url + '/Tag'): 182 | pass 183 | 184 | except UsageError, err: 185 | print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg) 186 | print >> sys.stderr, "\t for help use --help" 187 | return 2 188 | 189 | 190 | if __name__ == "__main__": 191 | sys.exit(main(sys.argv)) 192 | -------------------------------------------------------------------------------- /static/chili/mysql.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: "sql" 14 | , _case: false 15 | , _main: { 16 | mlcom: { 17 | _match: /\/\*[^*]*\*+([^\/][^*]*\*+)*\// 18 | , _style: "color: gray;" 19 | } 20 | , com: { 21 | _match: /(?:--\s+.*)|(?:[^\\]\#.*)/ 22 | , _style: "color: green;" 23 | } 24 | , string: { 25 | _match: /([\"\'])(?:(?:[^\1\\\r\n]*?(?:\1\1|\\.))*[^\1\\\r\n]*?)\1/ 26 | , _style: "color: purple;" 27 | } 28 | , quid: { 29 | _match: /(`)(?:(?:[^\1\\\r\n]*?(?:\1\1|\\.))*[^\1\\\r\n]*?)\1/ 30 | , _style: "color: fuchsia;" 31 | } 32 | , value: { 33 | _match: /\b(?:NULL|TRUE|FALSE)\b/ 34 | , _style: "color: gray; font-weight: bold;" 35 | } 36 | , number: { 37 | _match: /\b[+-]?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?\b/ 38 | , _style: "color: red;" 39 | } 40 | , hexnum: { 41 | _match: /\b0[xX][\dA-Fa-f]+\b|\b[xX]([\'\"])[\dA-Fa-f]+\1/ 42 | , _style: "color: red; font-weight: bold;" 43 | } 44 | , variable: { 45 | _match: /@([$.\w]+|([`\"\'])(?:(?:[^\2\\\r\n]*?(?:\2\2|\\.))*[^\2\\\r\n]*?)\2)/ 46 | , _replace: '@$1' 47 | , _style: "color: #4040c2;" 48 | } 49 | , keyword: { 50 | _match: /\b(?:A(?:CTION|DD|FTER|G(?:AINST|GREGATE)|L(?:GORITHM|L|TER)|N(?:ALYZE|D|Y)|S(?:C(?:II|)|ENSITIVE|)|UTO_INCREMENT|VG(?:_ROW_LENGTH|))|B(?:ACKUP|DB|E(?:FORE|GIN|RKELEYDB|TWEEN)|I(?:GINT|N(?:ARY|LOG)|T)|LOB|O(?:OL(?:EAN|)|TH)|TREE|Y(?:TE|))|C(?:A(?:CHE|LL|S(?:CADE(?:D|)|E))|H(?:A(?:IN|NGE(?:D|)|R(?:ACTER|SET|))|ECK(?:SUM|))|IPHER|L(?:IENT|OSE)|O(?:DE|L(?:LAT(?:E|ION)|UMN(?:S|))|M(?:M(?:ENT|IT(?:TED|))|P(?:ACT|RESSED))|N(?:CURRENT|DITION|NECTION|S(?:ISTENT|TRAINT)|T(?:AINS|INUE)|VERT))|R(?:EATE|OSS)|U(?:BE|R(?:RENT_(?:DATE|TIME(?:STAMP|)|USER)|SOR)))|D(?:A(?:T(?:A(?:BASE(?:S|)|)|E(?:TIME|))|Y(?:_(?:HOUR|MI(?:CROSECOND|NUTE)|SECOND)|))|E(?:ALLOCATE|C(?:IMAL|LARE|)|F(?:AULT|INER)|L(?:AY(?:ED|_KEY_WRITE)|ETE)|S(?:C(?:RIBE|)|_KEY_FILE)|TERMINISTIC)|I(?:RECTORY|S(?:ABLE|CARD|TINCT(?:ROW|))|V)|O(?:UBLE|)|ROP|U(?:AL|MPFILE|PLICATE)|YNAMIC)|E(?:ACH|LSE(?:IF|)|N(?:ABLE|CLOSED|D|GINE(?:S|)|UM)|RRORS|SCAPE(?:D|)|VENTS|X(?:ECUTE|I(?:STS|T)|P(?:ANSION|LAIN)|TENDED))|F(?:A(?:LSE|ST)|ETCH|I(?:ELDS|LE|RST|XED)|L(?:OAT(?:4|8|)|USH)|O(?:R(?:CE|EIGN|)|UND)|R(?:AC_SECOND|OM)|U(?:LL(?:TEXT|)|NCTION))|G(?:E(?:OMETRY(?:COLLECTION|)|T_FORMAT)|LOBAL|R(?:ANT(?:S|)|OUP))|H(?:A(?:NDLER|SH|VING)|ELP|IGH_PRIORITY|O(?:STS|UR(?:_(?:MI(?:CROSECOND|NUTE)|SECOND)|)))|I(?:DENTIFIED|F|GNORE|MPORT|N(?:DEX(?:ES|)|FILE|N(?:ER|O(?:BASE|DB))|OUT|SE(?:NSITIVE|RT(?:_METHOD|))|T(?:1|2|3|4|8|E(?:GER|RVAL)|O|)|VOKER|)|O_THREAD|S(?:OLATION|SUER|)|TERATE)|JOIN|K(?:EY(?:S|)|ILL)|L(?:A(?:NGUAGE|ST)|E(?:A(?:DING|VE(?:S|))|FT|VEL)|I(?:KE|MIT|NES(?:TRING|))|O(?:AD|C(?:AL(?:TIME(?:STAMP|)|)|K(?:S|))|GS|NG(?:BLOB|TEXT|)|OP|W_PRIORITY))|M(?:A(?:STER(?:_(?:CONNECT_RETRY|HOST|LOG_(?:FILE|POS)|P(?:ASSWORD|ORT)|S(?:ERVER_ID|SL(?:_(?:C(?:A(?:PATH|)|ERT|IPHER)|KEY)|))|USER)|)|TCH|X_(?:CONNECTIONS_PER_HOUR|QUERIES_PER_HOUR|ROWS|U(?:PDATES_PER_HOUR|SER_CONNECTIONS)))|E(?:DIUM(?:BLOB|INT|TEXT|)|RGE)|I(?:CROSECOND|DDLEINT|GRATE|N(?:UTE(?:_(?:MICROSECOND|SECOND)|)|_ROWS))|O(?:D(?:E|IF(?:IES|Y)|)|NTH)|U(?:LTI(?:LINESTRING|PO(?:INT|LYGON))|TEX))|N(?:A(?:ME(?:S|)|T(?:IONAL|URAL))|CHAR|DB(?:CLUSTER|)|E(?:W|XT)|O(?:NE|T|_WRITE_TO_BINLOG|)|U(?:LL|MERIC)|VARCHAR)|O(?:FFSET|LD_PASSWORD|N(?:E(?:_SHOT|)|)|P(?:EN|TI(?:MIZE|ON(?:ALLY|)))|R(?:DER|)|UT(?:ER|FILE|))|P(?:A(?:CK_KEYS|RTIAL|SSWORD)|HASE|O(?:INT|LYGON)|R(?:E(?:CISION|PARE|V)|I(?:MARY|VILEGES)|OCE(?:DURE|SS(?:LIST|)))|URGE)|QU(?:ARTER|ERY|ICK)|R(?:AID(?:0|_(?:CHUNKS(?:IZE|)|TYPE))|E(?:A(?:D(?:S|)|L)|COVER|DUNDANT|FERENCES|GEXP|L(?:AY_(?:LOG_(?:FILE|POS)|THREAD)|EASE|OAD)|NAME|P(?:AIR|EAT(?:ABLE|)|L(?:ACE|ICATION))|QUIRE|S(?:ET|T(?:ORE|RICT)|UME)|TURN(?:S|)|VOKE)|IGHT|LIKE|O(?:LL(?:BACK|UP)|UTINE|W(?:S|_FORMAT|))|TREE)|S(?:AVEPOINT|CHEMA(?:S|)|E(?:C(?:OND(?:_MICROSECOND|)|URITY)|LECT|NSITIVE|PARATOR|RIAL(?:IZABLE|)|SSION|T)|H(?:ARE|OW|UTDOWN)|I(?:GNED|MPLE)|LAVE|MALLINT|NAPSHOT|O(?:ME|NAME|UNDS)|P(?:ATIAL|ECIFIC)|QL(?:EXCEPTION|STATE|WARNING|_(?:B(?:IG_RESULT|UFFER_RESULT)|CA(?:CHE|LC_FOUND_ROWS)|NO_CACHE|SMALL_RESULT|T(?:HREAD|SI_(?:DAY|FRAC_SECOND|HOUR|M(?:INUTE|ONTH)|QUARTER|SECOND|WEEK|YEAR)))|)|SL|T(?:A(?:RT(?:ING|)|TUS)|O(?:P|RAGE)|R(?:AIGHT_JOIN|I(?:NG|PED)))|U(?:BJECT|PER|SPEND))|T(?:ABLE(?:S(?:PACE|)|)|E(?:MP(?:ORARY|TABLE)|RMINATED|XT)|HEN|I(?:ME(?:STAMP(?:ADD|DIFF|)|)|NY(?:BLOB|INT|TEXT))|O|R(?:A(?:ILING|NSACTION)|IGGER(?:S|)|U(?:E|NCATE))|YPE(?:S|))|U(?:N(?:COMMITTED|D(?:EFINED|O)|I(?:CODE|ON|QUE)|KNOWN|LOCK|SIGNED|TIL)|P(?:DATE|GRADE)|S(?:AGE|E(?:R(?:_RESOURCES|)|_FRM|)|ING)|TC_(?:DATE|TIME(?:STAMP|)))|V(?:A(?:LUE(?:S|)|R(?:BINARY|CHAR(?:ACTER|)|IABLES|YING))|IEW)|W(?:ARNINGS|EEK|H(?:E(?:N|RE)|ILE)|ITH|ORK|RITE)|X(?:509|A|OR)|YEAR(?:_MONTH|)|ZEROFILL)\b/ 51 | , _style: "color: navy; font-weight: bold;" 52 | } 53 | , 'function': { 54 | _match: /\b(?:A(?:BS|COS|DD(?:DATE|TIME)|ES_(?:DECRYPT|ENCRYPT)|REA|S(?:BINARY|IN|TEXT|WK(?:B|T))|TAN(?:2|))|B(?:ENCHMARK|I(?:N|T_(?:AND|COUNT|LENGTH|OR|XOR)))|C(?:AST|E(?:IL(?:ING|)|NTROID)|HAR(?:ACTER_LENGTH|_LENGTH)|O(?:ALESCE|ERCIBILITY|MPRESS|N(?:CAT(?:_WS|)|NECTION_ID|V(?:ERT_TZ|))|S|T|UNT)|R(?:C32|OSSES)|UR(?:DATE|TIME))|D(?:A(?:TE(?:DIFF|_(?:ADD|FORMAT|SUB))|Y(?:NAME|OF(?:MONTH|WEEK|YEAR)))|E(?:CODE|GREES|S_(?:DECRYPT|ENCRYPT))|I(?:MENSION|SJOINT))|E(?:LT|N(?:C(?:ODE|RYPT)|DPOINT|VELOPE)|QUALS|X(?:P(?:ORT_SET|)|T(?:ERIORRING|RACT)))|F(?:I(?:ELD|ND_IN_SET)|LOOR|O(?:RMAT|UND_ROWS)|ROM_(?:DAYS|UNIXTIME))|G(?:E(?:OM(?:COLLFROM(?:TEXT|WKB)|ETRY(?:COLLECTIONFROM(?:TEXT|WKB)|FROM(?:TEXT|WKB)|N|TYPE)|FROM(?:TEXT|WKB))|T_LOCK)|LENGTH|R(?:EATEST|OUP_(?:CONCAT|UNIQUE_USERS)))|HEX|I(?:FNULL|N(?:ET_(?:ATON|NTOA)|STR|TER(?:IORRINGN|SECTS))|S(?:CLOSED|EMPTY|NULL|SIMPLE|_(?:FREE_LOCK|USED_LOCK)))|L(?:AST_(?:DAY|INSERT_ID)|CASE|E(?:AST|NGTH)|INE(?:FROM(?:TEXT|WKB)|STRINGFROM(?:TEXT|WKB))|N|O(?:AD_FILE|CATE|G(?:10|2|)|WER)|PAD|TRIM)|M(?:A(?:KE(?:DATE|TIME|_SET)|STER_POS_WAIT|X)|BR(?:CONTAINS|DISJOINT|EQUAL|INTERSECTS|OVERLAPS|TOUCHES|WITHIN)|D5|I(?:D|N)|LINEFROM(?:TEXT|WKB)|ONTHNAME|PO(?:INTFROM(?:TEXT|WKB)|LYFROM(?:TEXT|WKB))|ULTI(?:LINESTRINGFROM(?:TEXT|WKB)|PO(?:INTFROM(?:TEXT|WKB)|LYGONFROM(?:TEXT|WKB))))|N(?:AME_CONST|OW|U(?:LLIF|M(?:GEOMETRIES|INTERIORRINGS|POINTS)))|O(?:CT(?:ET_LENGTH|)|RD|VERLAPS)|P(?:ERIOD_(?:ADD|DIFF)|I|O(?:INT(?:FROM(?:TEXT|WKB)|N)|LY(?:FROM(?:TEXT|WKB)|GONFROM(?:TEXT|WKB))|SITION|W(?:ER|)))|QUOTE|R(?:A(?:DIANS|ND)|E(?:LEASE_LOCK|VERSE)|O(?:UND|W_COUNT)|PAD|TRIM)|S(?:E(?:C_TO_TIME|SSION_USER)|HA(?:1|)|I(?:GN|N)|LEEP|OUNDEX|PACE|QRT|RID|T(?:ARTPOINT|D(?:DEV(?:_(?:POP|SAMP)|)|)|R(?:CMP|_TO_DATE))|U(?:B(?:DATE|STR(?:ING(?:_INDEX|)|)|TIME)|M)|YS(?:DATE|TEM_USER))|T(?:AN|IME(?:DIFF|_(?:FORMAT|TO_SEC))|O(?:UCHES|_DAYS)|RIM)|U(?:CASE|N(?:COMPRESS(?:ED_LENGTH|)|HEX|I(?:QUE_USERS|X_TIMESTAMP))|PPER|UID)|V(?:AR(?:IANCE|_(?:POP|SAMP))|ERSION)|W(?:EEK(?:DAY|OFYEAR)|ITHIN)|X|Y(?:EARWEEK|))(?=\()/ 55 | , _style: "color: #e17100;" 56 | } 57 | , id: { 58 | _match: /[$\w]+/ 59 | , _style: "color: maroon;" 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /static/chili/php.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | /* ---------------------------------------------------------------------------- 13 | * this recipe uses a little trick for highlighting php code 14 | * 1: replace each php snippet with a placeholder 15 | * 2: highlight html without php and php snippets apart 16 | * 3: replace each placeholder with its highlighted php snippet 17 | * 18 | * the trick is not perfect only if the html without php is broken 19 | * however, in such a case many highlighters get fooled but Chili does not 20 | * 21 | * --- 22 | * this recipe has been adapted for working with Safari 23 | * in fact, Safari cannot match more than 101236 characters with a lazy star 24 | * --------------------------------------------------------------------------*/ 25 | { 26 | _name: "php" 27 | , _case: true 28 | , _main: { 29 | all: { 30 | _match: /[\w\W]*/ 31 | , _replace: function( all ) { 32 | var placeholder = String.fromCharCode(0); 33 | var blocks = []; 34 | var that = this; 35 | var no_php_1 = all.replace( /<\?[^?]*\?+(?:[^>][^?]*\?+)*>/g, function( block ) { 36 | blocks.push( that.x( block, '/block/php_1' ) ); 37 | return placeholder; 38 | } ); 39 | var no_php_2 = no_php_1.replace( /^[^?]*\?+(?:[^>][^?]*\?+)*>|<\?[\w\W]*$/g, function( block ) { 40 | blocks.push( that.x( block, '/block/php_2' ) ); 41 | return placeholder; 42 | } ); 43 | if( blocks.length ) { 44 | var html = this.x( no_php_2, 'html' ); 45 | var count = 0; 46 | return html.replace( new RegExp( placeholder, "g" ), function() { 47 | return blocks[ count++ ]; 48 | } ); 49 | } 50 | else { 51 | return this.x( all, '/php' ); 52 | } 53 | } 54 | } 55 | } 56 | , block: { 57 | php_1: { // --- --- 58 | _match: /(<\?(?:php\b)?)([^?]*\?+(?:[^>][^?]*\?+)*>)/ 59 | , _replace: function( all, open, content ) { 60 | return "" + this.x( open ) + "" 61 | + this.x( content.replace( /\?>$/, '' ), '/php' ) 62 | + "" + this.x( '?>' ) + ""; 63 | } 64 | , _style: { 65 | start: "color: red; font-weight: bold" 66 | , end: "color: red;" 67 | } 68 | } 69 | , php_2: { // +++ ?> --- ][^?]*\?+)*>)|(<\?(?:php\b)?)([\w\W]*)/ 71 | , _replace: function( all, content, open2, content2 ) { 72 | if( open2 ) { 73 | return "" + this.x( open2 ) + "" 74 | + this.x( content2, '/php' ); 75 | } 76 | else { 77 | return this.x( content.replace( /\?>$/, '' ), '/php' ) 78 | + "" + this.x( '?>' ) + ""; 79 | } 80 | } 81 | , _style: { 82 | start: "color: red; font-weight: bold" 83 | , end: "color: red;" 84 | } 85 | } 86 | } 87 | , php: { 88 | mlcom: { 89 | _match: /\/\*[^*]*\*+([^\/][^*]*\*+)*\// 90 | , _style: "color: gray;" 91 | } 92 | , com: { 93 | _match: /(?:\/\/.*)|(?:[^\\]\#.*)/ 94 | , _style: "color: green;" 95 | } 96 | , string1: { 97 | _match: /\'[^\'\\]*(?:\\.[^\'\\]*)*\'/ 98 | , _style: "color: purple;" 99 | } 100 | , string2: { 101 | _match: /\"[^\"\\]*(?:\\.[^\"\\]*)*\"/ 102 | , _style: "color: fuchsia;" 103 | } 104 | , value: { 105 | _match: /\b(?:[Nn][Uu][Ll][Ll]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])\b/ 106 | , _style: "color: gray; font-weight: bold;" 107 | } 108 | , number: { 109 | _match: /\b[+-]?(\d*\.?\d+|\d+\.?\d*)([eE][+-]?\d+)?\b/ 110 | , _style: "color: red;" 111 | } 112 | , const1: { 113 | _match: /\b(?:DEFAULT_INCLUDE_PATH|E_(?:ALL|CO(?:MPILE_(?:ERROR|WARNING)|RE_(?:ERROR|WARNING))|ERROR|NOTICE|PARSE|STRICT|USER_(?:ERROR|NOTICE|WARNING)|WARNING)|P(?:EAR_(?:EXTENSION_DIR|INSTALL_DIR)|HP_(?:BINDIR|CONFIG_FILE_(?:PATH|SCAN_DIR)|DATADIR|E(?:OL|XTENSION_DIR)|INT_(?:MAX|SIZE)|L(?:IBDIR|OCALSTATEDIR)|O(?:S|UTPUT_HANDLER_(?:CONT|END|START))|PREFIX|S(?:API|HLIB_SUFFIX|YSCONFDIR)|VERSION))|__COMPILER_HALT_OFFSET__)\b/ 114 | , _style: "color: red;" 115 | } 116 | , const2: { 117 | _match: /\b(?:A(?:B(?:DAY_(?:1|2|3|4|5|6|7)|MON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9))|LT_DIGITS|M_STR|SSERT_(?:ACTIVE|BAIL|CALLBACK|QUIET_EVAL|WARNING))|C(?:ASE_(?:LOWER|UPPER)|HAR_MAX|O(?:DESET|NNECTION_(?:ABORTED|NORMAL|TIMEOUT)|UNT_(?:NORMAL|RECURSIVE))|R(?:EDITS_(?:ALL|DOCS|FULLPAGE|G(?:ENERAL|ROUP)|MODULES|QA|SAPI)|NCYSTR|YPT_(?:BLOWFISH|EXT_DES|MD5|S(?:ALT_LENGTH|TD_DES)))|URRENCY_SYMBOL)|D(?:AY_(?:1|2|3|4|5|6|7)|ECIMAL_POINT|IRECTORY_SEPARATOR|_(?:FMT|T_FMT))|E(?:NT_(?:COMPAT|NOQUOTES|QUOTES)|RA(?:_(?:D_(?:FMT|T_FMT)|T_FMT|YEAR)|)|XTR_(?:IF_EXISTS|OVERWRITE|PREFIX_(?:ALL|I(?:F_EXISTS|NVALID)|SAME)|SKIP))|FRAC_DIGITS|GROUPING|HTML_(?:ENTITIES|SPECIALCHARS)|IN(?:FO_(?:ALL|C(?:ONFIGURATION|REDITS)|ENVIRONMENT|GENERAL|LICENSE|MODULES|VARIABLES)|I_(?:ALL|PERDIR|SYSTEM|USER)|T_(?:CURR_SYMBOL|FRAC_DIGITS))|L(?:C_(?:ALL|C(?:OLLATE|TYPE)|M(?:ESSAGES|ONETARY)|NUMERIC|TIME)|O(?:CK_(?:EX|NB|SH|UN)|G_(?:A(?:LERT|UTH(?:PRIV|))|C(?:ONS|R(?:IT|ON))|D(?:AEMON|EBUG)|E(?:MERG|RR)|INFO|KERN|L(?:OCAL(?:0|1|2|3|4|5|6|7)|PR)|MAIL|N(?:DELAY|EWS|O(?:TICE|WAIT))|ODELAY|P(?:ERROR|ID)|SYSLOG|U(?:SER|UCP)|WARNING)))|M(?:ON_(?:1(?:0|1|2|)|2|3|4|5|6|7|8|9|DECIMAL_POINT|GROUPING|THOUSANDS_SEP)|_(?:1_PI|2_(?:PI|SQRTPI)|E|L(?:N(?:10|2)|OG(?:10E|2E))|PI(?:_(?:2|4)|)|SQRT(?:1_2|2)))|N(?:EGATIVE_SIGN|O(?:EXPR|STR)|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|P(?:ATH(?:INFO_(?:BASENAME|DIRNAME|EXTENSION)|_SEPARATOR)|M_STR|OSITIVE_SIGN|_(?:CS_PRECEDES|S(?:EP_BY_SPACE|IGN_POSN)))|RADIXCHAR|S(?:EEK_(?:CUR|END|SET)|ORT_(?:ASC|DESC|NUMERIC|REGULAR|STRING)|TR_PAD_(?:BOTH|LEFT|RIGHT))|T(?:HOUS(?:ANDS_SEP|EP)|_FMT(?:_AMPM|))|YES(?:EXPR|STR))\b/ 118 | , _style: "color: red;" 119 | } 120 | , global: { 121 | _match: /(?:\$GLOBALS|\$_COOKIE|\$_ENV|\$_FILES|\$_GET|\$_POST|\$_REQUEST|\$_SERVER|\$_SESSION|\$php_errormsg)\b/ 122 | , _style: "color: red;" 123 | } 124 | , keyword: { 125 | _match: /\b(?:__CLASS__|__FILE__|__FUNCTION__|__LINE__|__METHOD__|abstract|and|array|as|break|case|catch|cfunction|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exception|exit|extends|extends|final|for|foreach|function|global|if|implements|include|include_once|interface|isset|list|new|old_function|or|php_user_filter|print|private|protected|public|require|require_once|return|static|switch|this|throw|try|unset|use|var|while|xor)\b/ 126 | , _style: "color: navy; font-weight: bold;" 127 | } 128 | , variable: { 129 | _match: /\$(\w+)/ 130 | , _replace: '$$1' 131 | , _style: "color: #4040c2;" 132 | } 133 | , heredoc: { 134 | _match: /(\<\<\<\s*)(\w+)((?:(?!\2).*\n)+)(\2)\b/ 135 | , _replace: '$1$2$3$4' 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /utils/template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2007 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | 18 | """A simple wrapper for Django templates. 19 | 20 | Note: This code is slightly altered from google.appengine.ext.webapp. 21 | Changes by Bill Katz on original: 22 | - Allow setting of template directory hierarchy in render() and load() 23 | 24 | The main purpose of this module is to hide all of the package import pain 25 | you normally have to go through to get Django to work. We expose the Django 26 | Template and Context classes from this module, handling the import nonsense 27 | on behalf of clients. 28 | 29 | Typical usage: 30 | 31 | from google.appengine.ext.webapp import template 32 | print template.render('templates/index.html', {'foo': 'bar'}) 33 | 34 | Django uses a global setting for the directory in which it looks for templates. 35 | This is not natural in the context of the webapp module, so our load method 36 | takes in a complete template path, and we set these settings on the fly 37 | automatically. Because we have to set and use a global setting on every 38 | method call, this module is not thread safe, though that is not an issue 39 | for applications. 40 | 41 | Django template documentation is available at: 42 | http://www.djangoproject.com/documentation/templates/ 43 | """ 44 | 45 | 46 | import logging 47 | import os 48 | 49 | try: 50 | from django import v0_96 51 | except ImportError: 52 | pass 53 | import django 54 | 55 | import django.conf 56 | try: 57 | django.conf.settings.configure( 58 | DEBUG=False, 59 | TEMPLATE_DEBUG=False, 60 | TEMPLATE_LOADERS=( 61 | 'django.template.loaders.filesystem.load_template_source', 62 | ), 63 | ) 64 | except (EnvironmentError, RuntimeError): 65 | pass 66 | import django.template 67 | import django.template.loader 68 | 69 | from google.appengine.ext import webapp 70 | 71 | def render(template_path, template_dict, debug=False, template_dirs=()): 72 | """Renders the template at the given path with the given dict of values. 73 | 74 | Example usage: 75 | render("templates/index.html", {"name": "Bret", "values": [1, 2, 3]}) 76 | 77 | Args: 78 | template_path: path to a Django template 79 | template_dict: dictionary of values to apply to the template 80 | """ 81 | t = load(template_path, debug, template_dirs) 82 | return t.render(Context(template_dict)) 83 | 84 | 85 | template_cache = {} 86 | def load(path, debug=False, template_dirs=()): 87 | """Loads the Django template from the given path. 88 | 89 | It is better to use this function than to construct a Template using the 90 | class below because Django requires you to load the template with a method 91 | if you want imports and extends to work in the template. 92 | """ 93 | abspath = os.path.abspath(path) 94 | 95 | if not debug: 96 | template = template_cache.get(abspath, None) 97 | else: 98 | template = None 99 | 100 | if not template: 101 | directory, file_name = os.path.split(abspath) 102 | if directory: 103 | template_dirs = [directory] + template_dirs 104 | new_settings = { 105 | 'TEMPLATE_DIRS': template_dirs, 106 | 'TEMPLATE_DEBUG': debug, 107 | 'DEBUG': debug, 108 | } 109 | old_settings = _swap_settings(new_settings) 110 | try: 111 | template = django.template.loader.get_template(file_name) 112 | finally: 113 | _swap_settings(old_settings) 114 | 115 | if not debug: 116 | template_cache[abspath] = template 117 | 118 | def wrap_render(context, orig_render=template.render): 119 | URLNode = django.template.defaulttags.URLNode 120 | save_urlnode_render = URLNode.render 121 | old_settings = _swap_settings(new_settings) 122 | try: 123 | URLNode.render = _urlnode_render_replacement 124 | return orig_render(context) 125 | finally: 126 | _swap_settings(old_settings) 127 | URLNode.render = save_urlnode_render 128 | 129 | template.render = wrap_render 130 | 131 | return template 132 | 133 | 134 | def _swap_settings(new): 135 | """Swap in selected Django settings, returning old settings. 136 | 137 | Example: 138 | save = _swap_settings({'X': 1, 'Y': 2}) 139 | try: 140 | ...new settings for X and Y are in effect here... 141 | finally: 142 | _swap_settings(save) 143 | 144 | Args: 145 | new: A dict containing settings to change; the keys should 146 | be setting names and the values settings values. 147 | 148 | Returns: 149 | Another dict structured the same was as the argument containing 150 | the original settings. Original settings that were not set at all 151 | are returned as None, and will be restored as None by the 152 | 'finally' clause in the example above. This shouldn't matter; we 153 | can't delete settings that are given as None, since None is also a 154 | legitimate value for some settings. Creating a separate flag value 155 | for 'unset' settings seems overkill as there is no known use case. 156 | """ 157 | settings = django.conf.settings 158 | old = {} 159 | for key, value in new.iteritems(): 160 | old[key] = getattr(settings, key, None) 161 | setattr(settings, key, value) 162 | return old 163 | 164 | 165 | def create_template_register(): 166 | """Used to extend the Django template library with custom filters and tags. 167 | 168 | To extend the template library with a custom filter module, create a Python 169 | module, and create a module-level variable named "register", and register 170 | all custom filters to it as described at 171 | http://www.djangoproject.com/documentation/templates_python/ 172 | #extending-the-template-system: 173 | 174 | templatefilters.py 175 | ================== 176 | register = webapp.template.create_template_register() 177 | 178 | def cut(value, arg): 179 | return value.replace(arg, '') 180 | register.filter(cut) 181 | 182 | Then, register the custom template module with the register_template_module 183 | function below in your application module: 184 | 185 | myapp.py 186 | ======== 187 | webapp.template.register_template_module('templatefilters') 188 | """ 189 | return django.template.Library() 190 | 191 | 192 | def register_template_library(package_name): 193 | """Registers a template extension module to make it usable in templates. 194 | 195 | See the documentation for create_template_register for more information.""" 196 | if not django.template.libraries.get(package_name, None): 197 | django.template.add_to_builtins(package_name) 198 | 199 | 200 | Template = django.template.Template 201 | Context = django.template.Context 202 | 203 | 204 | def _urlnode_render_replacement(self, context): 205 | """Replacement for django's {% url %} block. 206 | 207 | This version uses WSGIApplication's url mapping to create urls. 208 | 209 | Examples: 210 | 211 | 212 | {% url MyPageHandler implicit_args=False %} 213 | {% url MyPageHandler "calendar" %} 214 | {% url MyPageHandler "jsmith","calendar" %} 215 | """ 216 | args = [arg.resolve(context) for arg in self.args] 217 | try: 218 | app = webapp.WSGIApplication.active_instance 219 | handler = app.get_registered_handler_by_name(self.view_name) 220 | return handler.get_url(implicit_args=True, *args) 221 | except webapp.NoUrlFoundError: 222 | return '' 223 | -------------------------------------------------------------------------------- /static/chili/css.js: -------------------------------------------------------------------------------- 1 | /* 2 | =============================================================================== 3 | Chili is the jQuery code highlighter plugin 4 | ............................................................................... 5 | LICENSE: http://www.opensource.org/licenses/mit-license.php 6 | WEBSITE: http://noteslog.com/chili/ 7 | 8 | Copyright 2008 / Andrea Ercolino 9 | =============================================================================== 10 | */ 11 | 12 | { 13 | _name: 'css' 14 | , _case: true 15 | , _main: { 16 | comment: { 17 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 18 | , _style: "color: olive;" 19 | } 20 | , directive: { 21 | _match: /@\w+/ 22 | , _style: "color: fuchsia;" 23 | } 24 | , url: { 25 | _match: /\b(url\s*\()([^)]+)(\))/ 26 | , _replace: "$1$2$3" 27 | , _style: "color: fuchsia;" 28 | } 29 | , block: { 30 | _match: /\{([\w\W]*?)\}/ 31 | , _replace: function( all, pairs ) { 32 | return '{' + this.x( pairs, '/definition' ) + '}'; 33 | } 34 | } 35 | , 'class': { 36 | _match: /\.\w+/ 37 | , _style: "color: #CC0066; font-weight: bold;" 38 | } 39 | , id: { 40 | _match: /#\w+/ 41 | , _style: "color: IndianRed; font-weight: bold;" 42 | } 43 | , pseudo: { 44 | _match: /:\w+/ 45 | , _style: "color: #CC9900;" 46 | } 47 | , element: { 48 | _match: /\w+/ 49 | , _style: "color: Purple; font-weight: bold;" 50 | } 51 | } 52 | , definition: { 53 | comment: { 54 | _match: /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\// 55 | } 56 | , property: { 57 | _match: /\b(?:zoom|z-index|writing-mode|word-wrap|word-spacing|word-break|width|widows|white-space|volume|voice-family|visibility|vertical-align|unicode-bidi|top|text-underline-position|text-transform|text-shadow|text-overflow|text-kashida-space|text-justify|text-indent|text-decoration|text-autospace|text-align-last|text-align|table-layout|stress|speech-rate|speak-punctuation|speak-numeral|speak-header|speak|size|scrollbar-track-color|scrollbar-shadow-color|scrollbar-highlight-color|scrollbar-face-color|scrollbar-dark-shadow-color|scrollbar-base-color|scrollbar-arrow-color|scrollbar-3d-light-color|ruby-position|ruby-overhang|ruby-align|right|richness|quotes|position|play-during|pitch-range|pitch|pause-before|pause-after|pause|page-break-inside|page-break-before|page-break-after|page|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-Y|overflow-X|overflow|outline-width|outline-style|outline-color|outline|orphans|min-width|min-height|max-width|max-height|marks|marker-offset|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|line-break|letter-spacing|left|layout-grid-type|layout-grid-mode|layout-grid-line|layout-grid-char-spacing|layout-grid-char|layout-grid|layout-flow|layer-background-image|layer-background-color|include-source|ime-mode|height|font-weight|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-family|font|float|filter|empty-cells|elevation|display|direction|cursor|cue-before|cue-after|cue|counter-reset|counter-increment|content|color|clip|clear|caption-side|bottom|border-width|border-top-width|border-top-style|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-left-width|border-left-style|border-left-color|border-left|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-color|border-bottom|border|behavior|background-repeat|background-position-y|background-position-x|background-position|background-image|background-color|background-attachment|background|azimuth|accelerator)\s*:/ 58 | , _style: "color: #330066;" 59 | } 60 | , special: { 61 | _match: /\b(?:-use-link-source|-set-link-source|-replace|-moz-user-select|-moz-user-modify|-moz-user-input|-moz-user-focus|-moz-outline-width|-moz-outline-style|-moz-outline-color|-moz-outline|-moz-opacity|-moz-border-top-colors|-moz-border-right-colors|-moz-border-radius-topright|-moz-border-radius-topleft|-moz-border-radius-bottomright|-moz-border-radius-bottomleft|-moz-border-radius|-moz-border-left-colors|-moz-border-bottom-colors|-moz-binding)\s*:/ 62 | , _style: "color: #330066; text-decoration: underline;" 63 | } 64 | , url: { 65 | _match: /\b(url\s*\()([^)]+)(\))/ 66 | , _replace: "$1$2$3" 67 | } 68 | , value: { 69 | _match: /\b(?:xx-small|xx-large|x-soft|x-small|x-slow|x-low|x-loud|x-large|x-high|x-fast|wider|wait|w-resize|visible|url|uppercase|upper-roman|upper-latin|upper-alpha|underline|ultra-expanded|ultra-condensed|tv|tty|transparent|top|thin|thick|text-top|text-bottom|table-row-group|table-row|table-header-group|table-footer-group|table-column-group|table-column|table-cell|table-caption|sw-resize|super|sub|status-bar|static|square|spell-out|speech|solid|soft|smaller|small-caption|small-caps|small|slower|slow|silent|show|separate|semi-expanded|semi-condensed|se-resize|scroll|screen|s-resize|run-in|rtl|rightwards|right-side|right|ridge|rgb|repeat-y|repeat-x|repeat|relative|projection|print|pre|portrait|pointer|overline|outside|outset|open-quote|once|oblique|nw-resize|nowrap|normal|none|no-repeat|no-open-quote|no-close-quote|ne-resize|narrower|n-resize|move|mix|middle|message-box|medium|marker|ltr|lowercase|lower-roman|lower-latin|lower-greek|lower-alpha|lower|low|loud|local|list-item|line-through|lighter|level|leftwards|left-side|left|larger|large|landscape|justify|italic|invert|inside|inset|inline-table|inline|icon|higher|high|hide|hidden|help|hebrew|handheld|groove|format|fixed|faster|fast|far-right|far-left|fantasy|extra-expanded|extra-condensed|expanded|embossed|embed|e-resize|double|dotted|disc|digits|default|decimal-leading-zero|decimal|dashed|cursive|crosshair|cross|crop|counters|counter|continuous|condensed|compact|collapse|code|close-quote|circle|center-right|center-left|center|caption|capitalize|braille|bottom|both|bolder|bold|block|blink|bidi-override|below|behind|baseline|avoid|auto|aural|attr|armenian|always|all|absolute|above)\b/ 70 | , _style: "color: #3366FF;" 71 | } 72 | , string: { 73 | _match: /(?:\'[^\'\\\n]*(?:\\.[^\'\\\n]*)*\')|(?:\"[^\"\\\n]*(?:\\.[^\"\\\n]*)*\")/ 74 | , _style: "color: teal;" 75 | } 76 | , number: { 77 | _match: /(?:\b[+-]?(?:\d*\.?\d+|\d+\.?\d*))(?:%|(?:(?:px|pt|em|)\b))/ 78 | , _style: "color: red;" 79 | } 80 | , color : { 81 | _match: /(?:\#[a-fA-F0-9]{3,6})|\b(?:yellow|white|teal|silver|red|purple|olive|navy|maroon|lime|green|gray|fuchsia|blue|black|aqua|YellowGreen|Yellow|WhiteSmoke|White|Wheat|Violet|Turquoise|Tomato|Thistle|Teal|Tan|SteelBlue|SpringGreen|Snow|SlateGrey|SlateGray|SlateBlue|SkyBlue|Silver|Sienna|SeaShell|SeaGreen|SandyBrown|Salmon|SaddleBrown|RoyalBlue|RosyBrown|Red|Purple|PowderBlue|Plum|Pink|Peru|PeachPuff|PapayaWhip|PaleVioletRed|PaleTurquoise|PaleGreen|PaleGoldenRod|Orchid|OrangeRed|Orange|OliveDrab|Olive|OldLace|Navy|NavajoWhite|Moccasin|MistyRose|MintCream|MidnightBlue|MediumVioletRed|MediumTurquoise|MediumSpringGreen|MediumSlateBlue|MediumSeaGreen|MediumPurple|MediumOrchid|MediumBlue|MediumAquaMarine|Maroon|Magenta|Linen|LimeGreen|Lime|LightYellow|LightSteelBlue|LightSlateGrey|LightSlateGray|LightSkyBlue|LightSeaGreen|LightSalmon|LightPink|LightGrey|LightGreen|LightGray|LightGoldenRodYellow|LightCyan|LightCoral|LightBlue|LemonChiffon|LawnGreen|LavenderBlush|Lavender|Khaki|Ivory|Indigo|IndianRed|HotPink|HoneyDew|Grey|GreenYellow|Green|Gray|GoldenRod|Gold|GhostWhite|Gainsboro|Fuchsia|ForestGreen|FloralWhite|FireBrick|DodgerBlue|DimGrey|DimGray|DeepSkyBlue|DeepPink|Darkorange|DarkViolet|DarkTurquoise|DarkSlateGrey|DarkSlateGray|DarkSlateBlue|DarkSeaGreen|DarkSalmon|DarkRed|DarkOrchid|DarkOliveGreen|DarkMagenta|DarkKhaki|DarkGrey|DarkGreen|DarkGray|DarkGoldenRod|DarkCyan|DarkBlue|Cyan|Crimson|Cornsilk|CornflowerBlue|Coral|Chocolate|Chartreuse|CadetBlue|BurlyWood|Brown|BlueViolet|Blue|BlanchedAlmond|Black|Bisque|Beige|Azure|Aquamarine|Aqua|AntiqueWhite|AliceBlue)\b/ 82 | , _style: "color: green;" 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /models/blog.py: -------------------------------------------------------------------------------- 1 | # The MIT License 2 | # 3 | # Copyright (c) 2008 William T. Katz 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation 8 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | # and/or sell copies of the Software, and to permit persons to whom the 10 | # Software is furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | 23 | import logging 24 | 25 | from google.appengine.api import memcache 26 | from google.appengine.ext import db 27 | 28 | import config 29 | import models 30 | from models import search 31 | 32 | # Handle generation of thread strings 33 | def get_thread_string(article, cur_thread_string): 34 | min_str = cur_thread_string + '000' 35 | max_str = cur_thread_string + '999' 36 | q = db.GqlQuery("SELECT * FROM Comment " + 37 | "WHERE article = :1 " + 38 | "AND thread >= :2 AND thread <= :3", 39 | article, min_str, max_str) 40 | num_comments = q.count(999) 41 | if num_comments > 998: 42 | return None # Only allow 999 comments on each tree level 43 | return cur_thread_string + "%03d" % (num_comments + 1) 44 | 45 | class Article(search.SearchableModel): 46 | unsearchable_properties = ['permalink', 'legacy_id', 'article_type', 47 | 'excerpt', 'html', 'format', 'tag_keys'] 48 | json_does_not_include = ['assoc_dict'] 49 | 50 | permalink = db.StringProperty(required=True) 51 | # Useful for aliasing of old urls 52 | legacy_id = db.StringProperty() 53 | title = db.StringProperty(required=True) 54 | article_type = db.StringProperty(required=True, 55 | choices=set(["article", "blog entry"])) 56 | # Body can be in any format supported by Bloog (e.g. textile) 57 | body = db.TextProperty(required=True) 58 | # If available, we use 'excerpt' to summarize instead of 59 | # extracting the first 68 words of 'body'. 60 | excerpt = db.TextProperty() 61 | # The html property is generated from body 62 | html = db.TextProperty() 63 | published = db.DateTimeProperty(auto_now_add=True) 64 | updated = db.DateTimeProperty(auto_now_add=True) 65 | format = db.StringProperty(required=True, 66 | choices=set(["html", "textile", 67 | "markdown", "text"])) 68 | # Picked dict for sidelinks, associated Amazon items, etc. 69 | assoc_dict = db.BlobProperty() 70 | # To prevent full query when just showing article headlines 71 | num_comments = db.IntegerProperty(default=0) 72 | # Use keys instead of db.Category for consolidation of tag names 73 | tags = db.StringListProperty(default=[]) 74 | tag_keys = db.ListProperty(db.Key, default=[]) 75 | two_columns = db.BooleanProperty() 76 | allow_comments = db.BooleanProperty() 77 | # A list of languages for code embedded in article. 78 | # This lets us choose the proper javascript for pretty viewing. 79 | embedded_code = db.StringListProperty() 80 | 81 | def get_comments(self): 82 | """Return comments lexicographically sorted on thread string""" 83 | q = db.GqlQuery("SELECT * FROM Comment " + 84 | "WHERE article = :1 " + 85 | "ORDER BY thread ASC", self.key()) 86 | return [comment for comment in q] 87 | comments = property(get_comments) # No set for now 88 | 89 | def set_associated_data(self, data): 90 | """ 91 | Serialize data that we'd like to store with this article. 92 | Examples include relevant (per article) links and associated 93 | Amazon items. 94 | """ 95 | import pickle 96 | self.assoc_dict = pickle.dumps(data) 97 | 98 | def get_associated_data(self): 99 | import pickle 100 | return pickle.loads(self.assoc_dict) 101 | 102 | def full_permalink(self): 103 | return config.BLOG['root_url'] + '/' + self.permalink 104 | 105 | def rfc3339_published(self): 106 | return self.published.strftime('%Y-%m-%dT%H:%M:%SZ') 107 | 108 | def rfc3339_updated(self): 109 | return self.updated.strftime('%Y-%m-%dT%H:%M:%SZ') 110 | 111 | def is_big(self): 112 | guess_chars = len(self.html) + self.num_comments * 80 113 | if guess_chars > 2000 or \ 114 | self.embedded_code or \ 115 | '' in self.html or \ 117 | '
    ' in self.html:
    118 |             return True
    119 |         else:
    120 |             return False
    121 | 
    122 |     def next_comment_thread_string(self):
    123 |         'Returns thread string for next comment for this article'
    124 |         return get_thread_string(self, '')
    125 | 
    126 |     def to_atom_xml(self):
    127 |         """Returns a string suitable for inclusion in Atom XML feed
    128 |         
    129 |         Internal html property should already have XHTML entities
    130 |         converted into unicode.  However, ampersands are valid ASCII
    131 |         and will cause issues with XML, so reconvert ampersands to
    132 |         valid XML entities &
    133 |         """
    134 |         import re
    135 |         return re.sub('&(?!amp;)', '&', self.html)
    136 | 
    137 | class Comment(models.SerializableModel):
    138 |     """Stores comments and their position in comment threads.
    139 | 
    140 |     Thread string describes the tree using 3 digit numbers.
    141 |     This allows lexicographical sorting to order comments
    142 |     and easy indentation computation based on the string depth.
    143 |     Example for comments that are nested except first response:
    144 |     001
    145 |       001.001
    146 |       001.002
    147 |         001.002.001
    148 |           001.002.001.001
    149 |     NOTE: This means we assume less than 999 comments in
    150 |       response to a parent comment, and we won't have
    151 |       nesting that causes our thread string > 500 bytes.
    152 |       TODO -- Put in error checks
    153 |     """
    154 |     name = db.StringProperty()
    155 |     email = db.EmailProperty()
    156 |     homepage = db.StringProperty()
    157 |     title = db.StringProperty()
    158 |     body = db.TextProperty(required=True)
    159 |     published = db.DateTimeProperty(auto_now_add=True)
    160 |     article = db.ReferenceProperty(Article)
    161 |     thread = db.StringProperty(required=True)
    162 | 
    163 |     def get_indentation(self):
    164 |         # Indentation is based on degree of nesting in "thread"
    165 |         nesting_str_array = self.thread.split('.')
    166 |         return min([len(nesting_str_array), 10])
    167 | 
    168 |     def next_child_thread_string(self):
    169 |         'Returns thread string for next child of this comment'
    170 |         return get_thread_string(self.article, self.thread + '.')
    171 | 
    172 | 
    173 | class Tag(models.MemcachedModel):
    174 |     # Inserts these values into aggregate list returned by Tag.list()
    175 |     list_includes = ['counter.count', 'name']
    176 | 
    177 |     def delete(self):
    178 |         self.delete_counter()
    179 |         super(Tag, self).delete()
    180 | 
    181 |     def get_counter(self):
    182 |         counter = models.Counter('Tag' + self.name)
    183 |         return counter
    184 | 
    185 |     def set_counter(self, value):
    186 |         # Not implemented at this time
    187 |         pass
    188 | 
    189 |     def delete_counter(self):
    190 |         models.Counter('Tag' + self.name).delete()
    191 | 
    192 |     counter = property(get_counter, set_counter, delete_counter)
    193 | 
    194 |     def get_name(self):
    195 |         return self.key().name()
    196 |     name = property(get_name)
    197 |     
    
    
    --------------------------------------------------------------------------------
    /views/default/bloog/base.html:
    --------------------------------------------------------------------------------
      1 | 
      2 | 
      3 | 	
      4 | 		
      5 | 		 {{ title }}
      6 | 		
      7 | 		
      8 | 		
      9 | 		
     10 | 		
     11 | 		{% block head %}
     12 | 		{% endblock %}
     13 | 	
     14 | 	
     15 | 		
    16 | 17 |
    18 |

    {{ blog.title }}

    19 |
    20 | 21 |

    22 | {{ blog.description }} 23 |

    24 |
    25 |
    26 | 54 | 61 | 62 |
    63 | 64 | {% block first_column %} 65 |
    66 | {% for article in articles %} 67 | {% include 'article_excerpt.html' %} 68 | {% endfor %} 69 | {% if not articles %} 70 | {% include 'bloog_intro.html' %} 71 | {% endif %} 72 | {% include 'pager.html' %} 73 |
    74 | {% endblock %} 75 | 76 | 77 | {% block second_column %} 78 |
    79 | {% block search %} 80 |
    81 |
    82 | 83 | 84 |
    85 |
    86 | {% endblock %} 87 | 88 | {% block tags %} 89 | 104 | {% endblock %} 105 | 106 | {% block extra_panel %} 107 | {% endblock %} 108 | 109 | {% if user_is_admin %} 110 | 111 | 122 | {% endif %} 123 | 124 | {% block featuredPages1 %} 125 | 139 | {% endblock %} 140 | 141 | {% block featuredPages2 %} 142 | 156 | {% endblock %} 157 | 158 | {% block subscribe %} 159 | 170 | {% endblock %} 171 | {% block books %} 172 | 191 | {% endblock %} 192 |
    193 | {% endblock %} 194 |
    195 | 196 | 260 |
    261 | 262 | 263 | 264 | 267 | 268 | 269 | 270 | 271 | {% if user_is_admin %} 272 | {% include "form_editor.html" %} 273 | {% endif %} 274 | 275 | {% block end_body %} 276 | {% endblock %} 277 | 278 | 279 | --------------------------------------------------------------------------------